Analyzing dns logs for exfiltration
Skill killvxk/cybersecurity-skills-zh/skills/analyzing-dns-logs-for-exfiltration
分析 DNS 查询日志,利用熵值分析、查询量异常检测和子域名长度检测,在 SIEM 平台中检测 DNS 隧道数据外泄、DGA 域名通信和隐蔽 C2 信道。适用于 SOC 团队识别绕过传统网络安全控制的 DNS 威胁。From its SKILL.md
npx -y skills add killvxk/cybersecurity-skills-zh --skill analyzing-dns-logs-for-exfiltrationAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
What its file declares
Copied from the file, not written here
The file declares its own license as Apache-2.0. That is the author’s claim about this one file, and it is not the same thing as the license GitHub reports for the repository, which is listed with the other numbers below.
SKILL.md
10.7 KB, as published. Nobody here has run it
分析 DNS 日志中的数据外泄
适用场景
在以下情况下使用本技能:
- SOC 团队怀疑通过 DNS 隧道进行数据外泄以绕过防火墙/代理控制
- 威胁情报显示对手使用基于 DNS 的 C2 信道(例如 Cobalt Strike DNS Beacon)
- UEBA 检测到特定主机存在异常 DNS 查询量
- 恶意软件分析揭示具有 DNS-over-HTTPS(DoH)或 DNS 隧道能力
不适用于标准 DNS 故障排除或可用性监控——本技能专注于与安全相关的 DNS 滥用检测。
前置条件
- 已启用 DNS 查询日志记录(Windows DNS Server、Bind、Infoblox 或 Cisco Umbrella)
- DNS 日志已摄取到 SIEM(Splunk 的
Stream:DNS、dns数据源或 Zeek DNS 日志) - 用于历史域名解析分析的被动 DNS 数据
- 正常 DNS 行为基线(查询量、域名分布、TXT 记录频率)
- Python(含
math和collections库)用于熵值计算
工作流程
步骤 1:通过子域名长度分析检测 DNS 隧道
DNS 隧道将数据编码在子域名标签中,产生异常长的查询:
index=dns sourcetype="stream:dns" query_type IN ("A", "AAAA", "TXT", "CNAME", "MX")
| eval domain_parts = split(query, ".")
| eval subdomain = mvindex(domain_parts, 0, mvcount(domain_parts)-3)
| eval subdomain_str = mvjoin(subdomain, ".")
| eval subdomain_len = len(subdomain_str)
| eval tld = mvindex(domain_parts, -1)
| eval registered_domain = mvindex(domain_parts, -2).".".tld
| where subdomain_len > 50
| stats count AS queries, dc(query) AS unique_queries,
avg(subdomain_len) AS avg_subdomain_len,
max(subdomain_len) AS max_subdomain_len,
values(src_ip) AS sources
by registered_domain
| where queries > 20
| sort - avg_subdomain_len
| table registered_domain, queries, unique_queries, avg_subdomain_len, max_subdomain_len, sources
步骤 2:检测高熵域名查询(DGA 检测)
域名生成算法(DGA)产生看似随机的域名:
index=dns sourcetype="stream:dns"
| eval domain_parts = split(query, ".")
| eval sld = mvindex(domain_parts, -2)
| eval sld_len = len(sld)
| eval char_count = sld_len
| eval vowels = len(replace(sld, "[^aeiou]", ""))
| eval consonants = len(replace(sld, "[^bcdfghjklmnpqrstvwxyz]", ""))
| eval digits = len(replace(sld, "[^0-9]", ""))
| eval vowel_ratio = if(char_count > 0, vowels / char_count, 0)
| eval digit_ratio = if(char_count > 0, digits / char_count, 0)
| where sld_len > 12 AND (vowel_ratio < 0.2 OR digit_ratio > 0.3)
| stats count AS queries, dc(query) AS unique_domains, values(src_ip) AS sources
by query
| where unique_domains > 10
| sort - queries
基于 Python 的 DNS 查询香农熵(Shannon Entropy)计算:
import math
from collections import Counter
def shannon_entropy(text):
"""计算字符串的香农熵"""
if not text:
return 0
counter = Counter(text.lower())
length = len(text)
entropy = -sum(
(count / length) * math.log2(count / length)
for count in counter.values()
)
return round(entropy, 4)
# 测试示例
normal_domain = "google" # 低熵
dga_domain = "x8kj2m9p4qw7n" # 高熵
tunnel_subdomain = "aGVsbG8gd29ybGQ.evil.com" # Base64 编码的数据
print(f"正常: {shannon_entropy(normal_domain)}") # ~2.25
print(f"DGA: {shannon_entropy(dga_domain)}") # ~3.70
print(f"隧道: {shannon_entropy(tunnel_subdomain)}") # ~3.50
# 阈值:子域名熵值 > 3.5 = 可能是隧道/DGA
Splunk 熵值评分实现:
index=dns sourcetype="stream:dns"
| eval domain_parts = split(query, ".")
| eval check_string = mvindex(domain_parts, 0)
| eval check_len = len(check_string)
| where check_len > 8
| eval chars = split(check_string, "")
| stats count AS total_chars, dc(chars) AS unique_chars by query, src_ip, check_string, check_len
| eval entropy_estimate = log(unique_chars, 2) * (unique_chars / check_len)
| where entropy_estimate > 3.5
| stats count AS high_entropy_queries, dc(query) AS unique_queries by src_ip
| where high_entropy_queries > 50
| sort - high_entropy_queries
步骤 3:检测异常 DNS 查询量
识别产生异常 DNS 流量的主机:
index=dns sourcetype="stream:dns" earliest=-24h
| bin _time span=1h
| stats count AS queries, dc(query) AS unique_domains by src_ip, _time
| eventstats avg(queries) AS avg_queries, stdev(queries) AS stdev_queries by src_ip
| eval z_score = (queries - avg_queries) / stdev_queries
| where z_score > 3 OR queries > 5000
| sort - z_score
| table _time, src_ip, queries, unique_domains, avg_queries, z_score
检测 TXT 记录滥用(常见隧道方法):
index=dns sourcetype="stream:dns" query_type="TXT"
| stats count AS txt_queries, dc(query) AS unique_txt_domains,
values(query) AS domains by src_ip
| where txt_queries > 100
| eval suspicion = case(
txt_queries > 1000, "CRITICAL — 可能是 DNS 隧道",
txt_queries > 500, "HIGH — 可能是 DNS 隧道",
txt_queries > 100, "MEDIUM — 异常 TXT 查询量"
)
| sort - txt_queries
| table src_ip, txt_queries, unique_txt_domains, suspicion
步骤 4:检测已知 DNS 隧道工具
搜索常见 DNS 隧道工具的特征:
index=dns sourcetype="stream:dns"
| eval query_lower = lower(query)
| where (
match(query_lower, "\.dnscat\.") OR
match(query_lower, "\.dns2tcp\.") OR
match(query_lower, "\.iodine\.") OR
match(query_lower, "\.dnscapy\.") OR
match(query_lower, "\.cobalt.*\.beacon") OR
query_type="NULL" OR
(query_type="TXT" AND len(query) > 100)
)
| stats count by src_ip, query, query_type
| sort - count
检测 DNS over HTTPS(DoH)绕过本地 DNS:
index=proxy OR index=firewall
dest IN ("1.1.1.1", "1.0.0.1", "8.8.8.8", "8.8.4.4",
"9.9.9.9", "149.112.112.112", "208.67.222.222")
dest_port=443
| stats sum(bytes_out) AS total_bytes, count AS connections by src_ip, dest
| where connections > 100 OR total_bytes > 10485760
| eval alert = "可能的 DoH 绕过 — 通过 HTTPS 将 DNS 查询发送到公共解析器"
| sort - total_bytes
步骤 5:将 DNS 发现与终端数据关联
将可疑 DNS 与进程数据进行交叉参考:
index=dns src_ip="192.168.1.105" query="*.evil-tunnel.com" earliest=-24h
| stats count AS dns_queries, earliest(_time) AS first_query, latest(_time) AS last_query
by src_ip, query
| join src_ip [
search index=sysmon EventCode=3 DestinationPort=53 Computer="WORKSTATION-042"
| stats count AS connections, values(Image) AS processes by SourceIp
| rename SourceIp AS src_ip
]
| table src_ip, query, dns_queries, first_query, last_query, processes
步骤 6:估算数据外泄量
估算 DNS 查询中编码数据的体积:
index=dns src_ip="192.168.1.105" query="*.evil-tunnel.com" earliest=-24h
| eval domain_parts = split(query, ".")
| eval encoded_data = mvindex(domain_parts, 0)
| eval encoded_bytes = len(encoded_data)
| eval decoded_bytes = encoded_bytes * 0.75 -- Base64 解码因子
| stats sum(decoded_bytes) AS total_bytes_estimated, count AS total_queries,
earliest(_time) AS first_seen, latest(_time) AS last_seen
| eval estimated_kb = round(total_bytes_estimated / 1024, 1)
| eval estimated_mb = round(total_bytes_estimated / 1048576, 2)
| eval duration_hours = round((last_seen - first_seen) / 3600, 1)
| eval rate_kbps = round(estimated_kb / (duration_hours * 3600) * 8, 2)
| table total_queries, estimated_mb, duration_hours, rate_kbps, first_seen, last_seen
核心概念
| 术语 | 定义 |
|---|---|
| DNS 隧道 | 将数据编码在 DNS 查询/响应中,通过 DNS 外泄数据或建立 C2 信道的技术 |
| DGA | 域名生成算法——恶意软件技术,生成伪随机域名以提高 C2 韧性 |
| 香农熵 | 字符串随机性的数学度量——域名中高熵(>3.5)表明存在 DGA 或隧道 |
| TXT 记录滥用 | 利用 DNS TXT 记录(设计用于文本数据)作为数据隧道的高带宽信道 |
| DNS over HTTPS(DoH) | 通过 HTTPS(端口 443)加密的 DNS 查询,绕过传统 DNS 监控 |
| 被动 DNS | 历史 DNS 解析记录,显示某域名随时间解析到的 IP 地址 |
工具与系统
- Splunk Stream:提供解析 DNS 查询数据的网络流量捕获插件,用于 SIEM 分析
- Zeek(Bro):生成详细 DNS 事务日志的网络安全监视器
- Cisco Umbrella(OpenDNS):云 DNS 安全平台,阻断恶意域名并记录查询数据
- Infoblox DNS Firewall:提供基于 RPZ 阻断和详细查询日志的 DNS 层安全
- Farsight DNSDB:被动 DNS 数据库,用于历史域名解析查询和基础设施映射
常见场景
- Cobalt Strike DNS Beacon:检测向 C2 域定期发送含编码载荷的 TXT 查询
- 数据外泄:大量唯一子域名查询,以 Base64/十六进制编码窃取的数据
- DGA 恶意软件:检测对算法生成域名的 DNS 查询(高熵、无 Web 内容)
- DNS-over-HTTPS 绕过:员工使用 DoH 绕过企业 DNS 过滤和监控
- 慢速滴漏式外泄:低量 DNS 隧道保持在阈值告警以下(需要与基线比较)
输出格式
DNS 外泄分析 — WORKSTATION-042
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
时间段: 2024-03-14 至 2024-03-15
来源: 192.168.1.105(WORKSTATION-042,财务部门)
发现:
[CRITICAL] 检测到 DNS 隧道至 evil-tunnel[.]com
查询量: 18 小时内 12,847 次查询
平均子域名长: 63 字符(正常值:<20)
平均熵值: 3.82(阈值:3.5)
查询类型: TXT(89%)、A(11%)
估算数据量: 约 4.7 MB 通过 DNS 外泄
速率: 0.58 kbps(慢速滴漏模式)
[HIGH] 已解析 DGA 类域名
唯一 DGA 域名: 247 个域名已解析
模式: 15 字符随机字母数字.xyz TLD
熵值范围: 3.6 - 4.1
进程归因:
进程: svchost_update.exe(伪装——非合法 svchost)
PID: 4892
父进程: explorer.exe
哈希: SHA256: a1b2c3d4...(VT: 34/72 恶意 — Cobalt Strike Beacon)
遏制措施:
[已完成] 主机已通过 EDR 隔离
[已完成] 域名 evil-tunnel[.]com 已添加到 DNS 沉洞
[已完成] 事件 IR-2024-0448 已创建