事件是 15:20 开始的,而我当时 15:30 刚好有个会,所以这个事件只经历了一点开头:
当时我正在整理自己的收藏夹,进行到 windbg 和 ollydbg 的时候,发现需要翻墙才能访问了。刚开始我在疑惑为啥 GFW 会对调试工具下手,难道愚民政策已经扩展到技术界了?随后我发现大部分时间这两个网站的域名被解析到了 65.49.2.178 这个 IP 上。少部分时间的尝试是正确的,但这个概率小得不足以完成大部分文件的载入。由于我用的 DNS 一直都是四个 8 ,所以第一个反应就是这两个站点被 DNS 污染了。
既然是污染,那么应该能抓到正确的 DNS 回应包,只是慢点而已。但这次我发现 DNS 回应包是一对一的,没有多余的包回来。只是第一次解析的时候往往能解析到正确的地址上,后面再解析,回复的就是 65.49.2.178 了。当时我还没试别的网站。
然后开会的时间到了,我就走了。再回来的时候,故障已经基本结束了,没什么时间和机会去分析。技术方面的分析,可以参考 这个。我认为分析得靠谱,符合此次事件的各种特征。我在这里只补充一些文中没有提到的部分。
首先,这不是「DNS 污染」,是「DNS 劫持」。
我这里不是在抠字眼,或者讨论这两个词的定义问题。我只是指出这个事实:这次 GFW 对于 DNS 的攻击方式,跟以往(或者说一直以来)的 DNS 污染有所不同。
一般,GFW 的做法是抢在 DNS 服务器的正常应答之前,伪造一个应答,欺骗客户端。正常的应答仍旧会返回到客户端,只不过 GFW 的欺骗包会很快,相当快,使得客户端不理睬正常的回应包。
但这次不同,从抓包的结果看来,「一问一答」,并没有多余的回应包。即使 DNS 是境外的 8.8.8.8,也是如此。境内的 DNS,尚有别的办法可以进行控制。境外的 DNS,必须是在 DNS 查询 / 回应包的转发路径上对其进行劫持 / 丢弃,才能实现这种效果。
其次,探索一下 GFW 这次这个 DNS 劫持功能的工作模式。
通常而言,要进行 DNS 劫持,GFW 可以有两种基本做法:
- 劫持 DNS 查询包:截获 DNS 查询包,不把它向目的 DNS 进行转发,然后自己伪造一个 DNS 回应包给客户端发去。
- 丢弃 DNS 回应包:截获 DNS 回应包,伪造一个 DNS 回应包发给客户端,然后把正确的回应包丢弃,使其不能正常到达客户端。
实际上的情况,可能比这要更复杂一点。比如,丢包的事情,应该是 GFW 的一个状态防火墙完成的。而防火墙的规则添加可能需要一点时间,所以第一次查询时有可能会有正确的回应包漏过。
另外,根据每次都是「一问一答」看来,伪造的 DNS 回应包可能是防火墙规则自动触发。也就是说,DNS 回应包被拦截后,才会伪造一个发给客户端。如果没被拦截,就不会伪造。否则无法解释第一次查询时正确的回应包漏过之时为什么也是「一问一答」。
综合上述情况,并结合 GFW 的部署和需求特点,这次的 DNS 劫持功能应该是采用的第二种工作模式,也就是说对 DNS 回应包进行处理。很显然,对于 GFW 而言:从「非受控区域」过来的数据,才是真正需要控制的「有害数据」。从「受控区域」出去的数据,就算会被判定为「有害」,也没必要进行处理,守株待兔就可以,说不定对方根本就不存在呢。
顺便,对 GFW 的一些技术细节,从这次事件可以有更多的认识。
要完成 DNS 劫持,有一个前提:GFW 可以控制 DNS 包转发路径中的(至少)某个路由器,或者 GFW 自身(的一部分)就是 DNS 包转发路径中串进去的一环。相比之下,如果要做到以前的 DNS 污染,只需要在交换机上旁路接入一台设备,不需要串在路由路径中。
具体而言,从这个 DNS 劫持的功能看来,应该会有三个部件参与:
- 识别模块:这个可以由一个 IDS 性质的设备来完成。对来自「非受控区域」的 DNS 回应包进行检测。这个完全可以在旁路慢慢做,不影响网络出口的性能。
- 过滤模块:当识别模块检测到「有害信息」后,会向一个状态防火墙添加一条动态规则。生效不一定很快,但因为是全国性质的,漏也漏不了多少。这条动态规则包括丢弃符合条件的 DNS 回应包,以及驱动欺骗模块去伪造一个 DNS 回应包。这个设备必须串入骨干路由,应该是部署在互联网出口处,基本上必须是一个群集。
- 欺骗模块:伪造的工作是跟过滤模块联动的,即:丢弃-伪造。这个模块以前应该就存在,部署在旁路上就可以。
回头想想,这些都并不是什么前沿的技术。花不了多少时间我自己都能写出来。目前阻碍这个功能(DNS 劫持)大规模应用的因素,可能主要还是来自性能方面的压力。否则现在对那些敏感域名仍然在大量采用的低效的 DNS 污染早就该换成劫持了。在性能方面而言,用旁路模式当然会好很多。有关人等大概也知道,在 DNS 上无论怎么折腾,也都是锁君子锁不住小人,索性不把宝贵的性能浪费在这里了。