GCC – S3lab http://s3lab.deusto.es S3lab Security Blog Wed, 06 May 2020 12:51:35 +0000 es hourly 1 https://wordpress.org/?v=5.1.5 ¿Cómo funciona un compilador? http://s3lab.deusto.es/como-funciona-compilador/ Sat, 13 May 2017 18:28:41 +0000 http://s3lab.deusto.es/?p=9069 En la serie de posts de Hardening de binarios hemos visto que muchas defensas vienen implantadas en los propios compiladores, pero ¿cómo se implementan? Tomando el caso de GCC, The GNU Compiler Collection, vamos a explicar la infraestructura general de

The post ¿Cómo funciona un compilador? appeared first on S3lab.

]]>
En la serie de posts de Hardening de binarios hemos visto que muchas defensas vienen implantadas en los propios compiladores, pero ¿cómo se implementan? Tomando el caso de GCC, The GNU Compiler Collection, vamos a explicar la infraestructura general de GCC y en grandes rasgos, cómo funciona un compilador.

Si hemos tenido una asignatura de Compiladores en nuestra vida, en muchos casos nos habrán simplificado el proceso de compilación diciéndonos que se divide en dos componentes, por un lado el front-end y por otro el back-end. El front-end es el encargado de parsear el código fuente y traducirlo a un lenguaje intermedio que el compilador entienda; finalmente en el back-end es donde se traduce ese lenguaje intermedio a ensamblador.

La realidad, en el caso de GCC, es algo más complicada, siendo la primera diferencia el número de componentes de los que se compone el compilador y la segunda que utiliza varios lenguajes intermedios. GCC se divide en tres componentes, el front-end, el middle-end y el back-end.

En el front-end se parsean los diferentes códigos fuente y se traducen a un lenguaje independiente de la máquina, GIMPLE. GIMPLE es un lenguaje basado en la representación de un código de tres direcciones (three-address code), donde todas las instrucciones tienen como máximo tres operandos. En esta fase es donde el compilador nos avisará si tenemos errores sintácticos en el programa a compilar. Cuando GCC compila C ó C++ la traducción pasa directamente de C/C++ a GIMPLE, si se trata de otro lenguaje, como FORTRAN, se traduce primero a GENERIC, otro lenguaje intermedio de GCC basado en árboles de sintaxis abstracta (ASTs de sus siglas inglesas), que a su vez es traducido a GIMPLE.

En el middle-end es donde se producen todas las optimizaciones, tanto intra-procedurales (propias de cada función) como inter-procedurales (que tienen en cuenta el estado de todas las funciones). En esta fase el compilador nos avisará si hemos cometido errores semánticos, si tenemos variables sin usar etc. En el middle-end se utilizan tres lenguajes diferentes, que son tres formas de GIMPLE: high-GIMPLE, low-GIMPLE y SSA-GIMPLE. En el high-GIMPLE los programas no tienen una representación de tres direcciones pura y en el low-GIMPLE sí. Finalmente el SSA-GIMPLE es un lenguaje intermedio también de tres direcciones, en el que se utiliza la forma de Static Single Assignment (SSA form) para hacer optimizaciones de forma más sencilla. La forma SSA tiene la particularidad de que las asignaciones “generan” una versión de la variable nueva, por ejemplo:

a = 10; a_1 = 10;
a = a + 1; a_2 = a_1 + 1;
b = a; b_1 = a_2;

Las funciones típicas de este componente son la propagación de constantes y la optimización de tail-calls, además UBSan, VTV y ASan son implementadas en este componente. Finalmente, en el back-end se traduce el SSA-GIMPLE a un lenguaje dependiente de la máquina, RTL (Register Transfer Language) que ya se va pareciendo más a el lenguaje ensamblador, pero con la particularidad de que es funcional y deriva de Lisp.

The post ¿Cómo funciona un compilador? appeared first on S3lab.

]]>
Hardening de binarios (VII) – Format Strings http://s3lab.deusto.es/hardening-binarios-7/ Mon, 20 Mar 2017 14:05:36 +0000 http://s3lab.deusto.es/?p=8961 Aunque las vulnerabilidades causadas por el uso indebido de format strings nos pueden parecer un tema del siglo pasado, una pequeña búsqueda en la base de datos de CVEs nos muestra que estos errores siguen pasando. El peligro de los

