Minería y trazado por diversión y ganancias: Centro de respuestas de seguridad de Microsoft

Introducción

Cobalt Strike es un marco comercial de comando y control creado por Helpsystems. Puede encontrar más información sobre Cobalt Strike en el Página MITRE ATT&CK. Pero también puede ser utilizado por adversarios reales. En esta publicación, describimos cómo usar RiskIQ y otras tecnologías de Microsoft para ver si tiene cargas útiles de Cobalt Strike (también llamadas «balizas») en su red.

La búsqueda de balizas Cobalt Strike en entornos grandes puede ser un desafío para los equipos de caza de amenazas. Pero con eso viene una gran cantidad de creatividad y oportunidad. En esta publicación de blog, el equipo de Threat Hunting de Microsoft Security Response Center (MSRC) busca mejorar la visibilidad de nuestro entorno, tanto para la seguridad interna como para nuestros clientes, mediante la exploración de metodologías de caza para el tráfico de Cobalt Strike Command and Control (C2).

Encontrar servidores de Cobalt Strike Team

En enero de 2021, RiskIQ publicó una publicación de blog sobre la utilización de hashes JARM para identificar infraestructura maliciosa en Internet en su plataforma. Desde entonces, ha habido un número cada vez mayor de grupos de caza de amenazas y SOC que utilizan hashes JARM dentro de sus organizaciones para detectar actividades maliciosas. Más tarde ese año, en SANS Threat Hunting Summit & Training 2021, José Hernandez y Michael Haag presentó un marco fantástico a la comunidad por utilizar los servicios de escaneo de Internet combinados con hashes JARM para obtener listas de servidores Cobalt Strike Team potenciales. Luego, estas direcciones IP se prueban para la configuración de la baliza, utilizando un código abierto Guión NMAP de Wade Hickey y Zach Stanford. Puede obtener más información sobre JARM aquí.

Como explicaron José Hernandez y Michael Haag en su charla SANS Threat Hunting Summit, existen varios beneficios para identificar servidores Cobalt Strike mediante el uso de servicios de escaneo como Microsoft Defender Threat Intelligence. El principal es que evitamos escanear todo Internet, lo que puede ser problemático. También podemos aprovechar la multitud de tipos de datos que recopilan para variar nuestras capacidades en la identificación de los servidores de los equipos. Tres de estos tipos de datos son Jarm Fuzzy Hashing, Banners y TLS Serials/hashes.

Una cosa a tener en cuenta sobre el uso de hashes JARM es que no siempre brindan respuestas exactas para encontrar servidores Cobalt Strike Team. Como demostrado por Raphael Mudge en el sitio web de HelpSystems, las huellas dactilares de JARM se pueden modificar en función de la configuración del servidor. Por lo tanto, puede ser útil utilizar otros tipos de datos dentro de los conjuntos de datos de escaneo de Internet.

En nuestra bifurcación del marco de trabajo de José Hernandez y Michael Haag, utilizamos hash difuso JARM, banners y seriales/hashes TLS. Tenemos un ejemplo público de esta implementación con un módulo RiskIQ para JARM & TLS Hashes aquí.

A continuación, se destaca una implementación de módulo de ejemplo del marco Melting-Cobalt que utiliza la clase de certificado SSL de la API de la comunidad RiskIQ para obtener todas las direcciones IP que coinciden con el hash SHA1 para los servidores Cobalt Strike Team predeterminados:

‘6ece5ece4192683d2d84e25b0ba7e04f9cb7eb7c’

Figura 1 Ejemplo de llamadas API a RiskIQ para obtener el hash del certificado SSL

Podemos ejecutar este escáner usando una máquina virtual en Azure o utilizando soluciones sin servidor como Azure Functions. Luego, podemos comenzar a extraer direcciones IP de las API de nuestro servicio de escaneo y probarlas en busca de balizas Cobalt Strike. Si se devuelve una baliza Cobalt Strike, almacenamos los datos listos para la ingesta en nuestra base de datos.

Figura 2 Ejemplo de una baliza Cobalt Strike extraída

Ahora que hemos discutido una metodología para identificar los servidores Cobalt Strike Team y crear configuraciones de balizas, necesitamos un lugar para ingerir los datos para comenzar a buscar.

