Uso indebido de enclaves de VBS para crear malware evasivo

Ori David

escrito por

Ori David

February 25, 2025

Ori David

escrito por

Ori David

Ori David es experto en seguridad en Akamai. Su investigación se centra en la seguridad ofensiva, el análisis de malware y la búsqueda de amenazas. 

El malware que consigue ejecutarse dentro de un enclave podría ser invisible para la detección y el análisis forense basados en memoria.
El malware que consigue ejecutarse dentro de un enclave podría ser invisible para la detección y el análisis forense basados en memoria.

Contenido

Introducción

La seguridad basada en la virtualización (VBS) es uno de los avances en seguridad más recientes y fascinantes. La capacidad de aislar los componentes críticos del sistema operativo ha permitido a Microsoft lograr mejoras sustanciales en la seguridad con funciones como Credential Guard y HVCI (Integridad de código protegida por hipervisor).

Una función que suele pasar desapercibida y que se activa con VBS son los enclaves de VBS, una tecnología que permite el aislamiento de una región de un proceso, haciéndola inaccesible a otros procesos, al propio proceso e incluso al kernel. 

Los enclaves de VBS pueden tener una amplia gama de aplicaciones de seguridad, y Microsoft las utiliza para implementar varios servicios importantes, incluyendo la controvertida función Recuerdos. Además, Microsoft apoya el desarrollo de enclaves de VBS de terceros y promueve activamente su adopción.

Aunque los enclaves pueden ayudar a proteger los sistemas, también pueden resultar muy atractivos para los atacantes: un malware que consigue ejecutarse dentro de un enclave podría ser invisible para la detección y el análisis forense basados en memoria.

Nos propusimos explorar los enclaves de VBS y comprender cómo se podrían utilizar con fines malintencionados, y en esta entrada de blog detallaremos nuestros principales hallazgos. Nos sumergiremos en los enclaves de VBS mediante la exploración de comportamientos no documentados previamente, la descripción de diferentes escenarios que hacen posible que los atacantes puedan ejecutar código malicioso en ellos, y el análisis de las diversas técnicas que puede utilizar el "malware de enclave".

También presentaremos Mirage, una técnica de evasión de memoria basada en un nuevo enfoque que denominamos "traiga su propio enclave vulnerable". Exploraremos de qué modo pueden los atacantes utilizar versiones antiguas y vulnerables de enclaves legítimos para implementar esta técnica de evasión sigilosa.

Niveles de confianza virtual 

Windows ha confiado tradicionalmente en los niveles de anillo del procesador para evitar que las aplicaciones de usuario manipularan el sistema operativo. Esta función de hardware permite separar el sistema operativo de las aplicaciones de usuario: el kernel se ejecuta en ring0, aislado de las aplicaciones de modo de usuario de ring3. El problema de este enfoque es que proporciona a los atacantes una ruta relativamente fácil para comprometer el sistema operativo: los ataques al kernel.

El kernel de Windows expone una superficie de ataque muy amplia. Una enorme lista de controladores de terceros, junto con la amplia variedad de servicios expuestos por el propio kernel, da lugar al desarrollo de un flujo aparentemente constante de ataques al kernel. Mediante un ataque de este tipo, un atacante podría controlar todos los aspectos del sistema operativo. El límite del modo usuario/kernel era insuficiente.

Para cerrar esta brecha, Microsoft introdujo un límite de seguridad adicional en el sistema operativo en forma de niveles de confianza virtual (VTL). Los privilegios de VTL se basan en el acceso a la memoria: cada nivel de confianza proporciona a las entidades que se ejecutan en él diferentes permisos de acceso a la memoria física. Entre otras cosas, estos permisos garantizan que los VTL inferiores no puedan acceder a la memoria de los superiores.

De forma similar a la arquitectura tradicional en anillo del procesador, los VTL separan el sistema operativo en diferentes "modos" de ejecución, comenzando desde el VTL0 y terminando (posiblemente) en el VTL16. A diferencia de los anillos de procesador, donde ring0 es el que tiene más privilegios, los VTL superiores tienen más privilegios que los inferiores.

Windows utiliza actualmente dos niveles de confianza principales: el VTL0 y el VTL1 (también se utiliza el VTL2, pero está fuera del ámbito de esta entrada de blog). El VTL0 se utiliza para ejecutar los componentes tradicionales del sistema operativo, incluidas las aplicaciones de modo de usuario y Kernel. El VTL1, que tiene más privilegios que el VTL0, crea dos nuevos modos de ejecución: modo de kernel seguro y modo de usuario aislado.