The post Hardening de binarios (VII) – Format Strings appeared first on S3lab.

]]>
Aunque las vulnerabilidades causadas por el uso indebido de format strings nos pueden parecer un tema del siglo pasado, una pequeña búsqueda en la base de datos de CVEs nos muestra que estos errores siguen pasando.

El peligro de los format string viene dado cuando no se verifica correctamente una entrada (de red, de un usuario etc) y esta se pasa a funciones inseguras de la familia de printf, que no comprueban los límites de la entrada a formatear, permiten format strings peligrosos, o el programador se olvida de poner un format string. En este último caso, si la entrada maliciosa tuviera un %s, podríamos ver el contenido de una dirección de memoria, con un %x podríamos ver el contenido de la pila y con %n podríamos escribir un valor en memoria.

En el año 2001 ya se presentaban las primeras defensas, como FormatGuard , para prevenir exploits causados por esta clase de errores, hoy en día todos los compiladores modernos nos pueden avisar del uso indebido de format strings o de funciones peligrosas (otra cosa es que nos olvidemos de compilar con estos warnings). En GCC podemos utilizar las opciones -Wformat -Wformat-security -Werror=format-security para que nuestros programas no sean susceptibles a esta clase de errores.

The post Hardening de binarios (VII) – Format Strings appeared first on S3lab.

]]>
Hardening de binarios (V) – UBSan http://s3lab.deusto.es/hardening-binarios-5/ Sat, 10 Dec 2016 12:21:42 +0000 http://s3lab.deusto.es/?p=8691 El comportamiento indefinido en C/C++ es causado cuando no existen restricciones en el comportamiento del programa; es decir, cuando el estándar no especifica qué debe hacer la implementación, esta es libre de hacer lo que le parezca. En los tiempos

The post Hardening de binarios (V) – UBSan appeared first on S3lab.

]]>
El comportamiento indefinido en C/C++ es causado cuando no existen restricciones en el comportamiento del programa; es decir, cuando el estándar no especifica qué debe hacer la implementación, esta es libre de hacer lo que le parezca. En los tiempos de Usenet, el grupo comp.std.c acuñó el término nasal demons para referirse al comportamiento indefinido de C, ya que “When the compiler encounters [a given undefined construct] it is legal for it to make demons fly out of your nose”.

Ejemplos típicos de nasal demons en C/C++ son la división por cero, el uso de una variable no inicializada, desreferenciar un puntero a NULL etc, algunos de estos casos ya están corregidos, pero aún es posible compilar programas con comportamiento no definido. Por ejemplo, dado el siguiente programa:


int function() { }
int main(){
int array[1];
array[-1] = 1;
int b;
if (b)
printf("lucky!\n");
printf(“%f\n”, 1.01 / 0.00);
int ret = function();
return 0;}

¿Qué hará el compilador cuando tratemos de asignar 1 a array[-1] ?
¿Tendremos suerte?
¿Qué pasa cuando dividimos por cero, en enteros, floats y doubles?
¿Qué retorna function?

Para detectar este tipo de errores podemos usar UBSan (Undefined Behavior Sanitizer), el detector de comportamiento indefinido en tiempo de ejecución para GCC y Clang. UBSan instrumenta el programa para detectar de forma general el comportamiento indefinido con -fsanitize=undefined, o se pueden instrumentar opciones específicas, como -fsanitize=return para comprobar que no se llegue al final de una función no void sin retornar un valor, -fsanitize=null para asegurar que no de-referenciamos un puntero a NULL, -fsanitize=vprt para que las conversiones entre clases base y derivadas tengan el tipo correcto…

Las vulnerabilidades por comportamiento indefinido en C/C++ están a la orden del día, siendo las más comunes el bad casting y el use-after-free, que afectan gravemente a navegadores, servidores web etc. Además de UBSan, DANGNULL, CaVer y TypeSan son algunos checkers de esta clase de errores.

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

