ORW+栈迁移SROP
[ NewStarCTF 2025 ]only_read
一个开启了沙箱的 SROP:
1 | ssize_t vuln() |
1 | __int64 sandbox() |
1 | __int64 gift() |
栈迁移:
1 | payload = b'\x00'*0x10 + p64(bss - 0x30) + p64(0x401349) |
- 目的: 将程序的栈指针 rsp 劫持到我们可以控制的 .bss 段。
- b’\x00’*0x10: 填充 16 字节填满当前局部变量空间。之后用 p64(bss - 0x30): 覆盖 saved rbp。
- p64(0x401349): 覆盖返回地址 ret addr。0x401349 应该是原程序中调用 read 函数的汇编地址。
- 函数返回执行 leave; ret 后,rbp 被修改为 bss - 0x30,并且程序再次执行 read。因为此时是在以 bss - 0x30 为基底运行,下一次 read 会直接把数据写到我们刚刚指定的 BSS 段中。
布置第一个 SROP (读取 ORW Payload)
1 | payload = p64(bss - 0x10)*3 + p64(gift) |
- 目的: 往新栈中注入一个 Sigreturn Frame,用来执行
read(0, bss - 0x8, 0x400),以便读入第三阶段超长的 ORW ROP 链。 - 执行流:
- 前面的
p64(bss - 0x10)*3是为了填充对齐,使程序正常滑行到 p64(gift)。 - gift 被执行,触发 sys_rt_sigreturn,CPU 会将后面 bytes(frame) 的数据当做上下文直接恢复到各个寄存器
- 恢复后,程序执行 syscall(因为 rip 被改成了 syscall_ret),此时相当于调用了 sys_read。
- **核心控制:**read 结束后,栈指针 rsp 会变成 bss。这意味着 read 结束后执行的 ret,会从 bss 地址取指令。
通过连续 SROP 执行 ORW
利用上一步的 read,利用不断修改 rsp 的方式,串联了 3 次 SROP 调用,完成 Open -> Read -> Write 的操作
步骤 A:Open(“./flag”)
1 | payload = p64(0x67616c662f2e) # "./flag" 字符串被写入 bss - 0x8 |
- 这一步打开了 flag 文件,正常情况下文件描述符(fd)会返回 3。
步骤 B:Read(3, bss - 0x100, 0x30)
1 | payload += p64(gift) # 位于 bss + 0x100,紧接上一个 SROP |
- 这一步从 fd=3 的文件中读取 0x30 个字节的 flag 内容,存放到 bss - 0x100 的位置。
步骤 C:Write(1, bss - 0x100, 0x30)
1 | payload += p64(gift) # 位于 bss + 0x200 |
- 最后一次 SROP,调用 sys_write,把刚才读到 bss - 0x100 的 flag 打印到屏幕上。
总 EXP:
1 | from pwn import * |
更新: 2026-04-12 20:10:38
原文: https://www.yuque.com/idcm/wnemg9/acxaw7fqoc9m14gf