Dado que aquí en Microsoft tenemos una gran cantidad de infraestructura, nos gusta utilizar plataformas de análisis de datos rápidas y escalables para respaldar nuestra búsqueda, como Microsoft Sentinel y Azure Data Explorer. En una publicación de blog posterior, analizaremos el uso de Microsoft Sentinel para usar estos datos para buscar dentro de su entorno SIEM. Para obtener más información sobre cómo puede integrar una fuente de este tipo en su instancia de Sentinel, diríjase a Documentos de Microsoft donde puede obtener más información sobre la transferencia de fuentes STIX/TAXII a Sentinel. Ingerir las balizas Cobalt Strike extraídas en su instancia de Sentinel puede ser una forma rápida y efectiva de correlacionar los datos de la red en su SIEM.

Pero por ahora, discutiremos cómo puede utilizar estos datos dentro de Azure Data Explorer. Esto nos permite tener grandes conocimientos sobre la transmisión de datos, el análisis de series temporales y un lenguaje de consulta avanzado para crear modelos que detecten patrones dentro de los datos de nuestra red. Ingerir y trazar los datos

Como equipo de búsqueda, nos encanta usar Kusto Query Language (KQL) para crear consultas de búsqueda y producir señales. Azure Data Explorer (además de productos como Sentinel) proporciona excelentes terrenos de caza en función de dónde se encuentran sus datos. Para ingerir datos en Azure Data Explorer mediante Python, tenemos una aplicación fácil de usar que puede ayudarlo a comenzar: azure-kusto-python/quick_start en maestro · Azure/azure-kusto-python (github.com).

