La lucha de Windows contra la ejecución de código: Éxitos y fracasos (II)

Sergio de los Santos    30 julio, 2018
La lucha de Windows contra la ejecución de código: Éxitos y fracasos (II)
Es complicado entender todo el entramado de seguridad que está montando Microsoft alrededor de Windows 10, con medidas de seguridad cada vez más integradas y complejas. Es muy positivo, sino fuera porque algunas medidas están adquiriendo una complejidad tal que para el usuario medio (poco instruido en seguridad) le resultan poco menos que esotéricas, incompresibles y por tanto, inútiles si no están activadas por defecto (que muchas no lo están). Hablamos en la anterior entrega de Windows Defender Exploit Guard englobada dentro de lo que Microsoft llama Windows Defender. Veamos concretamente en estas entradas, qué técnicas a bajo nivel utiliza.

La tecnología específica de Windows Defender para evitar los exploits es «Exploit Guard». A su vez, dentro de esta fórmula, Exploit Guard aborda cuatro puntos de control:

  • Exploit Protection
  • Attack surface reduction
  • Network Protection
  • Controlled Folder Access

Si en la entrada anterior hablamos del resto de puntos de control, nos centramos ahora en Exploit Protection. Vamos a cubrir algunas técnicas de protección contra exploits, heredada de lo que se comenzó con EMET y hoy en día integrada en Windows 10.

MemGC
UAF (Use after free) es un método muy popular para conseguir ejecutar código (como puede ser un desbordamiento de pila, por poner un ejemplo). Menos cómodo de explotar, pero muy conocido y usado. Se produce a bajo nivel en la pila de memoria, cuando un puntero queda libre y es posible reaprovecharlo cambiando la estructura en memoria a la que apunta con otra información. Así, cuando el programa llame de nuevo a ese puntero (función) después de haber sido liberada de memoria, se puede ejecutar código porque se ha puesto shellcode ahí (por ejemplo haciendo heap spraying).
Internet Explorer sufría a menudo vulnerabilidades de ejecución de código como consecuencia de este problema. Una vez limitados y cada vez más escasos los desbordamientos «de toda la vida», los atacantes buscaban estos fallos para conseguir ejecutar código. Así que Microsoft tuvo que poner foco en una solución para los UAF. Se le ocurrieron varias ideas desde 2014, que introdujeron sin hacer mucho ruido ni grandes anuncios:

  • En julio de 2014 introdujo el concepto de delay free o MemoryProtection. Otra mejora a la hora de liberar la memoria que pretendía reducir el riesgo de que las vulnerabilidades provocadas por «use after free» fueran explotadas fácilmente. De forma básica, digamos que el navegador se aseguraba de que quedaba liberado el espacio del objeto en la pila y que no pudiera ser usado inmediatamente por el atacante, introduciendo una función de «delay» o retraso que impedía que el atacante ocupase el espacio a tiempo con su propio código.
Delay free o MemoryProtection código imagen
Fuente: TrendMicro

Delay free era interesante e innovadora (el aislamiento del heap no tanto). Y funcionaba. Se redujeron el número de fallos explotables por estas causas. Gracias a estas dos estrategias, se cerraron muchas puertas. Pero no eran infalibles (como casi nada). Ya en febrero de 2015, en FireEye detectaron exploits «in the wild» que conseguían eludir estas mitigaciones. Los investigadores Brian Gorenc, AbdulAziz Hariri y Simon Zuckerbraun demostraron en junio de 2015 cómo conseguir eludiarlas con todo lujo de detalles. Google llegó a una conclusión parecida a principios de enero… Pero esta tecnología tuvo un problema mayor que el simple «bypass» en sí. La introducción de estas técnicas, de rebote, hizo que la aleatorización de ASLR en máquinas de 32 bits, se redujera bastante y por tanto, abrió todo un ventanal para otros tipos de ataque. No solo no les funcionó sino que les salió rana.

