
Análisis técnico detallado e implementación de exploit para CVE-2015-0057, una vulnerabilidad use-after-free en win32k.sys, que cubre sistemas Windows de 32 y 64 bits desde XP hasta 8.1.
Autor: Aaron Adams
Traducción: 55-AA
Nota del traductor: Algunas partes de este artículo se han traducido libremente; consulte el original si tiene dudas.
Terminología:
A principios de este año, me topé con una interesante vulnerabilidad en win32k.sys (CVE-2015-0057) y logré una explotación estable en sistemas de 32 y 64 bits, que abarca desde XP hasta Windows 8.1 (con algunas excepciones). Este artículo describe en detalle cómo logré la explotación en ambas plataformas, y al final incluyo algunas cosas adicionales. También se describe cómo explotar con permisos de baja integridad en Windows 8.1 con SMEP habilitado.
Este artículo es largo; me esfuerzo por proporcionar tantos detalles como sea posible para mostrar la complejidad de explotar esta vulnerabilidad, en lugar de ocultarlos, aunque también omito algunos. Espero que estos detalles sean útiles para todos.
El 10 de febrero de 2015, Microsoft publicó los detalles de MS15-010. Este bug fue descubierto por Udi Yavo de enSilo. Udi dio un excelente análisis en el blog breaking malware: "one bit rule-bypassing windows 10 protections using single bit". Recomiendo leerlo detenidamente para comprender mejor el bug, aunque en este artículo daré todos los detalles posibles, especialmente sobre los obstáculos que deben superarse al desencadenar la vulnerabilidad. La explotación de este bug es muy interesante, y muchos detalles provienen del blog de Udi; a continuación su declaración:
Divulgación razonable: aunque este blog es técnico, no revelaremos ningún código ni detalles completos para evitar que cualquier experto técnico reproduzca la explotación.
Como recompensa adicional por explotar esta vulnerabilidad, obtuvimos una evolución de Pokémon: el Pokémon Técnico. Creo que debo darle crédito a Udi por descubrir el bug, proporcionar la información relevante en su blog y los detalles para explotarlo; todo eso fue muy útil.
Anteriormente, nunca había explotado una vulnerabilidad de win32k.sys, y no estaba familiarizado con las devoluciones de llamada en modo usuario ni con muchas de las API relacionadas, así que también agradezco a conocidos investigadores de seguridad por los recursos novedosos que han publicado en línea, como Skywing, Tarjei Mandt, Alex Ionescu y j00ru. Todas estas personas merecen reconocimiento por proporcionar tanta información técnica de forma abierta. El artículo que más utilicé fue el de Tarjei Mandt: Win32k.sys exploitation paper.
Mientras escribía esta explotación, un excelente ingeniero inverso implementó una explotación estable de CVE-2015-1701, y el código de ejemplo sobre devoluciones de llamada en modo usuario fue muy útil; gracias a ese autor.
Vale la pena señalar que mi análisis a continuación se realizó en Windows 7, porque parece ser la única versión donde todas las estructuras en win32k.sys tienen sus correspondientes símbolos. La mayoría de estos símbolos se pueden usar para estructuras en otras versiones de Win32k.sys. Por alguna razón, Microsoft eliminó estos símbolos a partir de Windows 8.
Finalmente, quiero decir que mi método de explotación es bastante complejo. Es muy posible que exista un método más fácil que no he descubierto. Me encantaría escuchar si alguien usa un método diferente. De cualquier manera, espero que todo esto sea útil para estudiar vulnerabilidades en win32k.sys.
A continuación vemos el bug en el desensamblado de win32k!xxxEnableWndSBArrows, un bug bastante sutil:
Sin parche:
.text:FFFFF97FFF1B157D mov r8d, r13d
.text:FFFFF97FFF1B1580 mov rdx, r14
.text:FFFFF97FFF1B1583 call xxxDrawScrollBar ; desencadena callback en modo usuario
.text:FFFFF97FFF1B1588 jmp short loc_FFFFF97FFF1B1519
[...]
.text:FFFFF97FFF1B1519 mov eax, [rbx] ; referencia al puntero tagSBINFO sin comprobación
.text:FFFFF97FFF1B151B mov ebp, 0FFFFFFFBh
.text:FFFFF97FFF1B1520 xor eax, esi
En el código anterior, Win32K!xxxdrawscrollbar puede, en las circunstancias adecuadas, hacer una devolución de llamada al espacio de usuario; en el código de usuario, el puntero tagSBINFO podría ser liberado por el atacante, y al regresar al código anterior, la instrucción en 0xFFFFF97FFF1B1519 hará referencia a un puntero inválido.
Con parche:
.text:FFFFF97FFF1D69C3 xor r8d, r8d
.text:FFFFF97FFF1D69C6 mov rdx, rbp
.text:FFFFF97FFF1D69C9 call xxxDrawScrollBar ; desencadena callback en modo usuario
.text:FFFFF97FFF1D69CE cmp rbx, [rdi+0B0h] ; comprueba que el puntero tagSBINFO sea correcto
---.text:FFFFF97FFF1D69D5 jz short loc_FFFFF97FFF1D69E4; si es correcto, continúa el flujo original
| .text:FFFFF97FFF1D69D7 mov rcx, rbp
| .text:FFFFF97FFF1D69DA call _ReleaseDC
| .text:FFFFF97FFF1D69DF jmp loc_FFFFF97FFF1D6958 ; salta a la salida de la función
|->.text:FFFFF97FFF1D69E4 mov eax, [rbx] ; usa el puntero tagSBINFO correcto con seguridad
.text:FFFFF97FFF1D69E6 xor eax, r14d
En la versión con parche, vemos que se comprueba la validez del puntero tagSBINFO antes de usarlo. La información de la estructura relacionada se proporcionará más adelante.
Para implementar esta explotación, realizamos múltiples corrupciones. En una de ellas se desencadena la vulnerabilidad.
La causa técnica de este bug es un UAF (use-after-free) en el montón del escritorio. Al principio, esto me confundió, ya que no estaba familiarizado con el mecanismo de devolución de llamada en modo usuario de win32k.sys ni con su funcionamiento. Por lo tanto, pensé que era una condición de carrera con bloqueos que provocaba el UAF. De hecho, los bloqueos de esa estructura se usan correctamente y el flujo es el esperado. En resumen, la verdadera razón del problema es:
Eso es todo; sin considerar la devolución de llamada en modo usuario, esta fase es bastante clara.