
Prueba de concepto CVE-2025-24990 (controlador de Agere Systems)
ltmdm64.sys). Este controlador es muy antiguo y no se carga de forma predeterminada en mi máquina de pruebas, por lo que lo explotaré en un escenario BYOVD. Curiosamente, según mi investigación, este controlador ha existido desde Windows 7 y tiene al menos un error. aquí
En ese momento, MSRC no tomó ninguna medida 🤡
Algunos IOCTL dentro de este controlador usan METHOD_NEITHER pero no verifican si el búfer de direcciones proporcionado por el llamador proviene del modo de usuario o del modo kernel. Aquí hay un ejemplo de código IOCTL que decodifiqué con OSR:
Esto significa que puedes proporcionar una dirección de kernel a la API DeviceIoControl y el controlador la manejará normalmente.
Ten en cuenta que primero debes omitir kASLR para filtrar la dirección del kernel. Usaré EnumDeviceDrivers (En Windows 24h2 necesitas SeDebugPriv para hacer esto).
El problema está en el IOCTL 0x802b200f (ud_response). Nuevamente, este envío de IOCTL no valida la dirección que proporciono desde el modo de usuario, pero lo aprovecharé más adelante.
ud_response llama a ll_load_diagnostics, y llegaré al siguiente código:
Al principio, la variable global eeprom no está inicializada, por lo que contendrá NULL. Aquí hay un código simple que activará esto.
Aprovecharé esto más adelante.
Este IOCTL simplemente convierte la cadena de versión del controlador "8.36" al número 0x836 (un DWORD) y lo escribe en la dirección proporcionada por el llamador (gracias a METHOD_NEITHER). Técnicamente, puedo escribir estos cuatro bytes (36 08 00 00) en una dirección de kernel arbitraria. Aprovecharé esto para sobrescribir las variables globales del controlador y cambiar el flujo de ejecución.
Llamaré a este 0x802b2003 IOCTL_GET_VERSION
Byte nulo arbitrario:
Volviendo al caso de desreferencia nula, uso la API VirtualAlloc para asignar una dirección fija (0x083600000000). Luego uso IOCTL_GET_VERSION para escribir en *(eeprom + 4) los cuatro bytes descritos anteriormente. Cuando el controlador desreferencie eeprom más tarde, leerá desde la dirección que asigné.
Después de corregir la desreferencia nula, el IOCTL escribe una cadena en la dirección que proporciono desde el modo de usuario, según el tamaño del búfer.
Este código simplemente demuestra lo que describí anteriormente: asignar un búfer y llenarlo con 0xAA, corregir la desreferencia nula y luego llamar al controlador. Ten en cuenta que asigno 11 bytes pero solo proporciono un tamaño de búfer de 10 al controlador para ver cómo se comporta.
Escribe una secuencia fija de bytes en mi búfer y luego anula el byte final (el 11), incluso si solo proporciono un tamaño de 10. Reemplaza el último 0xAA en mi búfer con 0x00. Esto indica que si proporciono un tamaño de 0, el controlador aún escribe un solo byte 0x00 en la dirección de destino.
Decremento arbitrario
Ahora que tengo el byte nulo y los 4 bytes fijos arbitrarios, vamos a crear otra primitiva.
Este IOCTL establecerá el LtMsgEvent global en mi búfer de usuario, luego verificará si WDM es nulo y lo establecerá en cero nuevamente.
Luego, en 0x802b2207, llamará a la API ObfReferenceObject.
En el estado inicial, WDM es nulo, pero con la ayuda de IOCTL_GET_VERSION puedo establecer WDM en 0x36 (su tamaño es solo 1 byte) y LtMsgEvent sigue siendo mi búfer. Luego anularé WDM y llamaré a 0x802b2207. Finalmente llegaré a ObfReferenceObject. Llamaré a estos dos IOCTL IOCTL_SET_LtMsgEvent y IOCTL_DEREF_LtMsgEvent.
La técnica de exploit que usa ObfReferenceObject cambia el PreviousMode de nuestro KTHREAD de UserMode a KernelMode; puedes leer sobre ello aquí). Sin embargo, Windows ha corregido este exploit, por lo que no podemos usarlo.
Pero la primitiva en ObfReferenceObject aún existe. La API resta 0x30 de la dirección que proporcionamos, convierte el resultado en un entero de 8 bytes y luego resta 1.
*(signed long long)(LtMsgEvent-0x30) -= 1
Pero el problema es que verifica si el siguiente valor es 0 o si el valor actual es < 1 (interpretado como un entero con signo de 8 bytes). Si cualquiera de las condiciones es verdadera, salta a KeBugCheckEx y bloquea el sistema.
Escritura arbitraria
Con el decremento arbitrario en mano, necesito encontrar otro lugar para escribir el byte 0xFF y luego decrementarlo al byte que quiero. Encontré este IOCTL 0x802b2243: