Búsqueda de Ciberamenazas

El ransomware BlackByte desactiva el EDR a través del abuso de RTCore64.sys

Con informes de un nuevo sitio de fuga de datos publicado por los ciberdelincuentes detrás del ransomware BlackByte, decidimos echar otro vistazo a la variante más reciente escrita en Go.

Encontramos una sofisticada técnica para eludir los productos de seguridad abusando de una vulnerabilidad conocida en el controlador legítimo vulnerable RTCore64.sys. La técnica de evasión permite desactivar una lista enorme de más de 1.000 controladores en los que se basan los productos de seguridad para proporcionar protección. Los productos de Sophos proporcionan mitigaciones contra las tácticas discutidas en este artículo.

“Bring Your Own Driver” es el nombre dado a esta técnica: explotar un sistema objetivo abusando de un controlador legítimo firmado con una vulnerabilidad explotable. En julio de 2022, Trend Micro informó sobre el uso indebido de un controlador vulnerable para el juego Genshin Impact, llamado mhyprot2.sys, para matar los procesos y servicios antivirus para desplegar en masa el ransomware. En mayo de 2022, otro informe mostró cómo una variante del ransomware AvosLocker también abusaba del controlador vulnerable del antirrootkit de Avast, aswarpot.sys, para eludir las funciones de seguridad.

Ahora que los actores que están detrás del ransomware BlackByte y de esta sofisticada técnica han vuelto tras un breve paréntesis, es muy probable que sigan abusando de los controladores legítimos para eludir los productos de seguridad. Para ayudar a la industria a prevenir proactivamente estos ataques, compartimos nuestros hallazgos en este informe.

Un vistazo a CVE-2019-16098

RTCore64.sys y RTCore32.sys son controladores utilizados por MSI AfterBurner 4.6.2.15658 de Micro-Star, una utilidad de overclocking de tarjetas gráficas muy utilizada que ofrece un control ampliado sobre las tarjetas gráficas del sistema. CVE-2019-16098 permite a un usuario autenticado leer y escribir en memoria arbitraria, lo que podría ser explotado para la escalada de privilegios, la ejecución de código bajo altos privilegios o la divulgación de información.

Los códigos de control de E/S en RTCore64.sys son directamente accesibles por los procesos en modo usuario. Como se indica en la directriz de Microsoft sobre la seguridad de los códigos IOCTL en los controladores, se considera peligroso definir códigos IOCTL que permitan a los llamantes leer o escribir en áreas no específicas de la memoria del kernel. No es necesario un shellcode o un exploit para abusar de la vulnerabilidad, sólo acceder a estos códigos de control con intenciones maliciosas. Más adelante en este artículo, explicaremos cómo BlackByte abusa de esta vulnerabilidad para desactivar productos de seguridad.

An annotated screen capture showing unprotected code controls in RTCode64
Figura 1: Códigos de control desprotegidos en RTCore64.sys que permiten operaciones de lectura y escritura en la memoria del kernel

Rutinas de notificación del kernel

Las rutinas de notificación del kernel son utilizadas por los controladores cargados para ser notificados por el kernel de la actividad del sistema. Algunas de estas actividades del sistema notificadas incluyen:

Cuando se registra una función callback, la dirección de la función de la misma se añade a un array. Por ejemplo, el array que contiene todas las llamadas de retorno registradas a través de PsSetCreateProcessNotifyRoutine se llama PspCreateProcessNotifyRoutine.

Para entenderlo mejor, imagina un proceso A.EXE, que intenta crear un nuevo proceso B.EXE. A.EXE notificará al kernel de Windows NTOSKRNL.EXE que un nuevo proceso debe ser creado. El kernel de Windows asignará un nuevo ID de proceso al proceso que pronto se creará, pero no permitirá ejecutar el código en modo usuario de B.EXE todavía. El proceso B.EXE permanece primero en estado de suspensión.

Si un controlador ha registrado una llamada de retorno a través dePsSetCreateProcessNotifyRoutine, el núcleo entregará el control y ejecutará la función de llamada de retorno del controlador registrado. Después  que la rutina del controlador haya terminado, el control será transferido de nuevo al kernel, y permitirá la continuación del código en modo usuario. Todo este proceso se ilustra a continuación.

A flowchart showing the steps just described in the text
Figura 2: Cómo funcionan las rutinas de notificación del núcleo a alto nivel

