作者: Anonymous
English version: I'll shake your hand: what happens after DNS poisoning
首次发布日期: 2020年11月13日,星期五
一次典型的 DNS 污染事件包含三个步骤:
但在第三步之后会发生什么?
人们通常认为,发往这些错误 IP 地址的数据包会被丢弃或经由空路由处理; 而在本报告中,我们记录了一个有趣的现象: GFW 会冒充部分被注入的 IP 地址, 接受(或拒绝)来自客户端的 TCP 握手。 这种行为会诱使受审查的客户端发送数据; 如果握手未被接受,这些数据原本绝不会被发送。 审查者由此可以进一步了解其审查措施的效果以及客户端的意图。
我们刻画了这一行为,并对审查设备进行了指纹分析。
我们的发现表明,该审查设备很可能是无状态的;
它还采用了某种负载均衡机制,会接受约 75% 的流量。
我们定位了该审查设备的注入点,并发现发往这些 IP 地址的 DNS 查询或 TLS 连接不受 DNS 或 SNI 审查影响。
最后,我们建议用户加密 DNS 查询,并
阻止所有发往这些被注入 IP 地址的出站流量。
75% 的流量。该负载均衡算法与 (srcIP, dstIP, srcPort, dstPort) 四元组有关。我们的调查始于一个无法解释的现象。
如 Anonymous 等人(2020)的图 3 所示,
从中国和美国测试这些被注入 IP 地址的可达性时,
约有 40% 的结果不同。
例如,
从中国测试时,0.4% 的 IP—端口对为 open,
而从美国测试时则为 filtered。
(端口状态 open、closed 和 filtered 的定义参见 Nmap 手册。)
为了查明这种不一致的原因, 我们重新测试了这些地址的可达性。 首先,我们从开放数据集中取得了被注入的 IP 地址。 随后,我们在中国使用 Nmap 对这 215 个 IP 地址的全部 65535 个端口进行 SYN ping:
nmap -iL ips_after_drop.txt -p1-65535 -Pn --min-rate=500 -oX all_ips_65535.xml
我们总共发现 14 个 IP 地址至少有一个非 filtered 端口;
其余 201 个 IP 地址似乎在任何端口上都不响应。
根据端口状态,我们将这 14 个 IP 地址分为三组:
open,但从美国测试时全部为 filtered:
8.7.198.46
46.82.174.69
59.24.3.174
93.46.8.90
closed 端口,其余所有端口从中国测试时均为 filtered;从美国测试时,所有端口均为 filtered:
8.7.198.45
67.228.126.62
93.46.8.89
118.5.49.6
188.5.4.96
203.98.7.65
208.101.48.171
open 端口和少数 closed 端口,其余端口均为 filtered。从美国测试时也发现了相似但略有差异的结果。
31.13.64.49
31.13.72.54
31.13.85.1
我们首先讨论第 1 组中的四个 IP 地址:
8.7.198.46
46.82.174.69
59.24.3.174
93.46.8.90
我们称它们为监听 IP 地址,因为
从中国测试时,观察到它们从 1 到 65535 的所有端口都会接受 TCP 握手。
既有研究表明,GFW 使用多个 DNS 注入器, 每个注入器维护各自独立的黑名单(参见图 5),并注入不同的一组 IP 地址(参见表 3)。 因此,很自然会产生两个问题:
监听 IP 地址?利用开放数据集可以轻松回答这些问题:
grep --max-count 1 "59\.24\.3\.174" injector*.csv
有趣的是,
我们发现这四个监听 IP 地址与表 3第一行的地址完全一致。
换言之,
它们恰好就是注入器 1 使用的四个 IP 地址,并被用于污染 88 个域名:
cut -d";" -f2 injector1.csv | sort | uniq
www.8800.org
www.expressvpn.com
www.google.as
www.google.bf
www.google.bi
www.google.bj
www.google.bs
www.google.bt
www.google.by
www.google.cat
www.google.cd
www.google.cg
www.google.ci
www.google.cm
www.google.co.ao
www.google.co.ck
www.google.co.ls
www.google.com.af
www.google.com.ag
www.google.com.ai
www.google.com.ar
www.google.com.bd
www.google.com.bz
www.google.com.cu
www.google.com.do
www.google.com.eg
www.google.com.et
www.google.com.fj
www.google.com.gh
www.google.com.gi
www.google.com.lb
www.google.com.ly
www.google.com.mm
www.google.com.ng
www.google.com.np
www.google.com.pg
www.google.com.pk
www.google.com.py
www.google.com.sb
www.google.com.sl
www.google.com.tj
www.google.com.vc
www.google.co.mz
www.google.co.tz
www.google.co.ug
www.google.co.uz
www.google.co.ve
www.google.co.zw
www.google.cv
www.google.dj
www.google.ga
www.google.gg
www.google.gl
www.google.gm
www.google.gp
www.google.gy
www.google.hn
www.google.ht
www.google.im
www.google.iq
www.google.it
www.google.je
www.google.kg
www.google.ki
www.google.la
www.google.li
www.google.me
www.google.mg
www.google.ml
www.google.mn
www.google.mv
www.google.mw
www.google.ne
www.google.pn
www.google.ps
www.google.rs
www.google.sm
www.google.sn
www.google.so
www.google.sr
www.google.st
www.google.td
www.google.tg
www.google.tl
www.google.tm
www.google.to
www.google.ws
www.kuniao.com
如上所列, 这些域名大多与 Google 有关, 但有三个例外:
审查设备的行为相当简单。
它会冒充这四个监听 IP 地址,并且:
SYN 标志、并且关闭了 PSH 和 ACK 标志的数据包时,它会回复一个 SYN+ACK 数据包。PSH 标志的数据包时,它会回复一个 RST 数据包。你也可以使用 Nping 快速测试:
sudo nping -c 0 --tcp 59.24.3.174 -p65535 --flags S
sudo nping -c 0 --tcp 59.24.3.174 -p65535 --flags P
理论上,GFW 有能力回复客户端的请求, 但我们没有观察到这种行为。 无论客户端发送何种数据,GFW 似乎都会断开连接。 我们从中国尝试与这四个 IP 地址建立典型的 HTTP 或 TLS 连接,以此进行了测试。 使用的三条命令如下:
# HTTP GET
wget http://59.24.3.174 -v
# TLS with SNI=www.google.com
openssl s_client -servername www.google.com -tlsextdebug -msg -connect 59.24.3.174:443
# TLS with SNI=www.baidu.com
openssl s_client -servername www.baidu.com -tlsextdebug -msg -connect 59.24.3.174:443
结果是, 所有连接在成功完成握手后都被 RST 断开。
对于审查者为何采取这种行为,我们有两种不同的推测。
第一种推测是,审查者只是想破坏与这四个 IP 地址的 TCP 连接。
事实上,伪造的 SYN+ACK 和 RST 让人联想到
GFW 过去在残留审查期内破坏 TCP 连接的方式。
(请注意,我们之所以称其为“过去的方式”,是因为截至 2020 年 11 月 10 日,GFW 在 60 秒的残留审查期内已不再发送任何数据包。我们通过发送带有敏感 SNI 的 TLS ClientHello 触发了残留审查。)
正如 Wang 等人在 TCP 连接重置一节中所述:
“在此期间,两个端点之间的任何 SYN 数据包都会触发 GFW 发送一个序列号错误的伪造 SYN/ACK 数据包,从而阻碍正常的握手;任何其他数据包则会触发伪造的 RST 和 RST/ACK 数据包,从而断开连接。”
然而, 第一种推测无法解释审查者为何要耗费额外资源发送数据包, 而不是直接丢弃或以空路由处理所有发往这四个 IP 地址的数据包。
第二种推测是,
GFW 通过冒充这些被注入的 IP 地址接受 TCP 握手,
可以诱使客户端发送在握手未被接受时绝不会发送的数据。
审查者由此可以进一步了解其审查措施的效果以及客户端的意图。
具体来说,
对这些被注入 IP 地址的连接尝试
为审查者提供了另一个衡量 DNS 审查成效的角度。
如果审查者仅统计在互联网骨干网或边界上观察到的敏感 DNS 查询,
就会低估 DNS 审查事件的数量。
这是因为中国的大多数客户端使用本地解析器,
它们的敏感 DNS 查询由缓存已被污染的本地解析器回答,
而不是由 GFW 回答。
因此,通过观察发往这些被注入 IP 地址的流量,
审查者可以更准确地了解有多少客户端收到了被污染的 DNS 答案。
此外,
通过诱使受审查客户端发送比 SYN 数据包更多的数据,
GFW 可以进一步了解如果连接未受审查,客户端原本会做什么。
我们提醒读者, 尽管这些推测听起来可能合理, 但我们无法证实或证伪它们。
多项证据表明,审查设备似乎是无状态的, 而且其实现较为粗糙。例如:
ACK,审查设备也不会重传 SYN+ACK。RST 响应 SYN+ACK。首先,
为了测量审查设备的超时值,
我们使用以下命令连接其中一个监听 IP 地址:
nc -v 59.24.3.174 443
出现 Connection to 59.24.3.174 443 port [tcp/https] succeeded! 后,
我们刻意超过 30 分钟不发送任何数据;
然而,审查设备并没有发送任何数据包来关闭连接。
我们一通过 nc 发送一段数据 TEST,
审查设备就发送一个 RST 来断开连接。
该实验表明,审查设备要么没有超时机制,
要么超时值异常大。
如果审查设备确实是有状态的,
如此大的超时值很容易耗尽其资源。
其次, 即使客户端没有发送 ACK,审查设备也不会重传 SYN+ACK。 可以使用如下命令进行测试:
# capture traffic usig tcpdump,
# as nping will not show if RST was sent by kernel
sudo tcpdump -n host 59.24.3.174 and port 442
## open another terminal:
# drop RST sent by kernel due to unexpected SYN+ACK
sudo iptables -A OUTPUT -p tcp --tcp-flags RST RST -s $(hostname -I) -j DROP
# send SYN packets
sudo nping -c 0 --tcp 59.24.3.174 -p443 --flags S
# delete the dropping RST rule
sudo iptables -D OUTPUT -p tcp --tcp-flags RST RST -s $(hostname -I) -j DROP
第三,
审查设备不会以 RST 响应意外收到的 SYN+ACK:
# Sending SA to open ports of listening IPs will not get RST
sudo nping -c 0 --tcp 59.24.3.174 -p443 --flags SA
# Sending SA to open ports of common TCP server will get RST
sudo nping -c 0 --tcp 1.1.1.1 -p443 --flags SA
第四,
即使 SYN 数据包带有错误的 IP 和/或 TCP 校验和,
审查设备仍会回复 SYN+ACK:
sudo nping -c 0 --tcp 59.24.3.174 -p443 --flags S --badsum-ip --badsum
以上所有证据都表明,该审查设备的 TCP 实现简单而粗糙。 事实上,有时“越差越好”。 如果审查者只需要诱导 TCP 握手, 那么这种无状态实现就意味着代码简单、资源效率高、指纹更少且攻击面更小。
我们使用以下脚本对这些监听 IP 地址进行模糊测试并发送数据包:
#!/usr/bin/env python3
from scapy.all import *
packet=Ether() / fuzz(IP(dst="59.24.3.174")) / fuzz(TCP())
sendp(packet, loop=1)
随后,我们比较了审查设备伪造并回复的数据包。
在 IP 层,伪造回复的指纹如下:
0x68(启用了 throughput 位)。Don't Fragment (DF) 位与触发数据包相同;审查设备不会响应启用了 More Fragments (MF) 位的数据包。在 TCP 层,伪造回复的指纹如下:
0 到 2^32 之间随机取值。SYN 位,且关闭了 PSH 和 ACK 位,则回复的标志是触发数据包标志的副本,并启用 ACK 位;如果触发数据包启用了 PSH 位,则回复的标志是触发数据包标志的副本,并启用 RST 位,同时关闭 ACK、PSH、SYN、FIN 位。1424。如果你想快速检查 你所在地区的审查设备是否具有相同指纹, 可以使用与下面类似的命令:
sudo nping -c 0 --tcp 59.24.3.174 -p443 --id 3333 --tos 0x02 -df --flags S --win 1 -v4
我们还注意到,
GFW 的一些看似随机的指纹只有在以非常高的速度探测 GFW 时才会呈现出某种规律。
一个例子是 Anonymous 等人的图 4.a。
因此,我们对其中一个监听 IP 地址进行了 SYN 泛洪(不到 2 秒)。
尽管
我们以每秒 15,000 个数据包的速度收到了 GFW 伪造的 SYN+ACK,
但如下图所示,TCP 序列号仍然显得随机。
我们发现,SYN 或 PSH 数据包并不总能触发审查设备发送相应的 SYN+ACK 或 RST。
进一步调查表明,
对于给定的 (srcIP, dstIP, srcPort, dstPort) 四元组,
数据包能否触发审查设备是确定的。
此外,
在固定 (srcIP, dstIP, dstPort) 并枚举 1 到 65535 的所有 srcPort 时,
几乎恰好有 75%(最小值:49132/65535;最大值:49136/65535)的 srcPort 能够触发审查设备。
这一结果表明,审查设备采用了某种负载均衡机制。
我们无法确定其使用的具体负载均衡算法。 原始草稿曾提到一个包含代码、数据和分析的目录,但没有提供链接。 欢迎你探索这个有趣的问题。
如上所述, 审查设备发送的数据包所带的 IP TTL 值与 触发数据包被接收时的 IP TTL 值相同。 这种 IP TTL 镜像行为也存在于 GFW 的一些 DNS 污染注入器中(参见图 8)。
IP TTL 镜像的一个重要影响是,
该审查设备看起来会比其实际位置离测试主机远得多。
我们在分析中考虑了这一点,
发现审查设备距我们的主机 8—9 跳。
随后,我们使用受限 TTL 方法,定位了从这台主机到与 G2 IP 地址处于同一 /30 网段内某个 IP 地址的路径上的 DNS 注入点。
我们发现审查设备可能与 DNS 注入点处于同一位置。
之所以说“可能”,是因为路由不对称使我们无法准确定位该审查设备。
我们还发现,与这些监听 IP 地址的连接不受 DNS 或 SNI 审查;
但同一 /30 网段中的其他 IP 地址会受到审查。
例如:
# 46.82.174.69 is one of the listening IPs and we get no forged response.
dig @46.82.174.69 www.google.sm
# 46.82.174.70 is within the same /30 and we get forged responses.
dig @46.82.174.70 www.google.sm
这 7 个 IP 地址有少数关闭的端口;从中国测试时,其余所有端口均为过滤状态。
从美国对这些地址进行 SYN ping 时,
全部 65535 个端口似乎都是 filtered。
我们使用受限 TTL 对这些端口进行了 SYN ping。
结果显示,我们甚至在到达实际 IP 地址之前就收到了 RST,
这表明这些 RST 实际上是由 GFW 发送的。
这些 closed 端口如下:
118.5.49.6,1723,closed,pptp
188.5.4.96,1723,closed,pptp
203.98.7.65,1080,closed,socks
203.98.7.65,1723,closed,pptp
208.101.48.171,5222,closed,xmpp-client
67.228.126.62,443,closed,https
8.7.198.45,443,closed,https
8.7.198.45,1080,closed,socks
8.7.198.45,1723,closed,pptp
93.46.8.89,443,closed,https
请注意,118.5.49.6 与 188.5.4.96 看起来非常相似,
仿佛是有人粗心挑选出来的。
在这 10 个 closed 端口中,
通常运行的服务包括 PPTP、SOCKS、xmpp-client 和 HTTPS。
PPTP 和 SOCKS 都可用作规避审查的协议。
我们发现,即使初始 TTL 已足以触发三个 RST,仍然可以收到 ICMP TTL=0 消息。
sudo nping -c 0 --tcp 203.98.7.65 -p1080 --flags S --ttl 10
SENT (0.0019s) TCP host:19825 > 203.98.7.65:1080 S ttl=10 id=951 iplen=40 seq=18295394 win=1480
RCVD (0.2008s) TCP 203.98.7.65:1080 > host:19825 RA ttl=251 id=20366 iplen=40 seq=0 win=3509
RCVD (0.2008s) TCP 203.98.7.65:1080 > host:19825 RA ttl=251 id=20366 iplen=40 seq=0 win=3509
RCVD (0.2008s) TCP 203.98.7.65:1080 > host:19825 RA ttl=251 id=20366 iplen=40 seq=0 win=3509
RCVD (0.2008s) ICMP [202.97.90.114 > host TTL=0 during transit (type=11/code=0) ] IP [ttl=243 id=17562 iplen=96 ]
SENT (1.0033s) TCP host:19825 > 203.98.7.65:1080 S ttl=10 id=951 iplen=40 seq=1829538994 win=1480
RCVD (1.2208s) ICMP [202.97.90.114 > host TTL=0 during transit (type=11/code=0) ] IP [ttl=243 id=17732 iplen=96 ]
这表明,路径上的 GFW 注入了三个 RST,
但并未丢弃那些 SYN 数据包。
GFW 发送 RST 后会有持续 31 秒的残留审查。
在此期间,
如果我们的 SYN 使用同一个 (srcIP, dstIP, srcPort, dstPort) 四元组,
GFW 就不会向我们的 SYN 发送 RST。
sudo nping -c 0 --tcp 118.5.49.6 -p1723 -g10001 --flags S
SENT (0.0020s) TCP host:10001 > 118.5.49.6:1723 S ttl=64 id=45322 iplen=40 seq=3600506539 win=1480
RCVD (0.2010s) TCP 118.5.49.6:1723 > host:10001 RA ttl=251 id=29186 iplen=40 seq=0 win=2857
RCVD (0.2010s) TCP 118.5.49.6:1723 > host:10001 RA ttl=251 id=29186 iplen=40 seq=0 win=2857
RCVD (0.2010s) TCP 118.5.49.6:1723 > host:10001 RA ttl=251 id=29186 iplen=40 seq=0 win=2857
SENT (1.0022s) TCP host:10001 > 118.5.49.6:1723 S ttl=64 id=45322 iplen=40 seq=3600506539 win=1480
SENT (2.0037s) TCP host:10001 > 118.5.49.6:1723 S ttl=64 id=45322 iplen=40 seq=3600506539 win=1480
SENT (3.0051s) TCP host:10001 > 118.5.49.6:1723 S ttl=64 id=45322 iplen=40 seq=3600506539 win=1480
....
SENT (31.0406s) TCP host:10001 > 118.5.49.6:1723 S ttl=64 id=45322 iplen=40 seq=3600506539 win=1480
RCVD (31.2090s) TCP 118.5.49.6:1723 > host:10001 RA ttl=251 id=29359 iplen=40 seq=0 win=2904
RCVD (31.2090s) TCP 118.5.49.6:1723 > host:10001 RA ttl=251 id=29359 iplen=40 seq=0 win=2904
SENT (32.0410s) TCP host:10001 > 118.5.49.6:1723 S ttl=64 id=45322 iplen=40 seq=3600506539 win=1480
RCVD (32.0411s) TCP 118.5.49.6:1723 > host:10001 RA ttl=251 id=29359 iplen=40 seq=0 win=2904
与第 1 组和第 2 组 IP 地址不同, 我们没有发现 GFW 冒充第 3 组 IP 地址的证据。 但是, 我们确实发现发往它们部分端口的数据包会被 GFW 丢弃。
具体来说, 我们首先分别从中国和美国对它们的所有端口进行了 SYN ping:
nmap 31.13.64.49 31.13.72.54 31.13.85.1 -p1-65535 -Pn --min-rate=5000
中国的测试结果如下:
31.13.64.49 (443 open; other filtered)
31.13.72.54 (80,443 open; 843,5222,5228,8883 closed; other filtered)
31.13.85.1 (80,443 open; 843,5222,8883 closed; other filtered)
美国的测试结果如下:
31.13.64.49 (80,443 open; 843,5222,5228,8883 closed, other filtered)
31.13.72.54 (80,443 open; 843,5222,5228,8883 closed; other filtered)
31.13.85.1 (80,443 open; 843,5222,5228,8883 closed; other filtered)
我们多次重复 SYN ping,以排除丢包造成的不一致; 但仍有一些端口无法从中国访问。 进一步使用类似 traceroute 的 SYN ping 进行调查后, 我们发现发往这些端口的数据包被中国电信(CHINANET)骨干网上的路由器丢弃。 例如:
# This will get ICMP TTL=0 from the router at the CHINANET backbone
sudo nping -c 0 --tcp 31.13.64.49 -p443 --flags S -ttl 7
# But this will not get such message, suggesting packets drop
sudo nping -c 0 --tcp 31.13.64.49 -p80 --flags S -ttl 7
对于那些可从中国访问的端口,
我们尝试与其建立 HTTP 或 TLS 连接。
结果表明,客户端可以成功收到相应的 HTTP 400 Bad Request
或服务器的 TLS 证书,
且连接不会受到 GFW 干扰。
我们使用的命令如下:
wget http://31.13.85.1:80
openssl s_client -tlsextdebug -msg -connect 31.13.85.1:443
我们建议用户尽可能加密其 DNS 流量。 同时, 可以使用 iptables 规则阻止所有发往这些 IP 地址的出站流量, 以免这些连接尝试被 GFW 记录。 阻止发往这些 IP 地址的流量几乎不会损害互联网连通性, 因为这些 IP 地址本来就已被封锁、冒充或过滤。 具体来说,可以尝试执行以下命令来添加 iptables 规则:
#!/bin/bash
# Get the 215 injected IP addresses
wget https://gfw.report/publications/foci20_dns/data/foci20_anonymous/injected_ips/ips_after_drop.txt
# source: https://www.cyberciti.biz/faq/iptables-read-and-block-ips-subnets-from-text-file/
### Setup our black list ###
# Create a new chain
$IPT -N droplist
# Filter out comments and blank lines
# store each ip or subnet in $ip
while IFS="" read -r p || [ -n "$p" ]
do
# Append everything to droplist
iptables -A droplist -s "$ip" -j LOG --log-prefix " Drop Bad IP List "
iptables -A droplist -s "$ip" -j DROP
done <ips_after_drop.txt
# Finally, insert or append our black list
iptables -I INPUT -j droplist
iptables -I OUTPUT -j droplist
iptables -I FORWARD -j droplist