Y así es como se creó a partir de aquí un nuevo concepto: Memory GC (MemGC). Se trata de la evolución de lo anterior para Edge e IE bajo Windows 10, aprovechando el salto cualitiativo hacia Edge y Windows 10, rompiendo con todo lo anterior. Se trata de un recolector de basura (Garbaje Collector) que, en vez de dejar que el programador se encargue (al final, se le pasa…), lo intenta hacer de forma automática. Así, comprueba si hay alguna referencia a un objeto a memoria, y si es así, la libera automáticamente. Se corregían así la facilidad para eludir MemoryProtection incluyendo el heap entre lo que se monitorizaba y un uso más eficiente del recolector, por ejemplo. Tiene sus debilidades, como se ha demostrado en la Black Hat de 2017  y en la RSA. Actualmente, aunque con ciertas debilidades, es un buena contramedida.

Control Flow Guard
También introducida alrededor de 2015, se enmarca junto con otras técnicas dentro de la política de CFI (Control Flow Integrity). Se aplica al programa que se crea cuando se compila con Visual Studio, así queda marcado en la cabecera del ejecutable y en el momento de ejecución, el sistema opesativo realiza la protección. Es un vigilante que hace que los punteros a funciones de ejecución, solo lo hagan a los puntos de entradas de funciones previamente «registrados» y que no venga nadie a inyectar código en cualquier parte y saltar a él para tomar el control del sistema. Establece unas restricciones que limitan desde dónde (en su propio código) una aplicación puede o no ejecutar código. O sea, ataca los fundamentos de la explotación. Es sólo válido en Windows 10 y Windows 8.1 Update 3 (actualización KB3000850 ).

Resulta interesante el hecho de que no solo es posible habilitar CFG a nivel global en la aplicación, sino que puede hacerse a nivel código. O sea, es posible intercalar fragmentos de código con «CFG activo» y otros sin CFG, algo que ofrece una flexibilidad mayor a la hora de desplegar aplicaciones así protegidas. A pesar de esto, Microsoft recomienda habilitarlo a nivel global.

Veamos más detalles. La implementación de CFG se basa en la idea de que las llamadas indirectas a instrucciones deben realizarse únicamente a la posición inicial de memoria de una función válida por lo que CFG se centra en mitigar el uso de llamadas indirectas que puedan ser explotadas para realizar llamadas a posiciones no permitidas de memoria. Para conseguirlo, previamente es necesario identificar las posiciones de memoria no válidas e interponerse en su camino para verificar si están en la lista. CFG permite además realizar una especie de lista blanca y negra para controlar el conjunto objetivo de llamadas indirectas que son consideradas válidas por CFG, de modo que todas las demás no podrán ser accedidas.

CFG comprobación imagen
CFG comprobando si esa llamada está autorizada antes de ejecutarla. Fuente: TrendMicro
Este método resulta interesante (y eficaz, realmente supone una mejora en la seguridad) aunque ya ha sido eludido por atacantes (como el resto). Existen varias técnicas para eludir su protección. Francisco Falcón describe una de ellas, y se basa en la premisa de que el código generado en tiempo de ejecución no puede ser protegido por CFG.

Zhang Yunhai por su parte en la Black Hat de 2015 recopiló algunas de estas técnicas para mostrar finalmente lo que denominó «Universal Bypass» cuyo objetivo era poder realizar llamadas indirectas a cualquier dirección de memoria, aunque Microsoft solucionó rápidamente esta vulnerabilidad en marzo de 2015 poco después de ser reportada. En otra conferencia ese mismo año (DerbyCon) Jared DeMott (que ya eludió toda protección de EMET en su día) informó de cómo eludir CFG. Finalmente CFG también ha salido un poco rana. A pesar de los buenos resultados en general de la política de expotación desde 2006 (como muestra Microsoft en su propia página), en junio de 2018 retiraba CFG de su política de recompensas por encontrar fallos. Leyendo entre líneas, se comprende cómo CFG ha sido diseñado con un problema de diseño que lo hace poco eficiente y por tanto ya apenas tiene mérito el eludirlo. Recogen el guante y replantearán en el futuro esta técnica.

porcentaje fallos ejecución código y elevación privilegios explotados tras 30 días de parche imagen
Obsérvese que, aunque baja el número de fallos de ejecución de código y elevación de privilegios explotados tras 30 días de parche, aumenta considerablemente el número global de los que no se tiene constancia de que hayan sido explotados.
De 84 en 2006 a 398 en 2017.

Seguiremos analizando en las siguientes entradas qué otras técnicas anti-explotación (heredadas o no de EMET) mantiene Windows 10.

Deja una respuesta

Tu dirección de correo electrónico no será publicada.