Estas rutinas suelen ser utilizadas por los controladores relacionados con los productos de seguridad para recoger información sobre la actividad del sistema. Un objetivo de un atacante podría ser eliminar estas devoluciones de llamada de la memoria del kernel. Sin embargo, las mitigaciones modernas del sistema operativo, como el refuerzo de la firma del controlador, significan que los atacantes no pueden simplemente cargar su propio rootkit o controlador en el sistema objetivo para leer o escribir en la memoria del kernel. Para saltarse esta característica de seguridad, el atacante tiene las siguientes opciones:

  1. Robar certificados de firma de código válidos o adquirirlos de forma anónima
  2. Abusar de los controladores firmados existentes para leer, escribir o ejecutar código en la memoria del kernel

La opción más fácil es la segunda, ya que hay una amplia gama de controladores legítimos disponibles y ponerlos todos en una lista negra simplemente no es posible. De ahí que la técnica “Bring Your Own Driver” haya sido abusada a menudo en el pasado por los adversarios [1][2][3].

Profundizando en el bypass EDR de BlackByte

Nuestro análisis se centró en la muestra de BlackByte con el sha256

9103194d32a15ea9e8ede1c81960a5ba5d21213de55df52a6dac409f2e58bcfe

La muestra ya fue analizada por otros investigadores, por lo que nos centramos únicamente en la técnica de derivación EDR (endpoint detection and response) que encontramos. Sin embargo, encontramos múltiples medidas de antianálisis durante nuestra investigación, que enumeramos en el apéndice siguiente. La lista no está completa, pero debería facilitar el trabajo de otros ingenieros inversos para llegar al bypass del EDR.

Además, también hemos identificado rutinas para desactivar el proveedor de ETW (Event Tracing for Windows) Microsoft-Windows-Threat-Intelligence, una característica que proporciona registros sobre el uso de llamadas a la API comúnmente abusadas de forma maliciosa como NtReadVirtualMemory para inyectar en la memoria de otro proceso. Esto hace que todas las características de seguridad que dependen de este proveedor sean inútiles.

Este artículo se centra únicamente en cómo se implementa la eliminación de la devolución de llamada del kernel. Sin embargo, la implementación para deshabilitar el proveedor Microsoft-Windows-Threat-Intelligence está casi completamente copiada de la implementación de EDRSandblast. Si los lectores están interesados en cómo funciona este método, recomendamos la lectura del artículo de slaeryan de CNO Development Labs.

Una vez que terminan las comprobaciones anti-análisis, BlackByte intenta recuperar un manejador de archivo del Master Boot Record, como se ve en la Figura 3. Si falla, el ransomware intenta, al menos, saltarse el Control de Acceso de Usuario y reiniciarse con privilegios superiores a través de CMLUA o CMSTPLUA UAC Bypass.

A flow chart with three small screens showing the described retrieval attempt
Figura 3: CreateFile en PHYSICALDRIVE0, mostrando el intento de recuperación

Una vez reiniciado con privilegios más altos, ahora entrará en la rutina de Bypass EDR. Podemos dividir este proceso en dos fases:

  1. Identificación del kernel e Instalación del servicio
  2. Eliminación de las Rutinas de Notificación del Kernel

Fase 1: Identificación del Kernel e Instalación del Servicio

En primer lugar, se extraerá la información de la versión de ntoskrnl.exe mediante GetFileVersionInfoW. La información de la versión se agrupar con un prefijo ntoskrnl_. La cadena construida se compara con una lista de IDs de versiones del kernel soportadas. La lista se incrusta en el binario y se descifra mediante una sencilla combinación de descifrado en base64 y descifrado con clave XOR de 8 bytes. La determinación de la versión del kernel es esencial para seleccionar los desplazamientos correctos a las estructuras de la memoria del kernel que se supone que deben ser parcheadas. La figura 4 ilustra todo el proceso de la fase 1.

A flowchart showing the process described above
Figura 4: Coincidencia del identificador de la versión del núcleo y extracción de las compensaciones de la lista de compensaciones del núcleo descifrado

[PIE Figura 4: Coincidencia del identificador de la versión del núcleo y extracción de las compensaciones de la lista de compensaciones del núcleo descifrado]

En general, para cada identificador de versión del kernel, se proporcionan los siguientes offsets:

Campo Descripción
ntoskrnlVersion Identificador único para la versión de ntoskrnl.exe, construido concatenando como se describe en el bloque de texto anterior
PspCreateProcessNotifyRoutineOffset Desplazamiento a la matriz que contiene las devoluciones de llamada del controlador registradas a través de PsSetCreateProcessNotifyRoutine
PspCreateThreadNotifyRoutineOffset Desplazamiento a la matriz que contiene las devoluciones de llamada del controlador registradas a través de PsSetCreateThreadNotifyRoutine
PspLoadImageNotifyRoutineOffset Desplazamiento a la matriz que contiene las devoluciones de llamada del controlador registradas a través de PsSetLoadImageNotifyRoutine
_PS_PROTECTIONOffset Desplazamiento al campo _PS_PROTECTION de la estructura EPROCESS, que define el nivel de protección de un proceso
EtwThreatIntProvRegHandleOffset Desplazamiento a la estructura que contiene los campos GuidEntry y ProviderEnableInfo, necesarios para eliminar el proveedor ETW Microsoft-Windows-Threat-Intelligence
EtwRegEntry_GuidEntryOffset Desplazamiento a GuidEntry en la estructura anterior
EtwGuidEntry_ProviderEnableInfoOffset Desplazamiento hacia ProviderEnableInfo en la estructura anterior

Tabla 1: Desplazamientos de correspondencia para cada ID de núcleo

Una vez identificada la versión del kernel y determinados los offsets, BlackByte continúa colocando RTCore64.sys en la carpeta AppData\Roaming. El nombre del archivo está codificado en el binario y omite la extensión del archivo.

Se crea un servicio mediante CreateServiceW y finalmente se inicia. El nombre del servicio y el nombre para mostrar están codificados en el binario. Mientras que el nombre del servicio es siempre el mismo, el nombre de la pantalla se selecciona al azar de una lista de cadenas muy deprimentes, que se enumeran a continuación.

Nombres para mostrar codificados (seleccionados al azar)

I’m so lonely, help me.

Stop doing this, go away, they are waiting for you at home.

You laugh a lot, because you simply don’t have the strength to cry.

When will it end? I want this.

AAAAAAAAAAAAAA!!!!!!!!!!!!!!!

If I had feelings, then I would probably be happy and scared at the same time.

Who are you? However, it doesn’t matter. Nobody ever cares about you.

The routine dragged on.

I’m at a dead end, help me.

I’m empty inside, help me.

May be enough?

Bad ending.

Tabla 2: Una selección de nombres de visualización

Fase 2: Eliminación de las rutinas de notificación del núcleo

Una vez que se han determinado los desplazamientos y se ha instalado el servicio, la muestra continúa eliminando las devoluciones de llamada de la memoria del kernel. En esta fase, BlackByte abusa de la vulnerabilidad de lectura y escritura arbitraria en RTCore64.sys. Así, todas las operaciones de lectura y escritura mencionadas en la memoria del kernel son a través del controlador explotable.

Como se explica en la sección “Rutina de notificación del kernel”, hay al menos tres matrices diferentes que pueden contener direcciones a funciones de devolución de llamada:

  • PspCreateProcessNotifyRoutine para la creación de procesos, completado por PsSetCreateProcessNotifyRoutine
  • PspCreateThreadNotifyRoutine para la creación de hilos, completado por PsSetCreateThreadNotifyRoutine
  • PspLoadImageNotifyRoutine para la carga de imágenes, completado por PsSetLoadImageNotifyRoutine

En aras de la simplicidad, nos centraremos en cómo se eliminan las devoluciones de llamada de creación de procesos. El proceso para los otros dos eventos es el mismo, aunque se utilicen diferentes desplazamientos.

En general, para eliminar estos callbacks BlackByte necesita completar las siguientes tres fases:

2a. Identificar la dirección del array PspCreateProcessNotifyRoutine

2b. Identificar a qué controlador pertenece la función de callback correspondiente

2c. Sobrescribir la función callback dentro del array con ceros

2a. Identificar la dirección del array PspCreateProcessNotifyRoutine

El ejemplo identificó la versión del kernel y obtuvo los correspondientes offsets necesarios de la lista codificada. Dependiendo del array que estemos iterando, se utiliza un offset diferente. En este caso, el offset 0xCEC3A0 lleva a PspCreateProcessNotifyRoutine.

Recupera la dirección base de ntoskrnl.exe a través de EnumDeviceDrivers y añade el offset a PspCreateProcessNotifyRoutine. Esto recuperará el puntero a PspCreateProcessNotifyRoutine que contiene todas las devoluciones de llamada registradas a través de PsSetCreateProcessNotifyRoutine.

A flowchart showing the retrieval process described above
Figura 5: Recuperación de la dirección del array PspCreateProcessRoutine

2b. Identificar a qué controlador pertenece la función callback correspondiente

A continuación, BlackByte necesita identificar si la función de devolución de llamada pertenece a un controlador utilizado por los productos EDR. Para ello, BlackByte utiliza un procedimiento para calcular el controlador más probable a partir de la propia dirección de la llamada de retorno.

