1chunghu/dns-tunnel-detect
GitHub: 1chunghu/dns-tunnel-detect
一个无依赖的单文件 Python 脚本,通过熵、唯一比率、子域长度和流量等信号从 DNS 日志中标记可能的 DNS 隧道与数据外泄行为。
Stars: 0 | Forks: 0
# dns-tunnel-detect
从 DNS 查询日志中标记可能的 **DNS 隧道 / DNS 数据外泄** —— 这是一个用于解决蓝队问题的单文件、无依赖 Python 脚本。
DNS 隧道将数据隐藏在查询名称本身中。攻击者控制某个域名的权威服务器,受控主机将数据编码到子域中并将其查询出去:
```
x000.INHU4RSJIRCU4VCJIFGCA4DSN5SCARCCEBRXEZLE.t.attacker.example
x001.OMFGQ33TOQ6TCMBOGEYC4MJQFY2TACTVONSXEPLB.t.attacker.example
...
```
对于仅允许 `udp/53` 的防火墙来说,这是“正常的 DNS”。但在记录了每个查询的 **解析器** 上,这非常惹眼——惹眼就意味着可被检测。该工具正是用来衡量这种惹眼程度的。
背景 / 其诞生的实验室:
[你允许的 DNS 就是一个数据外泄通道](https://1chung.net/en/blog/dns-tunnel-egress/)
([繁中](https://1chung.net/blog/dns-tunnel-egress/))。
## 它的检测维度
它会按父域名聚合查询,并根据区分隧道与正常流量的信号对每个域名进行评分:
| 信号 | 正常流量 | DNS 隧道 |
|---|---|---|
| **标签熵** (比特/字符) | ~1–3,可读的名称 | ~4.3–5.0,编码后的乱码 |
| **唯一比率** | 低 —— 相同的少数几个名称不断重复 | ~1.0 —— 几乎每个子域都是唯一的 |
| **子域长度** | 短 | 长,标签几乎塞满 63 字符的限制 |
| **流量** | 分散在多个域名中 | 集中在单一父域名上的突发流量 |
当父域名超过流量阈值,*并且* 同时满足熵、唯一比率和子域长度的所有阈值时,它就会被**标记**。每个父域也会获得一个 0–100 的评分用于排名。
## 用法
```
# resolver 日志(自动检测 dnsmasq 风格的行)
./dns-tunnel-detect.py /var/log/dnsmasq.log
# 纯 query 名称,每行一个(例如文章中的小型 authoritative server)
grep -oE '\S+\.t\.attacker\.example' capture.log | ./dns-tunnel-detect.py
# 通过 tshark 从 pcap 中提取,无需代码
tshark -r dns.pcap -Y 'dns.flags.response==0' -T fields -e dns.qry.name \
| ./dns-tunnel-detect.py
# 机器可读,用于 pipe 到 alert
./dns-tunnel-detect.py --json /var/log/dnsmasq.log
# 证明其适用于合成 normal + tunnel 流量
./dns-tunnel-detect.py --selftest --all
```
当有项目被标记时退出状态为 `1`,否则为 `0` —— 因此可以直接放入 cron 或 CI 检查中运行:
```
./dns-tunnel-detect.py /var/log/dnsmasq.log || notify "possible DNS tunnel"
```
### 输入格式
- `--format auto`(默认):优先尝试解析 dnsmasq 的 `query[T] name from client` 行,否则提取该行中最后一个看起来像主机名的 token(如果有两个或以上的 token,则将第一个 token 作为客户端)。也支持纯 qname 列表。
- `--format dnsmasq` / `--format qname`:强制使用指定的解析器。
### 调优
```
--labels N labels that form the parent domain (default 2)
--min-queries N ignore parents below this volume (default 20)
--entropy X bits/char threshold (default 3.8)
--uniq-ratio X unique-subdomain ratio threshold (default 0.8)
--sub-len X mean subdomain length threshold (default 20)
```
## 环境要求
Python 3.9+。仅使用标准库 —— 无需 `pip install`,直接将其放在解析器上运行即可。
## 诚实的局限性
这能捕获**高频大流量**的数据外泄场景,这也是最常见的情况。它不是万能药,配套文章明确解释了为什么检测(而不是防火墙规则)才是此处的真正解法:
- **低速且隐蔽能绕过阈值。** 每小时只缓慢发送几个低熵、短查询的攻击者可以躲避所有阈值。流量和熵是必要信号,但非充分信号 —— 请将其与随时间推移建立的每个客户端的速率基线结合使用。
- **父域名是基于 2 个标签的近似值。** 在没有 Public Suffix List 的情况下,`--labels 2` 会将 `foo.co.uk` 的父域视为 `co.uk`。由于隧道几乎总是使用裸域名(如 `attacker.example`),因此这对检测没有影响;如果你关心精确的 eTLD+1,可以针对使用多标签后缀的域名调高 `--labels`。
- **目前没有响应码信号。** 每个客户端较高的 `NXDOMAIN` 比率是另一个强烈的隧道特征;此版本仅根据查询名称进行评分。
- **DoH/DoT 对它是不可见的。** 一旦 DNS 搭载在 `https://` 上,它就不会到达解析器日志 —— 那是 443 端口的问题,而不是本工具的问题。请首先强制终端使用系统解析器。
## 许可证
MIT —— 见 [LICENSE](LICENSE)。
标签:DNS隧道检测, Python, 动态分析, 无后门, 网络安全, 逆向工具, 隐私保护