Update 2017-11-15: 修复脚本
DNS 污染是指一些伪造的 DNS 请求,把域名指往不正确的 IP 地址。DNS 污染是国内某知名互联网服务的主要手段之一(其他手段包括 IP 封锁,端口封锁,TCP 连接重置等)。那么,如何判断一个域名是否被污染了呢?
DNS 污染的原理
DNS 设计是有缺陷的:
- 数据都是明文的,可以轻松嗅探
- 缺乏验证机制,无法识别虚假的 DNS 回应
只要做关键字匹配,发送虚假 DNS 查询结果即可。
dig twitter.com
; <<>> DiG 9.9.2-P2 <<>> twitter.com
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 3709
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
;; QUESTION SECTION:
;twitter.com. IN A
;; ANSWER SECTION:
twitter.com. 300 IN A 37.61.54.158
;; Query time: 38 msec
;; SERVER: 127.0.0.1#53(127.0.0.1)
;; WHEN: Wed Aug 27 00:13:43 2014
;; MSG SIZE rcvd: 56
判断域名是否被污染
墙在做 DNS 污染时是很粗暴的,只要关键字匹配,一律发送虚假的 A 记录。例如,thisisnottwitter.com
这个域名并不存在,但依然被污染了:
dig @a.root-servers.net. thisisnottwitter.com
; <<>> DiG 9.9.2-P2 <<>> @a.root-servers.net. thisisnottwitter.com
; (2 servers found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 28008
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0
;; QUESTION SECTION:
;thisisnottwitter.com. IN A
;; ANSWER SECTION:
thisisnottwitter.com. 300 IN A 37.61.54.158
;; Query time: 128 msec
;; SERVER: 198.41.0.4#53(198.41.0.4)
;; WHEN: Wed Aug 27 00:14:47 2014
;; MSG SIZE rcvd: 74
事实上,根据 DNS 协议,根 DNS 服务器应该返回的是 com.
的权威域,不可能返回 A 记录。正确的回应应该是这样的:
dig @a.root-servers.net. thisisnottwitter.com
; <<>> DiG 9.8.2rc1-RedHat-9.8.2-0.23.rc1.el6_5.1 <<>> @a.root-servers.net. thisisnottwitter.com
; (2 servers found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 23058
;; flags: qr rd; QUERY: 1, ANSWER: 0, AUTHORITY: 13, ADDITIONAL: 14
;; WARNING: recursion requested but not available
;; QUESTION SECTION:
;thisisnottwitter.com. IN A
;; AUTHORITY SECTION:
com. 172800 IN NS a.gtld-servers.net.
com. 172800 IN NS b.gtld-servers.net.
com. 172800 IN NS c.gtld-servers.net.
com. 172800 IN NS d.gtld-servers.net.
com. 172800 IN NS e.gtld-servers.net.
com. 172800 IN NS f.gtld-servers.net.
com. 172800 IN NS g.gtld-servers.net.
com. 172800 IN NS h.gtld-servers.net.
com. 172800 IN NS i.gtld-servers.net.
com. 172800 IN NS j.gtld-servers.net.
com. 172800 IN NS k.gtld-servers.net.
com. 172800 IN NS l.gtld-servers.net.
com. 172800 IN NS m.gtld-servers.net.
;; ADDITIONAL SECTION:
a.gtld-servers.net. 172800 IN A 192.5.6.30
b.gtld-servers.net. 172800 IN A 192.33.14.30
c.gtld-servers.net. 172800 IN A 192.26.92.30
d.gtld-servers.net. 172800 IN A 192.31.80.30
e.gtld-servers.net. 172800 IN A 192.12.94.30
f.gtld-servers.net. 172800 IN A 192.35.51.30
g.gtld-servers.net. 172800 IN A 192.42.93.30
h.gtld-servers.net. 172800 IN A 192.54.112.30
i.gtld-servers.net. 172800 IN A 192.43.172.30
j.gtld-servers.net. 172800 IN A 192.48.79.30
k.gtld-servers.net. 172800 IN A 192.52.178.30
l.gtld-servers.net. 172800 IN A 192.41.162.30
m.gtld-servers.net. 172800 IN A 192.55.83.30
a.gtld-servers.net. 172800 IN AAAA 2001:503:a83e::2:30
;; Query time: 122 msec
;; SERVER: 2001:503:ba3e::2:30#53(2001:503:ba3e::2:30)
;; WHEN: Wed Aug 27 00:15:38 2014
;; MSG SIZE rcvd: 498
根据这一点即可判断某个域名是否被污染。写成 Bash 脚本如下:
#!/usr/bin/env bash
set -e
usage() {
echo "Usage: $(basename $0) domain"
echo " -h, --help Show this help"
}
isblocked() {
if [[ $(dig @a.root-servers.net. ${1} | grep 'ANSWER SECTION') ]]; then
return 0
else
return 1
fi
}
set -e
[[ "$#" -eq 0 ]] && usage && exit 0
domain=$1
if isblocked "${domain}"; then
echo "${domain} is polluted."
else
echo "${domain} is not polluted."
fi