Modo de kernel seguro

El modo de kernel seguro hace referencia al modo de ejecución ring0 VTL1. Este modo se utiliza para ejecutar el kernel seguro, un kernel que se ejecuta en el VTL1 y, por lo tanto, tiene más privilegios que el kernel normal. Con estos privilegios, el kernel seguro puede aplicar políticas en el kernel normal y restringir su acceso a regiones de memoria confidenciales.

Como señalamos, el kernel expone una amplia superficie de ataque y es vulnerable a los ataques. Si se eliminan algunos privilegios del kernel y se otorgan al kernel seguro, podemos reducir el impacto de un ataque al kernel.

En teoría, un ataque al kernel seguro también haría posible que un atacante pusiera en peligro todo el sistema. A pesar de ello, este escenario es mucho menos probable porque el kernel seguro es muy restrictivo y no admite controladores de terceros, lo que crea una superficie de ataque significativamente reducida.

Modo de usuario aislado

El nivel VTL1 también crea otro interesante modo de ejecución denominado IUM (Modo de usuario aislado), que hace referencia al modo de ejecución ring3 VTL1. El modo IUM se utiliza para ejecutar procesos seguros, un tipo especial de proceso en el modo de usuario que utiliza las capacidades de aislamiento de memoria de VTL1. No es posible acceder a la memoria interna del IUM mediante ningún código de VTL0, incluido el kernel normal. Este modo de ejecución es el pilar de funciones basadas en el aislamiento como Credential Guard.

En resumen, la incorporación del VTL0/1 da como resultado cuatro modos de ejecución (Figura 1).

  • Ring0 VTL0: modo de kernel normal
  • Ring0 VTL1: modo de kernel seguro
  • Ring3 VTL0: modo de usuario normal
  • Ring3 VTL1: modo de usuario aislado
En resumen, la incorporación de VTL0/1 da como resultado cuatro modos de ejecución (Figura 1). Fig. 1: Modos de ejecución creados mediante la adición de VTL

¿Qué son los enclaves de VBS?

Otra función que se activa a través del modo IUM son los enclaves de VBS. Un enclave de VBS es una sección de un proceso en el modo de usuario que reside en el modo IUM y en el que podemos cargar archivos DLL denominados "módulos de enclave".

Cuando se carga un módulo en un enclave, este se convierte en un "entorno de ejecución de confianza": los datos y el código del enclave son inaccesibles para cualquier elemento que se ejecute en el VTL0 y, por lo tanto, no se pueden manipular ni robar (Figura 2). Esta función es útil para aislar las operaciones confidenciales para protegerlas de los atacantes que pueden poner en peligro el sistema.

Cuando se carga un módulo en un enclave, este se convierte en un "entorno de ejecución de confianza": los datos y el código del enclave son inaccesibles para cualquier elemento que se ejecute en VTL0 y, por lo tanto, no se pueden manipular ni robar (Figura 2). Fig. 2: Diseño de memoria de enclave de VBS. Fuente: https://learn.microsoft.com/en-us/windows/win32/trusted-execution/vbs-enclaves

Un proceso en el modo de usuario puede llamar a las API de Windows dedicadas para crear un enclave, cargar un módulo en él e inicializarlo. Un módulo de enclave tiene la forma de un DLL que se ha compilado específicamente para este propósito. Una vez inicializado el enclave, el proceso en el modo de usuario de alojamiento no puede acceder a su memoria y solo puede interactuar con él invocando las funciones exportadas por sus módulos mediante la API CallEnclave .

La figura 3 es un ejemplo de código que implementa este proceso (Microsoft proporciona un ejemplo más detallado).

La figura 3 es un ejemplo de código que implementa este proceso (Microsoft proporciona un ejemplo más detallado). Fig. 3: Ejemplo de código de creación de enclave de VBS

Dado que Microsoft pretende restringir el acceso a VTL1 tanto como sea posible, la carga de una DLL en un enclave requiere que esté firmado correctamente mediante un certificado especial emitido por Microsoft. Cualquier intento de cargar un módulo sin dicha firma fallará. La opción de firmar módulos de enclave solo se delega a terceros de confianza. Curiosamente, no hay restricciones sobre quién puede cargar estos módulos; mientras estén firmados, cualquier proceso puede cargar módulos arbitrarios en un enclave.

Los módulos de enclave están destinados a servir como pequeñas "unidades computacionales" que tienen una capacidad muy limitada para interactuar con el sistema o dañarlo. Por esta razón, están limitados a usar un conjunto mínimo de API, lo que les impide acceder a la mayoría de los componentes del sistema operativo.

