Double Free

Support HackTricks

Basic Information

Se si libera un blocco di memoria più di una volta, può danneggiare i dati dell'allocatore e aprire la porta ad attacchi. Ecco come avviene: quando si libera un blocco di memoria, torna in un elenco di chunk liberi (ad esempio, il "fast bin"). Se si libera lo stesso blocco due volte di seguito, l'allocatore lo rileva e genera un errore. Ma se si libera un altro chunk nel mezzo, il controllo del double-free viene bypassato, causando corruzione.

Ora, quando si richiede nuova memoria (utilizzando malloc), l'allocatore potrebbe fornire un blocco che è stato liberato due volte. Questo può portare a due puntatori diversi che puntano alla stessa posizione di memoria. Se un attaccante controlla uno di quei puntatori, può modificare il contenuto di quella memoria, il che può causare problemi di sicurezza o addirittura consentire loro di eseguire codice.

Example:

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

int main() {
// Allocate memory for three chunks
char *a = (char *)malloc(10);
char *b = (char *)malloc(10);
char *c = (char *)malloc(10);
char *d = (char *)malloc(10);
char *e = (char *)malloc(10);
char *f = (char *)malloc(10);
char *g = (char *)malloc(10);
char *h = (char *)malloc(10);
char *i = (char *)malloc(10);

// Print initial memory addresses
printf("Initial allocations:\n");
printf("a: %p\n", (void *)a);
printf("b: %p\n", (void *)b);
printf("c: %p\n", (void *)c);
printf("d: %p\n", (void *)d);
printf("e: %p\n", (void *)e);
printf("f: %p\n", (void *)f);
printf("g: %p\n", (void *)g);
printf("h: %p\n", (void *)h);
printf("i: %p\n", (void *)i);

// Fill tcache
free(a);
free(b);
free(c);
free(d);
free(e);
free(f);
free(g);

// Introduce double-free vulnerability in fast bin
free(h);
free(i);
free(h);


// Reallocate memory and print the addresses
char *a1 = (char *)malloc(10);
char *b1 = (char *)malloc(10);
char *c1 = (char *)malloc(10);
char *d1 = (char *)malloc(10);
char *e1 = (char *)malloc(10);
char *f1 = (char *)malloc(10);
char *g1 = (char *)malloc(10);
char *h1 = (char *)malloc(10);
char *i1 = (char *)malloc(10);
char *i2 = (char *)malloc(10);

// Print initial memory addresses
printf("After reallocations:\n");
printf("a1: %p\n", (void *)a1);
printf("b1: %p\n", (void *)b1);
printf("c1: %p\n", (void *)c1);
printf("d1: %p\n", (void *)d1);
printf("e1: %p\n", (void *)e1);
printf("f1: %p\n", (void *)f1);
printf("g1: %p\n", (void *)g1);
printf("h1: %p\n", (void *)h1);
printf("i1: %p\n", (void *)i1);
printf("i2: %p\n", (void *)i2);

return 0;
}

In questo esempio, dopo aver riempito il tcache con diversi chunk liberati (7), il codice libera il chunk h, poi il chunk i, e poi h di nuovo, causando un double free (noto anche come Fast Bin dup). Questo apre la possibilità di ricevere indirizzi di memoria sovrapposti durante la riallocazione, il che significa che due o più puntatori possono puntare alla stessa posizione di memoria. Manipolare i dati attraverso un puntatore può quindi influenzare l'altro, creando un rischio critico per la sicurezza e potenziale per sfruttamento.

Eseguendolo, nota come i1 e i2 abbiano ottenuto lo stesso indirizzo:

Allocazioni iniziali:
a: 0xaaab0f0c22a0
b: 0xaaab0f0c22c0
c: 0xaaab0f0c22e0
d: 0xaaab0f0c2300
e: 0xaaab0f0c2320
f: 0xaaab0f0c2340
g: 0xaaab0f0c2360
h: 0xaaab0f0c2380
i: 0xaaab0f0c23a0
Dopo le riallocazioni:
a1: 0xaaab0f0c2360
b1: 0xaaab0f0c2340
c1: 0xaaab0f0c2320
d1: 0xaaab0f0c2300
e1: 0xaaab0f0c22e0
f1: 0xaaab0f0c22c0
g1: 0xaaab0f0c22a0
h1: 0xaaab0f0c2380
i1: 0xaaab0f0c23a0
i2: 0xaaab0f0c23a0

Esempi

  • Possiamo allocare solo chunk di dimensioni Fast-Bin tranne per la dimensione 0x70, che impedisce la solita sovrascrittura di __malloc_hook.

  • Invece, utilizziamo indirizzi PIE che iniziano con 0x56 come obiettivo per Fast Bin dup (1/2 possibilità).

  • Un luogo in cui sono memorizzati gli indirizzi PIE è in main_arena, che si trova all'interno di Glibc e vicino a __malloc_hook.

  • Miriamo a un offset specifico di main_arena per allocare un chunk lì e continuare ad allocare chunk fino a raggiungere __malloc_hook per ottenere l'esecuzione del codice.

  • Utilizzando i bin Tcache e un overflow di byte nullo, possiamo ottenere una situazione di double-free:

  • Allocchiamo tre chunk di dimensione 0x110 (A, B, C)

  • Liberiamo B

  • Liberiamo A e allocchiamo di nuovo per utilizzare l'overflow di byte nullo

  • Ora il campo di dimensione di B è 0x100, invece di 0x111, quindi possiamo liberarlo di nuovo

  • Abbiamo un Tcache-bin di dimensione 0x110 e uno di dimensione 0x100 che puntano allo stesso indirizzo. Quindi abbiamo un double free.

  • Sfruttiamo il double free utilizzando Tcache poisoning

Riferimenti

Supporta HackTricks

Last updated