lunes, marzo 03, 2014

SSDT Hooking V2

Cuando hablamos de rootkits hablamos de los internals, del kernel, hablamos de las tripas del sistema operativo (Microsoft Windows y arquitectura x86 en el caso que nos ocupa), y hablamos de la modificación de su flujo y alteración de su comportamiento sin pérdida de funcionalidad; todo ello, claro está, efectuándose de un modo oculto y silencioso.

Alterar la SSDT (System Service Descriptor Table) es uno de los métodos de hooking más empleados por rootkits comunes, sin olvidarnos de la mayoría de antivirus del mercado. Sencillamente se modifican o intercambian entradas en esta tabla indexada y así se altera el comportamiento del sistema. No obstante, dicha tabla se encuentra en una zona de la memoria con permisos read-only y se requiere habilitar el permiso de escritura antes de su modificación.

The Rootkit Arsenal detalla dos formas de realizar esta operación, y si queremos olvidarnos de la segunda (basada en Memory Descriptor List’s o MDL’s, técnica que ya habían descrito con claridad Greg Hoglund y James Butler en “Rootkits: Subverting the Windows Kernel”), estamos en todo nuestro derecho.

La primera técnica, como decíamos, se basa en deshabilitar el WP bit (Write Protection bit) del registro de procesador CR0. Intel nos dice que si este bit es igual a 0, cualquier código en el kernel (Ring 0) tiene acceso de lectura/escritura en todas las páginas de memoria. Lo curioso de esta técnica es que el registro CR0 puede modificarse mediante instrucciones assembler igual que cualquier otro registro de operación básico.
PUSH EBX
MOV EBX, CR0
AND EBX, 0XFFFEFFFF
MOV CR0, EBX
POP EBX
Analicemos por qué todo esto es necesario:
lkd> dps nt!KeServiceDescriptorTable
82daf9c0 82cb66f0 nt!KiServiceTable
82daf9c4 00000000
82daf9c8 00000191
82daf9cc 82cb6d38 nt!KiArgumentTable
KeServiceDescriptorTable es un array de cuatro estructuras que contienen 4 valores DWORD. La primera dirección apunta directamente a la SSDT, el tercer DWORD (0x191) nos indica la cantidad de funciones que contiene la SSDT. Veamos algo acerca de la página en la que se aloja la SSDT:
lkd> !pte nt!KiServiceTable
VA 82cb66f0
PDE at C06020B0 PTE at C04165B0
contains 00000000001D2063 contains 0000000002CB6121
pfn 1d2 ---DA--KWEV pfn 2cb6 -G--A--KREV
Como se puede ver en (-G--A--KREV), la página pertenece al kernel (K) y tiene permisos de sólo lectura (R).

Ahora viene lo bueno. La alternativa que yo planteo en este artículo es la de modificar la SSDT sin tener que habilitar los permisos de escritura en la memoria. ¿Cómo puede ser esto posible? Si observamos la página sobre la que se asienta KeServiceDescriptorTable obtenemos lo siguiente:
lkd> !pte nt!KeServiceDescriptorTable
VA 82daf9c0
PDE at C06020B0 PTE at C0416D78
contains 00000000001D2063 contains 0000000002DAF963
pfn 1d2 ---DA--KWEV pfn 2daf -G-DA--KWEV
Como vemos en (-G-DA--KWEV), esta zona de memoria sí tiene permisos de escritura (W). ¿Cómo podemos aprovechar esta situación? Pues la solución pasa por crear una nueva SSDT y sobrescribir la dirección de la KiServiceTable original por la nueva. Para ello, lo que hacemos en el código es ver el número de API's que contiene la SSDT original y reservar un espacio de memoria adecuado para la nueva SSDT:
newSSDT = ExAllocatePool(NonPagedPool, nCalls * sizeof(DWORD));
Imaginemos que la dirección devuelta es 0x8537b9b8, veamos los permisos de la página:
lkd> !pte 8537b9b8
VA 8537b9b8
PDE at C0602148 PTE at C0429BD8
contains 000000007F70F863 contains 000000007F37B963
pfn 7f70f ---DA--KWEV pfn 7f37b -G-DA--KWEV
Como siempre, perteneciente al kernel (K) y escribible (W). Lo siguiente es hacer una copia de todas las direcciones de las API contenidas en la SSDT original en la nueva, al tiempo que hookeamos la API elegida:
for ( i = 0; i < nCalls; i++ ) {
if ( i == idx )
newSSDT[i] = newApi;
else
newSSDT[i] = GetSSDTEntry(i);
Ahora sólo nos queda modificar el primer DWORD de KeServiceDescriptorTable con la dirección de la nueva SSDT:
oldSSDT = GetSystemCallTable();
serviceDesc = &KeServiceDescriptorTable;
serviceDesc->KiServiceTable = newSSDT;
Y ya lo tenemos hecho. El módulo o driver puede cargarse de múltiples formas, el programa OSR Loader es lo más sencillo para la etapa de pruebas (sc y métodos de inyección deben ser utilizados por rootkits funcionales). En la siguiente figura utilizamos Windbg para revelar el contenido de KeServiceDescriptorTable antes y después de la creación y sustitución de la nueva SSDT.

Figura 1: Contenido de KeServiceDescriptorTable antes y después

DebugView es la utilidad perfecta para mostrar los resultados de nuestra técnica:

Figura 2: Resultados obtenidos

¿Qué ventajas puede tener este método?
1) Por un lado guardamos la dirección de la SSDT original intacta. Para dejar el sistema como estaba no hace falta unhookear cada API una por una, sino tan sólo modificar otra vez el primer DWORD en KeServiceDescritorTable con la dirección de la SSDT original. 
 2) En ningún momento modificamos el registro CR0 y por lo tanto cualquier código inmerso en el kernel chequeando este valor de forma periódica es evadido. 
3) Imagine un código antirootkit que haya obtenido la dirección de la SSDT original, y la mantenga almacenada como un valor estático, cada vez que acceda a esta dirección de memoria y compruebe las direcciones de las API's contra una copia no alterada no descubrirá cambio alguno en la tabla.
Muestro a continuación el código completo que hace hooking sobre la API ZwSetValueKey() siguiendo la metodología descrita en esta sección.



Queda a tu elección qué mecanismo usar, esta es tan sólo una alternativa que me ha parecido relativamente más sencilla y que puede ofrecer alguna que otra ventaja. Aunque, al igual que el modo descrito en The Rootkit Arsenal puede ser fácilmente detectable. En este caso no habría que comprobar todas las API's sino tan sólo comparar el valor introducido en KeServiceDescriptorTable con el original previamente almacenado. Por supuesto comprobar que las entradas de la SSDT no apuntan a la memoria propia de “ntkrnl*.exe” es una técnica común.

Feliz Hacking!

by blackngel (blackngel@blackbycode.com)
Autor del libro "Linux Exploiting"

4 comentarios:

Anónimo dijo...

en hora buena black... hace un tiempo queria hacer un post similar, un saludo!

Anónimo dijo...

Muy interesante e instructivo !!! Una delicia leer estas entrada de blackngel

Anónimo dijo...

Hola blackangel, me gustaria aprender este arte del exploting, podrias darme unas referencias por donde empezar desde 0,algunos manuales... lo q pasa q no tengo dinero para comprar tu libro, un saludo desde Mexico.

Ibañez dijo...

bestial post.

Entrada destacada

Programa de Especialización "Inteligencia Artificial para Expertos en Ciberseguridad" 2ª Edición.

Hoy, en medio del verano, os traigo información de la 2ª Edición del   Programa de Especialización  de "Inteligencia Artificial para Ex...

Entradas populares