分析命令与控制通信 是做什么的?
何时使用
- 逆向工程恶意软件样本时发现网络通信,需要进行协议分析
- 为特定 C2 框架(Cobalt Strike、Metasploit、Sliver)构建网络层检测签名
- 映射 C2 基础设施,包括主服务器、故障转移域名和死信点
- 分析加密或编码的 C2 流量以理解命令集和数据格式
- 基于 C2 基础设施模式及工具归属恶意软件到特定威胁行为者
请勿用于 通用网络异常检测;此技能专门用于通过恶意软件分析来理解已知或可疑的 C2 协议。
先决条件
- 恶意软件网络流量的 PCAP 捕获(来自沙箱、网络分流器或全包捕获)
- Wireshark/tshark 用于数据包级分析
- 逆向工程工具(Ghidra、dnSpy)用于理解恶意软件二进制中的 C2 代码
- Python 3.8+,并安装
scapy、dpkt和requests用于协议分析和重放 - 威胁情报数据库用于 C2 基础设施关联(VirusTotal、Shodan、Censys)
- JA3/JA3S 指纹数据库用于基于 TLS 的 C2 识别
工作流程
步骤 1:识别 C2 通道
确定用于 C2 通信的协议和传输层:
C2 通信通道:
━━━━━━━━━━━━━━━━━━━━━━━━━
HTTP/HTTPS: 最常见;使用标准 Web 流量隐藏自身
指示特征:定期 POST/GET 请求、特定 URI 模式、自定义头部
DNS: 通过 DNS 查询和响应隧道传输数据
指示特征:大量 TXT 查询、长子域名、高熵
自定义 TCP/UDP:非标准端口上的专有二进制协议
指示特征:高端口上的非 HTTP 流量、未知协议
ICMP: 数据编码在 ICMP 回显请求/响应负载中
指示特征:带有大或非标准负载的 ICMP 数据包
WebSocket: 用于实时 C2 的持久化双向连接
指示特征:WebSocket 升级后跟二进制帧
云服务: 使用合法 API(Telegram、Discord、Slack、GitHub)
指示特征:来自非预期进程的云服务 API 调用
电子邮件: SMTP/IMAP 用于 C2 命令和数据外泄
指示特征:来自非邮件进程的自动邮件操作
步骤 2:分析信标模式
表征定期通信模式:
from scapy.all import rdpcap, IP, TCP
from collections import defaultdict
import statistics
import json
packets = rdpcap("c2_traffic.pcap")
# 按目标分组 TCP SYN 数据包
connections = defaultdict(list)
for pkt in packets:
if IP in pkt and TCP in pkt and (pkt[TCP].flags & 0x02):
key = f"{pkt[IP].dst}:{pkt[TCP].dport}"
connections[key].append(float(pkt.time))
# 分析每个目标是否存在信标行为
for dst, times in sorted(connections.items()):
if len(times) < 3:
continue
intervals = [times[i+1] - times[i] for i in range(len(times)-1)]
avg_interval = statistics.mean(intervals)
stdev = statistics.stdev(intervals) if len(intervals) > 1 else 0
jitter_pct = (stdev / avg_interval * 100) if avg_interval > 0 else 0
duration = times[-1] - times[0]
beacon_data = {
"destination": dst,
"connections": len(times),
"duration_seconds": round(duration, 1),
"avg_interval_seconds": round(avg_interval, 1),
"stdev_seconds": round(stdev, 1),
"jitter_percent": round(jitter_pct, 1),
"is_beacon": 5 < avg_interval < 7200 and jitter_pct < 25,
}
if beacon_data["is_beacon"]:
print(f"[!] 检测到信标: {dst}")
print(f" 间隔: {avg_interval:.0f}s +/- {stdev:.0f}s ({jitter_pct:.0f}% 抖动)")
print(f" 会话数: {len(times)} 持续 {duration:.0f}s")
步骤 3:解码 C2 协议结构
从捕获流量反向工程消息格式:
# 基于 HTTP 的 C2 协议分析
import dpkt
import base64
with open("c2_traffic.pcap", "rb") as f:
pcap = dpkt.pcap.Reader(f)
for ts, buf in pcap:
eth = dpkt.ethernet.Ethernet(buf)
if not isinstance(eth.data, dpkt.ip.IP):
continue
ip = eth.data
if not isinstance(ip.data, dpkt.tcp.TCP):
continue
tcp = ip.data
if tcp.dport == 80 or tcp.dport == 443:
if len(tcp.data) > 0:
try:
http = dpkt.http.Request(tcp.data)
print(f"\n--- C2 请求 ---")
print(f"方法: {http.method}")
print(f"URI: {http.uri}")
print(f"头部: {dict(http.headers)}")
if http.body:
print(f"主体 ({len(http.body)} 字节):")
# 尝试 Base64 解码
try:
decoded = base64.b64decode(http.body)
print(f" 解码后: {decoded[:200]}")
except:
print(f" 原始: {http.body[:200]}")
except:
pass
步骤 4:识别 C2 框架
将观察到的模式与已知 C2 框架匹配:
已知 C2 框架签名:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Cobalt Strike:
- 默认 URI:/pixel, /submit.php, /___utm.gif, /ca, /dpixel
- Malleable C2 配置文件可自定义所有流量特征
- JA3:因配置而异,目录位于 ja3er.com
- 信标配置中的 Watermark(每个许可证唯一)
- 配置提取:使用 CobaltStrikeParser 或 1768.py
Metasploit/Meterpreter:
- 默认阶段化 URI 模式:随机 4 字符校验和
- 反向 HTTP(S) 处理程序模式
- Meterpreter TLV(类型-长度-值)协议结构
Sliver:
- mTLS、HTTP、DNS、WireGuard 传输选项
- Protobuf 编码消息
- 通信中的唯一植入标识符
Covenant:
- 基于 .NET 的 C2 框架
- HTTP 支持可自定义的配置文件
- 基于任务的命令执行
PoshC2:
- 基于 PowerShell/C# 开发
- HTTP 通信,负载加密
- 基于 Cookie 的会话管理
# 从 PCAP 或样本中提取 Cobalt Strike 信标配置
python3 << 'PYEOF'
# 使用 CobaltStrikeParser (pip install cobalt-strike-parser)
from cobalt_strike_parser import BeaconConfig
try:
config = BeaconConfig.from_file("suspect.exe")
print("Cobalt Strike 信标配置:")
for key, value in config.items():
print(f" {key}: {value}")
except Exception as e:
print(f"不是 Cobalt Strike 信标或解析错误:{e}")
PYEOF
步骤 5:映射 C2 基础设施
记录完整的 C2 基础设施及故障转移机制:
# 基础设施映射
import requests
import json
c2_indicators = {
"primary_c2": "185.220.101.42",
"domains": ["update.malicious.com", "backup.evil.net"],
"ports": [443, 8443],
"failover_dns": ["ns1.malicious-dns.com"],
}
# 使用 Shodan 增强
def shodan_lookup(ip, api_key):
resp = requests.get(f"https://api.shodan.io/shodan/host/{ip}?key={api_key}")
if resp.status_code == 200:
data = resp.json()
return {
"ip": ip,
"ports": data.get("ports", []),
"os": data.get("os"),
"org": data.get("org"),
"asn": data.get("asn"),
"country": data.get("country_code"),
"hostnames": data.get("hostnames", []),
"last_update": data.get("last_update"),
}
return None
# 使用被动 DNS 增强
def pdns_lookup(domain):
# 使用 VirusTotal 被动 DNS
resp = requests.get(
f"https://www.virustotal.com/api/v3/domains/{domain}/resolutions",
headers={"x-apikey": VT_API_KEY}
)
if resp.status_code == 200:
data = resp.json()
resolutions = []
for r in data.get("data", []):
resolutions.append({
"ip": r["attributes"]["ip_address"],
"date": r["attributes"]["date"],
})
return resolutions
return []
步骤 6:创建网络检测签名
基于分析的 C2 特征构建检测规则:
# 针对已分析 C2 的 Suricata 规则
cat << 'EOF' > c2_detection.rules
# HTTP 信标模式
alert http $HOME_NET any -> $EXTERNAL_NET any (
msg:"恶意软件 MalwareX C2 HTTP 信标";
flow:established,to_server;
http.method; content:"POST";
http.uri; content:"/gate.php"; startswith;
http.header; content:"User-Agent: Mozilla/5.0 (compatible; MSIE 10.0)";
threshold:type threshold, track by_src, count 5, seconds 600;
sid:9000010; rev:1;
)
# JA3 指纹匹配
alert tls $HOME_NET any -> $EXTERNAL_NET any (
msg:"恶意软件 MalwareX TLS JA3 指纹";
ja3.hash; content:"a0e9f5d64349fb13191bc781f81f42e1";
sid:9000011; rev:1;
)
# DNS 信标检测(高熵子域名)
alert dns $HOME_NET any -> any any (
msg:"恶意软件 疑似 DNS C2 隧道";
dns.query; pcre:"/^[a-z0-9]{20,}\./";
threshold:type threshold, track by_src, count 10, seconds 60;
sid:9000012; rev:1;
)
# 基于证书的检测
alert tls $HOME_NET any -> $EXTERNAL_NET any (
msg:"恶意软件 MalwareX 自签名 C2 证书";
tls.cert_subject; content:"CN=update.malicious.com";
sid:9000013; rev:1;
)
EOF
关键概念
| 术语 | 定义 |
|---|---|
| 信标 | 恶意软件定期向 C2 服务器发送的签到通信,常加入抖动以避免被模式检测到 |
| 抖动 | 应用于信标间隔的随机化(例如 60s +/- 15%),使时间模式更难预测、更难以检测 |
| Malleable C2 | Cobalt Strike 功能,允许操作者自定义 C2 流量(URI、头部、编码)的所有方面,以模仿合法服务 |
| 死信点 | 中间位置(粘贴站点、云存储、社交媒体),C2 命令在此发布供恶意软件检索 |
| 域名前置 | 在 TLS SNI 中使用受信任的 CDN 域名,同时将流量路由到不同的后端,使 C2 流量看起来像流向合法服务 |
| 快速通量 | 快速更改 C2 域名的 DNS 记录,以分布到多个 IP 地址,抵抗下架操作 |
| C2 框架 | 提供 C2 服务器、植入生成器和操作界面的软件工具包(Cobalt Strike、Metasploit、Sliver、Covenant) |
工具 & 系统
- Wireshark:数据包分析器,用于在数据包级别进行详细的 C2 协议分析
- RITA (Real Intelligence Threat Analytics):开源工具,分析 Zeek 日志以进行信标检测和 DNS 隧道检测
- CobaltStrikeParser:用于从样本和内存转储中提取 Cobalt Strike 信标配置的工具
- JA3/JA3S:TLS 指纹识别方法,通过 TLS 实现特征识别 C2 框架
- Shodan/Censys:互联网扫描平台,用于映射 C2 基础设施并识别相关服务器
常见场景
场景:逆向工程自定义 C2 协议
背景:一个恶意软件样本通过未知的 TCP 端口 8443 上的二进制协议与其 C2 服务器通信。需要解码该协议以理解命令集并构建检测签名。
方法:
- 过滤 PCAP,提取 TCP 端口 8443 的会话,并导出 TCP 流
- 分析最初的几次交换以识别握手/认证机制
- 映射消息结构(长度前缀、类型字段、负载编码)
- 交叉参考恶意软件中发送/接收函数的 Ghidra 反汇编结果
- 识别命令分发器,并记录每个命令代码的功能
- 用 Python 构建协议解码器,用于持续流量分析
- 创建匹配协议握手或静态头部字节的 Suricata 规则
陷阱:
- 假设协议是静态的;某些 C2 框架会在握手期间协商加密
- 未捕获足够流量以看到所有命令类型(某些命令很少出现)
- 遗漏当主通道失败时会激活的故障转移 C2 通道(DNS、ICMP)
- 混淆加密负载数据与协议框架结构
输出格式
C2 通信分析报告
===================================
样本: malware.exe (SHA-256: e3b0c44...)
C2 框架: Cobalt Strike 4.9
信标配置
C2 服务器: hxxps://185.220.101[.]42/updates
信标类型: HTTPS (反向)
休眠时间: 60 秒
抖动: 15%
用户代理: Mozilla/5.0 (Windows NT 10.0; Win64; x64)
URI (GET): /dpixel
URI (POST): /submit.php
水印: 1234567890
协议分析
传输层: HTTPS (TLS 1.2)
JA3 哈希: a0e9f5d64349fb13191bc781f81f42e1
证书: CN=Microsoft Update (自签名)
编码: Base64 使用 XOR 密钥 0x69
命令格式: [4B 长度][4B 命令 ID][负载]
命令集
0x01 - Sleep 更改信标间隔
0x02 - Shell 执行 cmd.exe 命令
0x03 - Download 从 C2 传输文件
0x04 - Upload 将文件外泄到 C2
0x05 - Inject 进程注入
0x06 - Keylog 启动键盘记录器
0x07 - Screenshot 捕获屏幕
基础设施
主服务器: 185.220.101[.]42 (AS12345, Hosting Co, 荷兰)
故障转移: 91.215.85[.]17 (AS67890, VPS Provider, 俄罗斯)
DNS: update.malicious[.]com -> 185.220.101[.]42
注册商: NameCheap
注册日期: 2025-09-01
检测签名
SID 9000010: HTTP 信标模式
SID 9000011: JA3 TLS 指纹
SID 9000013: C2 证书匹配