Canlendula的博客
首页技术随笔

© 2026 Canlendula的博客

GitHubPowered by Next.js
技术2026年7月25日

国内 DoH 被投毒:DNS 污染排查记录

DoH与DNS投毒污染对代理的影响

上篇文章修好了路由环路,route_exclude_address 加上之后,VPS IP 直出物理网卡,sing-box TUN 不再吃自己的出站流量。晚上查看了几天的日志,received real certificate 从万条降到个位数,体感零丢包。安稳了差不多两周多

然后新的问题来了

复现

2026-7-24 晚,注意到TUN 模式下的 Codex 出现交互异常,部分鉴权操作卡住了,第一时间去测了curl google,想法是先排除win环境中 TUN 的毛病,哈哈果然,curl 不通。 但是这次不同的是,Bing 搜索、Google 搜索、GitHub 全部挂掉。v2rayN 日志抛出大量io错误,涉及到的ip全部是112.121.x,这个ip是哪来的?

netstat 验证,一看:

TCP    172.18.0.1:56377    112.121.185.214:443    Established
TCP    172.18.0.1:56378    112.121.185.214:443    Established
TCP    172.18.0.1:56379    112.121.185.214:443    Established
...(数百条,端口从 55800 一路涨到 56400+)

112.121.185.x,一个完全不认识的 IP 段。连接状态混着 Established、SynSent、CloseWait、LastAck。关键是——目标端口全是 443。

ping VPS(我的vps ip:443):0% 丢包

把pc连接从有线网络切换到同一 wifi 下(同一个宽带,所以是同一个 ISP 出口),出现了同样的问题,把pc连接从有线网络切到手机热点:一切正常,我草坏事了

按照上一次的排查经验,可能是某个 win 服务、内网设备的ip,导致本机一直在连接。那么和上一次一样,把这个ip加到route_exclude_address就解决了? NoNo,在这个问题出现时反查是哪个应用在连这个ip,查出来是xray.

排除 TCP 劫持

第一时间怀疑 ISP 在 TCP 层面劫持了到 VPS 的 443 端口,把连接重定向到 112.121.185.x 的 DPI 节点。但这个假设对不上:

  1. VPS ping 正常:如果 ISP 做了 TCP 劫持,ICMP 大概率也一并受影响。但 VPS 0% 丢包,151ms 延迟稳定。
  2. 多 IP 不固定:如果是劫持到 DPI 节点,应该固定一个 IP。但实际观察到 .22、.202、.214、.186、.210 多个 IP,每次复现还不一样。
  3. 热点正常。同样的客户端、同样的配置,切到移动数据就完全没问题。

ISP 层面的 TCP 劫持解释不了 2 和 3,线索指向了更上层。

抓到根因

开了 v2rayN 的 debug 日志,同时写了个实时监控脚本跟踪 112.121.185.x 的连接。问题复现时,脚本输出:

Time     RemoteAddr        State   ProcessName   PID
21:48:11 112.121.185.186  SynSent xray          34460
21:48:11 112.121.185.186  SynSent xray          34460
21:48:11 112.121.185.186  SynSent xray          34460
...(同时间戳连续 16 条)

xray 在尝试连接 112.121.185.186:443。这不是 sing-box TUN 的流量,而是 xray-core 自己的出站连接。

如果你看过上一篇文章,应该知道 xray 的任务是连我的 VPS:我的域名:443。但它实际在连 112.121.185.186:443。中间发生了什么?

翻 Windows DNS 缓存:

ipconfig /displaydns

记录名称:      我的域名
记录类型:      1 (A)
生存时间:      3566
A (主机)记录:  112.121.185.210

DNS 被投毒污染了: 我的域名应该解析到 我的vps ip,但 DNS 缓存里写的是 112.121.185.210。

五分钟后跑第二次诊断,DNS 缓存还是同一个假 IP,TTL 从 3566 递减到 3302——每次查询都拿到被污染的同一条记录

log截图122:31 log截图222:36

投毒在哪里

v2rayN 的 DNS 规则把 我的域名 交给了 direct_dns(v2rayN GUI没写明,实际上是 dns.alidns.com 的 DoH 端点)。换句话说,域名解析走的是 DoH 到阿里云。

我把 nslookup(UDP 明文 DNS)和 curl(DoH HTTPS DNS)做了对照:

UDP DNS:

nslookup 我的域名 223.5.5.5     → 我的vps ip ✓
nslookup 我的域名 114.114.114.114 → 我的vps ip ✓
nslookup 我的域名 8.8.8.8       → 我的vps ip ✓

DoH DNS:

curl https://dns.alidns.com/resolve?name=我的域名&type=A
→ {"data":"112.121.185.210","TTL":137}
路径结果
UDP(端口53)→ 阿里/114/Google全部正常
DoH(端口443)→ dns.alidns.com被投毒 112.121.185.210

DNS 服务器本身没作恶——UDP 是干净的。问题发生在 TLS 连接上:GFW/ISP 的 DPI 设备对发往 dns.alidns.com 的 DoH 流量做了 TLS 中间人,解密后用黑名单匹配域名,匹配到 我的域名 → 篡改响应为假 IP → 重新加密发回。

因为 dns.alidns.com 的 TLS 证书在国内,GFW/ISP 有条件做 MITM 而不触发证书错误。

懒得截图了,反正就是这个意思
curl拿的假IP,nslookup拿的真ip。

交叉验证

当晚排查出问题并修复后,就跑去玩漫威斗魂的公测了(好玩)

游戏结束后看到了某论坛的帖子:"疑似出现 DNS 劫持, 腾讯/阿里 doh 也被污染",发帖时间和我遇到问题的时间差不多,前后脚的事