Al comienzo del procedimiento, se obtienen todas las direcciones base mediante EnumDeviceDrivers. Cada dirección base se compara con la dirección de la función de devolución de llamada. De todas las direcciones obtenidas, se elige la dirección base con el menor delta respecto a la dirección de la función de llamada de retorno y se pasa a GetDeviceDriverBaseNameW, que devolverá el nombre del controlador correspondiente.

El nombre del controlador se compara entonces con una lista de más de 1000 nombres de controladores. Si el nombre del controlador coincide con uno de los nombres de la lista, el binario continuará eliminando el callback.

2c. Eliminar la función callback del array

En el último paso, el malware eliminará la entrada del callback del array PspCreateProcessRoutine. La sobrescritura de la entrada se realiza llamando a DeviceIoControl para interactuar de nuevo con RTCore64.sys. El elemento que contiene la dirección de la función callback del controlador simplemente se sobrescribe con ceros.

Similitudes entre EDRSandblast y el EDR Bypass de BlackByte

Durante nuestro análisis, hemos encontrado múltiples similitudes entre la herramienta de código abierto EDRSandblast y la implementación de EDR Bypass que acabamos de analizar. EDRSandblast es una herramienta escrita en C para convertir en armas los controladores firmados vulnerables para eludir las detecciones de EDR a través de varios métodos. Por tanto, creemos que el grupo que está detrás de BlackByte ha copiado al menos varios fragmentos de código de la herramienta de código abierto y los ha reimplementado en el ransomware. A continuación se muestra una lista de similitudes entre la herramienta de código abierto y la implementación de BlackByte:

  • La lista de controladores conocidos relacionados con el software de seguridad es casi, si no completamente, idéntica.
  • El repositorio de github de EDRSandblast contiene una lista de las versiones y ajustes del kernel compatibles en un archivo CSV. Si desciframos la lista de kernel offset de BlackByte, es casi, si no completamente, idéntica a la lista del repositorio de GitHub, salvo que falta la cabecera del archivo CSV.
  • Múltiples funciones definidas en EDRSyblast pueden encontrarse casi si no completamente idénticas en la implementación de BlackByte.

Para concluir, sugerimos lo siguiente para defenderse de forma proactiva contra este tipo de ataques:

  • Los ciberdelincuentes rara vez despliegan controladores legítimos con vulnerabilidades de día cero. Normalmente, las vulnerabilidades de los ataques son bien conocidas y están documentadas. Si se está al tanto de las últimas noticias sobre seguridad, puedes prepararte de antemano e investigar qué controladores legítimos son explotados actualmente por los ciberdelincuentes, por ejemplo, mediante el bloqueo de los controladores que se sabe que son explotables.
  • Lleva siempre un registro de los controladores instalados en tus sistemas. Los controladores legítimos vulnerables también pueden instalarse en el sistema objetivo de antemano, de manera que no sea necesario que los actores de la amenaza los dejen caer en el sistema objetivo. Por lo tanto, siempre hay que mantener el sistema actualizado.

Para obtener una lista de IoCs asociados a esta amenaza, consulta nuestro GitHub.

Apéndice: Trucos anti-análisis de BlackByte

  • BlackByte llama a la API IsDebuggerPresent y CheckRemoteDebuggerPresent. Si se detecta un depurador, la ejecución se detendrá.
  • La muestra intenta ocultar el hilo principal del depurador llamando a NetSetInformationThreadW con el valor no documentado THREAD_INFORMATION_CLASS::ThreadHideFromDebugger para evitar que un depurador se adjunte a un proceso en ejecución.
  • BlackByte intenta detectar si se ha establecido un punto de interrupción de hardware a través de GetThreadContext. Aunque no lo confirmamos del todo, creemos que esta llamada a la API se utiliza para detectar los puntos de interrupción de hardware establecidos por un depurador. Se sabe que GetThreadContext es utilizado por el malware para detectar dichos puntos de interrupción.
  • La muestra realiza una simple comprobación de la longitud del nombre del archivo. Si tiene más de 10 caracteres, la ejecución se detendrá.
  • Del mismo modo, como se explica en el artículo de ZScaler sobre BlackByte, la muestra realiza una comprobación de si se inyecta en el binario alguna DLL de enganche conocida. Si se encuentra una DLL en la lista negra, la ejecución se detendrá. La lista es consistente con las proporcionadas por el artículo enlazado.
  • El ransomware BlackByte requiere que se pase una semilla a través del parámetro “-s”. La semilla correcta está codificada en el binario como una cadena cifrada. Si la semilla no coincide, la ejecución se detendrá.

Más información

Dejar un comentario

Your email address will not be published. Required fields are marked *