The post Hardening de binarios (V) – UBSan appeared first on S3lab.

]]>
Hardening de binarios (IV) – VTV http://s3lab.deusto.es/hardening-binarios-4/ Sat, 29 Oct 2016 09:22:11 +0000 http://s3lab.deusto.es/?p=8582 En el post anterior de esta serie hablamos de cómo Google había incluido ASan en GCC para detectar errores de corrupción de memoria. Virtual-Table Verification (VTV) es otra opción de GCC (>4.9) desarrollada por Google para tratar de prevenir ataques

The post Hardening de binarios (IV) – VTV appeared first on S3lab.

]]>
En el post anterior de esta serie hablamos de cómo Google había incluido ASan en GCC para detectar errores de corrupción de memoria. Virtual-Table Verification (VTV) es otra opción de GCC (>4.9) desarrollada por Google para tratar de prevenir ataques que traten de corromper punteros de la vtable.

En C++ se implementa el polimorfismo del paradigma de la orientación a objetos mediante virtual tables, o vtables. Una vtable es una estructura de datos que guarda punteros a función  de métodos virtuales, cada instancia de una clase tendrá un puntero a una o varias vtables (en casos de herencia múltiple). Estas vtables se utilizan para resolver a qué método hay que llamar cuando un objeto quiere utilizar una función virtual.

De forma maliciosa, un atacante podría explotar un error de use-after-free para hacer hijacking de la vtable de un objeto, inyectando en heap (vía heap-spraying u otra técnica) una vtable maliciosa que llame a una función designada por el atacante en vez de que el programa haga un SIGSEGV. Ver el siguiente ejemplo como prueba de concepto:

class A {
public:
int virtual foo ()
{…}
};
class B : public A {
public:
int virtual foo ()
{…}
};
int main() {
B *p1 = new B();
B *p2 = p1;
p1->foo();
delete (p1);
p2->foo ();
return 0;
}

vtv-example

La protección de VTV hace que en tiempo de ejecución por cada  llamada a un método virtual el puntero por el cual se llega a la vtable no haya sido corrompido o modificado. De esta forma, el ataque anteriormente descrito sería frustrado.

Para utilizar VTV tenemos que compilar nuestro programa con las opciones fvtable-verify=std ó -fvtable-verify=preinit. La primera genera las estructuras necesarias para verificar los punteros de vtables después de que las bibliotecas compartidas hayan sido cargadas e inicializadas, mientras que la segunda las genera antes.

Si al tratar de compilar el linker salta con el error de que no encuentra libvtv, nuestra versión de GCC no ha sido compilada con la biblioteca de vtv, por lo que tendremos que re-compilar GCC (con paciencia, tarda ~30 mins con 8 cores), configurando previamente con –enable-vtable-verify.

VTV fue presentado en USENIX Security en 2014, el artículo, la presentación y las transparencias están disponibles para más información. Previamente, en la conferencia de GNU Cauldron de 2012 una de las autoras del artículo presentó el progreso de VTV con el ejemplo que hemos visto además de más detalles, para más información sobre ataques y defensas sobre vtables ver este artículo.

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

The post Hardening de binarios (IV) – VTV appeared first on S3lab.

]]>
Hardening de binarios (III) – ASan http://s3lab.deusto.es/hardening-binarios-3/ Sat, 17 Sep 2016 14:08:41 +0000 http://s3lab.deusto.es/?p=8430 En entregas anteriores hemos hablado de opciones de GCC que hacen nuestros ejecutables más seguros, en esta entrega en cambio, presentamos opciones de compilación que generan informes, avisando de errores en el código. AddressSanitizer (ASan) es una opción para Clang

The post Hardening de binarios (III) – ASan appeared first on S3lab.

]]>
En entregas anteriores hemos hablado de opciones de GCC que hacen nuestros ejecutables más seguros, en esta entrega en cambio, presentamos opciones de compilación que generan informes, avisando de errores en el código.