帖子里报告的细节几乎一模一样:

112.121.185.214  (v4)
2a02:5740:102:45::2  (v6)

doggo xxx.com @https://doh.pub/dns-query
→ A:    112.121.185.214
→ AAAA: 2a02:5740:102:45::2

受影响的不只是阿里 DNS——腾讯 DNS(doh.pub)也一起中招。ipv4 到 112.121.185.*,ipv6 到 2a02:5740:102:45::2

基本统一时间点的社区论坛论坛截图

既然不是我的个例,贴子里也有别的回复,看来结论就是对DoH的集中投毒,阿里腾讯全部跑不掉

不是 DoH 的问题,是"国内 DoH"的问题

DoH 本身是用加密手段防监听的——如果你查的是 8.8.8.8 的 DoH,GFW 看不到内容。但国内 DoH 的服务端(dns.alidns.com、doh.pub)位于国内,TLS 连接在骨干网上发生。老大哥在国内线路上有管辖权👀,可以对国内服务器的 TLS 做 MITM。

这就是为什么 UDP DNS 没事、DoH 反而有事——不是 DoH 被攻破了,是"国内 DoH"的 TLS 连接在 GFW 管辖范围内。

为什么 TUN 模式更明显

非 TUN 模式同样受影响——xray 的 DNS 规则是一样的,拿到的假 IP 也是同一个。区别在于——

TUN 模式:

  • sing-box TUN 劫持全系统流量
  • 代理一挂,所有请求涌入 xray → 全部超时
  • 浏览器、终端、系统更新全挂 → 体感是整个网络崩了
  • 112.121.185.x 连接堆积几百条 → 日志里一眼就看到了

非 TUN 模式:

  • 系统流量不走 TUN
  • 只有配置了代理的应用受影响
  • 体感"代理挂了" → 浏览器某些网站打不开 → 重启或切节点 → 好了 → 不深究
  • 日志不刷屏 → 发现不了是 DNS 污染

同一个根因,TUN 只是把问题放大到了无法忽略的程度

解决方案

hosts 写死

我的vps ip 我的域名

Windows 的 DNS 解析器在查 DNS 之前先查 hosts。xray 作为 Go 程序,在 Windows 上用的是系统解析器,优先取 hosts。这样域名→IP 的映射在到达任何 DNS 之前就被截断了

IP 直连

把 v2rayN 节点地址从域名改成 我的vps ip, xray 看到目标是 IP,不需要 DNS 解析,直接发起 TCP 连接。完全绕过了整条 DNS 链。

两种方案本质一样——都是在 DoH 之前截断。 区别在于 hosts 对系统内所有程序生效,IP 直连只对 v2rayN 有效,我是两个都上了。

Cloudflare Worker 做海外 DoH 代理

把 我的域名 的解析不交给国内 DoH,而是交给一个部署在 Cloudflare Workers 上的 DoH 转发服务。请求发往 https://your-worker.workers.dev/dns-query, 服务端在海外解析域名,返回真 IP。

GFW 看不到 DoH 内容——Worker 跑在 Cloudflare CDN 上,TLS 证书在海外,没有国内 PKI,无法做 MITM。

方案一、二的先有鸡先有蛋问题在这里无效,因为Worker 自己不需要走 VPS 隧道,直连可达。

预期代价:海外节点查询比国内 DoH 慢几十毫秒,对代理域名来说几小时查一次,感知不到。 但是DoH接口绝对不能暴露,如果被扫到了一次DDOS就刷爆了。

对协议选择的启示

上篇文章我提过:Trojan-go 因为 Go TLS 指纹被识别,所以迁移到了 VLESS-Reality。现在回头看这个选型,Reality 在这次 DoH 投毒中还有额外的一条命:

协议是否IP直连原因和其他
VLESS + Reality✅证书是借的,跟自己的域名/IP 无关
Shadowsocks✅无 TLS 层,等价于裸奔,一般直接秒封
Trojan-go / Trojan❌证书绑定域名,IP 直连证书校验失败
VLESS + WS + TLS❌同上
VMess + TLS❌同上

Trojan 类协议必须用域名,因为 TLS 证书的 CN/SAN 是域名,用 IP 直连证书对不上,如果设置跳过证书验证也约等于自杀。

那么如果域名被 DoH 投毒,只有写死 hosts 或者海外 DoH 转发。而 Reality 天生的证书外挂机制让它刚好能切 IP 直连。

上篇文章选 Reality 的理由是防 TLS 指纹识别。这次多了一个理由:防 DNS 投毒。这两个问题不是独立的——GFW 的攻击面在 TLS 指纹和 DNS 两个方向同时展开,Reality 恰好两边都能应对。

GFW 的阳谋

以下内容全部是推测的

回看这次投毒的模式:GFW 只污染国内 DoH,不碰 UDP DNS,为什么?

攻击者可能的算盘:
  1. 投毒国内 DoH → 依赖域名的代理节点全废
  2. 用户切 hosts / IP 直连 → 少数人能自救,多数人放弃
  3. GFW 不需要追封 IP —— DNS 投毒已经拦住了绝大多数流量, 域名失去意义: 没有 CDN、没有 DDNS、没有快速切换节点
  4. 真要到封 IP 那步,它本来就能查域名拿 IP,不需要等你切换

UDP DNS 投毒是十几年前的老手段了,成本极低——伪造一个 UDP 包就行。但部署 DoH 加密后,代理工具全切了过去,UDP 投毒基本废了。GFW 现在升级了硬件和 PKI 能力,能吞吐国内 DoH 的 TLS 解密,才开始了这波操作

世界潮流,浩浩荡荡,顺之则昌,逆之则亡

评论加载中...

← 返回首页