A continuación se puede encontrar un ejemplo de configuración de Kusto para ingerir nuestros datos de Cobalt Strike Beacon, donde estamos ingeriendo datos en una tabla llamada «CobaltStrikeActiveDiscovery»:



	"kustoUri" : "https://YOURADXCLUSTER.kusto.windows.net",
	"ingestUri" : "https://ingest-YOURADXCLUSTER.kusto.windows.net",
	"databaseName" : "ThreatHunting",
	"tableName" : "CobaltStrikeActiveDiscovery",
	"useExistingTable": false,
	"alterTable": true,
	"queryData": true,
	"ingestData": true,
	"tableSchema" : "(TIMESTAMP:datetime, nmap_cmd:string, ip:string, port:string, protocol:string, service:string, hostnames:string, x64_sha1:string, x64_sha246:string, x64_md5:string, x86_sha1:string, x86_sha256:string, x86_md5:string, x64_config_method_1:string, x64_config_method_2:string, x64_config_port:string, x64_config_spawn_to_x64:string, x64_config_spawn_to_x86:string, x64_config_jitter:string, max_dns:string, dns_idle:string, dns_sleep:string, user_agent:string, watermark:string, c2_host_header:string, x64_config_polling:string, x64_config_c2_server:string, x64_config_beacon_type:string, x64_config_http_method_path_2:string, x64_uri_queried:string, x86_config_method_1:string, x86_config_method_2:string, x86_config_port:string, x86_config_spawn_to_x64:string, x86_config_spawn_to_x86:string, x86_config_jitter:string, x86_config_polling:string, x86_config_c2_server:string, x86_config_beacon_type:string, x86_config_http_method_path_2:string)",
	"data" :
	[
		
			"sourceType": "localFileSource",
			"dataSourceUri": "results.json",
			"format": "MULTIJSON",
			"useExistingMapping": false,
			"mappingName": "CobaltStrikeMapping",
			"mappingValue": "[\"Properties\":\"Path\":\"$.TIMESTAMP\",\"column\":\"TIMESTAMP\",\"datatype\":\"datetime\", \"Properties\":\"Path\":\"$.nmap_cmd\",\"column\":\"nmap_cmd\",\"datatype\":\"string\", \"Properties\":\"Path\":\"$.ip\",\"column\":\"ip\",\"datatype\":\"string\", \"Properties\":\"Path\":\"$.port\",\"column\":\"port\",\"datatype\":\"string\", \"Properties\":\"Path\":\"$.protocol\",\"column\":\"protocol\",\"datatype\":\"string\", \"Properties\":\"Path\":\"$.service\",\"column\":\"service\",\"datatype\":\"string\", \"Properties\":\"Path\":\"$.hostnames\",\"column\":\"hostnames\",\"datatype\":\"string\", \"Properties\":\"Path\":\"$.x64_sha1\",\"column\":\"x64_sha1\",\"datatype\":\"string\", \"Properties\":\"Path\":\"$.x64_sha246\",\"column\":\"x64_sha246\",\"datatype\":\"string\", \"Properties\":\"Path\":\"$.x64_md5\",\"column\":\"x64_md5\",\"datatype\":\"string\", \"Properties\":\"Path\":\"$.x86_sha1\",\"column\":\"x86_sha1\",\"datatype\":\"string\", \"Properties\":\"Path\":\"$.x86_sha256\",\"column\":\"x86_sha256\",\"datatype\":\"string\", \"Properties\":\"Path\":\"$.x86_md5\",\"column\":\"x86_md5\",\"datatype\":\"string\", \"Properties\":\"Path\":\"$.x64_config_method_1\",\"column\":\"x64_config_method_1\",\"datatype\":\"string\", \"Properties\":\"Path\":\"$.x64_config_method_2\",\"column\":\"x64_config_method_2\",\"datatype\":\"string\", \"Properties\":\"Path\":\"$.x64_config_port\",\"column\":\"x64_config_port\",\"datatype\":\"string\", \"Properties\":\"Path\":\"$.x64_config_spawn_to_x64\",\"column\":\"x64_config_spawn_to_x64\",\"datatype\":\"string\", \"Properties\":\"Path\":\"$.x64_config_spawn_to_x86\",\"column\":\"x64_config_spawn_to_x86\",\"datatype\":\"string\",  \"Properties\":\"Path\":\"$.x64_config_jitter\",\"column\":\"x64_config_jitter\",\"datatype\":\"string\", \"Properties\":\"Path\":\"$.max_dns\",\"column\":\"max_dns\",\"datatype\":\"string\", \"Properties\":\"Path\":\"$.dns_idle\",\"column\":\"dns_idle\",\"datatype\":\"string\", \"Properties\":\"Path\":\"$.user_agent\",\"column\":\"user_agent\",\"datatype\":\"string\", \"Properties\":\"Path\":\"$.watermark\",\"column\":\"watermark\",\"datatype\":\"string\",  \"Properties\":\"Path\":\"$.c2_host_header\",\"column\":\"c2_host_header\",\"datatype\":\"string\", \"Properties\":\"Path\":\"$.x64_config_polling\",\"column\":\"x64_config_polling\",\"datatype\":\"string\", \"Properties\":\"Path\":\"$.x64_config_http_method_path_2\",\"column\":\"x64_config_http_method_path_2\",\"datatype\":\"string\", \"Properties\":\"Path\":\"$.x64_config_beacon_type\",\"column\":\"x64_config_beacon_type\",\"datatype\":\"string\", \"Properties\":\"Path\":\"$.x64_uri_queried\",\"column\":\"x64_uri_queried\",\"datatype\":\"string\", \"Properties\":\"Path\":\"$.x86_config_method_1\",\"column\":\"x86_config_method_1\",\"datatype\":\"string\", \"Properties\":\"Path\":\"$.x86_config_method_2\",\"column\":\"x86_config_method_2\",\"datatype\":\"string\", \"Properties\":\"Path\":\"$.x86_config_port\",\"column\":\"x86_config_port\",\"datatype\":\"string\", \"Properties\":\"Path\":\"$.x86_config_spawn_to_x64\",\"column\":\"x86_config_spawn_to_x64\",\"datatype\":\"string\", \"Properties\":\"Path\":\"$.x86_config_spawn_to_x86\",\"column\":\"x86_config_spawn_to_x86\",\"datatype\":\"string\", \"Properties\":\"Path\":\"$.x86_config_jitter\",\"column\":\"x86_config_jitter\",\"datatype\":\"string\", \"Properties\":\"Path\":\"$.x86_config_polling\",\"column\":\"x86_config_polling\",\"datatype\":\"string\", \"Properties\":\"Path\":\"$.x86_config_c2_server\",\"column\":\"x86_config_c2_server\",\"datatype\":\"string\", \"Properties\":\"Path\":\"$.x86_config_beacon_type\",\"column\":\"x86_config_beacon_type\",\"datatype\":\"string\", \"Properties\":\"Path\":\"$.x86_config_http_method_path_2\",\"column\":\"x86_config_http_method_path_2\",\"datatype\":\"string\"]"
		
	]

