Protección de ejecutables: ASLR

ASLR o Address Space Layout Randomization es una técnica utilizada para mitigar los ataques contra los desbordamientos de buffer, haciendo que los segmentos de memoria no tengan una posición fija en memoria sino que esta varíe de forma aleatoria en cada ejecución. Esta técnica fue incluida en el kernel de linux en su versión 2.6.12 (de junio de 2005) aunque se podía instalar anteriormente en forma de parche. 

Como vimos en una entrada anterior, NX marcaba algunas zonas de memoria no ejecutables, entre ellas el Stack. Esto hace que no se pueda ejecutar algunos exploit que aprovechando un desbordamiento de buffer inyectan un shellcode para ejecutarse en estas zonas de memoria. Para evitar esta contramedida se ideó una técnica llamada ROP ‘return-oriented programming’.
ROP es una técnica simple que reutiliza instrucciones existentes en el programa, que son ejecutables, en lugar de inyectar instrucciones arbitrarias en la memoria y ejecutarlas. Cuando ROP ejecuta instrucciones presentes en la librería libc, la técnica se denomina ret2libc o "regreso a libc". Libc es la biblioteca estándar de C que contiene funciones básicas como printf() o exit(). Con este sistema lo que hace un exploit es llamar directamente a las funciones de esta librería. Al estar siempre en la misma zona de memoria es fácil saber donde se encuentra cada función.

Para mitigar estos y otros exploit que aprovechan direcciones conocidas de determinados segmentos en la memoria se utiliza el Address Space Layout Randomization o ASLR.  

ASLR no es un parámetro que activemos en los ejecutables (como NX) sino un parámetro del kernel de linux.

Podemos ver el valor de este parámetro en nuestra disto con:

cat /proc/sys/kernel/randomize_va_space 

El valor resultante corresponde a:

  • 0 = ASLR desactivado. No hay aleatorización
  • 1 = Aleatorización de las bibliotecas compartidas, stack y VDSO
  • 2 = Además de los elementos enumerados en el punto anterior, heap también se asigna en una dirección de memoria aleatoria

Vamos a ver como se asignan las diferentes zonas en la memoria. Para ello vamos a utilizar un sencillo programa que nos muestra las direcciones de las diferentes zonas de memoria que nos interesan:

/*Protección:ASLR */ 
#include <stdio.h> 
#include <stdlib.h> 
#include <string.h> 
#include <sys/auxv.h> 
 
char *global_var = "comunidadlibre"; 
 
int main(){ 
    char *heap_var = malloc(100); 
    char *stack_var[] = {"comunidadlibre"}; 
 
    printf("[Heap] dirección: %p\n", heap_var); 
    printf("[Stack] dirección: %p\n", stack_var); 
    printf("[libc] dirección: %p\n", (void*)exit); 
    printf("[.data] dirección: %p\n", global_var); 
    printf("[vDSO] dirección: 0x%08lx\n", getauxval(AT_SYSINFO_EHDR)); 
 
    getchar(); 
 
 
    free(heap_var); 
    exit(EXIT_SUCCESS); 
} 

Tenemos que compilar el programa sin la protección PIE que veremos próximamente:

gcc -Wall -no-pie -g aslr.c -o aslr

Primero vamos a poner la protección ASLR con el valor 0, es decir sin aleatoriedad:

echo 0 > /proc/sys/kernel/randomize_va_space

y ejecutamos el programa varias veces para comprobar las direcciones:

david@u1804libre:~$ ./aslr 
[Heap]  dirección: 0x602260
[Stack] dirección: 0x7fffffffe490
[libc] dirección: 0x7ffff7a27120
[.data] dirección: 0x4007d4
[vDSO] dirección: 0x7ffff7ffa000

david@u1804libre:~$ ./aslr
[Heap]  dirección: 0x602260
[Stack] dirección: 0x7fffffffe490
[libc] dirección: 0x7ffff7a27120
[.data] dirección: 0x4007d4
[vDSO] dirección: 0x7ffff7ffa000

Como vemos son completamente iguales, estáticas.

También podemos ver las direcciones de memoria lanzando la aplicación y desde otro terminal (como hacíamos en el post sobre NX) lanzamos cat /proc/[pid de aslr]/maps.

Vamos a poner ahora la protección como 1:

echo 1 > /proc/sys/kernel/randomize_va_space

y volvemos a lanzar el programa un par de veces:

david@u1804libre:~$ ./aslr 
[Heap]  dirección: 0x602260
[Stack] dirección: 0x7ffd06002d50
[libc] dirección: 0x7f1e59575120
[.data] dirección: 0x4007d4
[vDSO] dirección: 0x7ffd0612c000

david@u1804libre:~$ ./aslr
[Heap]  dirección: 0x602260
[Stack] dirección: 0x7ffe252e3610
[libc] dirección: 0x7fdcb9ca9120
[.data] dirección: 0x4007d4
[vDSO] dirección: 0x7ffe253e8000

Podemos ver que el Stack, libc y vDSO tienen direcciones aleatorias, pero el Heap y .data continuan siendo estáticas.

Ponemos ahora la protección como 2:

echo 2 > /proc/sys/kernel/randomize_va_space

y lanzamos el programa un par de veces nuevamente:

david@u1804libre:~$ ./aslr 
[Heap]  dirección: 0xd64260
[Stack] dirección: 0x7fff02efe5b0
[libc] dirección: 0x7fdcf2a18120
[.data] dirección: 0x4007d4
[vDSO] dirección: 0x7fff02f9e000

david@u1804libre:~$ ./aslr
[Heap]  dirección: 0x7e7260
[Stack] dirección: 0x7fffc88f64c0
[libc] dirección: 0x7f23d76dd120
[.data] dirección: 0x4007d4
[vDSO] dirección: 0x7fffc898e000

Esta vez tenemos también Heap asignado de forma aleatoria. Pero vemos que .data continua teniendo un direccionamiento estático en cada ejecución. Para solventar este problema tenemos que recurrir a otra protección, PIE que veremos próximamente.

Modificado por última vez enLunes, 01 Junio 2020 10:16
(1 Voto)
Etiquetado como :

Deja un comentario

Asegúrese de introducir toda la información requerida, indicada por un asterisco (*). No se permite código HTML.