Page cover

Heap - Fastbin Dup

Introduction

Fastbin duplication is a classic heap exploitation technique that leverages from the way glibc's malloc implementation manages small memory chunks. This post demonstrates the attack step-by-step using pwndbg to visualize heap internals.

Vulnerable Code

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
 
// compile with
// gcc -no-pie -Wl,-z,norelro -z now -ggdb test.c -o test
// Get this code from https://www.bordergate.co.uk/heap-fastbin-exploitation/
struct test {
   char test[30];
} tests;
 
struct user {
   char  username[16];
   char  target[16];
} users;
 
int main () {
                        
strcpy( users.target, "TARGET");
 
char *m_array [8];
 
m_array[0] = (char *)0x0;
m_array[1] = (char *)0x0;
m_array[2] = (char *)0x0;
m_array[3] = (char *)0x0;
m_array[4] = (char *)0x0;
m_array[5] = (char *)0x0;
m_array[6] = (char *)0x0;
m_array[7] = (char *)0x0;
 
setvbuf(stdout,(char *)0x0,2,0);
 
printf("Enter username: ");
 
read(STDIN_FILENO, users.username, 0x10);
 
  int i;
  int ChunkNumber = 0;
  for (i = 1; i < 20; ++i)
  {
      int selection;
      printf( "Target : %s\n", users.target);
      printf("Next chunk number: %d/7 \n", ChunkNumber);
 
     
      printf("1) malloc\n");
      printf("2) free\n");
      printf("3) quit\n");
      printf(">");
      scanf("%d", &selection);
 
      fflush (stdin);
  switch(selection){
 
    int mallocSize;
    char inputData[256];
     
    case 1:
      printf ("malloc size: \n");
      scanf("%d", &mallocSize);
 
      printf ("input data: \n");
      scanf("%s", inputData);
 
      // Allocate heap memory chunk. Size based on previous user input
      char *heapChunk;
      m_array[ChunkNumber] = (char *) malloc(mallocSize);
      strcpy(m_array[ChunkNumber],inputData);
 
      printf("chunk allocated: %d/7 \n", ChunkNumber);
 
      ChunkNumber++;
      break;
    case 2:
      printf("Select chunk to free: ");
      scanf("%d", &selection);
      printf("Freeing chunk: %d\n", selection);
      free(m_array[selection]);
      break;
    case 3:
        exit(0);
      break;
    default:
      printf("Invalid selection\n");
      break;
  }
 
  }
    
  return(0);
}

Target Code Analysis

Our target is to overwrite users.target (initially "Target") with arbitrary data, ultimately achieving code execution.

Vulnerability

The code has several critical vulnerabilities:

  1. No bounds checking on malloc size.

  2. Use-after-free-chunks can be freed but pointers remain in m_array

  3. Double-free - same chunk can be freed multiple times.

  4. Heap overflow - strcpy has no size validation.

Understanding Fastbins

What are Fastbins?

Fastbins are singly-linked lists that cache small freed chunks (16-80 bytes on x64) for quick reallocation. Key characteristics:

  • LIFO structure (Last In, First Out)

  • No coalescing with adjacent chunks

  • Fast allocation/deallocation

  • Single-linked list using fd (forward) pointer

Fastbin Structure

Exploitation Steps

Step 1: Initial Setup

Start the program and enter username:

Step 2: Allocate Chunks in Fastbin Range

Allocate three chunks of size 0x18 bytes (fastbin size):

pwndbg inspection:

Step 3: Create Fastbin Dup

Free chunk 0, then chunk 1, then chunk 0 again:

pwndbg after double-free:

Step 4: Arbitrary Write Setup

Now we have chunk 0 appearing twice in the fastbin. Next allocations will return:

  1. First malloc → Chunk 0

  2. Second malloc → Chunk 1

  3. Third malloc → Chunk 0 again!

Allocate chunk 0 and overwrite its fd pointer with target address:

Step 5: Overwrite Target

pwndbg verification:

Code Execution

Final exploit script:

This exploit performs a fastbin dup attack to overwrite __malloc_hook with a one_gadget address, giving you a shell when malloc is called.

Detailed Breakdown

1. Chunk Size: 0x68 (104 bytes)

Why 0x68?

  • User requests 0x68 (104) bytes

  • Malloc adds 0x10 (16) bytes of metadata (prev_size + size)

  • Total chunk size = 0x68 + 0x10 = 0x78 (120 bytes)

  • This falls into the 0x70 fastbin (fastbin sizes: 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80)

We chose this size strategically to:

  1. Stay in fastbin range (faster, less security checks)

  2. Match a fake chunk size near __malloc_hook

2. Offset: -35 bytes

Finding the offset:

The value 0x7f (127 in decimal) appears naturally in libc memory, and it matches fastbin size 0x70!

Calculation:

  • __malloc_hook is at offset +0x10 from chunk start (after metadata)

  • We need to point to where size field (0x7f) appears

  • If 0x7f is at __malloc_hook - 0x23 (35 bytes before)

  • Then our fake chunk starts at __malloc_hook - 0x23 - 0x10 = __malloc_hook - 0x33

But since we target the fd pointer location (which is at chunk + 0x10), we use __malloc_hook - 35

3. Payload Size: 0x13 (19 bytes)

Math:

  • __malloc_hook is at offset 0x23 (35 bytes) from fake chunk start

  • User data starts at offset 0x10 (16 bytes)

  • Distance = 0x23 - 0x10 = 0x13 (19 bytes)

  • So we need 19 bytes of junk, then our one_gadget address

4. One_gadget: 0xe1fa1

What is this?

This is a one_gadget RCE - a single gadget in libc that executes execve("/bin/sh", NULL, NULL) when called.

How to find it:

The exploit uses 0xe1fa1 because it has constraints that are satisfied when malloc is called.

And with this we get a shell:

pwndbg Commands Cheat Sheet

Conclusion

Fastbin dup exploitation demonstrates the importance of proper memory management. While modern mitigations make these attacks harder, understanding the fundamentals helps in both offensive security and defensive programming.

References

Last updated