Las API disponibles en el interior de los enclaves se importan desde bibliotecas dedicadas cargadas en el VTL1. Por ejemplo, mientras que los procesos normales dependen de la biblioteca ntdll.dll para solicitar servicios del sistema operativo, los módulos de enclave usan vertdll.dll , un ntdll "sustituto" que se utiliza para comunicarse con el kernel seguro a través de syscalls.

Malware de enclave

El concepto de malware de enclave puede ser ciertamente atractivo para los atacantes, ya que proporciona dos ventajas significativas:

  1. Ejecución en una región de memoria aislada: el espacio de direcciones de los enclaves es inaccesible para todo lo se ejecute en el VTL0, incluidas las EDR y las herramientas de análisis, lo que hace que la detección sea mucho más compleja.

  2. Llamadas de API no rastreables: las llamadas de API realizadas desde el interior de un enclave pueden ser invisibles para las EDR. Las EDR supervisan las API en el modo de usuario colocando interceptores dentro de las bibliotecas del sistema, y emplean controladores para supervisar la actividad en el kernel. Estas técnicas no pueden detectar las llamadas de API activadas desde un enclave, ya que las llamadas de enclave se realizan desde el VTL1 y no pasan por ninguno de estos componentes del VTL0 con interceptores.

En la figura 4 se muestra esta ventaja: un proceso normal que invoca una API de Windows se puede supervisar mediante un interceptor en el interior de NTDLL o del propio kernel. Sin embargo, un módulo de enclave pasa a través de vertdll residente en el VTL1 e invoca llamadas al kernel seguro, ambos inaccesibles para las EDR.

La figura 4 muestra esta ventaja: Un proceso normal que invoca una API de Windows se puede supervisar mediante un interceptor en el interior de NTDLL o del propio kernel. Fig. 4: Diferencias entre el modo de usuario normal y los procesos de llamada de API del IUM

Reconociendo este potencial, comenzamos a investigar el concepto de malware de enclave. Para usar los enclaves con esta finalidad, deben plantearse dos cuestiones:

  1. ¿Cómo pueden los atacantes ejecutar código malicioso dentro de un enclave?
  2. ¿Qué técnicas pueden emplear los atacantes una vez que se encuentran dentro de un enclave?

¿Cómo pueden los atacantes ejecutar código malicioso dentro de un enclave?

Como ya mencionamos anteriormente, los módulos de enclave tienen que estar firmados con un certificado emitido por Microsoft para que puedan cargarse, lo que significa que solo las entidades aprobadas por Microsoft podrían ejecutar su propio código dentro de un enclave. A pesar de ello, los atacantes siguen teniendo algunas opciones.

En primer lugar, un atacante puede aprovechar una vulnerabilidad del sistema operativo. Un ejemplo de esto fue la CVE-2024-49706, descubierta por Alex Ionescu, (mientras trabajaba para Winsider Seminars & Solutions), que podría permitir a un atacante cargar un módulo sin firmar en un enclave. Microsoft ha corregido esta vulnerabilidad, pero los atacantes motivados pueden identificar errores similares en el futuro.

Un segundo enfoque directo sería obtener una firma legítima, lo que podría ser posible, ya que Microsoft expone la firma de enclave a terceros a través de la plataforma Trusted Signing . Aunque ciertamente no es algo sencillo, un atacante avanzado podría obtener acceso a una entidad de Trusted Signing y firmar sus propios enclaves.

Además de estas dos opciones, hemos analizado dos técnicas adicionales que podrían permitir a los atacantes ejecutar código dentro de un enclave de VBS: el uso indebido de enclaves depurables y la explotación de enclaves vulnerables.

Uso indebido de enclave depurables

Al crear un módulo de enclave de VBS, el desarrollador puede configurarlo de modo que sea depurable. La compilación de un módulo con esta configuración permite que pueda depurarse. Como los módulos de enclave se ejecutan en el VTL1, normalmente no es posible depurarlos, ya que el depurador no puede acceder a la memoria del enclave para recuperar datos o colocar puntos de interrupción. La figura 5 muestra un ejemplo de un depurador que no puede leer de una dirección de memoria dentro de un enclave.

La figura 5 muestra un ejemplo de un depurador que no puede leer de una dirección de memoria dentro de un enclave. Fig. 5: Intento de leer la memoria de un módulo de enclave de VBS no depurable

