Stack Canaries
Last updated
Last updated
Learn & practice AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE) Learn & practice GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
StackGuard inserta un valor especial conocido como canary antes del EIP (Extended Instruction Pointer), específicamente 0x000aff0d
(representando null, newline, EOF, carriage return) para proteger contra desbordamientos de búfer. Sin embargo, funciones como recv()
, memcpy()
, read()
, y bcopy()
siguen siendo vulnerables, y no protege el EBP (Base Pointer).
StackShield adopta un enfoque más sofisticado que StackGuard al mantener un Global Return Stack, que almacena todas las direcciones de retorno (EIPs). Esta configuración asegura que cualquier desbordamiento no cause daño, ya que permite una comparación entre las direcciones de retorno almacenadas y las reales para detectar ocurrencias de desbordamiento. Además, StackShield puede verificar la dirección de retorno contra un valor límite para detectar si el EIP apunta fuera del espacio de datos esperado. Sin embargo, esta protección puede ser eludida a través de técnicas como Return-to-libc, ROP (Return-Oriented Programming), o ret2ret, lo que indica que StackShield tampoco protege las variables locales.
-fstack-protector
:Este mecanismo coloca un canary antes del EBP, y reorganiza las variables locales para posicionar los búferes en direcciones de memoria más altas, evitando que sobrescriban otras variables. También copia de manera segura los argumentos pasados en la pila por encima de las variables locales y utiliza estas copias como argumentos. Sin embargo, no protege los arreglos con menos de 8 elementos o los búferes dentro de la estructura de un usuario.
El canary es un número aleatorio derivado de /dev/urandom
o un valor predeterminado de 0xff0a0000
. Se almacena en TLS (Thread Local Storage), permitiendo que los espacios de memoria compartidos entre hilos tengan variables globales o estáticas específicas del hilo. Estas variables se copian inicialmente del proceso padre, y los procesos hijos pueden alterar sus datos sin afectar al padre o a los hermanos. Sin embargo, si se utiliza un fork()
sin crear un nuevo canary, todos los procesos (padre e hijos) comparten el mismo canary, lo que lo hace vulnerable. En la arquitectura i386, el canary se almacena en gs:0x14
, y en x86_64, en fs:0x28
.
Esta protección local identifica funciones con búferes vulnerables a ataques e inyecta código al inicio de estas funciones para colocar el canary, y al final para verificar su integridad.
Cuando un servidor web utiliza fork()
, permite un ataque de fuerza bruta para adivinar el byte del canary byte por byte. Sin embargo, usar execve()
después de fork()
sobrescribe el espacio de memoria, negando el ataque. vfork()
permite que el proceso hijo se ejecute sin duplicación hasta que intente escribir, momento en el cual se crea una duplicación, ofreciendo un enfoque diferente para la creación de procesos y el manejo de memoria.
En binarios x64
, el cookie del canary es un 0x8
byte qword. Los primeros siete bytes son aleatorios y el último byte es un byte nulo.
En binarios x86
, el cookie del canary es un 0x4
byte dword. Los primeros tres bytes son aleatorios y el último byte es un byte nulo.
El byte menos significativo de ambos canaries es un byte nulo porque será el primero en la pila proveniente de direcciones más bajas y, por lo tanto, las funciones que leen cadenas se detendrán antes de leerlo.
Filtrar el canary y luego sobrescribirlo (por ejemplo, desbordamiento de búfer) con su propio valor.
Si el canary se bifurca en procesos hijos, podría ser posible forzarlo byte por byte:
Si hay alguna filtración interesante o vulnerabilidad de lectura arbitraria en el binario, podría ser posible filtrarlo:
Sobrescribiendo punteros almacenados en la pila
La pila vulnerable a un desbordamiento de pila podría contener direcciones a cadenas o funciones que pueden ser sobrescritas para explotar la vulnerabilidad sin necesidad de alcanzar el canary de la pila. Ver:
Pointer RedirectingModificando tanto el canary maestro como el de hilo
Un desbordamiento de búfer en una función con hilos protegida con canary puede ser utilizado para modificar el canary maestro del hilo. Como resultado, la mitigación es inútil porque la verificación se utiliza con dos canaries que son los mismos (aunque modificados).
Además, un desbordamiento de búfer en una función con hilos protegida con canary podría ser utilizado para modificar el canary maestro almacenado en el TLS. Esto se debe a que podría ser posible alcanzar la posición de memoria donde se almacena el TLS (y, por lo tanto, el canary) a través de un bof en la pila de un hilo. Como resultado, la mitigación es inútil porque la verificación se utiliza con dos canaries que son los mismos (aunque modificados). Este ataque se realiza en el writeup: http://7rocky.github.io/en/ctf/htb-challenges/pwn/robot-factory/#canaries-and-threads
Ver también la presentación de https://www.slideshare.net/codeblue_jp/master-canary-forging-by-yuki-koike-code-blue-2015 que menciona que generalmente el TLS se almacena mediante mmap
y cuando se crea una pila de hilo también se genera mediante mmap
de acuerdo a esto, lo que podría permitir el desbordamiento como se mostró en el writeup anterior.
Modificar la entrada GOT de __stack_chk_fail
Si el binario tiene Partial RELRO, entonces puedes usar una escritura arbitraria para modificar la entrada GOT de __stack_chk_fail
para que sea una función ficticia que no bloquee el programa si el canary se modifica.
Este ataque se realiza en el writeup: https://7rocky.github.io/en/ctf/other/securinets-ctf/scrambler/
Learn & practice AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE) Learn & practice GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)