Hardening de binarios (I) – La pila

Dentro de las múltiples opciones de compilación que nos facilita GCC, algunas están hechas para hacer que nuestros binarios sean más robustos contra técnicas de corrupción de memoria. Las técnicas de protección de la pila tratan de evitar que se sobreescriban bloques contiguos de memoria, causados por un buffer overflow, y que pueden contener información crítica de control del programa, como direcciones de memoria, punteros a función y el frame pointer. Entre las soluciones disponibles en los compiladores tenemos a StackGuard, Stack Shield, ProPolice (Stack Smashing Protector, SSP) y -fstack-protector.

StackGuard se presentó en 1998 en Usenix Security como un parche para GCC,  se basa en la colocación de “canary words” (haciendo alusión a los canarios que utilizaban los mineros para saber si el aire de la mina era venenoso) pegadas a la dirección de retorno de la pila. Por aquella época los ataques de buffer-overflow más comunes utilizaban stack smashing, es decir, causar un overflow de un buffer, para cambiar la dirección de retorno de una función a una zona de memoria controlada por el atacante. StackGuard se basaba en que si se protegía la dirección de retorno de las funciones, entonces el atacante no podría invocar su código; para esto se utilizaban canary words. Antes de pasar a ejecutar la instrucción de retorno de una función se comprobaba que la integridad del canary se mantenía, sino el programa se abortaba.

pilaStrings

Stack Shield por otro lado, trataba de copiar las direcciones de retorno de la pila a una zona que no pudiera sobreescribirse por un buffer-overflow, el segmento de datos, y después comparaba el valor “seguro” contra el valor actual; si eran diferentes el programa terminaba. En uno de los famosos artículos de la revista Phrack, se muestra que tanto StackGuard como Stack Shield pueden circunvalarse.

Tras StackGuard y Stack Shield vino ProPolice desarrollado por IBM, que en vez de comprobar los canaries de las instrucciones de retorno monitorizaba los cambios de la pila. ProPolice reordena las variables, argumentos, direcciones de retorno y frame pointers para proveer un modelo de pila “seguro”.

Este modelo resultó en el flag de compilación -fstack-protector que protege de buffer-overflows a funciones que llaman a alloca y funciones con buffers de más de 8 bytes (4 en algunas distribuciones Linux), y posteriormente en -fstack-protector-all que protege todas las funciones.

Los compiladores modernos utilizan una de estas opciones por defecto, y el ahora clásico “*** stack smashing detected ***: ./a.out terminated Segmentation fault”  muestra que el SSP de ProPolice está funcionando.

¿Es esto suficiente? Lo descubriremos en los siguientes posts.

Irene Díez
Acerca de
Investigadora de DT
Expertise: Operating systems, program analysis