Curiosamente, cuando se ejecuta un módulo de enclave depurable, este sigue cargado en el VTL1. Para habilitar la depuración, el kernel seguro implementa algunas excepciones que se aplican a los módulos de enclave depurables. Por ejemplo, al intentar leer de la memoria de un módulo de este tipo de módulo, el kernel normal emite una llamada a la llamada al kernel seguro SkmmDebugReadWriteMemory , que verifica que el módulo de destino es efectivamente depurable, antes de realizar la operación solicitada.

Un ejemplo de esto se puede ver en la figura 6: después de cargar un módulo de enclave depurable, el depurador puede leer con éxito de la memoria del enclave.

Un ejemplo de esto se puede ver en la figura 6: después de cargar un módulo de enclave depurable, el depurador puede leer con éxito de la memoria del enclave. Fig. 6: Lectura correcta de la misma dirección que en la figura 5 cuando el módulo de enclave es depurable

De manera similar, las permisos de memoria dentro de un módulo de enclave depurable también pueden ser modificados por el proceso del VTL0 (una excepción implementada en la llamada al kernel seguro SkmmDebugProtectVirtualMemory ).

Microsoft recomienda encarecidamente a los desarrolladores que no envíen módulos de enclave depurables, ya que al hacerlo se desvirtúa la finalidad fundamental de los enclaves, que es aislar una sección de memoria del VTL0 (Figura 7). Usar un módulo depurable significa que los datos que este gestiona pueden verse fácilmente expuestos.

Microsoft recomienda encarecidamente a los desarrolladores que no envíen módulos de enclave depurables, ya que al hacerlo se desvirtúa la finalidad fundamental de los enclaves, que es aislar una sección de memoria del VTL0 (Figura 7). Fig. 7: Recomendación de Microsoft sobre los módulos de enclave depurables. Fuente: https://learn.microsoft.com/en-us/windows/win32/trusted-execution/vbs-enclaves-dev-guide#:~:text=Step%204%3A%20Debugging%20VBS%20enclaves

Los riesgos de una aplicación de producción que ejecuta un módulo de enclave depurable son claros, pero los atacantes pueden hacer un uso indebido de ellos con otra finalidad: ejecutar código no firmado en el VTL1. Si un atacante obtiene cualquier módulo de enclave firmado depurable, puede lograr la ejecución de código en el VTL1 siguiendo estos cuatro pasos:

  1. Obtener la dirección de una rutina dentro del módulo de enclave utilizando GetProcAddress
  2. Cambiar la protección de memoria de la rutina a RWX
  3. Sobrescribir el código de la rutina con código de shell arbitrario
  4. Activar la rutina con CallEnclave

La figura 8 muestra el código que implementa este proceso.

La figura 8 muestra el código que implementa este proceso. Fig. 8: Código de ejemplo que ejecuta código de shell no firmado dentro de un enclave.

El problema evidente desde el punto de vista del atacante es que se trata de una espada de doble filo: del mismo modo que el atacante puede acceder a la memoria del enclave, una EDR también puede hacerlo. A pesar de ello, tiene la ventaja de evitar la supervisión de API: las llamadas a las API realizadas por el módulo de enclave seguirán pasando por las DLL y el kernel seguro del VTL1, lo que limita la visibilidad de las EDR.

En general, este método podría ser útil para crear un implante sigiloso "semi-VTL1" que aproveche algunas de las ventajas obtenidas al ejecutarse dentro de un enclave.

Hemos intentado utilizar VirusTotal y otras fuentes para identificar un módulo de enclave firmado y depurable, pero en el momento de redacción de este documento, no habíamos obtenido ningún resultado. Sin embargo, creemos que es seguro suponer que con el tiempo suficiente y una adopción más amplia de la tecnología de enclave, algunos se filtrarán finalmente.

traiga su propio enclave vulnerable

Como ya hemos comentado, Windows utiliza firmas para evitar la carga de enclaves que no son de confianza en el VTL1. Este enfoque no es exclusivo de los enclaves, el concepto surgió de la aplicación de la firma del controlador (DSE), que impedía que los controladores que no son de confianza se ejecutaran en el kernel de Windows.

Para superar esta aplicación, los atacantes comenzaron a utilizar la técnica traiga su propio conductor vulnerable (BYOVD). Es decir, un atacante no puede cargar su propio controlador, por lo que carga en su lugar un controlador firmado legítimo con una vulnerabilidad conocida. Posteriormente, puede explotar esta vulnerabilidad para ejecutar código sin firmar en el kernel.

