关于 analyzing-cobalt-strike-beacon-configuration
用于从 PE 文件、shellcode 或内存转储中提取并解读 Cobalt Strike Beacon 配置的安全分析技能。帮助事件响应人员识别 C2 设置、流量配置和水印,以支持检测与基础设施关联分析。
主要功能
- 01提取 Beacon 配置
- 02解析 C2 地址与协议
- 03检查 Malleable C2 配置
- 04获取水印与通信间隔参数
- 05关联配置与网络流量
适用场景
- 01调查 Cobalt Strike 安全事件
- 02关联攻击者基础设施
- 03编写 YARA 和网络检测规则
- 04比对 Beacon 配置与实测通信间隔
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ mukul975/ anthropic-cybersecurity-skills --skill analyzing-cobalt-strike-beacon-configuration安装后在 Claude Code 中输入/analyzing-cobalt-strike-beacon-configuration确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
LICENSEassets/template.mdreferences/api-reference.mdstandards.mdworkflows.mdscripts/agent.pyprocess.py详细介绍
分析 Cobalt Strike Beacon 配置
概述
Cobalt Strike 是一款商业化对手模拟工具,常被威胁行为者滥用于后渗透行动。Beacon 载荷中嵌入的配置数据会揭示 C2 服务地址、通信协议、休眠间隔、抖动值、可塑性 C2 配置文件设置、水印标识符和加密密钥。从 PE 文件、Shellcode 或内存转储中提取这些配置,对于事件响应人员绘制攻击者基础设施并归因攻击活动至关重要。Beacon 配置使用单字节(版本 3 为 0x69,版本 4 为 0x2e)进行 XOR 编码,并以类型-长度-值(TLV)格式存储在 .data 节中。
适用场景
- 调查需要分析 Cobalt Strike Beacon 配置的安全事件时
- 为此领域构建检测规则或威胁狩猎查询时
- SOC 分析师需要此类分析的结构化流程时
- 验证针对相关攻击技术的安全监控覆盖情况时
前提条件
- Python 3.9+,以及
dissect.cobaltstrike、pefile、yara-python - SentinelOne CobaltStrikeParser(
parse_beacon_config.py) - 用于手动检查的十六进制编辑器(010 Editor、HxD)
- 了解 PE 文件格式和 XOR 编码
- 内存转储采集工具(Volatility3、WinDbg)
- 用于关联 C2 流量的网络分析工具(Wireshark)
关键概念
Beacon 配置结构
Cobalt Strike Beacon 会将配置存储为 TLV(类型-长度-值)条目组成的数据块,位于 PE 的 .data 节中。无阶段 Beacon 会使用 4 字节密钥对整个 Beacon 代码进行 XOR 运算。配置数据块本身使用单字节 XOR 密钥。每个 TLV 条目包含一个 2 字节类型标识符(例如,BeaconType 为 0x0001,C2Server 为 0x0008)、一个 2 字节长度字段和长度可变的数据。
可塑性 C2 配置文件
Beacon 配置中编码了可塑性 C2 配置文件,用于规定 HTTP 请求/响应转换方式,包括 URI 路径、标头、元数据编码(Base64、NetBIOS)和数据转换。分析这些设置可以揭示 Beacon 如何伪装流量,使其融入合法的 Web 流量。
水印与许可证识别
每个 Cobalt Strike 许可证都会在生成的 Beacon 中嵌入一个唯一水印(4 字节整数)。提取水印可以将多个 Beacon 关联到同一操作人员或破解的许可证。威胁情报提供商维护的已知水印数据库,可将水印映射到特定威胁行为者或泄露的许可证密钥。
工作流
步骤 1:使用 CobaltStrikeParser 提取配置
#!/usr/bin/env python3
"""Extract Cobalt Strike beacon config from PE or memory dump."""
import sys
import json
# Using SentinelOne's CobaltStrikeParser
# pip install dissect.cobaltstrike
from dissect.cobaltstrike.beacon import BeaconConfig
def extract_beacon_config(filepath):
"""Parse beacon configuration from file."""
configs = list(BeaconConfig.from_path(filepath))
if not configs:
print(f"[-] No beacon configuration found in {filepath}")
return None
for i, config in enumerate(configs):
print(f"\n[+] Beacon Configuration #{i+1}")
print(f"{'='*60}")
settings = config.as_dict()
# Critical fields for incident response
critical_fields = [
"SETTING_C2_REQUEST",
"SETTING_C2_RECOVER",
"SETTING_PUBKEY",
"SETTING_DOMAINS",
"SETTING_BEACONTYPE",
"SETTING_PORT",
"SETTING_SLEEPTIME",
"SETTING_JITTER",
"SETTING_MAXGET",
"SETTING_SPAWNTO_X86",
"SETTING_SPAWNTO_X64",
"SETTING_PIPENAME",
"SETTING_WATERMARK",
"SETTING_C2_VERB_GET",
"SETTING_C2_VERB_POST",
"SETTING_USERAGENT",
"SETTING_PROTOCOL",
]
for field in critical_fields:
value = settings.get(field, "N/A")
print(f" {field}: {value}")
return settings
return None
def extract_c2_indicators(config):
"""Extract actionable C2 indicators from beacon config."""
indicators = {
"c2_domains": [],
"c2_ips": [],
"c2_urls": [],
"user_agent": "",
"named_pipes": [],
"spawn_processes": [],
"watermark": "",
}
if not config:
return indicators
# Extract C2 domains
domains = config.get("SETTING_DOMAINS", "")
if domains:
for domain in str(domains).split(","):
domain = domain.strip().rstrip("/")
if domain:
indicators["c2_domains"].append(domain)
# Extract user agent
indicators["user_agent"] = str(config.get("SETTING_USERAGENT", ""))
# Extract named pipes
pipe = config.get("SETTING_PIPENAME", "")
if pipe:
indicators["named_pipes"].append(str(pipe))
# Extract spawn-to processes
for arch in ["SETTING_SPAWNTO_X86", "SETTING_SPAWNTO_X64"]:
proc = config.get(arch, "")
if proc:
indicators["spawn_processes"].append(str(proc))
# Extract watermark
indicators["watermark"] = str(config.get("SETTING_WATERMARK", ""))
return indicators
if __name__ == "__main__":
if len(sys.argv) < 2:
print(f"Usage: {sys.argv[0]} <beacon_file_or_dump>")
sys.exit(1)
config = extract_beacon_config(sys.argv[1])
if config:
indicators = extract_c2_indicators(config)
print(f"\n[+] Extracted C2 Indicators:")
print(json.dumps(indicators, indent=2))
步骤 2:手动 XOR 解密 Beacon 配置
import struct
def find_and_decrypt_config(data):
"""Manually locate and decrypt beacon configuration."""
# Cobalt Strike 4.x uses 0x2e as XOR key
xor_keys = [0x2e, 0x69] # v4, v3
for xor_key in xor_keys:
# Search for the config magic bytes after XOR
# Config starts with 0x0001 (BeaconType) XOR'd with key
magic = bytes([0x00 ^ xor_key, 0x01 ^ xor_key,
0x00 ^ xor_key, 0x02 ^ xor_key])
offset = data.find(magic)
if offset == -1:
continue
print(f"[+] Found config at offset 0x{offset:x} (XOR key: 0x{xor_key:02x})")
# Decrypt the config blob (typically 4096 bytes)
config_size = 4096
encrypted = data[offset:offset + config_size]
decrypted = bytes([b ^ xor_key for b in encrypted])
# Parse TLV entries
entries = parse_tlv(decrypted)
return entries
return None
def parse_tlv(data):
"""Parse Type-Length-Value configuration entries."""
entries = {}
offset = 0
# TLV field type mapping
field_names = {
0x0001: "BeaconType",
0x0002: "Port",
0x0003: "SleepTime",
0x0004: "MaxGetSize",
0x0005: "Jitter",
0x0006: "MaxDNS",
0x0007: "Deprecated_PublicKey",
0x0008: "C2Server",
0x0009: "UserAgent",
0x000a: "PostURI",
0x000b: "Malleable_C2_Instructions",
0x000c: "Deprecated_HttpGet_Metadata",
0x000d: "SpawnTo_x86",
0x000e: "SpawnTo_x64",
0x000f: "CryptoScheme",
0x001a: "Watermark",
0x001d: "C2_HostHeader",
0x0024: "PipeName",
0x0025: "Year",
0x0026: "Month",
0x0027: "Day",
0x0036: "ProxyHostname",
}
while offset + 6 <= len(data):
entry_type = struct.unpack(">H", data[offset:offset+2])[0]
entry_len_type = struct.unpack(">H", data[offset+2:offset+4])[0]
entry_len = struct.unpack(">H", data[offset+4:offset+6])[0]
if entry_type == 0:
break
value_start = offset + 6
value_end = value_start + entry_len
value_data = data[value_start:value_end]
field_name = field_names.get(entry_type, f"Unknown_0x{entry_type:04x}")
if entry_len_type == 1: # Short
value = struct.unpack(">H", value_data[:2])[0]
elif entry_len_type == 2: # Int
value = struct.unpack(">I", value_data[:4])[0]
elif entry_len_type == 3: # String/Blob
value = value_data.rstrip(b'\x00').decode('utf-8', errors='replace')
else:
value = value_data.hex()
entries[field_name] = value
print(f" {field_name}: {value}")
offset = value_end
return entries
步骤 3:用于检测 Beacon 的 YARA 规则
import yara
cobalt_strike_rule = """
rule CobaltStrike_Beacon_Config {
meta:
description = "Detects Cobalt Strike beacon configuration"
author = "Malware Analysis Team"
date = "2025-01-01"
strings:
// XOR'd config marker for CS 4.x (key 0x2e)
$config_v4 = { 2e 2f 2e 2c }
// XOR'd config marker for CS 3.x (key 0x69)
$config_v3 = { 69 68 69 6b }
// Common beacon strings
$str_pipe = "\\\\.\\pipe\\" ascii wide
$str_beacon = "beacon" ascii nocase
$str_sleeptime = "sleeptime" ascii nocase
// Reflective loader pattern
$reflective = { 4D 5A 41 52 55 48 89 E5 }
condition:
($config_v4 or $config_v3) or
(2 of ($str_*) and $reflective)
}
"""
def scan_for_beacons(filepath):
"""Scan file with YARA rules for Cobalt Strike beacons."""
rules = yara.compile(source=cobalt_strike_rule)
matches = rules.match(filepath)
for match in matches:
print(f"[+] YARA Match: {match.rule}")
for string_match in match.strings:
offset = string_match.instances[0].offset
print(f" String: {string_match.identifier} at offset 0x{offset:x}")
return matches
步骤 4:关联网络流量
from dissect.cobaltstrike.c2 import HttpC2Config
def analyze_c2_profile(beacon_config):
"""Analyze malleable C2 profile from beacon configuration."""
print("\n[+] Malleable C2 Profile Analysis")
print("=" * 60)
# HTTP GET configuration
get_verb = beacon_config.get("SETTING_C2_VERB_GET", "GET")
get_uri = beacon_config.get("SETTING_C2_REQUEST", "")
print(f"\n HTTP GET Request:")
print(f" Verb: {get_verb}")
print(f" URI: {get_uri}")
# HTTP POST configuration
post_verb = beacon_config.get("SETTING_C2_VERB_POST", "POST")
post_uri = beacon_config.get("SETTING_C2_POSTREQ", "")
print(f"\n HTTP POST Request:")
print(f" Verb: {post_verb}")
print(f" URI: {post_uri}")
# User Agent
ua = beacon_config.get("SETTING_USERAGENT", "")
print(f"\n User-Agent: {ua}")
# Host header
host = beacon_config.get("SETTING_C2_HOSTHEADER", "")
print(f" Host Header: {host}")
# Sleep and jitter for traffic pattern
sleep_ms = beacon_config.get("SETTING_SLEEPTIME", 60000)
jitter = beacon_config.get("SETTING_JITTER", 0)
print(f"\n Sleep Time: {sleep_ms}ms")
print(f" Jitter: {jitter}%")
# Generate Suricata/Snort signatures
print(f"\n[+] Suggested Network Signatures:")
if ua:
print(f' alert http any any -> any any (msg:"CS Beacon UA"; '
f'content:"{ua}"; http_user_agent; sid:1000001; rev:1;)')
if get_uri:
print(f' alert http any any -> any any (msg:"CS Beacon URI"; '
f'content:"{get_uri}"; http_uri; sid:1000002; rev:1;)')
验证标准
- 成功从 PE 文件或内存转储中提取 Beacon 配置
- 正确识别 C2 服务域名/IP、端口和协议
- 解码可塑性 C2 配置文件参数,显示 HTTP 转换方式
- 提取水印值以便进行归因关联
- 休眠时间和抖动值与观察到的网络 Beacon 间隔相符
- YARA 规则能够检测加壳和未加壳样本中的 Beacon
- 根据提取的 C2 配置文件生成网络特征