2014年1月22日星期三

2014年1月全国性DNS劫持事件评析

许久没更新博客了。这次全国性事件既然这么轰动,震惊互联网界及翻墙界,那我就借此机会说上两句。

事件是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上无论怎么折腾,也都是锁君子锁不住小人,索性不把宝贵的性能浪费在这里了。