Aunque los componentes de inteligencia artificial y machine learning de próxima generación de las soluciones de seguridad siguen mejorando las capacidades de detección basadas en el comportamiento, en esencia muchos siguen dependiendo de las detecciones basadas en firmas. Cobalt Strike, un popular marco de mando y control (C2) del equipo rojo utilizado tanto por los actores de amenazas como por los equipos rojos desde su lanzamiento, sigue contando con el firme respaldo de las soluciones de seguridad.
Para continuar con el uso operativo de Cobalt Strikes en el pasado, en el equipo de IBM® X-Force Red Adversary Simulation invertimos importantes esfuerzos de investigación y desarrollo para personalizar Cobalt Strike con herramientas internas. Algunas de nuestras herramientas internas específicas de Cobalt Strike tienen versiones públicas, como “InlineExecute-Assembly”, “CredBandit” y “BokuLoader”. En los últimos dos años, debido al exceso de firmas de Cobalt Strike, hemos restringido su uso a la simulación de actores de amenazas menos sofisticados y, en su lugar, utilizamos otros C2 de terceros y internos cuando realizamos ejercicios más avanzados del equipo rojo.
A través de los esfuerzos de investigación y desarrollo, hemos encontrado un mayor éxito operativo en los ejercicios avanzados del equipo rojo con:
Sin embargo, todavía hay una gran cantidad de actores de amenazas que aprovechan las copias pirateadas de Cobalt Strike, y sigue siendo importante poder simular a estos actores de amenazas. Para los equipos rojos dispuestos a realizar esfuerzos de investigación y desarrollo, aún pueden encontrar el éxito operativo con Cobalt Strike mientras simulan a estos adversarios. Además, Cobalt Strike es una gran herramienta de aprendizaje, que los recién llegados pueden aprovechar para obtener experiencia práctica con un marco C2 a través de cursos de formación del equipo rojo.
A medida que continuamos ampliando nuestras capacidades de C2, estamos compartiendo conocimiento sobre cómo nos hemos basado en el marco Cobalt Strike en el pasado, específicamente mediante el desarrollo de cargadores reflexivos personalizados. También está destinado a que los defensores comprendan cómo funciona Cobalt Strike para crear detecciones más sólidas.
Manténgase al día sobre las tendencias más importantes e intrigantes del sector en materia de IA, automatización, datos y mucho más con el boletín Think. Consulte la Declaración de privacidad de IBM.
Esta entrada de blog es la primera de una serie que sirve como introducción y cubre los conceptos básicos del desarrollo de un cargador reflexivo Cobalt Strike. A medida que avancemos en esta serie, construiremos sobre esta base y haremos referencia a esta publicación.
Al final de esta serie, nuestro objetivo es crear un cargador reflexivo que se integre con las características de evasión existentes de Cobalt Strike e incluso las mejore con técnicas avanzadas que actualmente no están presentes en la herramienta. Las próximas publicaciones profundizarán en el desarrollo de características específicas de evasión y en cómo implementarlas en nuestro cargador reflexivo Cobalt Strike.
Para empezar, esta publicación cubrirá:
A medida que exploramos la carga reflexiva de Cobalt Strike desde la perspectiva de un desarrollador de herramientas de seguridad ofensiva, destacaremos las oportunidades para la detección y la evasión. Algunos aspectos del desarrollo se omitirán o simplificarán, y le animamos a llenar los vacíos depurando los proyectos de cargadores reflexivos existentes, reconstruyéndolos desde cero o buscando formación.
El implante Cobalt Strike C2, conocido como Beacon, es una biblioteca de vínculos dinámicos (DLL) de Windows, y la capacidad modular de utilizar nuestro propio cargador DLL en Cobalt Strike se conoce como User-Defined Reflective Loader (UDRL).
Normalmente, el cargador DLL de Windows integrado es responsable de cargar los archivos DLL en el espacio de memoria virtual de un proceso. El cargador DLL de Windows existe principalmente en el espacio del usuario, aunque se traslada al espacio del núcleo cuando asigna DLL desde el disco.
El cargador DLL de Windows presenta algunas desventajas cuando se utiliza durante simulaciones de adversarios:
Por lo tanto, usar el cargador DLL de Windows para cargar nuestra DLL beacon no es una solución ideal. Para superar estos desafíos, cargamos la DLL beacon de la memoria con un cargador reflexivo.
Los tres principales puntos de detección que evitan las cargas reflexivas son:
La carga reflexiva puede entenderse simplemente como cargar una DLL sin procesar directamente desde la memoria, en lugar de cargarla desde el sistema de archivos.
La carga reflexiva y el cargador DLL de Windows tienen el mismo propósito: cargar un archivo DLL desde un formato de archivo sin procesar al espacio de memoria virtual de un proceso. Sin embargo, la carga reflexiva tiene una ventaja clave sobre el cargador DLL de Windows, ya que no requiere que el archivo DLL exista en el sistema de archivos. Esta carga en memoria permite un número ilimitado de fases de carga en cadena, ya que la DLL del implante C2 puede ocultarse dentro de capas de cifrado y codificación dentro de la memoria del proceso.
Un concepto clave que hay que entender cuando se carga una DLL, es saber que la DLL tendrá un formato diferente en disco que en memoria. Las principales diferencias entre la DLL en formato de archivo sin procesar y el formato de dirección virtual son:
Formato de archivo sin procesar:
Formato de dirección virtual:
Examinando una DLL beacon HTTP en la herramienta PE-Bear de Aleksandra Doniec, vemos las diferencias entre la dirección bruta y la dirección virtual de cada sección de la DLL:
Tabla que enumera las direcciones crudas y virtuales de cada sección de la DLL beacon.
Esta DLL beacon HTTP/S es
PE-Bear ofrece una representación visual de nuestra DLL beacon tal como existe en formato de archivo sin procesar frente a espacio de direcciones virtual:
Representación visual de la DLL de baliza en formato sin procesar (izquierda) frente a formato virtual (derecha)
Aunque no es la medida más acertada durante una simulación de adversario, colocar una DLL beacon sin ofuscar en el disco y cargarla con el cargador DLL de Windows es una excelente manera de desmitificar tanto beacon omo la carga de DLL. Básicamente, beacon es solo una DLL. El cargador DLL de Windows y un cargador reflexivo simplemente cargan una DLL en un proceso.
Para cargar la DLL beacon con el cargador DLL de Windows, realizamos los siguientes pasos:
LoadLibrary
API para cargar nuestra DLL beacon desde el disco.En primer lugar, desactivamos todas las opciones de Malleable PE que hacen que nuestra DLL beacon pueda descargarse con el cargador DLL de Windows. Para ello, modificamos nuestro perfil Malleable C2 y desactivamos las opciones de evasión de Maleable PE situadas en el bloque de etapas:
Bloque de etapa de perfil Malleable C2 modificado para deshabilitar las características de evasión de Cobalt Strike.
Después de modificar el perfil, reiniciamos el Cobalt Strike Team Server, suministrando nuestro
Nos conectamos al servidor del equipo con el cliente Cobalt Strike. Luego creamos un
Captura de pantalla de la creación de una DLL beacon "sin etapas" a partir del cliente Cobalt Strike
Usando el siguiente código, creamos un programa en C llamado
Código C de Windows para cargar la DLL beacon desde el disco utilizando el cargador DLL de Windows.
Utilizamos la API
Como parte del proceso de carga, el cargador DLL de Windows inicializará nuestra DLL beacon llamando a su punto de entrada con
Después de que el cargador DLL de Windows haya cargado e inicializado nuestra DLL beacon en el espacio de memoria virtual de nuestro proceso, tendremos que volver a llamar al punto de entrada de la DLL beacon virtual con el argumento
Nuestro programa debe conocer el punto de entrada de nuestra DLL beacon virtual para ejecutar nuestra DLL beacon virtual. Esto se puede hacer dinámicamente dentro del programa analizando los encabezados de la DLL beacon virtual para la dirección virtual relativa (RVA) del punto de entrada, o podemos ver rápidamente qué es y codificar el valor.
Para nuestra prueba de concepto, descubriremos manualmente y codificaremos el punto de entrada RVA de nuestra DLL beacon en nuestro programa. Con PE-Bear descubrimos que el punto de entrada del RVA a la baliza es
Captura de pantalla de la búsqueda del punto de entrada RVA del archivo DLL beacon utilizando PE-Bear.
Las
Con nuestro código listo, compilamos nuestro programa C en un ejecutable de Windows:
Comando utilizado para compilar nuestro programa.
Al colocar nuestra DLL beacon y nuestro programa ejecutable de cargador beacon en el mismo directorio, el cargador DLL de Windows podrá descubrir nuestra DLL mientras realiza su rutina de carga.
Colocamos ambos
La DLL beacon y el programa de carga están en el mismo directorio.
Desde nuestro escritorio de Windows, hacemos doble clic en nuestro programa loadBeaconDLL.exe y establecemos una conexión beacon activa con nuestro servidor de equipo.
Conexión correcta a C2 Team Server desde la DLL beacon cargada mediante el cargador DLL de Windows.
Cobalt Strike utiliza una versión modificada del proyecto Reflective Loader de Stephen Fewer. Este legendario cargador DLL en memoria tiene más de una década de antigüedad y se ha utilizado en Metasploit y otras notables herramientas de seguridad ofensivas.
A lo largo de los años, el cargador reflexivo Cobalt Strike se ha mejorado para manejar todas las características de evasión de Malleable PE que ofrece Cobalt Strike. La principal desventaja de utilizar un cargador reflexivo definido por el usuario (UDRL) personalizado es que las características de evasión de Malleable PE pueden o no ser compatibles de fábrica.
Algunas características de evasión se implementan completamente cuando se usa una UDRL y se integran en la DLL beacon mediante el motor Malleable PE de Cobalt Strikes durante la creación de la carga útil de beacon. Sin embargo, actualmente características como deben ser gestionadas por la UDRL, mientras que otras como y se pueden gestionar mediante un beacon con una integración de URL adecuada.
El proyecto original de Reflective Loader requiere compilar el
Luego, otro proyecto es responsable de:
Diagrama del cargador reflexivo original, cargando una DLL en la memoria virtual.
Un método alternativo es anteponer el cargador reflexivo a la DLL. Esto permite cargar cualquier DLL no administrada y no requiere compilar la DLL a partir del código fuente. Se trata de un sólido método de carga reflexiva capaz de cargar cualquier archivo PE (EXE o DLL).
Diagrama de un cargador reflexivo añadido a una DLL, cargando una DLL en memoria virtual.
La implementación de la carga reflexiva en Cobalt Strike utiliza un híbrido de los dos métodos anteriores. Este método de carga reflexiva puede resultar familiar para quienes conocen cómo Meterpreter de Metasploit realiza la carga reflexiva.
Al igual que el método original del cargador reflexivo, la función
Cuando se carga una UDRL en Cobalt Strike y un operador genera una carga útil de beacon desde el cliente Cobalt Strike, el motor Malleable PE de Cobalt Strike parchea el shellcode del cargador reflexivo en la compensación del archivo sin procesar de la exportación
Cuando el motor Malleable PE completa el parcheo de la DLL beacon sin procesar, la DLL beacon sin procesar se entrega al operador en un formato ejecutable similar al shellcode.
Diagrama del cargador reflexivo Cobalt Strike, cargando la DLL beacon en la memoria virtual.
Al observar los bytes iniciales en el desensamblador PE-Bear podemos ver que la DLL beacon en sí es ejecutable:
El stub del cargador reflexivo de llamadas se muestra como códigos de operación de ensamblaje ejecutables.
Los bytes iniciales
Después de ejecutar opcionalmente los bytes
Confirmamos que el archivo sin procesar se desplaza con la exportación
Captura de pantalla usando PE-Bear para determinar la compensación sin procesar del archivo de exportación ReflectiveLoader.
Tal como existe dentro del directorio de exportación, la dirección para la sección
Para descubrir la compensación del archivo sin procesar de la exportación
Las direcciones virtuales y sin procesar de las secciones
Direcciones sin procesar y virtuales de la sección .text de la DLL beacon.
La diferencia entre las dos es
Podemos confirmarlo en PE-Bear haciendo clic con el botón derecho en el RVA de la función de exportación
En resumen, el flujo del proceso de carga reflexiva de Cobalt Strike es:
Diagrama que muestra las fases principales de cómo Cobalt Strike realiza la carga reflexiva de la DLL beacon.
Dado que nuestro cargador reflexivo se ejecuta antes de que se cargue la DLL beacon, el código del cargador reflexivo debe ser shellcode puro.
La forma más sencilla de crear shellcode complejo es escribirlo en C sin dependencias externas. A continuación, el archivo C se compila en un archivo de objeto. Todo debe estar incluido en la sección del archivo objeto. Por último, arrancamos la.text
El motor Malleable PE de Cobalt Strike se encargará del trabajo de obtener el shellcode de nuestro archivo objeto de cargador reflexivo y de conectarlo a la DLL beacon sin procesar en la compensación del archivo sin procesar de la exportación
Script Aggressor para escribir shellcode de cargador reflexivo en la DLL beacon sin procesar aprovechando Cobalt Strike.
Nuestro script UDRL Aggressor hace que Cobalt Strike escriba en nuestro shellcode del cargador reflexivo siguiendo estos pasos:
<a href="https://hstechdocs.helpsystems.com/manuals/cobaltstrike/current/userguide/content/topics_aggressor-scripts/as-resources_functions.htm#extract_reflective_loader">extract_reflective_loader</a>analizará nuestro archivo de objetos UDRL desde la matriz de bytes
<a href="https://hstechdocs.helpsystems.com/manuals/cobaltstrike/current/userguide/content/topics_aggressor-scripts/as-resources_functions.htm#setup_reflective_loader">setup_reflective_loader</a>la función Cobalt Strike Aggressor utilizará el motor Malleable PE para descubrir la compensación del archivo sin procesar de nuestra
Cobalt Strike ha hecho el trabajo por nosotros en lo que respecta a la extracción de la sección del.text de nuestro archivo de objeto de cargador reflexivo, parchear nuestro shellcode del cargador reflexivo y llamar a nuestro cargador reflexivo con el stub del cargador reflexivo ubicado en el encabezado de la DLL beacon.
Estas son las fases que debemos desarrollar para cargar bBeacon de forma reflexiva:
Existen varios métodos diferentes que podemos usar para descubrir la dirección de la DLL beacon en forma de memoria. Algunos métodos son:
Cuando se utiliza un método que busca retrospectivamente, primero debemos obtener la dirección actual del puntero de instrucción de nuestro hilo (
Código ensamblador Intel x64 para obtener la dirección base sin procesar de la DLL beacon del registro RDI.
El proyecto original del cargador reflexivo busca retrospectivamente los encabezados MZ y PE. Estos encabezados se han convertido en puntos de detección. Para solucionarlo, Cobalt Strike añadió las características de evasión
La documentación de Cobalt Strike indica que la opción
Cuando se configura, los bytes
Estos bytes deben ser algo únicos, o el cargador reflexivo no podrá encontrarlos. Además, los bytes del encabezado MZ deben ser de operaciones nulas y ejecutables. No pueden ser valores como
Después de descubrir este posible punto de detección, desarrollé un método diferente, pero similar, para encontrar la dirección base de la DLL beacon sin procesar. Este método utiliza un buscador de eggs capaz de buscar retrospectivamente desde
La dirección
Como no tenemos acceso fácil al motor Java Malleable PE, se puede utilizar el script UDRL Aggressor
Script Aggressor para escribir un egg en la DLL beacon sin procesar y mostrar los cambios en la consola de scripts de Cobalt Strike.
El código UDRL debe conocer el valor del egg escrito en la DLL beacon sin procesar por el script UDRL. Una vez que se conoce el egg, el buscador de eggs busca retrospectivamente dos instancias del egg, como se ve en el siguiente código:
Código ensamblador Intel x64 para un buscador de eggs que busca retrospectivamente dos instancias de un egg de 64 bits.
Ahora que ya no se utilizan los encabezados MZ y PE, podemos eliminarlos en el script UDRL Aggressor:
Script Aggressor para enmascarar MZ, PE y bytes no utilizados del banner de DOS ubicados en los encabezados de la DLL beacon sin procesar.
También hay otra forma, específica de Cobalt Strike, para descubrir la dirección base de la DLL beacon sin procesar. Como vimos anteriormente, los bytes iniciales en el stub del cargador reflexivo de llamadas almacenan la dirección base de la DLL beacon sin procesar en el registro
Para analizar esto con más detalle en el depurador, generamos un beacon y anteponemos un punto de interrupción (
Captura de pantalla de X64dbg que muestra paso a paso el stub del cargador reflexivo de llamadas para ver que la dirección base de la DLL beacon sin procesar se guarda en el registro RDI antes de llamar al cargador reflexivo.
A continuación se muestra un ejemplo práctico de cómo obtener la dirección base de la DLL beacon sin procesar a partir del stub del cargador reflexivo de llamadas:
Código C de ensamblaje en línea para obtener la dirección base de la DLL beacon sin procesar desde el registro RDI.
Con la dirección base de la DLL de beacon sin procesar, ahora podemos obtener los valores necesarios para cargar el beacon en el espacio de direcciones virtuales del proceso.
La siguiente tabla enumera los valores que necesitamos de los encabezados de la DLL beacon sin procesar, las ubicaciones en las que los encontraremos y sus tipos:.
Tabla con los valores del encabezado de la DLL beacon sin procesar que son útiles para cargar la DLL beacon.
No todo el contenido de los encabezados es necesario para cargar la DLL beacon. Los valores obligatorios se pueden reempaquetar u ofuscar. Los valores no requeridos pueden eliminarse o aleatorizarse.
Una vez que conocemos elSizeOfImagef
Se pueden usar diferentes métodos para asignar memoria para la DLL beacon virtual. Los diferentes métodos utilizarán diferentes tipos de memoria. Los diferentes métodos compatibles con el cargador reflexivo predeterminado de Cobalt Strike son:
Tabla que muestra las opciones de asignación de memoria de Cobalt Strike para la DLL beacon virtual.
Esto puede ir un paso más allá con la UDRL. En su lugar, se puede utilizar la versión NTAPI de estas funciones. Además, las funciones NTAP podían llamarse mediante llamadas directas o indirectas al sistema, lo que puede o no ayudar a reforzar las capacidades de evasión.
Cuando el método asignador se establece en
Ejemplo de código del proyecto BokuLoader que muestra cómo se utiliza una llamada directa al sistema para asignar memoria a la DLL beacon virtual.
La siguiente imagen muestra un ejemplo de código del uso de los métodos HellsGate y HalosGate para determinar los números de llamada del sistema:
Ejemplo de código del proyecto BokuLoader que muestra cómo se descubren las llamadas al sistema desde el proceso.
Ahora que hemos asignado memoria para nuestra DLL beacon virtual, necesitamos copiar las secciones del beacon de sus compensaciones de archivo sin procesar, tal como existen en la DLL beacon sin procesar, a la memoria asignada en sus compensaciones virtuales relativos.
Si asignamos nuestra memoria conREADWRITE y su tamaño. Antes de llamar al punto de entrada de la DLL beacon virtual, tendremos que cambiar las protecciones de memoria de la sección al ejecutable.
Asignar nuestra memoria con facilita el proceso de carga reflexiva pero aumenta las posibilidades de detección por parte de las soluciones de seguridad.
A continuación se muestra un ejemplo simplificado de código, del proyecto BokuLoader, que demuestra esto:
Ejemplo de código del proyecto BokuLoader que muestra secciones copiadas de la DLL beacon sin procesar a la DLL beacon virtual.
Algunas características de evasión con respecto a las secciones de carga son:
En el proyecto público BokuLoader, los encabezados de la DLL beacon no se copian de la DLL beacon sin procesar a la DLL beacon virtual. Actualmente, los primeros
Otra posible oportunidad de evasión es hacer que el script UDRL Aggressor encripte las secciones. Las secciones podían descifrarse en memoria mediante la UDRL, usando una clave compartida entre la UDRL y el script UDRL Aggressor.
El beacon HTTP/S x64 depende de cuatro DLL para funcionar correctamente. Si estas DLL no están cargadas actualmente en el proceso, nuestro cargador reflexivo tendrá que cargarlas.
Las cuatro DLL se enumeran en el directorio de importación de la DLL beacon HTTP/S:
Captura de pantalla de PE-Bear con una lista de las DLL del directorio de importación de la DLL beacon.
El cargador reflexivo Cobalt Strike integrado utiliza la API kernel32.LoadLibraryA para la carga de DLL.
La carga de DLL se puede lograr de varias maneras diferentes, con diferentes consideraciones de seguridad operativa. Algunos métodos son:
Si la DLL ya existe en el proceso, aún se pueden utilizar las API de Windows mencionadas anteriormente para obtener las direcciones de la DLL, aunque esto puede desencadenar alertas de detección no deseadas.
Alternativamente, el PEB mantiene un puntero a la estructura
<a title="https://learn.microsoft.com/es-es/windows/win32/api/winternl/ns-winternl-peb_ldr_data" href="https://learn.microsoft.com/es-es/windows/win32/api/winternl/ns-winternl-peb_ldr_data">_PEB_LDR_DATA</a>
. Dentro, hay una lista enlazada con todas las DLL cargadas en el proceso y su información relativa (
). BokuLoader lo aprovecha para descubrir la información de la DLL, evitando llamadas innecesarias a la API.
Si la DLL no existe en el
La carga reflexiva anidada no se puede utilizar fácilmente para cargar dependencias de DLL porque los cargadores reflexivos generalmente no registran la DLL en el proceso. El código externo a la DLL no puede utilizar correctamente una DLL cargada de forma reflexiva. El proyecto DarkLoadLibrary puede ser capaz de cargar correctamente una DLL en memoria sin activar un evento de carga de imagen del núcleo.
Ejemplo de código del proyecto BokuLoader que muestra cómo se pueden resolver las direcciones base de DLL cargadas recurriendo a InMemoryOrderModuleList.
Con las DLL necesarias cargadas en el proceso, se deben resolver las API enumeradas en el directorio de importación. A continuación, las direcciones API deberán escribirse en la tabla de direcciones de importación (IAT) de la DLL beacon virtual. De esta forma, beacon sabe a qué dirección dirigirse cuando necesita llamar a API como
La entrada de importación deberá resolverse mediante el ordinal o la cadena de nombre.
En la imagen de abajo, vemos que la DLL beacon de Cobalt Strike utiliza una combinación de ordinales y cadenas de nombre para las entradas de importación:
Captura de pantalla de PE-Bear que muestra que algunas entradas de importación de la DLL beacon deben resolverse por ordinal.
El cargador reflexivo Cobalt Strike integrado utiliza la API
Algunos métodos de evasión para resolver direcciones API son:
GetProcAddress
NTDLL.LdrGetProcedureAddress
BokuLoader utiliza una implementación de código personalizado deGetProcAddress
La es capaz de gestionar tanto cadenas de nombres como ordinales. Si la dirección devuelta para la entrada de importación es un reenviador a otra DLL, BokuLoader toma por defecto para resolver el remitente.
Al escribir el IAT, el enganche se puede implementar escribiendo las direcciones virtuales de las funciones hook que hemos implementado en lugar de la dirección virtual de las API previstas. Mientras que el resultado esperado se devuelva a beacon cuando se llama a la dirección en la IAT, podemos ejecutar código adicional antes de volver a Beacon. Las publicaciones futuras y las versiones públicas de BokuLoader demostrarán cómo podemos aprovechar el hook IAT para características avanzadas de evasión.
Con un lanzamiento reciente, el proyecto público BokuLoader apoya la característica Malleable PE del perfil Cobalt Strike C2 con una implementación personalizada. Al modificar la clave de enmascaramiento en elBokuLoader.cna
En lo que respecta a la seguridad operativa, es importante saber que los motores de comparación de patrones son capaces de aplicar fuerza bruta a máscaras XOR de un solo byte. En futuras publicaciones se demostrará cómo podemos crear nuestro propio motor Malleable PE utilizando la funcionalidad de scripting Aggressor de Cobalt Strikes para ofuscar el beacon y superar la coincidencia de patrones.
La DLL beacon tiene muchas reubicaciones que deben resolverse y escribirse en la tabla de reubicación base de la DLL beacon virtual antes de ejecutarla.
En PE-Bear podemos ver que la DLL beacon por defecto tiene la dirección base de imagen de
Captura de pantalla de PE-Bear mostrando la dirección base de la imagen del DLL beacon.
Antes de empezar a escribir las reubicaciones, tenemos que calcular el delta entre la dirección base de nuestra DLL beacon virtual y la dirección base codificada.
Por ejemplo, supongamos que la dirección base de nuestra DLL beacon virtual es
A continuación, para determinar la dirección virtual de cada entrada de reubicación en la tabla de reubicación base, añadimos el delta de dirección base a la dirección de entrada de reubicación codificada para determinar la reubicación dentro de nuestra DLL beacon virtual.
En la imagen de abajo podemos ver que las entradas de reubicación de los beacons se escriben al revés en formato little-endian:
Captura de pantalla de PE-Bear que muestra que algunas entradas de reubicación existen en formato little-endian.
La dirección codificada para esta entrada de reubicación es
Añadimos esta dirección al delta de la dirección base, para obtener la dirección virtual de la reubicación tal como existe en la DLL de la baliza virtual:
Para cada entrada de reubicación tendremos que comprobar que el tipo es
<a title="https://learn.microsoft.com/es-es/windows/win32/debug/pe-format" href="https://learn.microsoft.com/es-es/windows/win32/debug/pe-format">IMAGE_REL_BASED_DIR64 (0xA)</a>
. Si es falso, nos saltaremos la escritura de la reubicación.
Una vez que determinamos la dirección virtual de la reubicación tal como existe dentro de la DLL beacon virtual, la escribimos en el espacio de memoria que contiene la dirección de entrada de reubicación codificada.
Si le interesa aprender más sobre cómo hacer reubicaciones de PE, eche un vistazo al código de la función doRelocations en el proyecto público BokuLoader. Antes de publicar esta entrada de blog, cambié el código de reubicaciones del código de ensamblaje al código C, que espero sea legible para humanos, para ayudar a otros que quieran saber los detalles técnicos de cómo se hace esto.
La ejecución de beacon se puede dividir en tres pasos:
Si la memoria que asignamos para nuestra DLL beacon virtual es
Si asignáramos nuestra memoria de beacon virtual como no ejecutable (
En el proyecto público BokuLoader, los cambios de protección de memoria se realizan mediante llamadas directas al sistema a
Ejemplo de código del proyecto BokuLoader que demuestra el cambio de la sección .text de la DLL beacon virtual a ejecutable.
El
Para que la DLL beacon virtual funcione correctamente, primero debe inicializarse llamando al punto de entrada de la DLL beacon virtual. El primer argumento es la dirección base de la DLL beacon virtual. El segundo argumento es el
Ejemplo de código del proyecto BokuLoader que inicializa la DLL beacon virtual.
Tras inicializar la DLL beacon virtual, podemos devolver el punto de entrada del beacon virtual al stub del cargador reflexivo de llamadas, o podemos llamar al punto de entrada de la DLL beacon virtual en nuestra UDRL con el
A diferencia de una DLL típica en la que el primer argumento
<a href="https://learn.microsoft.com/es-es/windows/win32/dlls/dllmain">DLLMAIN</a>
sería la dirección base de la DLL virtual, beacon espera la dirección base de la DLL beacon sin procesar. Si no se proporciona, algunas características de evasión de Malleable PE pueden fallar.
Ejemplo de código del proyecto BokuLoader que muestra dos formas diferentes de ejecutar la DLL beacon virtual.
Esperamos que esta entrada de blog ayude tanto a los equipos rojos como a los equipos azules a comprender mejor Cobalt Strike y el proceso de carga reflexiva. Todavía hay miles de oportunidades de evasión que se pueden implementar a través de la carga reflexiva. Al comprender mejor estos conceptos, las organizaciones pueden prepararse mejor para defenderse con éxito contra las ciberamenazas.
Las futuras publicaciones de esta serie se centrarán en la integración de UDRL con las características actuales de evasión de Cobalt Strike, profundizarán en las características de evasión no documentadas ya presentes en el BokuLoader público, así como en características avanzadas que aún no se han lanzado al público. ¡Estén atentos para obtener información más detallada y técnicas para aprender cómo llevar su juego Cobalt Strike al siguiente nivel con el desarrollo de UDRL!