skills/analyzing-dns-logs-for-exfiltration/SKILL.md
分析 DNS 查询日志,利用熵值分析、查询量异常检测和子域名长度检测,在 SIEM 平台中检测 DNS 隧道数据外泄、DGA 域名通信和隐蔽 C2 信道。适用于 SOC 团队识别绕过传统网络安全控制的 DNS 威胁。
npx skillsauth add killvxk/cybersecurity-skills-zh analyzing-dns-logs-for-exfiltrationInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
在以下情况下使用本技能:
不适用于标准 DNS 故障排除或可用性监控——本技能专注于与安全相关的 DNS 滥用检测。
Stream:DNS、dns 数据源或 Zeek DNS 日志)math 和 collections 库)用于熵值计算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
域名生成算法(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
识别产生异常 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
搜索常见 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
将可疑 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
估算 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 地址 |
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 已创建
testing
设计并执行社会工程学渗透测试,包括钓鱼、语音钓鱼、短信钓鱼和物理借口活动,以衡量人员安全韧性并识别培训差距。
testing
主持结构化的事件后审查,以识别根本原因、记录有效和无效的措施,并提出可操作的改进建议以提升未来的事件响应能力。
testing
通过分析举报的邮件、提取指标、评估凭据受攻陷情况、在全组织范围隔离恶意邮件并修复受影响账号来响应网络钓鱼事件。涵盖邮件头分析、URL/附件沙箱检测和邮箱范围清除操作。适用于网络钓鱼响应、邮件事件、凭据钓鱼、鱼叉式网络钓鱼调查或钓鱼修复相关请求。
tools
票据传递(Pass-the-Ticket,PtT)是一种横向移动技术,使用窃取的 Kerberos 票据(TGT 或 TGS)在不知道用户密码的情况下向服务进行认证。通过从已控制的主机内存中提取 Kerberos 票据,攻击者可以将这些票据注入自己的会话以模拟票据所有者。