Estoy deseando acabar con ella: DoS remota mediante Wininit.exe
Edición y contribuciones adicionales de Tricia Howard
Resumen ejecutivo
Stiv Kupchik, investigador de Akamai, encontró una nueva vulnerabilidad de denegación de servicio (DoS) en Wininit.exe de Microsoft, CVE-2022-44707, que obtuvo una puntuación de CVSS de 6,5.
La vulnerabilidad se divulgó de forma responsable a MSRC en agosto, y fue corregida como parte del Patch Tuesday de diciembre de 2022.
La vulnerabilidad ataca al mecanismo de almacenamiento en caché RPC, sobre el que hemos realizado una amplia investigación. Proporcionamos una prueba de concepto de los ataques en nuestro kit de herramientas de RPC.
Al aprovechar esta vulnerabilidad, un atacante puede omitir las comprobaciones de seguridad gracias al mecanismo de almacenamiento en caché e interactuar con el mecanismo de apagado en un equipo Windows remoto, obteniendo el control para detener o iniciar apagados.
La vulnerabilidad afecta a todas las versiones de Windows sin parches, a partir de Windows 8/Server 2012, lo que refuerza la importancia de aplicar parches a tiempo.
Introducción
En nuestra publicación anterior, Caché gélida, revelamos algunas vulnerabilidades RPC más que descubrimos con nuestro kit de herramientas de RPC, y por fin ha llegado el momento. Hoy, en otro episodio de artimañas de RPC, tenemos Wininit.
Wininit es un proceso esencial de Windows, que desempeña un papel crucial tanto en el arranque como en el apagado del sistema operativo Windows (es decir, si se bloquea, se bloquea todo el sistema). Así, expone varias interfaces de RPC que implementan la funcionalidad de apagado. Algunas de ellas están incluso documentadas. La vulnerabilidad que hemos encontrado está en la interfaz WindowsShutdown .
¿Qué es la interfaz WindowsShutdown?
WindowsShutdown es una de las pocas interfaces de RPC expuestas por Wininit. Es responsable del proceso de apagado (como el resto de las interfaces de RPC de Wininit; sorprendente, ¿verdad?). El UUID de la interfaz es d95afe70-a6d5-4259-822e-2c84da1ddb0d. Forma parte de MS-RSP (protocolo de apagado remoto) y tiene las siguientes capacidades:
Mirando el archivo IDL público, podemos ver que solo WsdrInitiateShutdown y WsdrAbortShutdown están documentadas. ¿Debemos preocuparnos por una función que no está documentada (es decir, nunca se ha preguntado a un experto por ella)? Para responder a esto, echemos un vistazo a la devolución de llamada de seguridad de esta interfaz.
Devolución de llamada de seguridad
La devolución de llamada comprueba primero el protocolo de transporte y solo permite ALPC o TCP. A continuación, se comprueba el nivel de autenticación. Solo RPC_C_AUTHN_LEVEL_PKT_PRIVACY está permitido. Si la función a la que se llama no es WsdrCheckForHiberboot, se comprueba el token del usuario que realiza la llamada remota. Se compara el token con un SID conocido (guardado en una variable global, creada durante la inicialización de Wininit): SECURITY_NETWORK_RID. Las llamadas a WsdrCheckForHiberboot no están restringidas por la devolución de llamada de seguridad.
La interfaz de RPC se registra con el comportamiento de caché predeterminado; por lo tanto, teóricamente, si podemos llamar correctamente a WsdrCheckForHiberboot, el resultado correcto almacenado en caché nos permitiría omitir la comprobación de SID en llamadas posteriores a WsdrInitiateShutdown y WsdrAbortShutdown.
Entonces, ¿qué tenemos que hacer para llamar a WsdrCheckForHiberboot?
WsdrCheckForHiberboot
Realmente no nos importa lo que haga la función, o lo que sea hiberboot (para quien se lo pregunte, es el inicio rápido de Windows), pero necesitamos saber cómo llamarla correctamente. Queremos definir la función correctamente en un archivo IDL para compilar un cliente en él. Aunque la función no esté documentada, podemos tratar de mirar el advapi CheckForHiberboot, el único cliente RPC documentado que encontramos para WsdrCheckForHiberboot. Solo requiere dos argumentos: un puntero booleano y un booleano.
Podemos recrear la definición de la función con estos argumentos, compilando un archivo IDL y un programa. Pero todavía nos falta algo. Al llamar a la función de forma remota con nuestro cliente, el tiempo de ejecución de RPC nos indica que estamos llamando a la función de forma incorrecta y que se devuelve un error de stub de RPC.
En lugar de seguir conjeturando, podemos hacer algo más delicado, pero engorroso. Como parte de la interfaz de stub de RPC, existe un campo denominado ProcFormatString. Es básicamente una cadena binaria larga que describe los tipos de argumentos y los valores devueltos de todas las funciones expuestas por la interfaz. Este campo lo utiliza el tiempo de ejecución de RPC al procesar las referencias de las llamadas de función (lo que devolvió el error de stub de RPC durante el primer intento). Al analizar minuciosamente la cadena binaria de forma manual, descubrimos que la función espera obtener otro argumento del tipo wchar_t*. En la siguiente figura, puede ver la sección de ProcFormatString de WsdrCheckForHiberboot, con nuestros comentarios sobre cada parte:
Después de añadir ese “nuevo” argumento a nuestra definición de función, conseguimos despegar. CheckForHiberboot vuelve correctamente y podemos continuar con la llamada a WsdrInitiateShutdown y WsdrAbortShutdown.
Dejaré que termine, pero…
Nuestro deseo inmediato es utilizar el ataque de almacenamiento en caché para llamar a WsdrInitiateShutdown y lograr un apagado remoto. Esta vez, sin conjeturas. No solo se documenta la definición de la función en el archivo IDL, sino que los indicadores que espera obtener se documentan en la función de advapi InitiateShutdownA. Por lo tanto, con la combinación de indicadores SHUTDOWN_GRACE_OVERRIDE, SHUTDOWN_HYBRID y SHUTDOWN_FORCE_OTHERS podemos forzar un apagado inmediato.
De esta forma, hemos terminado nuestra cadena de ataque, lo que nos permite omitir la devolución de llamada de seguridad y su comprobación de SID, así como apagar de forma remota. Técnicamente, se trata de una escalada de privilegios (que permite a cualquier usuario autenticado llamar a WsdrInitiateShutdown de forma remota en lugar de solo a los permitidos en SECURITY_NETWORK_RID). Como solo se puede lograr un apagado mediante esta interfaz, la vulnerabilidad se clasificó como una vulnerabilidad de DoS.
Detección
Proporcionamos una consulta de OSQuery para detectar versiones sin parches (y por lo tanto vulnerables) de Wininit.exe. Los clientes de Akamai Guardicore Segmentation pueden utilizar la función Insight junto con esta consulta para buscar activos vulnerables.
WITH product_version AS (
WITH os_minor AS (
WITH os_major AS (
SELECT substr(product_version, 0, instr(product_version, ".")) as os_major, substr(product_version, instr(product_version, ".")+1) as no_os_major_substr
FROM file
WHERE path = "c:\windows\system32\wininit.exe"
)
SELECT substr(no_os_major_substr, instr(no_os_major_substr, ".")+1) as no_os_minor_substr, substr(no_os_major_substr, 0, instr(no_os_major_substr, ".")) as os_minor, os_major
FROM os_major
)
SELECT
CAST(substr(no_os_minor_substr, instr(no_os_minor_substr, ".")+1) AS INTEGER) AS product_minor,
CAST(substr(no_os_minor_substr, 0, instr(no_os_minor_substr, ".")) AS INTEGER) AS product_major,
CAST(os_minor AS INTEGER) AS os_minor,
CAST(os_major AS INTEGER) AS os_major
FROM os_minor
)
SELECT
CASE
WHEN NOT ((os_major = 6 AND os_minor = 3) OR (os_major = 6 AND os_minor = 2) OR (os_major = 10 AND os_minor = 0))
THEN "not supported"
WHEN os_major = 6 AND os_minor = 3 AND product_major = 9600 AND product_minor >= 20716 THEN "patched"
WHEN os_major = 6 AND os_minor = 2 AND product_major = 9200 AND product_minor >= 24011 THEN "patched"
WHEN (
(product_major = 14393 AND product_minor >= 5582)
OR
(product_major = 10240 AND product_minor >= 19624)
OR
(product_major = 19041 AND product_minor >= 1620)
OR
(product_major = 22621 AND product_minor >= 963)
OR
(product_major = 22000 AND product_minor >= 1335)
OR
(product_major = 20348 AND product_minor >= 1366)
OR
(product_major = 17763 AND product_minor >= 3770)
)
THEN
"patched"
ELSE
"not patched"
Resumen
Aunque esta vulnerabilidad no es crítica (es solo un apagado remoto, y ni siquiera uno no autenticado en el momento), demuestra el potencial destructivo inherente de MS-RPC, ya que está integrado en los servicios esenciales del sistema operativo Windows. Además, a diferencia de otros tipos de vulnerabilidades, RPC apenas hay conjeturas: todas las cartas están sobre la mesa (o sea, sobre la tabla binaria); solo tiene que saber interpretarlas.
La búsqueda de aprovechar estas funciones críticas es precisamente la razón por la que continuamos nuestra amplia investigación sobre MS-RPC. A pesar de que su uso es generalizado, no se ha investigado lo suficiente. Vulnerabilidades como la que exponemos hoy demuestran por qué este tipo de trabajo es necesario, y esperamos que otros expertos trabajen en MS-RPC si así lo desean.
Esta vulnerabilidad se divulgó de forma responsable a finales de agosto y se solucionó en el Patch Tuesday de diciembre de 2022.