Quisimos explorar el siguiente enfoque en el contexto de los enclaves: ¿Podemos hacer un uso indebido de un módulo de enclave firmado y vulnerable para ejecutar código en el IUM?

El primer paso era encontrar un enclave de este tipo, lo que nos llevó rápidamente a la CVE-2023-36880 , una vulnerabilidad en un módulo de enclave de VBS que se utiliza en Microsoft Edge. Esta vulnerabilidad permite a un atacante leer y escribir datos arbitrarios dentro del enclave. Aunque Microsoft calificó esta vulnerabilidad como una vulnerabilidad de revelación de información, las notas también especifican que podría provocar a una ejecución de código limitada (Figura 9).

Aunque Microsoft calificó esta vulnerabilidad como una vulnerabilidad de revelación de información, las notas también especifican que podría provocar a una ejecución de código limitada (Figura 9). Fig. 9: Notas del parche de Microsoft para CVE-2023-36880 que indican que podría provocar la ejecución de código

Esta vulnerabilidad fue descubierta por Alex Gough, del equipo de seguridad de Chrome, que también compartió una prueba de concepto para explotarla. Después de encontrar una versión vulnerable de este enclave en VirusTotal, comenzamos a intentar explotarlo para lograr la ejecución del código.

Nuestra idea era hacer un uso indebido de la primitiva de lectura/escritura para sobrescribir la pila del enclave con una cadena ROP, lo que finalmente nos permitiría ejecutar código de shell dentro del enclave. Mientras explorábamos esta opción, descubrimos un hecho interesante: los enclaves están protegidos frente a la ejecución de código no firmado mediante la protección contra código arbitrario (ACG).

La ACG es una mitigación de seguridad diseñada para bloquear la ejecución de código generado dinámicamente (código creado en tiempo de ejecución en lugar de formar parte del ejecutable del proceso original o sus DLL). La ACG se implementa aplicando dos reglas:

  1. No se pueden generar nuevas páginas ejecutables después de la carga inicial del proceso
  2. No se puede escribir en una página que ya es ejecutable

La ACG se aplica por defecto en el kernel normal, pero en el modo de usuario solo se aplica a los procesos que están configurados para utilizarla. Nuestra investigación demostró que para los procesos del modo IUM, que incluyen los enclaves, la ACG parece aplicarse automáticamente, como lo hace en el kernel normal.

Podemos observar esto intentando asignar una nueva página RWX dentro de un enclave mediante VirtualAlloc: la operación falla con el código de error 0x677, STATUS_DYNAMIC_CODE_BLOCKED (Figura 10). Si se intenta utilizar VirtualProtect para modificar los permisos de una página ejecutable o para convertir una página en ejecutable, se obtendrá el mismo resultado.

Podemos observar esto intentando asignar una nueva página RWX dentro de un enclave mediante VirtualAlloc: la operación falla con el código de error 0x677, STATUS_DYNAMIC_CODE_BLOCKED (Figura 10). Fig. 10: Intento de asignar una página RWX dentro de un enclave

Para entender este comportamiento, podemos examinar SecureKernel!NtAllocateVirtualMemoryEx, la función de kernel seguro que gestiona la asignación dinámica de memoria en el IUM. Esta función evalúa la máscara de protección solicitada y, si se solicita una página ejecutable, devuelve el error de ACG STATUS_DYNAMIC_CODE_BLOCKED. Se implementan comprobaciones similares en SkmmProtectVirtualMemory para evitar que se realicen cambios en las páginas de IUM existentes (Figura 11).

Se implementan comprobaciones similares en SkmmProtectVirtualMemory para evitar que se realicen cambios en las páginas de IUM existentes (Figura 11). Fig. 11: El código de NtAllocateVirtualMemoryEx rechaza una asignación de memoria ejecutable

No hemos encontrado un método para omitir la ACG dentro de un enclave y cargar código sin firmar en él. En teoría, un ataque de ROP completo sería posible, lo que permitiría a los atacantes, por ejemplo, invocar API arbitrarias en el VTL1, pero no hemos seguido esta dirección. A pesar de ello, hemos conseguido identificar otra aplicación interesante para enclaves vulnerables, que analizaremos a continuación en esta publicación.

¿Qué técnicas pueden emplear los atacantes una vez que se encuentran dentro de un enclave?

Con el conocimiento de que los enclaves pueden tener mucho potencial para la actividad maliciosa y que su puesta en marcha podría ser posible para un atacante motivado, la siguiente pregunta que nos planteamos fue qué técnicas podrían estar disponibles para este tipo de malware.

