Off by one overflow
Last updated
Last updated
Aprende y practica Hacking en AWS:HackTricks Training AWS Red Team Expert (ARTE) Aprende y practica Hacking en GCP: HackTricks Training GCP Red Team Expert (GRTE)
Tener acceso a un desbordamiento de 1B permite a un atacante modificar el campo size
del siguiente chunk. Esto permite manipular qué chunks están realmente liberados, potencialmente generando un chunk que contiene otro chunk legítimo. La explotación es similar a double free o chunks superpuestos.
Hay 2 tipos de vulnerabilidades de desbordamiento por uno:
Byte arbitrario: Este tipo permite sobrescribir ese byte con cualquier valor.
Byte nulo (off-by-null): Este tipo permite sobrescribir ese byte solo con 0x00.
Un ejemplo común de esta vulnerabilidad se puede ver en el siguiente código donde el comportamiento de strlen
y strcpy
es inconsistente, lo que permite establecer un byte 0x00 al principio del siguiente chunk.
Esto puede ser explotado con la House of Einherjar.
Si se usa Tcache, esto puede aprovecharse para una situación de double free.
Entre otras verificaciones, ahora cada vez que un bloque se libera, el tamaño anterior se compara con el tamaño configurado en el bloque de metadatos, lo que hace que este ataque sea bastante complejo a partir de la versión 2.28.
Este ataque ya no funciona debido al uso de Tcaches.
Además, si intentas abusar de él usando bloques más grandes (para que no se involucren los tcaches), recibirás el error: malloc(): invalid next size (unsorted)
Hacer que un bloque esté contenido dentro de otro bloque, de modo que el acceso de escritura sobre ese segundo bloque permita sobrescribir el contenido del primero.
Desbordamiento off by one para modificar la información de metadatos del tamaño.
Asignar tres bloques A
, B
y C
(digamos tamaños 0x20), y otro para evitar la consolidación con el bloque superior.
Liberar C
(insertado en la lista de bloques libres de 0x20 Tcache).
Usar el bloque A
para desbordar sobre B
. Abusar del off-by-one para modificar el campo size
de B
de 0x21 a 0x41.
Ahora tenemos B
conteniendo el bloque libre C
.
Liberar B
y asignar un bloque de 0x40 (se colocará aquí nuevamente).
Podemos modificar el puntero fd
de C
, que sigue estando libre (envenenamiento de Tcache).
Se reservan 3 bloques de memoria (a, b, c) uno tras otro. Luego se libera el del medio. El primero contiene una vulnerabilidad de desbordamiento off by one y el atacante abusa de ella con un 0x00 (si el byte anterior era 0x10, haría que el bloque del medio indicara que es 0x10 más pequeño de lo que realmente es).
Luego, se asignan 2 bloques más pequeños en el bloque liberado del medio (b), sin embargo, como b + b->size
nunca actualiza el bloque c porque la dirección apuntada es más pequeña de lo que debería.
Luego, b1 y c se liberan. Como c - c->prev_size
aún apunta a b (b1 ahora), ambos se consolidan en un solo bloque. Sin embargo, b2 sigue estando entre b1 y c.
Finalmente, se realiza un nuevo malloc reclamando esta área de memoria que en realidad contendrá b2, permitiendo al propietario del nuevo malloc controlar el contenido de b2.
Esta imagen explica perfectamente el ataque:
Off-by-one debido a que strlen
considera el campo size
del siguiente bloque.
Se está utilizando Tcache, por lo que un ataque general off-by-one funciona para obtener una primitiva de escritura arbitraria con envenenamiento de Tcache.
Es posible abusar de un off by one para filtrar una dirección del heap porque el byte 0x00 al final de una cadena está siendo sobrescrito por el siguiente campo.
La escritura arbitraria se obtiene abusando del off by one para hacer que el puntero apunte a otro lugar donde se construirá una estructura falsa con punteros falsos. Luego, es posible seguir el puntero de esta estructura para obtener escritura arbitraria.
La dirección de libc se filtra porque si el heap se extiende usando mmap, la memoria asignada por mmap tiene un desplazamiento fijo desde libc.
Finalmente, se abusa de la escritura arbitraria para escribir en la dirección de __free_hook con un one gadget.
Hay una vulnerabilidad off by one NULL en la función getline
que lee líneas de entrada del usuario. Esta función se utiliza para leer la "clave" del contenido y no el contenido.
En el informe se crean 5 bloques iniciales:
chunk1 (0x200)
chunk2 (0x50)
chunk5 (0x68)
chunk3 (0x1f8)
chunk4 (0xf0)
chunk defense (0x400) para evitar la consolidación con el bloque superior.
Luego, se liberan los bloques 1, 5 y 3, así que:
[ 0x200 Chunk 1 (free) ] [ 0x50 Chunk 2 ] [ 0x68 Chunk 5 (free) ] [ 0x1f8 Chunk 3 (free) ] [ 0xf0 Chunk 4 ] [ 0x400 Chunk defense ]
[ 0x200 Chunk 1 (free) ] [ 0x50 Chunk 2 ] [ 0x68 Chunk 5 (free) ] [ 0x1f8 Chunk 3 (free) ] [ 0xf0 Chunk 4 ] [ 0x400 Chunk defense ]