Libc Protections
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)
Malloc asigna memoria en grupos de 8 bytes (32 bits) o 16 bytes (64 bits). Esto significa que el final de los chunks en sistemas de 32 bits debe alinearse con 0x8, y en sistemas de 64 bits con 0x0. La característica de seguridad verifica que cada chunk se alinee correctamente en estas ubicaciones específicas antes de usar un puntero de un bin.
La aplicación de la alineación de chunks en sistemas de 64 bits mejora significativamente la seguridad de Malloc al limitar la ubicación de chunks falsos a solo 1 de cada 16 direcciones. Esto complica los esfuerzos de explotación, especialmente en escenarios donde el usuario tiene un control limitado sobre los valores de entrada, haciendo que los ataques sean más complejos y difíciles de ejecutar con éxito.
Ataque Fastbin en __malloc_hook
Las nuevas reglas de alineación en Malloc también frustran un ataque clásico que implica el __malloc_hook
. Anteriormente, los atacantes podían manipular tamaños de chunks para sobrescribir este puntero de función y obtener ejecución de código. Ahora, el estricto requisito de alineación garantiza que tales manipulaciones ya no sean viables, cerrando una ruta común de explotación y mejorando la seguridad general.
El Enmascaramiento de Punteros es una mejora de seguridad utilizada para proteger los punteros Fd de fastbin y tcache en operaciones de gestión de memoria. Esta técnica ayuda a prevenir ciertos tipos de tácticas de explotación de memoria, específicamente aquellas que no requieren información de memoria filtrada o que manipulan ubicaciones de memoria directamente en relación con posiciones conocidas (sobrescrituras relativas).
La base de esta técnica es una fórmula de ofuscación:
Nuevo_Ptr = (L >> 12) XOR P
L es la Ubicación de Almacenamiento del puntero.
P es el Puntero Fd de fastbin/tcache real.
La razón por la cual el desplazamiento de bits de la ubicación de almacenamiento (L) por 12 bits hacia la derecha antes de la operación XOR es crítica. Esta manipulación aborda una vulnerabilidad inherente en la naturaleza determinista de los 12 bits menos significativos de las direcciones de memoria, que suelen ser predecibles debido a las limitaciones de la arquitectura del sistema. Al desplazar los bits, la parte predecible se elimina de la ecuación, mejorando la aleatoriedad del nuevo puntero enmascarado y protegiendo así contra exploits que dependen de la previsibilidad de estos bits.
Este puntero enmascarado aprovecha la aleatoriedad existente proporcionada por la Aleatorización del Diseño del Espacio de Direcciones (ASLR), que aleatoriza las direcciones utilizadas por los programas para dificultar que los atacantes predigan el diseño de memoria de un proceso.
Desenmascarar el puntero para recuperar la dirección original implica usar la misma operación XOR. Aquí, el puntero enmascarado se trata como P en la fórmula, y al hacer XOR con la ubicación de almacenamiento sin cambios (L), se revela el puntero original. Esta simetría en el enmascaramiento y desenmascaramiento garantiza que el sistema pueda codificar y decodificar eficientemente punteros sin una sobrecarga significativa, al tiempo que aumenta sustancialmente la seguridad contra ataques que manipulan punteros de memoria.
El enmascaramiento de punteros tiene como objetivo prevenir sobrescrituras parciales y completas de punteros en el heap, una mejora significativa en seguridad. Esta característica impacta las técnicas de explotación de varias maneras:
Prevención de Sobrescrituras Relativas de Bye Byte: Anteriormente, los atacantes podían cambiar parte de un puntero para redirigir chunks del heap a ubicaciones diferentes sin conocer direcciones exactas, una técnica evidente en el exploit House of Roman sin filtraciones. Con el enmascaramiento de punteros, tales sobrescrituras relativas sin una filtración de heap ahora requieren fuerza bruta, reduciendo drásticamente su probabilidad de éxito.
Mayor Dificultad en Ataques a Tcache Bin/Fastbin: Los ataques comunes que sobrescriben punteros de función (como __malloc_hook
) manipulando entradas de fastbin o tcache se ven obstaculizados. Por ejemplo, un ataque podría implicar filtrar una dirección de LibC, liberar un chunk en el tcache bin y luego sobrescribir el puntero Fd para redirigirlo a __malloc_hook
para ejecución de código arbitrario. Con el enmascaramiento de punteros, estos punteros deben enmascararse correctamente, necesitando una filtración de heap para una manipulación precisa, elevando así la barrera de explotación.
Requisito de Filtraciones de Heap en Ubicaciones No-Heap: Crear un chunk falso en áreas no-heap (como la pila, sección .bss o PLT/GOT) ahora también requiere una filtración de heap debido a la necesidad de enmascaramiento de punteros. Esto extiende la complejidad de explotar estas áreas, similar al requisito de manipulación de direcciones de LibC.
Hacer Filtraciones de Direcciones de Heap Más Desafiantes: El enmascaramiento de punteros restringe la utilidad de los punteros Fd en fastbin y tcache bins como fuentes de filtraciones de direcciones de heap. Sin embargo, los punteros en bins no ordenados, pequeños y grandes permanecen sin enmascarar, aún siendo utilizables para filtrar direcciones. Este cambio empuja a los atacantes a explorar estos bins en busca de información explotable, aunque algunas técnicas aún pueden permitir desenmascarar punteros antes de una filtración, aunque con limitaciones.
Para una mejor explicación del proceso consulta la publicación original desde aquí.
La fórmula utilizada para enmascarar y desenmascarar punteros es:
Nuevo_Ptr = (L >> 12) XOR P
Donde L es la ubicación de almacenamiento y P es el puntero Fd. Cuando L se desplaza hacia la derecha por 12 bits, expone los bits más significativos de P, debido a la naturaleza de XOR, que produce 0 cuando los bits se XOR con ellos mismos.
Pasos Clave en el Algoritmo:
Filtración Inicial de los Bits Más Significativos: Al hacer XOR entre el L desplazado y P, obtienes efectivamente los 12 bits superiores de P porque la parte desplazada de L será cero, dejando los bits correspondientes de P sin cambios.
Recuperación de Bits del Puntero: Dado que XOR es reversible, conocer el resultado y uno de los operandos te permite calcular el otro operando. Esta propiedad se utiliza para deducir el conjunto completo de bits para P al hacer XOR sucesivamente con conjuntos conocidos de bits con partes del puntero enmascarado.
Desenmascaramiento Iterativo: El proceso se repite, cada vez utilizando los bits de P recién descubiertos del paso anterior para decodificar el siguiente segmento del puntero enmascarado, hasta que se recuperan todos los bits.
Manejo de Bits Deterministas: Los últimos 12 bits de L se pierden debido al desplazamiento, pero son deterministas y pueden reconstruirse después del proceso.
Puedes encontrar una implementación de este algoritmo aquí: https://github.com/mdulin2/mangle
Pointer guard es una técnica de mitigación de exploits utilizada en glibc para proteger los punteros de funciones almacenados, especialmente aquellos registrados por llamadas de biblioteca como atexit()
. Esta protección implica mezclar los punteros mediante XOR con un secreto almacenado en los datos del hilo (fs:0x30
) y aplicando una rotación de bits. Este mecanismo tiene como objetivo evitar que los atacantes secuestren el flujo de control sobrescribiendo punteros de funciones.
Entendiendo las Operaciones de Pointer Guard: El mezclado de punteros se realiza utilizando la macro PTR_MANGLE
que realiza XOR con el puntero con un secreto de 64 bits y luego realiza una rotación a la izquierda de 0x11 bits. La operación inversa para recuperar el puntero original es manejada por PTR_DEMANGLE
.
Estrategia de Ataque: El ataque se basa en un enfoque de texto plano conocido, donde el atacante necesita conocer tanto la versión original como la mezclada de un puntero para deducir el secreto utilizado para el mezclado.
Explotando Textos Planos Conocidos:
Identificando Punteros de Funciones Fijos: Al examinar el código fuente de glibc o tablas de punteros de funciones inicializadas (como __libc_pthread_functions
), un atacante puede encontrar punteros de funciones predecibles.
Calculando el Secreto: Utilizando un puntero de función conocido como __pthread_attr_destroy
y su versión mezclada de la tabla de punteros de funciones, el secreto se puede calcular mediante una rotación inversa (rotación a la derecha) del puntero mezclado y luego XOR con la dirección de la función.
Textos Planos Alternativos: El atacante también puede experimentar con la mezcla de punteros con valores conocidos como 0 o -1 para ver si estos producen patrones identificables en la memoria, revelando potencialmente el secreto cuando se encuentran estos patrones en volcados de memoria.
Aplicación Práctica: Después de calcular el secreto, un atacante puede manipular punteros de manera controlada, esencialmente burlando la protección de Pointer Guard en una aplicación multiproceso con conocimiento de la dirección base de libc y la capacidad de leer ubicaciones de memoria arbitrarias.