Lo más sencillo sería utilizar los enclaves como estaban destinados a usarse. Del mismo modo que un enclave puede proteger los datos confidenciales de los atacantes, estos también pueden utilizarlos para ocultar sus propios "secretos" de las entidades del VTL0.

Esto puede resultar útil en muchos escenarios, como el almacenamiento de cargas útiles fuera del alcance de las EDR, el sellado de claves de cifrado ocultas a los analistas o el mantenimiento de la configuración de malware confidencial fuera de los volcados de memoria.

En cuanto a las opciones más avanzadas, muchas técnicas tradicionales de malware son imposibles de implementar dentro de un enclave. Dado que los enclaves están restringidos a un subconjunto limitado de API del sistema, se impide que interactúen con componentes clave del sistema operativo, como los archivos, el registro, la red, otros procesos, etc.

A pesar de esto, todavía hay una serie de técnicas que se pueden aplicar desde dentro de un enclave que nos permiten sacar partido de las ventajas obtenidas con la ejecución en el modo IUM.

Acceso a la memoria en el modo de usuario del VTL0

A pesar de su limitado acceso al sistema, los enclaves siguen teniendo acceso a un recurso crítico: la memoria de proceso. Los enclaves pueden realizar operaciones de lectura/escritura dentro del espacio de direcciones del proceso completo, incluido el VTL0.

Se aplican algunas restricciones a este acceso: el código que se ejecuta dentro de enclaves se adhiere a los permisos de memoria y no puede cambiarlos. Esto significa que un enclave no podrá escribir en una memoria que no permita la escritura, ni convertir una memoria no ejecutable en ejecutable. La figura 12 representa el código que se ejecuta dentro de un enclave y muestra estas diferentes posibilidades y restricciones.

La figura 12 representa el código que se ejecuta dentro de un enclave y muestra estas diferentes posibilidades y restricciones. Fig. 12: Código de ejemplo que muestra escenarios de acceso a la memoria del VTL0 desde un enclave

Al acceder a la memoria en el modo de usuario del VTL0, podemos implementar varias técnicas útiles. Con la carga de un enclave malicioso en un proceso de destino, podemos supervisar y robar información confidencial de forma sigilosa, o aplicar parches en los valores del proceso para cambiar su comportamiento.

La implementación de estas técnicas utilizando un enclave conlleva una ventaja significativa: como hemos descrito anteriormente, la activación de las API desde un enclave nos permite eludir la supervisión de las EDR. Dado que el acceso a la memoria en estas técnicas lo realiza un enclave, pueden mantenerse invisibles.

Ejecución de código en el modo de usuario del VTL0

Aunque un enclave puede leer/escribir de la memoria en el modo de usuario del VTL0 con los permisos adecuados, el código almacenado en el VTL0 nunca se puede ejecutar dentro de un enclave, aunque este tenga permisos de ejecución.

Aunque no pueda ejecutar código de VTL0 dentro del enclave, este tiene la opción de activarlo de forma “remota”. Mediante la API CallEnclave con una dirección de VTL0, un enclave puede activar la ejecución de código de VTL0 en un subproceso en el modo de usuario normal (Figura 13).

Al utilizar la API CallEnclave con una dirección de VTL0, un enclave puede activar la ejecución de código de VTL0 en un subproceso en el modo de usuario normal (Figura 13). Fig. 13: Código de ejemplo que muestra un enclave que no puede ejecutar código de VTL0

Al conseguir que el proceso en el modo de usuario "actúe en su nombre", los enclaves pueden acceder indirectamente al sistema de formas que normalmente no son posibles para ellos. Por ejemplo, un enclave puede activar una rutina de VTL0 que lea un archivo, cree un socket, etc.

Es importante tener en cuenta que ejecutar código en el modo de usuario no presenta ninguna ventaja en términos de evasión, ya que se ejecuta como cualquier otro código en el modo de usuario, y puede ser visible para las EDR.

Antidepuración

Otra aplicación interesante para el malware de enclave es la antidepuración. El hecho de que el código que se ejecuta en el enclave siga siendo inaccesible para las aplicaciones del VTL0, como los depuradores, proporciona al malware una ventaja significativa sobre ellas.

La reducción de las API expuestas a los enclaves significa que no estarán disponibles todas las técnicas de antidepuración tradicionales en un enclave. Por ejemplo, las API NtQueryInformationProcess o IsDebuggerPresent y todas las API de fecha y hora no están disponibles para los enclaves. A pesar de ello, todavía tenemos algunas opciones.

