Si bien la IA de próxima generación y los componentes de machine learning de las soluciones de seguridad continúan mejorando las capacidades de detección basadas en el comportamiento, en su núcleo muchos aún dependen de las detecciones basadas en firmas. Cobalt Strike al ser un marco popular de comando y control (C2) del equipo rojo utilizado tanto por los actores de amenazas como por los equipos rojos desde su debut, sigue siendo muy utilizado por 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 personalizarlo 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, dado el exceso de firmas de Cobalt Strike, restringimos su uso a la simulación de actores de amenazas menos sofisticados y, en su lugar, aprovechamos otros C2 de terceros e internos al realizar ejercicios de equipo rojo más avanzados.
A través de esfuerzos de investigación y desarrollo, hemos encontrado un mayor éxito operativo en ejercicios avanzados de 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 estos actores de amenazas. Para los equipos rojos dispuestos a realizar el esfuerzo 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 capacitación del equipo rojo.
A medida que seguimos ampliando nuestras capacidades de C2, compartimos algunos insights sobre cómo nos hemos basado en la infraestructura Cobalt Strike en el pasado, específicamente mediante el desarrollo de cargadores reflexivos personalizados. También se pretende que los defensores comprendan cómo funciona Cobalt Strike para crear detecciones más sólidas.
Boletín de la industria
Manténgase al día sobre las tendencias más importantes e intrigantes de la industria sobre IA, automatización, datos y más con el boletín Think. Consulte la Declaración de privacidad de IBM.
Su suscripción se entregará en inglés. En cada boletín, encontrará un enlace para darse de baja. Puede gestionar sus suscripciones o darse de baja aquí. Consulte nuestra Declaración de privacidad de IBM para obtener más información.
Esta entrada en el 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 realce con técnicas avanzadas que actualmente no están presentes en la herramienta. Las publicaciones futuras profundizarán en el desarrollo de características específicas de evasión y cómo implementarlas en nuestro cargador reflexivo Cobalt Strike.
Para comenzar, esta publicación cubrirá:
A medida que exploramos la carga reflexiva de Cobalt Strike a través de la lente de un desarrollador de herramientas de seguridad ofensivas, destacaremos las oportunidades de detecciones y evasiones. Algunos aspectos del desarrollo se omitirán o simplificarán, y lo alentamos a llenar los vacíos depurando proyectos de cargadores reflexivos existentes, reconstruyéndolos desde cero o buscando capacitación.
El implante Cobalt Strike C2, conocido como Beacon, es una biblioteca de enlace dinámico (DLL) de Windows, y la capacidad modular de usar nuestro propio cargador DLL en Cobalt Strike se conoce como cargador reflexivo definido por el usuario (UDRL).
Normalmente, el cargador DLL de Windows integrado es responsable de cargar archivos DLL en el espacio de memoria virtual de un proceso. El cargador de DLL de Windows existe principalmente dentro del espacio del usuario, aunque cruza el espacio del kernel al asignar archivos DLL desde el disco.
El uso de Windows DLL Loader presenta algunos inconvenientes cuando se utiliza durante simulaciones de adversarios:
Por lo tanto, usar el cargador de DLL de Windows para cargar nuestra DLL de baliza no es una solución ideal. Para superar estos desafíos, cargamos la DLL de baliza desde la memoria con un cargador reflexivo.
Los tres principales puntos de detección que evita la carga reflexiva son:
La carga reflexiva se puede considerar simplemente como cargar un archivo DLL sin procesar directamente desde la memoria, en lugar de cargarlo desde el sistema de archivos.
La carga reflexiva y el cargador de DLL de Windows integrado sirven para el mismo propósito de cargar una DLL desde el formato de archivo sin procesar en el 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 al cargar una DLL es saber que el formato de la DLL será diferente en el disco y en la 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:
Al examinar una DLL de baliza HTTP en la herramienta PE-Bear de Aleksandra Doniec, vemos las diferencias entre la dirección sin procesar y la dirección virtual para cada sección de la DLL:
Tabla que lista las direcciones sin procesar y virtuales de cada sección de la DLL de baliza.
El tamaño de esta DLL de baliza HTTP/S es
PE-Bear ofrece una representación visual de nuestra DLL de baliza tal y como existe en formato de archivo sin procesar frente al formato de dirección virtual:
Representación visual de la DLL de baliza en formato sin procesar (izquierda) versus formato virtual (derecha)
Si bien no es el movimiento más inteligente para realizar durante una simulación de adversario, colocar una DLL de baliza sin procesar sin ofuscación en el disco y cargarla con el cargador de DLL de Windows es una excelente manera de desmitificar tanto la carga de balizas como la de DLL. Básicamente, la baliza es solo una DLL. El cargador de DLL de Windows y un cargador reflexivo simplemente cargan una DLL en un proceso.
Para cargar la DLL de baliza con el cargador de DLL de Windows, realizamos los siguientes pasos:
LoadLibrary
API para cargar nuestro DLL de baliza desde el disco.En primer lugar, desactivamos todas las opciones de Malleable PE que impiden que el cargador de DLL de Windows descargue nuestra DLL de baliza. Para ello, modificamos nuestro perfil de Malleable C2 y deshabilitamos las opciones de evasión de Malleable PE ubicadas en el bloque de escenario:
Se modificó el bloque de escenario del perfil de Malleable C2 para deshabilitar las funciones de evasión de Cobalt Strike.
Después de modificar el perfil, reiniciamos Cobalt Strike Team Server, suministrando nuestro
Nos conectamos al Team Server con el cliente Cobalt Strike. A continuación, creamos un
Captura de pantalla de la creación de una DLL de baliza "sin procesar" desde Cobalt Strike Client
Usando el siguiente código, creamos un programa en C llamado
Código C de Windows para cargar la DLL de baliza desde el disco mediante el cargador de DLL de Windows.
Utilizamos la
Como parte del proceso de carga, Windows DLL Loader inicializará nuestra DLL de baliza llamando a su punto de entrada con
Después de que Windows DLL Loader haya cargado e inicializado nuestra DLL de baliza en el espacio de memoria virtual de nuestro proceso, tendremos que volver a llamar al punto de entrada de la DLL de baliza virtual con el argumento
Nuestro programa debe conocer el punto de entrada de nuestra DLL de baliza virtual para ejecutar nuestra DLL de baliza virtual. Esto se puede hacer dinámicamente dentro del programa analizando los encabezados de la DLL de la baliza 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 y codificaremos manualmente el punto de entrada RVA de nuestra DLL de baliza en nuestro programa. Con PE-Bear, descubrimos que el punto de entrada del RVA a la baliza es
Captura de pantalla de cómo encontrar el RVA del punto de entrada de la DLL de la baliza mediante PE-Bear
El
Con nuestro código listo, compilamos nuestro programa C en un ejecutable de Windows:
Comando utilizado para compilar nuestro programa.
Al colocar nuestra DLL de baliza y nuestro programa ejecutable de cargador de balizas en el mismo directorio, el cargador de DLL de Windows podrá descubrir nuestra DLL mientras realiza su rutina de carga.
Colocamos
La DLL de baliza y el programa cargador colocados en el mismo directorio.
Desde nuestro escritorio de Windows, hacemos doble clic en nuestro loadBeaconDLL.exe programar y establecer una conexión activa de baliza con nuestro servidor de equipo.
Conexión exitosa a C2 Team Server desde la DLL de baliza cargada mediante el cargador de DLL de Windows.
Cobalt Strike utiliza una versión modificada del proyecto Reflective Loader de Stephen Fewer. Este legendario cargador de DLL en memoria tiene más de una década de antigüedad y se ha utilizado en Metasploit y otras herramientas de seguridad ofensiva destacadas.
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 Cobalt Strike tiene para ofrecer. La principal desventaja de usar un cargador reflexivo definido por el usuario (UDRL) personalizado es que las funciones 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 utiliza una UDRL, y se aplican parches en la DLL de la baliza mediante el motor Cobalt Strikes Maleable PE en la creación de la carga útil de la baliza. Sin embargo, actualmente características como deben ser gestionadas por la UDRL, mientras que otras como y puede gestionarse con una baliza con una integración UDRL 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 método de carga reflexiva y robusto que puede cargar cualquier archivo PE (EXE o DLL).
Diagrama de un cargador reflexivo antepuesto a una DLL, cargando una DLL en la memoria virtual.
La implementación de la carga reflexiva de 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,
Cuando se carga una UDRL en Cobalt Strike, y un operador genera una carga útil de baliza desde el cliente de Cobalt Strike, el motor de Malleable PE de Cobalt Strike parchea el shellcode del cargador reflexivo en el desplazamiento del archivo sin procesar del
Cuando el motor Malleable PE completa el parcheo de la DLL de la baliza sin procesar, la DLL de la baliza sin procesar se entrega al operador en un formato ejecutable similar al shellcode.
Diagrama del cargador reflexivo Cobalt Strike, cargando la DLL de baliza en la memoria virtual.
Al observar los bytes iniciales en el desensamblador PE-Bear, podemos ver que la DLL de la baliza 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 antepuestos
Confirmamos que el desplazamiento del archivo sin procesar para la
Captura de pantalla del uso de PE-Bear para determinar el desplazamiento sin procesar del archivo de exportación de ReflectiveLoader.
Tal como existe dentro del directorio de exportación, la dirección para la
Para descubrir el desplazamiento del archivo sin procesar de la
Las direcciones virtual y sin procesar para
Direcciones sin procesar y virtuales de la sección .text de la DLL de baliza.
La diferencia entre las dos es
Podemos confirmar esto en PE-Bear haciendo clic derecho en el
En resumen, el flujo del proceso de carga reflexiva de Cobalt Strike es:
Diagrama que muestra las principales fases del proceso de carga reflexiva de la DLL de baliza por parte de Cobalt Strike.
Dado que nuestro cargador reflexivo se ejecuta antes de que se cargue la DLL de baliza, el código del cargador reflexivo debe ser shellcode puro.
La forma más fácil de crear shellcode complejo es escribirlo en C sin dependencias externas. Luego, el archivo C se compila en un archivo de objeto. Todo debe estar incluido en el Finalmente, sabemos que la.text
El motor Malleable PE de Cobalt Strike se encargará del trabajo de obtener el shellcode de nuestro archivo de objeto de cargador reflexivo y aplicarlo en la DLL de baliza sin procesar en el desplazamiento del archivo sin procesar de la
Script de Aggressor para escribir el shellcode de cargador reflexivo en la DLL de baliza sin procesar aprovechando Cobalt Strike.
Nuestro script UDRL Aggressor tiene escritura Cobalt Strike en nuestro shellcode reflexivo del cargador realizando 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>función Cobalt Strike Aggressor incorporada analizará nuestro archivo de objetos UDRL desde la
<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 Maleable PE para descubrir el desplazamiento del archivo sin procesar de nuestra
Cobalt Strike ha hecho el trabajo por nosotros con respecto a la extracción la sección .text de nuestro archivo de objeto de cargador reflexivo, parcheando nuestro shellcode de cargador reflexivo y llamando a nuestro cargador reflexivo con el stub de llamada de cargador reflexivo ubicado en el encabezado de la DLL de baliza.
Estas son las fases que debemos desarrollar para cargar reflexivamente la baliza:
Existen varios métodos diferentes que podemos usar para descubrir la dirección de la DLL de la baliza en forma de memoria. Algunos métodos son:
Cuando usamos un método que busca hacia atrás, 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 de baliza del registro RDI.
El proyecto original del cargador reflexivo busca hacia atrás los encabezados MZ y PE. Estos encabezados se han convertido en puntos de detección. Para superar este Cobalt Strike, se agregaron las
La documentación de Cobalt Strike establece que la
Cuando está configurado, los
Estos bytes deben ser algo único, o el cargador reflexivo no podrá encontrarlos. Además, los bytes para el encabezado MZ deben ser de no-operación 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 de baliza sin procesar. Este método utiliza un cazador de capaz de buscar hacia atrás desde
La dirección
Como no tenemos acceso fácil al motor Java Malleable PE, el
Aggressor para escribir un huevo en la DLL de baliza sin procesar y mostrar los cambios en la consola de scripts de Cobalt Strike.
El código UDRL debe conocer el valor de huevo escrito en la DLL de baliza sin procesar por el script UDRL. Con el huevo conocido, el cazador de huevos busca hacia atrás dos instancias del huevo, como se ve en el siguiente código:
Código ensamblador Intel x64 para un cazador de huevos que busca hacia atrás dos instancias de un huevo de 64 bits.
Ahora que los encabezados MZ y PE ya no se utilizan, podemos eliminarlos en el script UDRL Aggressor:
Secuencia de comandos de agresor para enmascarar MZ, PE y bytes no utilizados del banner de denegación del servicio (DoS) ubicado en los encabezados de la DLL de baliza sin procesar.
También hay otra forma, específica de Cobalt Strike, de descubrir la dirección base de la DLL de baliza sin procesar. Como vimos anteriormente, los bytes iniciales en el stub del cargador reflexivo de llamadas almacenan la dirección base de la DLL de baliza sin procesar en el
Para examinar esto más a fondo en el depurador, generamos una baliza, anteponemos un punto de interrupción (
Captura de pantalla de X64dbg que muestra el paso a través del stub del cargador reflexivo de llamadas para ver que la dirección base de la DLL de baliza 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 del DLL de la baliza 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 de la baliza sin procesar desde el registro RDI.
Con la dirección base de la DLL de baliza sin procesar, ahora podemos obtener los valores que necesitamos para cargar la baliza en el espacio de direcciones virtuales del proceso.
La siguiente tabla enumera los valores que necesitamos de los encabezados de la DLL de baliza sin procesar, las ubicaciones en las que los encontraremos y sus tipos:.
Tabla que enumera los valores del encabezado de la DLL de la baliza sin procesar que son útiles para cargar la DLL de la baliza.
No todo el contenido de los encabezados es necesario para cargar la DLL de baliza. Los valores requeridos se pueden reempaquetar u ofuscar. Los valores no requeridos pueden eliminar o aleatorizarse.
Una vez que conocemos elSizeOfImagef
Se pueden usar diferentes métodos para asignar memoria para la DLL de la baliza virtual. Los diferentes métodos utilizarán diferentes tipos de memoria. Los diferentes métodos admitidos por el cargador reflexivo predeterminado de Cobalt Strike son:
Tabla que muestra las opciones de asignación de memoria de Cobalt Strike para la DLL de baliza virtual.
Esto puede ir un paso más allá con UDRL. En su lugar, se puede utilizar la versión NTAPI de estas funciones. Además, las funciones NTAPI 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 del 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 de la baliza 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 de baliza virtual, necesitamos copiar las secciones de la baliza de sus compensaciones de archivos sin procesar, tal como existen en la DLL de baliza sin procesar, a la memoria asignada en sus compensaciones virtuales relativas.
Si asignamos nuestra memoria conREADWRITE y su tamaño. Antes de llamar al punto de entrada de la DLL de baliza virtual, tendremos que cambiar las protecciones de memoria de la sección
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 de código simplificado, del proyecto BokuLoader, que demuestra esto: esto:.
Ejemplo de código del proyecto BokuLoader que muestra secciones copiadas del DLL de baliza sin procesar al DLL de baliza virtual.
Algunas características de evasión con respecto a las secciones de carga son:
En el proyecto público de BokuLoader, los encabezados de la DLL de baliza no se copian de la DLL de baliza sin procesar a la DLL de baliza virtual. Actualmente, los primeros
Otra posible oportunidad de evasión es hacer que el script UDRL Aggressor cifre las secciones. La UDRL podría descifrar las secciones en la memoria, utilizando una clave compartida entre la UDRL y el script de UDRL Aggressor.
La baliza HTTP/S x64 se basa en cuatro DLL para funcionar correctamente. Si estas DLL no están cargadas actualmente en el proceso, nuestro cargador reflexivo deberá cargarlas.
Las cuatro DLL se enumeran en el directorio de importación de la DLL de baliza HTTP/S:
Captura de pantalla de PE-Bear que enumera las DLL del directorio de importación de la DLL de la baliza.
El cargador reflexivo Cobalt Strike integrado utiliza la API kernel32.LoadLibraryA para la carga de DLL.
La carga de DLL se puede lograr de diversas maneras, con diferentes consideraciones de seguridad operativa. Algunos métodos son:
Si la DLL ya existe en el proceso, las API de Windows anteriores se pueden seguir usando para obtener las direcciones base de la DLL, aunque esto puede desencadenar alertas de detección no deseadas.
Alternativamente, el PEB mantiene un puntero a la estructura
<un título="https://learn.microsoft.com/mx-es/windows/win32/api/winternl/ns-winternl-peb_ldr_data" href="https://learn.microsoft.com/mx-es/windows/win32/api/winternl/ns-winternl-peb_ldr_data">_PEB_LDR_DATA</a>
. Dentro, hay una lista vinculada de todas las DLL cargadas en el proceso y su información relativa (
). BokuLoader aprovecha esto para descubrir la información de DLL, evitando llamadas innecesarias a la API.
Si la DLL no existe en
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 usar correctamente una DLL cargada de forma reflexiva. El proyecto DarkLoadLibrary puede ser capaz de cargar correctamente una DLL en la 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 de DLLs cargadas recorriendo la InMemoryOrderModuleList.
Con las DLL necesarias cargadas en el proceso, se deben resolver las API enumeradas en el directorio de importación. Las direcciones API deberán escribirse en la tabla de direcciones de importación (IAT) de la DLL de la baliza virtual. De este modo, la baliza sabe a qué dirección saltar cuando necesita llamar a API como
La entrada de importación deberá resolverse a través de la cadena ordinal o de nombre.
En la imagen a continuación, vemos que la DLL de baliza 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 algunas entradas de importación para la DLL de baliza que deben resolverse por ordinal.
El cargador reflexivo Cobalt Strike incorporado utiliza la
Algunos métodos de evasión para resolver direcciones API son:
GetProcAddress
NTDLL.LdrGetProcedureAddress
BokuLoader utiliza una implementación de código personalizado deGetProcAddress
El también es capaz de manejar cadenas de nombres y ordinales. Si la dirección retornada para la entrada de importación es un reenviador a otra DLL, BokuLoader se asigna de manera predeterminada a para resolver al reenviador.
Al escribir el IAT, el enlace se puede implementar escribiendo las direcciones virtuales de las funciones de enlace que hemos implementado en lugar de la dirección virtual de las API previstas. Siempre que la salida esperada se devuelva a la baliza cuando se llame a la dirección en el IAT, podemos ejecutar código adicional antes de volver a la baliza. Las publicaciones futuras y los lanzamientos públicos de BokuLoader demostrarán cómo podemos aprovechar el enganche IAT para características avanzadas de evasión.
Con un lanzamiento reciente, el proyecto público BokuLoader apoya la Al modificar la clave de enmascaramiento en elBokuLoader.cna
En cuanto a la seguridad operativa, es importante saber que los motores de coincidencia de patrones son capaces de forzar a fuerza bruta máscaras XOR de un solo byte. Las publicaciones futuras demostrarán cómo podemos crear nuestro propio motor de Malleable PE utilizando la funcionalidad de scripting Cobalt Strikes Aggressor para ofuscar la baliza y superar la coincidencia de patrones.
La DLL de baliza tiene muchas reubicaciones que deben resolverse y escribirse en la tabla de reubicación base de la DLL de baliza virtual antes de ejecutarla.
En PE-Bear podemos ver que la DLL de baliza tiene por defecto la dirección base de la imagen de
Captura de pantalla de PE-Bear que muestra la dirección base de la imagen de la DLL de baliza.
Antes de comenzar a escribir reubicaciones, necesitamos calcular el delta entre la dirección base de nuestra DLL de baliza virtual y la dirección base codificada.
Por ejemplo, supongamos que la dirección base de nuestro DLL de baliza virtual es
A continuación, para determinar la dirección virtual de cada entrada de reubicación en la tabla de reubicación base, agregamos 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 de baliza virtual.
En la siguiente imagen, podemos ver que las entradas de reubicación de balizas 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
Agregamos 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 verificar que el tipo sea
<a title="https://learn.microsoft.com/mx-es/windows/win32/debug/pe-format" href="https://learn.microsoft.com/mx-es/windows/win32/debug/pe-format">IMAGE_REL_BASED_DIR64 (0xA)</a>
. Si esto es falso, omitiremos escribir la reubicación.
Una vez que determinamos la dirección virtual de la reubicación tal como existe dentro de la DLL de baliza virtual, la escribimos en el espacio de memoria que contiene la dirección de entrada de reubicación codificada.
Si te interesa saber más sobre cómo realizar reubicaciones PE, consulta el código de la función doRelocations en el proyecto público BokuLoader. Antes de publicar esta entrada en el blog, cambié el código de reubicación de ensamblador a código C legible por humanos, para ayudar a otros que desean conocer los detalles técnicos de cómo se hace esto.
La ejecución de la baliza se puede desglosar en tres pasos:
Si la memoria que asignamos para nuestra DLL de baliza virtual es
Si asignamos nuestra memoria de baliza virtual como no ejecutable (
En el proyecto público BokuLoader, los cambios en las protecciones de memoria se realizan mediante llamadas directas al sistema a
Ejemplo de código del proyecto BokuLoader que muestra cómo cambiar la sección .text de la DLL de la baliza virtual a ejecutable.
Hay
Para que la DLL de la baliza virtual se ejecute correctamente, primero debe inicializarse llamando al punto de entrada de la DLL de la baliza virtual. El primer argumento es la dirección base de la DLL de la baliza virtual. El segundo argumento es
Ejemplo de código del proyecto BokuLoader que inicializa la DLL de baliza virtual.
Después de inicializar la DLL de baliza virtual, podemos devolver el punto de entrada de la baliza virtual al stub del cargador reflexivo de llamadas, o podemos llamar al punto de entrada de la DLL de baliza virtual en nuestra UDRL con el
A diferencia de una DLL típica donde el primer argumento
<a href="https://learn.microsoft.com/mx-es/windows/win32/dlls/dllmain">DLLMAIN</a>
sería la dirección base de la DLL virtual, la baliza espera la dirección base de la DLL de baliza sin procesar. Si no se proporciona, algunas características de evasión de Maleable PE pueden fallar.
Ejemplo de código del proyecto BokuLoader que muestra dos formas diferentes de ejecutar la DLL de baliza virtual.
Esperamos que esta entrada en el 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 toneladas de oportunidades de evasión que se pueden implementar a través de la carga reflexiva. Con una comprensión más profunda de estos conceptos, las organizaciones pueden prepararse mejor para una defensa exitosa contra las amenazas cibernéticas.
Las publicaciones futuras de esta serie se centrarán en la integración de la UDRL con las características actuales de evasión de Cobalt Strike, profundizarán en las características de evasión no documentadas que ya están presentes en el BokuLoader público, así como en las características avanzadas que aún no se han lanzado al público. ¡Permanezca atento para obtener información y técnicas más detalladas para aprender cómo llevar su juego Cobalt Strike al siguiente nivel con el desarrollo UDRL!