Double Free

Support HackTricks

Información Básica

Si liberas un bloque de memoria más de una vez, puede desordenar los datos del asignador y abrir la puerta a ataques. Así es como sucede: cuando liberas un bloque de memoria, vuelve a una lista de fragmentos libres (por ejemplo, el "fast bin"). Si liberas el mismo bloque dos veces seguidas, el asignador detecta esto y lanza un error. Pero si liberas otro fragmento entre medio, la verificación de doble liberación se omite, causando corrupción.

Ahora, cuando pides nueva memoria (usando malloc), el asignador podría darte un bloque que ha sido liberado dos veces. Esto puede llevar a que dos punteros diferentes apunten a la misma ubicación de memoria. Si un atacante controla uno de esos punteros, puede cambiar el contenido de esa memoria, lo que puede causar problemas de seguridad o incluso permitirles ejecutar código.

Ejemplo:

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

En este ejemplo, después de llenar el tcache con varios chunks liberados (7), el código libera el chunk h, luego el chunk i, y luego h nuevamente, causando un doble free (también conocido como Fast Bin dup). Esto abre la posibilidad de recibir direcciones de memoria superpuestas al reallocar, lo que significa que dos o más punteros pueden apuntar a la misma ubicación de memoria. Manipular datos a través de un puntero puede afectar al otro, creando un riesgo crítico de seguridad y potencial de explotación.

Al ejecutarlo, nota cómo i1 e i2 obtuvieron la misma dirección:

Asignaciones iniciales:
a: 0xaaab0f0c22a0
b: 0xaaab0f0c22c0
c: 0xaaab0f0c22e0
d: 0xaaab0f0c2300
e: 0xaaab0f0c2320
f: 0xaaab0f0c2340
g: 0xaaab0f0c2360
h: 0xaaab0f0c2380
i: 0xaaab0f0c23a0
Después de las reallocaciones:
a1: 0xaaab0f0c2360
b1: 0xaaab0f0c2340
c1: 0xaaab0f0c2320
d1: 0xaaab0f0c2300
e1: 0xaaab0f0c22e0
f1: 0xaaab0f0c22c0
g1: 0xaaab0f0c22a0
h1: 0xaaab0f0c2380
i1: 0xaaab0f0c23a0
i2: 0xaaab0f0c23a0

Ejemplos

  • Solo podemos asignar chunks del tamaño de Fast-Bin excepto para el tamaño 0x70, lo que impide la habitual sobrescritura de __malloc_hook.

  • En su lugar, usamos direcciones PIE que comienzan con 0x56 como objetivo para Fast Bin dup (1/2 de probabilidad).

  • Un lugar donde se almacenan las direcciones PIE es en main_arena, que está dentro de Glibc y cerca de __malloc_hook.

  • Apuntamos a un desplazamiento específico de main_arena para asignar un chunk allí y continuar asignando chunks hasta alcanzar __malloc_hook para obtener ejecución de código.

  • Usando bins de Tcache y un desbordamiento de byte nulo, podemos lograr una situación de doble free:

  • Asignamos tres chunks de tamaño 0x110 (A, B, C)

  • Liberamos B

  • Liberamos A y asignamos nuevamente para usar el desbordamiento de byte nulo

  • Ahora el campo de tamaño de B es 0x100, en lugar de 0x111, por lo que podemos liberarlo nuevamente

  • Tenemos un Tcache-bin de tamaño 0x110 y uno de tamaño 0x100 que apuntan a la misma dirección. Así que tenemos un doble free.

  • Aprovechamos el doble free usando Tcache poisoning

Referencias

Apoya a HackTricks

Last updated