AddressSanitizer (ASan) es una opción para Clang (>=3.1) y GCC (>=4.8) que detecta errores de corrupción de memoria. ASan ha sido desarrollado por Google  y fue presentado en la conferencia Usenix ATC en 2012. ASan está formado por dos módulos: (a) un módulo de instrumentación de binarios en tiempo de compilación, que es el encargado de detectar si cada byte de la memoria de la aplicación es seguro de acceder mediante el uso de shadow memory, y (b) una biblioteca que en tiempo de ejecución es la que maneja la shadow memory. En líneas generales, sustituye los malloc y free por su propia implementación, controlando que se accedan a bloques de memoria dentro de las regiones adecuadas, que el espacio que se libere haya sido antes reservado, no accediendo a regiones liberadas etc.

Para usar ASan con GCC tenemos que compilar nuestro programa con la opción -fsanitize=address. Como ASan añade cierta sobrecarga al programa, se recomienda compilar con las opciones 01 ó 02 para tratar de optimizar el programa, y para que los errores que nos muestre sean más descriptivos tenemos que especificar que no queremos que GCC omita guardar el frame pointer en un registro, -fno-omit-frame-pointer. Para comprobar cómo funciona ASan tomamos como ejemplo el siguiente programa que presenta un error de tipo use-after-free:

#include <stdlib.h>
int main()
{
char *foo = (char*) malloc(2 * sizeof(char*));
free(foo);
return foo[0];
}

Primero compilamos sin ASan y ejecutamos:
$ gcc -o use-after-free use-after-free.c
$ ./use-after-free
y no nos enteramos de que algo va mal con nuestro programa.

Mientras que, compilando con ASan de la siguiente manera:
$ gcc -o use-after-free-asan-no-omit -fsanitize=address -O1 -fno-omit-frame-pointer use-after-free.c
$ ./use-after-free-asan-no-omit

asan-exampleEl  compilador nos muestra que tipo de error ha encontrado, diciéndonos que hemos hecho un use-after-free en esta posición: #0 0x4007b1 in main (/[OMITTED]/use-after-free-asan-no-omit+0x4007b1). Que había sido liberada en: #1 0x400793 in main (/[OMITTED]/use-after-free-asan-no-omit+0x400793). Y anteriormente reservada en: #1 0x400788 in main (/[OMITTED]/use-after-free-asan-no-omit+0x400788).

Podemos usar addr2line para hacer corresponder las direcciones del informe (0x4007b1, 0x400793, y 0x40078) a números de línea en nuestro programa y así tener una visión más clara de dónde hemos metido la pata.
$ addr2line -e ./use-after-free-asan-no-omit 0x4007b1 […]/use-after-free.c:8

Finalmente, si tenemos LLVM instalado, los informes de ASan tanto en Clang como en GCC podrán utilizar llvm-symbolizer para trasladar directamente las direcciones a número de línea si compilamos adicionalmente con -g y avisamos a ASan de que queremos que use el symbolizer:

$ which llvm-symbolizer-3.8
/usr/bin/llvm-symbolizer-3.8
$ export ASAN_SYMBOLIZER_PATH=/usr/bin/llvm-symbolizer-3.8
$ export ASAN_OPTIONS=symbolize=1
$ gcc -o use-after-free-asan-no-omit -fsanitize=address -O1 -fno-omit-frame-pointer -g use-after-free.c
$ ./use-after-free-asan-no-omit . . . omitido . . .
#0 0x4007b1 in main /[OMITTED]/use-after-free.c:8 . . .
#1 0x400793 in main /[OMITTED]/use-after-free.c:7 . . .
#1 0x400788 in main /[OMITTED]/use-after-free.c:6 . . . omitido . . .

El ejemplo que hemos tomado, y otros muchos se encuentran en la Wiki de ASan, donde se pueden consultar más detalles sobre el funcionamiento interno de ASan, y otros sanitizers, además la presentación de ASan en Usenix está disponible en su página.

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

The post Hardening de binarios (III) – ASan appeared first on S3lab.

]]>
Hardening de binarios (II) – PIE http://s3lab.deusto.es/hardening-binarios-2/ Tue, 28 Jun 2016 09:55:48 +0000 http://s3lab.deusto.es/?p=8304 Siguiendo con las opciones clásicas de compilación de GCC para tratar de tener binarios más robustos, en esta entrega vamos a hablar de las opciones -fpie y -fPIE, que permiten compilar ejecutables como “Position Independent Executables” y permiten que sus