Una vez ingerido en Kusto, su conjunto de datos de Azure Data Explorer debería tener este aspecto:

Figura 3 Ejemplo de datos de baliza ingeridos en el conjunto de datos n.º 1 de Azure Data Explorer

Figura 4 Ejemplo de datos de baliza ingeridos en el conjunto de datos n.º 2 de Azure Data Explorer

Puede ver que podemos tener transmisión de datos valiosos a esta base de datos lista para que construyamos análisis de datos por encima. Usando https://dataexplorer.azure.com/, podemos trazar algunos aspectos de los datos de los paneles que respaldan la actividad de caza de amenazas mediante Kusto Query Language. Estos son algunos ejemplos que usan los múltiples tipos visuales disponibles para nosotros al agregar un mosaico en el tablero del Explorador de datos:

Figura 5 Tabla de ejemplo que muestra los movimientos de la infraestructura de Team Server por marca de tiempo

Figura 6 Gráfico de barras de ejemplo que muestra el recuento de configuraciones de agente de usuario de Payload C2

Figura 7 Gráfico de barras de ejemplo que muestra encabezados de host C2

Figura 8 Gráfico de áreas de ejemplo que muestra la tendencia de volumen de Cobalt Strike Team Server por día

Para crear paneles más avanzados, podemos utilizar la multitud de funciones de análisis de datos integradas en Kusto Query Language (KQL). Podemos comenzar a utilizar estos datos para correlacionarlos con otras fuentes de datos, como los datos de nuestra red interna.

Búsqueda de posibles compromisos en la red

Una gran ventaja de Azure Data Explorer es que puede reunir varios conjuntos de datos y crear análisis de series temporales eficientes. Una nota clave importante es que nuestra recopilación de datos vale la pena al permitirnos obtener métricas sobre las configuraciones de balizas en Internet para respaldar nuestra caza. Dos puntos de datos clave para buscar en los datos de la red son Polling (o suspensión) y Jitter. Estos son determinados por el operador o se dejan por defecto para establecer las siguientes configuraciones:

Tiempo de sondeo o suspensión: el tiempo de suspensión entre cada devolución de llamada de baliza en milisegundos

Jitter: El % de jitter en el tiempo de sondeo. El valor predeterminado es 0. Un buen ejemplo de una configuración que hemos visto en la siguiente captura de pantalla: si el tiempo de sondeo se establece en 25000 milisegundos, que se redondea a 25 segundos y una tasa de fluctuación se establece en 37% (37% de 25 = 9,25), la baliza dormiría entre devoluciones de llamada entre 15,75 y 34,25 segundos.

Si observamos un conjunto de muestra de los datos de nuestro escáner, podemos obtener una imagen de las configuraciones que utilizan los actores. En esta instantánea a continuación, podemos ver que la configuración de fluctuación y sondeo más común es la predeterminada, la fluctuación se establece en 0 y la tasa de sondeo se establece en 1 minuto.

Figura 9 Instantánea de ejemplo de las tasas de fluctuación y sondeo

Al trazar nuestra configuración de sondeo, podemos ver que el 80,1 % de nuestros datos tiene la tasa de sondeo predeterminada establecida en 1 minuto. Siendo 30 segundos el segundo más común. Con un promedio de 51959 milisegundos o 51.959 segundos.

Figura 10 Configuración de sondeo en milisegundos

Yendo más allá, si graficamos nuestras configuraciones de Jitter, que se ven en la figura 11, podemos ver que el 86% de nuestras configuraciones están establecidas en 0% de Jitter. Utilizando el Función promedio en Kusto, calculamos el promedio del conjunto de datos, lo que da como resultado un porcentaje de fluctuación promedio del 4 %. Esto debería permitirnos obtener una mejor imagen de la variación en la frecuencia a través de los datos de nuestra red.

Figura 11 Trazado de las configuraciones de fluctuación de fase