En primer lugar, podemos confiar en el acceso del enclave al espacio de direcciones de VTL0 del proceso, lo que le permite leer el bloque de entorno del proceso (PEB) de forma manual y comprobar el valor del indicador "BeingDebugged". Si se detecta un depurador, el enclave puede terminar el proceso.

Otro enfoque sería implementar una técnica antidepuración basada en el tiempo (Figura 14). Aunque las API de fecha y hora no están disponibles para los enclaves, pueden seguir utilizando la instrucción de ensamblaje rdtsc . Esta instrucción devuelve el número de ciclos de reloj del procesador desde el arranque. Con ella, podemos medir el tiempo transcurrido entre diferentes llamadas de enclave y terminar el proceso si se detecta un retraso significativo.

Otro enfoque sería implementar una técnica antidepuración basada en el tiempo (Figura 14). Fig. 14: Antidepuración basada en el tiempo dentro de un enclave utilizando la instrucción de ensamblaje rdtsc.

Al mover partes críticas de nuestro código a un enclave junto con una comprobación antidepuración, podemos crear un malware que esté casi totalmente infalible al análisis dinámico.  El malware depende de que el enclave se ejecute correctamente, mientras que el proceso del modo de usuario no puede aplicar parches en las comprobaciones que se ejecutan en su interior. Implementado correctamente, este enfoque solo se podría frustrar depurando Hyper-V o el kernel seguro.

BYOVE: segundo asalto

En una sección anterior, intentamos explotar un módulo de enclave vulnerable para ejecutar código en el modo IUM. Cuando nos dimos cuenta de que no parecía posible, pensamos que podíamos comprobar si el concepto de BYOVE podría tener otras aplicaciones y decidimos explorar más a fondo el módulo de enclave vulnerable.

La vulnerabilidad (CVE-2023-36880) proviene de las funciones SealSettings y UnsealSettings exportadas por el módulo de enclave. La función SealSettings recibe un puntero a un búfer de datos, lo cifra y escribe los resultados en una dirección de destino proporcionada por el origen de la llamada. UnsealSettings funciona de forma similar, descifrando un búfer suministrado y escribiéndolo en una dirección especificada.

El problema con ambas funciones es que no validan ni la dirección de destino ni la dirección de búfer de origen, lo que les permite apuntar a cualquier dirección del proceso, incluidas las direcciones dentro del propio enclave. La figura 15 muestra el código vulnerable, donde UnsealSettings realiza una copia de memoria memcpy en una dirección arbitraria proporcionada por el usuario.

La Figura 15 muestra el código vulnerable, donde UnsealSettings realizan una copia de memoria memcpy en una dirección arbitraria proporcionada por el usuario. Fig. 15: Código vulnerable dentro de la función UnsealSettings

Esta vulnerabilidad proporciona a un atacante dos capacidades (Figura 16).

Esta vulnerabilidad proporciona a un atacante dos capacidades (Figura 16). Fig. 16: Explotación de la CVE-2023-36880 para leer/escribir datos arbitrarios dentro del enclave.
  1. Escritura arbitraria dentro del enclave: un atacante puede llamar a SealSettings para cifrar datos arbitrarios y, a continuación, llamar a UnsealSettings para apuntar a una dirección de destino dentro del enclave. Esto hace que los datos originales se escriban en la memoria del enclave.

  2. Lectura arbitraria dentro del enclave: un atacante puede llamar a SealSettings, mientras proporciona una dirección dentro del enclave como puntero al búfer de origen. Esto hará que el enclave cifre los datos de la memoria del enclave y los escriba en una ubicación controlada por el atacante. El atacante puede entonces descifrar este dato llamando a UnsealSettings, lo que le permite leer datos arbitrarios del enclave.

Mirage: evasión de memoria basada en VTL1

Aunque no nos permitió ejecutar código en el VTL1, la primitiva de escritura arbitraria proporciona dos capacidades únicas: 

  1. Almacenar datos arbitrarios en el VTL1, donde las entidades del VTL0 no pueden acceder a ellos

  2. Escribir datos arbitrarios en el espacio de direcciones del proceso normal (VTL0) desde el VTL1, lo que impide que las entidades del VTL0 supervisen esta operación

Además, como estas capacidades se facilitan a través de la carga de un módulo de enclave firmado, las podría utilizar cualquier atacante, sin exigirles que firme nada por sí mismo.

Para demostrar el potencial de estas capacidades, ideamos una técnica de evasión de análisis de memoria que denominamos "Mirage". Mirage se inspira en Gargoyle, una técnica de evasión que crea una carga útil que cambia continuamente entre un estado benigno y un estado convertido en arma (Figura 17).