The post Hardening de binarios (II) – PIE appeared first on S3lab.

]]>
apple-pieSiguiendo con las opciones clásicas de compilación de GCC para tratar de tener binarios más robustos, en esta entrega vamos a hablar de las opciones -fpie y -fPIE, que permiten compilar ejecutables como “Position Independent Executables” y permiten que sus secciones se carguen en posiciones aleatorias.

PaX Team presentó en 2001 ASLR (Address Space Layout Randomization), un parche para introducir aleatoriedad en las direcciones usadas por un programa. ASLR trata de evitar que las direcciones de memoria sean conocidas y así prevenir ataques que dependen del conocimiento de estas direcciones (tipo return2libc y otros). Hoy en día, Windows, Linux, Mac OS X, Android e iOs implementan alguna variante de ASLR.

ASLR se configura mediante el parámetro /proc/sys/kernel/randomize_va_space en Linux. Un valor 0 deshabilita ASLR, tendremos este valor cuando nuestra arquitectura no soporte la aleatorización o cuando nuestro kernel haya booteado con la opción norandmaps. Un 1 habilita la aleatorización de las bibliotecas compartidas (shared libraries), las direcciones base de mmap y la pila. Finalmente, un 2 tiene las opciones de 1 y añade la aleatorización del heap. En los sistemas actuales tendremos la opción 2, sin embargo, antiguamente (en tiempos pre-mmap) se utilizaban las llamadas del sistema brk y sbrk para reservar y liberar memoria, ASLR es posterior a esto. Por tanto, si CONFIG_COMPAT_BRK está activado por razones de compatibilidad hacia atrás, por ejemplo en binarios viejos que usen libc5 (no confundir con glibc), tendremos únicamente una aleatorización de tipo 1.

Por ejemplo, comprobamos la posición de libc en 5 procesos de /proc/X/maps con ASLR desactivado (32 bits):

start-end  perm  offset  major:minor  inode  image
f7dfe000-f7fa6000   r-xp   00000000   08:05   262413   /lib/i386-linux-gnu/libc-2.19.so
f7dfe000-f7fa6000   r-xp   00000000   08:05   262413   /lib/i386-linux-gnu/libc-2.19.so
f7dfe000-f7fa6000   r-xp   00000000   08:05   262413   /lib/i386-linux-gnu/libc-2.19.so
f7dfe000-f7fa6000   r-xp   00000000   08:05   262413   /lib/i386-linux-gnu/libc-2.19.so
f7dfe000-f7fa6000   r-xp   00000000   08:05   262413   /lib/i386-linux-gnu/libc-2.19.so

y podemos ver que la dirección es siempre la misma; mientras que con ASLR activado:

start-end  perm  offset  major:minor  inode  image
f7508000-f76b0000   r-xp   00000000   08:05   262413   /lib/i386-linux-gnu/libc-2.19.so
f751d000-f76c5000   r-xp   00000000   08:05   262413   /lib/i386-linux-gnu/libc-2.19.so
f7533000-f76db000   r-xp   00000000   08:05   262413   /lib/i386-linux-gnu/libc-2.19.so
f75eb000-f7793000   r-xp   00000000   08:05   262413   /lib/i386-linux-gnu/libc-2.19.so
f7566000-f770e000   r-xp   00000000   08:05   262413   /lib/i386-linux-gnu/libc-2.19.so

Tenemos un offset que siempre cambia.

Una vez que sepamos si nuestro sistema soporta ASLR haciendo $ cat /proc/sys/kernel/randomize_va_space podremos utilizar ASLR en nuestros binarios si están compilados con las opciones -fpie y -fPIE.

Hay que subrayar que ASLR es una defensa estadística, y por tanto limitada. El rango de entropía de ASLR está limitado por la memoria virtual disponible, y se ha demostrado que los sistemas de 32 bits son susceptibles a ataques de de-aleatorización. Por otro lado ASLR puede superarse con info leaks, como se muestra en un famoso artículo de Phrack.

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

The post Hardening de binarios (II) – PIE appeared first on S3lab.

]]>