METHOD_NEITHER: Automatizando la búsqueda de vulnerabilidades
Aunque nunca antes había centrado mi atención en el tema de las vulnerabilidades y pese a estar un poco apartado ya del low-level y los temas relacionados con la seguridad (mi única relación hoy en día es a traves de este blog) hace un tiempo me llamó la atención ese tipo de vulnerabilidades que parecían tener un montón de drivers por la mala gestión de IOCTLs con METHOD_NEITHER. Al corresponderse estas vulnerabilidades con un tipo de comportamiento muy especifico, se me ocurrió que podía ser relativamente sencillo automatizar la búsqueda de estos fallos evitando tener que pasar horas desensamblando codigo. Eso sí, sería necesario pasar algunas horas (tiempo, bendito tesoro) codeando una utilidad que hiciese el trabajo por nosotros. El resultado de poner la idea en practica es la pequeña utilidad que os presento a continuación. No es gran cosa, pero simplemente funciona (Everything should be made as simple as possible, but no simpler).
¿En que consiste la idea?
Es simple. Se trata de filtrar el envio de IOCTLS que se realicen en el sistema y de aquellos que empleen METHODE_NEITHER sustituir la dirección del buffer de salida por una dirección en ring0, de modo que si el buffer en esa nueva dirección es escrito, habremos identificado un driver vulnerable.
¿Como lo ponemos en practica?
Dedicandole unas horas en las noche de mis vacaciones y otros dos días sin poder ir a trabajar por culpa de una plaga de gastroenteritis.
Ahora en serio. Como siempre, será posible hacerlo de varias formas, a continuación os explico brevemente la vía por la que optó servidor:
El primer paso es identificar un lugar donde capturar las operaciones que se estan enviando en el sistema. El lugar escojido en este caso es la función «NTDeviceIoControlFile» que reside en «ntoskrnl.exe». Cada vez que alguien llama a la función «DeviceIoControl», el flujo pasa por «NTDeviceIoControlFile» y lo mismo sucede cuando un driver llama a «IoCallDriver«. Se trata por lo tanto de un lugar desde el que cubrimos la practica totalidad de operaciones que se realicen en el sistema. Además, al realizar el filtrado desde ring0, estaremos espiando a todos los procesos que corran en el mismo.
Una vez tomamos el control de «NTDeviceIoControlFile«, cada vez que se llame a esta función, recojeremos de la pila los parametros que recibe la misma, entre ellos el Io Control Code y el handle al device object destinatario de la operación. Comprobaremos si el IOCTL emplea METHOD_NEITHER. En caso afirmativo, obtendremos el nombre del device al que representa el handle e informaremos a traves de un pipe a un proceso en ring3 de los parametros que ha recibido «NTDeviceIoControlFile«, más el nombre del device object destinatario. Despues dejamos que la operación siga su curso, de modo que el sistema siga funcionando correctamente. Por cierto que a veces pienso en lo bonito que sería ser banquero y poder hacer estas cosas con dinero en lugar de bytes. Vocación equivocada supongo, que se le va a hacer.
Seguimos. En ring3 tendremos un proceso escuchando por el pipe a traves del cual nuestro driver envía los parametros de «NtDeviceIoControlFile«. También recibiremos el nombre del device object destinatario de la operación y por lo tanto podremos replicar la llamada que algun otro proceso le había hecho a «DeviceIoControl». Para replicar la llamada, haremos un «CreateFileW» sobre el nombre del device object para obtener un handle valido y obtendremos también a traves del pipe el Io Control Code de la operación. Con el handle y el Io Code llamaremos a «DeviceIoControl«. Logicamente, nuestro driver también capturará esta llamada, pero tras comprobar que el proceso que la realiza es nuestro proceso de ring3 realizará otras acciones diferentes a las que realiza cuando los demás procesos llaman a «DeviceIoControl«. Modificará en la pila la dirección del buffer de salida de la operación para que apunte a un buffer en ring0 (reservado por el driver) y modificará el valor de retorno en la pila por el de una función que también reside en nuestro driver para así tomar de nuevo el control despues de que la operación se haya procesado. Al retomar el control, lo único que le queda por hacer es copiar el contenido del buffer de salida de ring0 al buffer de salida original de ring3. Asi al retornar de la llamada a «DeviceIoControl«, nuestro proceso en ring3 podrá comprobar si el contenido de ese buffer se ha modificado e identificar así las operaciones vulnerables.
Pues bien, en realidad esto es todo. Pero el caso es que funciona. Y lo mejor de todo es que la utilidad no tiene ni GUI ni linea de comandos. Unicamente la he probado en XP. Y por supuesto, si vais a probarla y teneis medio dedo de frente, hacedlo en una VM. Y no digo que sereis incapaces de resistir la tentación de probarla, pero seguramente lo hagais x).
Bueno, en este .rar podeis encontrar los binarios y el código fuente, en enamblador para Masm.
Incluyo algunas librerias de Four-F y otras milenarias que creo recordar eran de Elicz. Cosas curiosas de la vida, a la utilidad en principio la bauticé como «Ioctlizer», pero un pavo publicó hace unos meses (creo que en rootkit.com) una utilidad con ese mismo nombre. Así que despues de días comiendome la cabeza, meditando, casi sin comer y pasando las noches en vela, vino a mi mente como una inspiración divina otro nombre que tampoco le viene mal: Ioctilizador. A tomar por culo.
El driver «hookIoctlizer.sys», hay que copiarlo a mano en «System32/drivers/». El Ioctlizador.exe, se puede lanzar desde el OllyDbg. Tiene metida una «int 3» despues de la comprobación del buffer de salida, de modo que si el mismo ha sido modificado, el Olly se detendrá. Podremos ver en «esi» el nombre del objeto y en «edi» el Io Control Code. Para poder hacer pruebas creé otra utilidad con su propio driver que emplea METHOD_NEITHER y escribe en el buffer de salida sin validar que sea un buffer en ring3. Son el «test.exe» y el «dummy.sys», que también hay que copiar a mano en «System32/drivers/». Asi que si teneis corriendo el Ioctlizador y ejecutais el test.exe, el Olly os debería parar y deberiais ser capaces de desarrollar un exploit (al menos un POC) en unos minutos ;-P.
Y bueno, como no he metido colorines ni nada en este post, al menos he inventado un trabalenguas para que veais que yo también me lo curro. Es muy original:
«El sistema esta ioctilizado, quien lo desioctilizara… el desioctilizador que lo desioctilice, buen desioctilizador será».
Aio!