Detectar Cobalt Strike Beacon en Red: Guía de Threat Hunting
Guía completa de threat hunting para detectar Cobalt Strike beacons en red. Indicadores de red (JA3/JA3S, user-agent, URI, sleep/jitter), memoria, named pipes, malleable C2 profiles. Hunting con Zeek, Suricata, YARA, Sigma y Volatility.
Cobalt Strike: la herramienta de doble filo
Cobalt Strike es un framework de post-explotación diseñado para pruebas de penetración. Desarrollado originalmente por Raphael Mudge y ahora mantenido por Fortra (antes HelpSystems), es la herramienta C2 más utilizada tanto por red teams legítimos como por actores de amenazas.
Según datos de Proofpoint, Cobalt Strike apareció en el 25% de los incidentes de ransomware analizados entre 2023 y 2025. Grupos como APT29, APT41, FIN7, y operadores de ransomware como LockBit, BlackCat y Conti lo usan como framework C2 principal.
Su popularidad entre atacantes se debe a tres factores: estabilidad probada en combate, capacidad de evasión mediante malleable C2 profiles, y la disponibilidad de versiones crackeadas que eliminan la barrera de coste. La versión 4.x sigue siendo la más utilizada, aunque existen variantes modificadas.
Para los defensores, esto significa que detectar Cobalt Strike es una habilidad fundamental. Este artículo cubre todos los vectores de detección: red, memoria, disco y logs.
Tipos de beacon
Cobalt Strike soporta varios tipos de beacon, cada uno con un canal de comunicación diferente:
| Tipo | Canal | Puerto típico | Dificultad de detección |
|---|---|---|---|
| HTTP | HTTP cleartext | 80, 8080 | Baja (inspección de contenido) |
| HTTPS | TLS encriptado | 443, 8443 | Media (JA3, certificados, metadatos) |
| DNS | Consultas DNS | 53 | Alta (requiere análisis de patrones DNS) |
| SMB | Named pipes | 445 | Alta (tráfico interno, sin salida a Internet) |
| TCP | Socket raw | Variable | Media (depende del puerto y contexto) |
Los beacons HTTP/HTTPS son los más comunes porque se mezclan con el tráfico web legítimo. Los beacons SMB se usan para lateral movement interno: un beacon HTTPS en un servidor perimetral actúa como relay para beacons SMB en la red interna.
Indicadores de red
JA3 y JA3S fingerprints
JA3 es un fingerprint del TLS ClientHello que identifica la implementación TLS del cliente, independientemente del destino. JA3S hace lo mismo con el ServerHello.
Fingerprints conocidos de Cobalt Strike:
# JA3 de Cobalt Strike (versiones comunes)
72a589da586844d7f0818ce684948eea # CS 4.x con Java 11
a0e9f5d64349fb13191bc781f81f42e1 # CS 4.x con Java 8
3f0f4a3b8c0e2d9c8a4e6b5f7d1c0e9a # CS 4.x custom keystore
# JA3S del teamserver
ae4edc6faf64d08308082ad26be60767 # Respuesta default del teamserver
b742b407517bac9536a77a7b0fee28e9 # Variante con certificado custom
Limitaciones del JA3:
- Los JA3 de Cobalt Strike pueden coincidir con aplicaciones Java legítimas.
- Malleable profiles no pueden cambiar el JA3 directamente, pero el atacante puede modificar la versión de Java del teamserver.
- Usar JA3 como indicador primario genera falsos positivos. Combinarlo con otros indicadores.
Detección por Zeek
Script de Zeek para detectar fingerprints JA3 sospechosos:
@load base/protocols/ssl
module CobaltStrikeDetect;
export {
redef enum Notice::Type += {
CS_JA3_Match,
CS_Beacon_Pattern
};
}
const cs_ja3_hashes: set[string] = {
"72a589da586844d7f0818ce684948eea",
"a0e9f5d64349fb13191bc781f81f42e1",
"3f0f4a3b8c0e2d9c8a4e6b5f7d1c0e9a"
};
event ssl_client_hello(c: connection, version: count, record_version: count,
possible_ts: time, client_random: string,
session_id: string, ciphers: index_vec,
comp_methods: index_vec) {
if (c$ssl?$ja3 && c$ssl$ja3 in cs_ja3_hashes) {
NOTICE([
$note=CS_JA3_Match,
$msg=fmt("Cobalt Strike JA3 detected: %s -> %s:%d (JA3: %s)",
c$id$orig_h, c$id$resp_h, c$id$resp_p, c$ssl$ja3),
$conn=c
]);
}
}
User-Agent analysis
Los malleable C2 profiles permiten configurar cualquier User-Agent, pero la configuración por defecto y los profiles más populares usan patrones identificables:
# Defaults de Cobalt Strike (sin malleable profile)
Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0)
Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; WOW64; Trident/4.0)
# Profiles populares (jQuery, Amazon, etc.)
Mozilla/5.0 (Windows NT 6.3; Trident/7.0; rv:11.0) like Gecko
Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36
La clave no es el User-Agent en sí, sino la inconsistencia:
- Un sistema que genera tráfico con User-Agent de IE9 cuando tiene Chrome instalado.
- Tráfico HTTPS con User-Agent moderno pero JA3 de Java (no de un navegador).
- Mismo User-Agent en todas las peticiones sin variación (los navegadores reales varían).
URI patterns
Los beacons envían datos en los URIs. Sin malleable profile, los patrones por defecto son:
# GET (check-in / beacon)
GET /dpixel HTTP/1.1
GET /pixel.gif HTTP/1.1
GET /___utm.gif HTTP/1.1
GET /submit.php?id=XXXXXXXX HTTP/1.1
GET /activity HTTP/1.1
# POST (data exfiltration)
POST /submit.php?id=XXXXXXXX HTTP/1.1
Con malleable profiles, los URIs cambian completamente. El profile jQuery emula peticiones a CDN:
GET /jquery-3.3.1.min.js HTTP/1.1
POST /jquery-3.3.2.min.js HTTP/1.1
Detección: Buscar peticiones repetitivas a URIs estáticos que deberían ser cacheados por el navegador. Un navegador real no descarga jquery-3.3.1.min.js cada 60 segundos.
Sleep y jitter: el patrón temporal
El beacon contacta al C2 a intervalos regulares definidos por sleep (milisegundos) y jitter (porcentaje de variación):
Sleep: 60000 ms (60 segundos)
Jitter: 37%
Intervalo real: 60000 ± (60000 * 0.37)
Rango: 37,800 ms a 82,200 ms
La distribución de intervalos sigue un patrón uniforme dentro del rango. Esto es detectable estadísticamente:
# Pseudocódigo para detectar beaconing
import numpy as np
intervals = calculate_request_intervals(flow_data)
# Test: distribución uniforme con media constante
mean_interval = np.mean(intervals)
std_interval = np.std(intervals)
coefficient_of_variation = std_interval / mean_interval
# CV entre 0.15 y 0.40 con media entre 30s y 300s
# es altamente sospechoso de beaconing
if 0.15 < coefficient_of_variation < 0.40 and 30 < mean_interval < 300:
alert("Possible C2 beaconing detected")
Certificados TLS
El teamserver de Cobalt Strike genera certificados TLS autofirmados con valores por defecto reconocibles:
# Valores por defecto del certificado CS
Subject: CN=Major Cobalt Strike
Issuer: CN=Major Cobalt Strike
Validity: 3 months
# Valores frecuentes en versiones crackeadas
Subject: CN=localhost
Subject: CN=*.local
Subject: O=Cobaltstrike, OU=AdvancedPenTesting
Los atacantes sofisticados usan certificados Let's Encrypt legítimos o certificados comprados, pero el serial number y la estructura del certificado pueden seguir revelando patrones.
Indicadores en memoria
Reflective DLL loading
Cobalt Strike carga su beacon en memoria usando reflective DLL injection: la DLL se carga sin tocar disco y sin usar la API estándar de carga de librerías de Windows.
Indicadores en memoria:
# Strings del beacon (no ofuscados en versiones antiguas)
"beacon.dll"
"beacon.x64.dll"
"ReflectiveLoader"
# MZ header en regiones de memoria no respaldadas por archivo
# (indica DLL cargada reflectively)
Análisis con Volatility
# Listar procesos sospechosos
$ vol3 -f memory.raw windows.pslist
# Buscar inyecciones de código (MZ headers en memoria no respaldada)
$ vol3 -f memory.raw windows.malfind
# Ejemplo de output sospechoso:
Process: rundll32.exe PID: 4892
Protection: PAGE_EXECUTE_READWRITE
Address: 0x00000189A4C10000
4d 5a 90 00 03 00 00 00 MZ......
04 00 00 00 ff ff 00 00 ........
Un proceso legítimo como rundll32.exe con regiones de memoria PAGE_EXECUTE_READWRITE que contienen un MZ header es un indicador casi definitivo de inyección de código.
Detección de configuración en memoria
Herramientas especializadas pueden extraer la configuración del beacon directamente de la memoria:
# CobaltStrikeParser de Sentinel One
$ python3 parse_beacon_config.py --process 4892 memory.raw
# 1768.py de Didier Stevens
$ python3 1768.py -r memory.raw
La configuración extraída revela: C2 server, sleep time, jitter, watermark, named pipes, spawn processes, y todas las opciones del malleable profile.
Named pipes
Los beacons SMB usan named pipes para comunicación lateral. Los nombres por defecto son:
# Named pipes por defecto de Cobalt Strike
\\.\pipe\msagent_##
\\.\pipe\MSSE-####-server
\\.\pipe\status_##
\\.\pipe\postex_####
\\.\pipe\postex_ssh_####
\\.\pipe\mojo.####.####.####
(Donde # representa caracteres hexadecimales variables.)
Detección con Sysmon
Sysmon EventID 17 (PipeCreated) y EventID 18 (PipeConnected) registran actividad de named pipes:
<Event>
<System>
<EventID>17</EventID>
</System>
<EventData>
<Data Name="PipeName">\msagent_f8</Data>
<Data Name="Image">C:\Windows\System32\rundll32.exe</Data>
</EventData>
</Event>
Regla Sigma para named pipes
title: Cobalt Strike Named Pipe Detected
id: f7a8b9c0-d1e2-4f3a-b4c5-d6e7f8a9b0c1
status: experimental
description: Detects named pipes commonly used by Cobalt Strike beacons
references:
- https://malwareintel.es/blog/casos-de-uso/cobalt-strike-beacon-red-hunting
logsource:
category: pipe_created
product: windows
detection:
selection:
PipeName|re:
- '\\msagent_[0-9a-f]{2}'
- '\\MSSE-[0-9]{4}-server'
- '\\postex_[0-9a-f]{4}'
- '\\postex_ssh_[0-9a-f]{4}'
- '\\status_[0-9a-f]{2}'
condition: selection
level: critical
tags:
- attack.command_and_control
- attack.t1071
- attack.lateral_movement
- attack.t1021.002
Malleable C2 profiles: el reto para el defensor
Los malleable C2 profiles son archivos de configuración que permiten al atacante personalizar completamente el tráfico del beacon. Un profile bien configurado puede imitar cualquier aplicación legítima.
Ejemplo: profile jQuery
# Fragmento de jquery-c2.4.0.profile (público en GitHub)
set sleeptime "60000";
set jitter "37";
set useragent "Mozilla/5.0 (Windows NT 6.3; Trident/7.0; rv:11.0) like Gecko";
http-get {
set uri "/jquery-3.3.1.min.js";
client {
header "Accept" "text/html,application/xhtml+xml";
header "Referer" "http://code.jquery.com/";
metadata {
base64url;
append "/__utm.gif?utmac=UA-2202604-2&utmcn=1&utmcs=ISO-8859-1&utmsr=1280x1024&utmsc=32-bit&utmul=en-US";
header "Cookie";
}
}
server {
header "Content-Type" "application/javascript; charset=utf-8";
header "Server" "NetDNA-cache/2.2";
output {
mask;
base64url;
prepend "!function(e,t){\"use strict\";\"object\"";
append "\".(jQuery)\";})();";
print;
}
}
}
Este profile hace que el tráfico parezca peticiones legítimas a jQuery CDN. La respuesta del C2 se oculta dentro de código JavaScript aparentemente legítimo.
Cómo detectar malleable profiles
-
Análisis estadístico del tráfico: Aunque el contenido parezca legítimo, el patrón temporal (sleep + jitter) sigue siendo detectable. Un CDN real no recibe peticiones a intervalos regulares desde un solo host.
-
Inspección de respuestas: Las respuestas del C2 tienen tamaños variables pero no cachean correctamente. Headers como
Cache-Control: no-cacheen recursos estáticos son sospechosos. -
Correlación de procesos: Si
rundll32.exegenera tráfico HTTP a jQuery CDN, algo va mal. Correlacionar el proceso origen con el destino. -
Anomalías en certificados: Incluso con un certificado válido, el nombre del servidor TLS puede no coincidir con la IP de destino.
-
Volume patterns: Los beacons enviando datos exfiltrados generan picos de tráfico POST que no corresponden con el uso normal del URI.
Hunting con Suricata
Reglas IDS para Cobalt Strike
# Detección de certificado autofirmado de Cobalt Strike
alert tls any any -> any any (msg:"Possible Cobalt Strike Default TLS Certificate";
tls.cert_subject; content:"O=Cobaltstrike"; sid:2026001; rev:1;)
# Detección de URI por defecto (sin malleable profile)
alert http any any -> any any (msg:"Cobalt Strike Default Beacon URI";
http.uri; content:"/dpixel"; sid:2026002; rev:1;)
alert http any any -> any any (msg:"Cobalt Strike Default Beacon URI";
http.uri; content:"/pixel.gif"; sid:2026003; rev:1;)
alert http any any -> any any (msg:"Cobalt Strike Submit URI";
http.uri; content:"/submit.php?id="; sid:2026004; rev:1;)
# Detección de beaconing por User-Agent obsoleto
alert http any any -> any any (msg:"Possible Cobalt Strike Default User-Agent";
http.user_agent; content:"MSIE 9.0"; content:"Trident/5.0";
sid:2026005; rev:1;)
# DNS beaconing: subdominios largos a un solo dominio
alert dns any any -> any any (msg:"Possible DNS C2 Beaconing - Long Subdomain";
dns.query; pcre:"/^[a-z0-9]{20,}\./i"; threshold:type both,
track by_src, count 10, seconds 60; sid:2026006; rev:1;)
Hunting con YARA
Regla para beacon en memoria
rule CobaltStrike_Beacon_Memory {
meta:
description = "Detects Cobalt Strike beacon in process memory"
author = "MalwareIntel Research"
date = "2026-06-06"
strings:
$reflective = "ReflectiveLoader" ascii
$beacon1 = "beacon.dll" ascii
$beacon2 = "beacon.x64.dll" ascii
$cfg1 = { 00 01 00 01 00 02 ?? ?? 00 02 00 01 00 02 ?? ?? }
$cfg2 = { 00 01 00 01 00 02 ?? ?? 00 02 00 02 00 04 ?? ?? ?? ?? }
$sleep_mask = { 4C 8B 53 08 45 8B 0A 45 8B 5A 04 4D 8D 52 08 45 85 C9 }
$pipe1 = "\\.\pipe\msagent_" ascii
$pipe2 = "\\.\pipe\MSSE-" ascii
$pipe3 = "\\.\pipe\postex_" ascii
$pipe4 = "\\.\pipe\status_" ascii
condition:
($reflective and ($beacon1 or $beacon2)) or
any of ($cfg*) or
$sleep_mask or
2 of ($pipe*)
}
Regla para stager PowerShell
rule CobaltStrike_Stager_PowerShell {
meta:
description = "Detects Cobalt Strike PowerShell stager patterns"
author = "MalwareIntel Research"
date = "2026-06-06"
strings:
$iex1 = "IEX" ascii nocase
$iex2 = "Invoke-Expression" ascii nocase
$dl1 = "Net.WebClient" ascii
$dl2 = "DownloadString" ascii
$dl3 = "DownloadData" ascii
$b64 = "FromBase64String" ascii
$xor = "-bxor" ascii
$hidden = "-w hidden" ascii
$bypass = "-ep bypass" ascii
$sc = "[System.Runtime.InteropServices.Marshal]" ascii
condition:
filesize < 50KB and
($iex1 or $iex2) and
($dl1 or $dl2 or $dl3) and
($b64 or $xor) and
($hidden or $bypass or $sc)
}
Hunting en SIEM con Sigma
Detección de beaconing por frecuencia
title: Periodic Outbound HTTP Beaconing Pattern
id: a8b9c0d1-e2f3-4a4b-c5d6-e7f8a9b0c1d2
status: experimental
description: Detects periodic HTTP/HTTPS connections from a single host
to a single destination with consistent intervals (beaconing)
logsource:
category: proxy
detection:
selection:
c-action: 'TCP_CONNECT'
timeframe: 15m
condition: selection | count(cs-uri) by src_ip, dst_ip > 12
level: medium
tags:
- attack.command_and_control
- attack.t1071.001
Detección de proceso sospechoso con conexión de red
title: Suspicious Process Making HTTP Connection
id: b9c0d1e2-f3a4-4b5c-d6e7-f8a9b0c1d2e3
status: experimental
description: Detects processes not typically making HTTP connections
logsource:
category: network_connection
product: windows
detection:
selection:
Image|endswith:
- '\rundll32.exe'
- '\regsvr32.exe'
- '\mshta.exe'
- '\certutil.exe'
DestinationPort:
- 80
- 443
- 8080
- 8443
condition: selection
level: high
tags:
- attack.command_and_control
- attack.t1071
- attack.defense_evasion
- attack.t1218
Detección de Mimikatz ejecutado desde beacon
title: LSASS Memory Access from Unusual Process
id: c0d1e2f3-a4b5-4c6d-e7f8-a9b0c1d2e3f4
status: experimental
description: Detects access to LSASS memory from non-security processes
logsource:
category: process_access
product: windows
detection:
selection:
TargetImage|endswith: '\lsass.exe'
GrantedAccess|contains:
- '0x1010'
- '0x1410'
- '0x1438'
filter:
SourceImage|endswith:
- '\MsMpEng.exe'
- '\csrss.exe'
- '\svchost.exe'
condition: selection and not filter
level: critical
tags:
- attack.credential_access
- attack.t1003.001
Hunting en memoria con Volatility
Workflow de análisis
# 1. Identificar procesos sospechosos
$ vol3 -f memory.raw windows.pslist | grep -E "rundll32|regsvr32|svchost"
# 2. Buscar inyecciones de código
$ vol3 -f memory.raw windows.malfind --pid 4892
# 3. Extraer DLLs cargadas reflectively
$ vol3 -f memory.raw windows.dlllist --pid 4892
# 4. Comparar DLLs en memoria vs disco
# Las DLLs cargadas reflectively NO aparecen en la lista de módulos del PEB
# pero SÍ aparecen con malfind como regiones PAGE_EXECUTE_READWRITE
# 5. Buscar strings del beacon en memoria del proceso
$ vol3 -f memory.raw windows.memmap --pid 4892 --dump
$ strings -a pid.4892.dmp | grep -iE "beacon|pipe|sleep|jitter"
# 6. Buscar named pipes activos
$ vol3 -f memory.raw windows.handles --pid 4892 | grep "\\Device\\NamedPipe"
Indicadores de inyección Cobalt Strike en Volatility
| Indicador | Comando Volatility | Qué buscar |
|---|---|---|
| Proceso huérfano | windows.pstree | rundll32.exe sin padre o con padre inesperado |
| Código inyectado | windows.malfind | Regiones RWX con MZ header en procesos legítimos |
| DLL fantasma | windows.dlllist vs windows.ldrmodules | DLL en memoria pero no en lista del loader |
| Named pipes | windows.handles | Pipes con nombres de patrón Cobalt Strike |
| Conexiones activas | windows.netscan | Conexiones desde procesos no-browser a puertos 443/8080 |
IOCs de referencia
Red
| Tipo | Valor | Contexto |
|---|---|---|
| JA3 | 72a589da586844d7f0818ce684948eea | CS 4.x Java 11 |
| JA3 | a0e9f5d64349fb13191bc781f81f42e1 | CS 4.x Java 8 |
| JA3S | ae4edc6faf64d08308082ad26be60767 | Teamserver default |
| URI | /dpixel, /pixel.gif, /__utm.gif | URIs default (sin profile) |
| URI | /submit.php?id= | URI de exfiltración default |
| User-Agent | MSIE 9.0; Windows NT 6.1; Trident/5.0 | UA default CS |
| Named Pipe | \\.\pipe\msagent_## | Pipe default para SMB beacon |
| Named Pipe | \\.\pipe\MSSE-####-server | Pipe default para post-exploitation |
| Named Pipe | \\.\pipe\postex_#### | Pipe default para post-exploitation |
| Cert Subject | O=Cobaltstrike, OU=AdvancedPenTesting | Certificado default |
Host
| Tipo | Valor | Contexto |
|---|---|---|
| Process | rundll32.exe sin argumentos legítimos | SpawnTo default del beacon |
| Registry | HKCU\Software\Microsoft\Windows\CurrentVersion\Run | Persistencia típica |
| Sysmon | EventID 17: PipeCreated con patrón CS | Creación de named pipe |
| Sysmon | EventID 10: ProcessAccess a lsass.exe | Credential dumping |
| Memory | PAGE_EXECUTE_READWRITE con MZ header | Beacon reflective-loaded |
Mapeo MITRE ATT&CK
| ID | Técnica | Relevancia para hunting |
|---|---|---|
| T1071.001 | Web Protocols | Beacons HTTP/HTTPS a C2 |
| T1071.004 | DNS | DNS beaconing para C2 |
| T1573.002 | Asymmetric Cryptography | TLS para cifrar C2 (JA3 detectable) |
| T1055.001 | DLL Injection | Reflective DLL loading del beacon |
| T1055.012 | Process Hollowing | Inyección en procesos legítimos |
| T1021.002 | SMB/Windows Admin Shares | Named pipes para lateral movement |
| T1059.001 | PowerShell | Stagers y post-exploitation |
| T1218.011 | Rundll32 | SpawnTo default del beacon |
| T1003.001 | LSASS Memory | Credential dumping via beacon |
| T1095 | Non-Application Layer Protocol | Raw TCP beacons |
Checklist de hunting
Para una sesión de threat hunting enfocada en Cobalt Strike, seguir este orden:
1. SIEM: Buscar JA3 fingerprints conocidos en logs TLS/proxy
2. SIEM: Correlacionar procesos no-browser con conexiones HTTP salientes
3. Zeek: Analizar patrones temporales de conexiones (beaconing)
4. Suricata: Revisar alertas de certificados autofirmados sospechosos
5. EDR: Buscar creación de named pipes con patrones CS
6. EDR: Buscar rundll32.exe con conexiones de red
7. Sysmon: EventID 10 con acceso a lsass.exe desde procesos inusuales
8. Memoria: malfind en procesos con regiones RWX sospechosas
9. Memoria: Extraer configuración con CobaltStrikeParser/1768.py
10. Red: Analizar DNS queries con subdominios anormalmente largos
Si cualquier paso produce un hit, pasar inmediatamente a contención y análisis forense completo. Un beacon activo significa que el atacante tiene acceso y puede escalar en cualquier momento.
Los fingerprints, patrones y reglas de este artículo están basados en versiones conocidas de Cobalt Strike y profiles públicos. Los atacantes sofisticados modifican estos valores. La detección efectiva requiere correlación de múltiples indicadores, no dependencia de uno solo.
Preguntas frecuentes
Libros recomendados
Artículos relacionados
Este contenido tiene fines exclusivamente educativos y de investigación en ciberseguridad defensiva. No se proporcionan binarios maliciosos ni payloads ejecutables. El uso indebido de esta información es responsabilidad exclusiva del usuario. Leer disclaimer completo.