Mirage se inspira en Gargoyle, una técnica de evasión que crea una carga útil que cambia continuamente entre un estado benigno y un estado convertido en arma (Figura 17). Fig. 17: Evasión de memoria de Mirage

Aunque Gargoyle implementa esta técnica cambiando entre la memoria ejecutable y la no ejecutable, Mirage pretende obtener un resultado similar realizando una transición entre la memoria del VTL1 y la del VTL0: almacena código de shell en la memoria de enclave del VTL1, lo transfiere periódicamente de vuelta al VTL0 utilizando la vulnerabilidad, lo ejecuta y, a continuación, lo borra rápidamente de la memoria del VTL0 (Figura 18).

Esta técnica almacena el código de shell en la memoria del enclave del VTL1, lo transfiere periódicamente de vuelta al VTL0 utilizando la vulnerabilidad, lo ejecuta y, a continuación, lo borra rápidamente de la memoria del VTL0 (Figura 18). Fig. 18: Ciclo de vida del proceso de evasión de Mirage

Este enfoque tiene dos ventajas principales. En primer lugar, como la carga útil pasa la mayor parte del tiempo oculta en el VTL1, es resistente a análisis y volcados de memoria. Esto supone una ventaja con respecto a la técnica Gargoyle porque durante su etapa inactiva nuestra carga útil no es solo "sigilosa", sino también inaccesible.

La segunda ventaja es que la escritura del código de shell en el VTL0 la realiza el enclave. Como hemos descrito anteriormente, las EDR no pueden supervisar el código que se ejecuta en la VTL1, lo que significa que los interceptores de EDR típicos no podrán interceptar el código de shell mientras se escribe en la memoria.

Hemos desarrollado una prueba de concepto (PoC) para Mirage (Figura 19). Esta PoC solo tiene como objetivo demostrar qué idea hay detrás de la técnica y, por lo tanto, no se ha convertido en arma completamente.

Hemos desarrollado una prueba de concepto para Mirage (Figura 19). Fig. 19: Demostración de ejecución de Mirage

Detección

A día de hoy, los enclaves se utilizan en un número muy limitado de aplicaciones. Incluso si se adoptan de forma más generalizada, solo se cargarán en procesos específicos, y no en los arbitrarios. Por ejemplo, calc.exe probablemente nunca debería cargar un enclave de VBS.

Por ello, el uso anómalo del enclave puede ser una gran oportunidad de detección. Los defensores deben aprovecharlo creando una referencia de usos legítimos y conocidos de los enclaves de VBS y marcando cualquier desviación de esta. El uso del enclave se puede identificar de dos formas: mediante la supervisión de las API de enclave y la detección de cualquier DLL de enclave cargada.

API de enclave

Las siguientes API se utilizan para gestionar enclaves de VBS mediante un proceso de alojamiento y probablemente indicarán que se está cargando uno:

  • CreateEnclave
  • LoadEnclaveImageW
  • InitializeEnclave
  • CallEnclave
  • DeleteEnclave
  • TerminateEnclave

DLL de enclave cargadas

Otra forma de detectar un uso anómalo del enclave sería detectar la carga de las DLL de entorno que suele utilizar, es decir, Vertdll.dll y ucrtbase_enclave.dll. Dado que estos archivos DLL solo los debe utilizar un enclave, su presencia indicará que el proceso probablemente lo está utilizando.

Conclusión

Los enclaves de VBS proporcionan una herramienta increíble para que los desarrolladores protejan las secciones confidenciales de las aplicaciones, pero, como acabamos de demostrar, los atacantes también pueden utilizarlos para "proteger" su malware. Aunque este concepto es meramente teórico en este momento, existe la posibilidad de que los atacantes avanzados empiecen a utilizar enclaves de VBS con fines maliciosos en el futuro. 

Agradecimientos

Nos gustaría expresar nuestro agradecimiento a Matteo Malvica de Offsec y Cedric Van Bockhaven de Outflange por el trabajo realizado en un proyecto de investigación muy similar a este llevado a cabo recientemente. Consulte su primera entrada de blog en una serie de dos partes, y permanezca atento a la parte 2, que se publicará después de Insomni’Hack 2025.



Ori David

escrito por

Ori David

February 25, 2025

Ori David

escrito por

Ori David

Ori David es experto en seguridad en Akamai. Su investigación se centra en la seguridad ofensiva, el análisis de malware y la búsqueda de amenazas.