Esto nos brinda algunas ideas interesantes para observar al correlacionar nuestros datos C2 con los datos de nuestra red. Dado que el tiempo de sondeo promedio es de 51,95 segundos y el tiempo de fluctuación promedio es del 4 %, en promedio podemos ver tráfico de balizas en nuestros datos que muestran devoluciones de llamada en promedio entre 49,87 segundos y 54,03 segundos. Este es, por supuesto, un cálculo promedio a través de un conjunto de datos de muestra, pero nos da a nosotros, como cazadores de amenazas, una idea de lo que podemos ver cuando buscamos Cobalt Strike.

Para aprovechar esto aún más, correlacionemos los datos de C2 que tenemos y correlacionémoslos con los datos de nuestra red. Puede usar una multitud de datos de red para realizar esta búsqueda, ya sea que capture Zeek o netflow, por ejemplo.

Con Azure Logic Apps, creamos una instantánea diaria de los datos de la red que se comunicaba con los C2 capturados en los datos de nuestro escáner. Podemos hacer esto creando Azure Logic App para que se ejecute en cadencia diaria, con el Ejecutar control asíncrono Comando para ingerir un conjunto de datos donde cualquiera de nuestro tráfico de red tiene una dirección de destino de un C2 que tenemos en nuestro conjunto de datos Cobalt Strike. Esto debería darnos una fuente diaria de todo el tráfico de red relacionado con los C2 que podemos usar para crear datos de series temporales.

Podemos usar el serie de fabricación para crear una serie de solicitudes agregadas por hora durante los últimos 7 días. Esto nos daría una indicación de cualquier tráfico de baliza que podamos observar en esta red. Aquí hay una consulta de ejemplo:

El resultado puede ser algo como esto:

Figura 12 Trazado de análisis de series de tiempo a través de datos de red

En nuestro ejemplo que se muestra arriba, podemos ver un valor atípico inmediato dentro de nuestros datos dentro de la línea verde trazada. Vemos una máquina que habla con frecuencia con un Cobalt Strike C2 de nuestro tráfico interno durante los últimos 7 días. Esto nos brinda un lugar inmediato para comenzar a buscar compromisos, ya que esta máquina está hablando externamente a un ritmo tan frecuente que se destaca como una anomalía predominante. Los datos que se muestran arriba en general muestran cantidades muy pequeñas de interacciones con nuestros Cobalt Strike C2 por hora. Es poco probable que puedan ser máquinas comprometidas debido al bajo volumen.

NOTA: En este ejemplo, se muestrean los datos de red que estamos usando, lo que da como resultado que el recuento sea inferior a una tasa de sondeo de ~51,959 segundos.

Con datos de red de fidelidad completa, es posible que vea un recuento promedio por hora de alrededor de ~50-~70 flujos, sin embargo, esto no incluye datos de red adicionales que pueden ser capturados durante un incidente sospechoso por un atacante que interactúa con una máquina comprometida, lo que crear un conteo de tráfico más alto. En situaciones en las que ve un conteo de tráfico superior a la tasa de sondeo promedio, es posible que vea ejemplos de dónde el atacante interactúa con la máquina comprometida. Esto proporciona aún más información sobre el compromiso, incluida la hora a la que los atacantes interactúan con la máquina, lo que puede admitir atribuciones de zonas horarias, etc.

En este ejemplo, esta máquina se vio comprometida por Cobalt Strike, y nuestro análisis de la serie temporal lo destacó de manera efectiva. Nos brindó un lugar inmediato para comenzar a buscar compromisos sin la necesidad de detecciones en la máquina misma.

Además, seleccionar áreas de alta comunicación puede facilitar las estrategias de caza y delimitar los postes de la meta izquierda y derecha cuando se busca a través de otros medios, como conjuntos de datos EDR o ETW.

Figura 13 Identificación de áreas de mayor volumen de tráfico de red para C2

Conclusión

Como cazadores de amenazas, debemos pensar en múltiples formas de identificar actividades maliciosas en nuestro entorno y cubrir múltiples medios de datos. En esta publicación de blog, hemos discutido un ejemplo de la utilización de datos API de servicios de escaneo, como Risk IQ, para identificar los servidores Cobalt Strike Team, sus C2 y su configuración de baliza. Luego, demostramos cómo utilizar estos datos para crear formas efectivas de obtener información valiosa sobre cómo se vería Cobalt Strike en nuestro entorno y formas de utilizar el análisis de series temporales para identificar compromisos.

Caza de amenazas MSRC



Fuente del articulo