本文发自 http://www.binss.me/blog/analyse-the-tcp-bypass-hijacking-of-china-unicom/,转载请注明出处。

ISP作恶不是一天两天了,记得十多年前家里刚拉ADSL时,用的是铁通的网络,在上网的时候各种右下角广告各种弹窗,而我却无可奈何。忍了好多年,联通的光纤铺到小区里了,果断换用联通。说实话,问了许多人,在大吃省这一片,联通算是良心的了:带宽不打折扣,游戏玩联通服能保持50ms以内,国际出口碾压电信,也没有什么乱七八糟的广告劫持。对联通的高度满意导致上次续网一口气续了三年。

然后平静的生活一直持续到今年一月某日,正当我在本地调试binsite 3.0调得焦头烂耳的时候,右下角突然弹出了广告!怒!!!检查之,发现是在加载新浪CDN的jQuery时被劫持了,而由于我调试本地网页没有开启屏蔽广告插件,于是就可耻地弹窗了。

于是开始搜索文章,学习相关姿势,感觉受益匪浅,以文记之(拖了好多天,毕竟截图真的很麻烦)。

运营商的劫持使用的是一项名字很高大上的技术: TCP旁路干扰

TCP相关知识

我们知道TCP需要经过三次握手才能建立连接(不考虑Fast Open):

  1. A发送带有seq=x(随机数)的[SYN]报文给B

  2. B发送带有seq=y(随机数)和ack=x+1的[SYN, ACK]报文给A

  3. A发送带有seq=x+1和ack=y+1的[ACK]报文给B

此时TCP连接建立。A和B可以通过连接发送和接收数据,直到连接中断。

连接中断有两种情况:

  1. FIN

    正常关闭,需要经过四次挥手。可由任意一方发起。

    1. A发送FIN,进入fin-wait状态

    2. B发送ACK,进入close-wait状态,A收到后进入fin-wait-2状态

    3. B发送FIN,进入last-ack状态

    4. A接收到后发送ACK,然后进入time-wait状态等待2个MSL才进入closed状态,B接收到ACK后也进入closed状态。

  2. RST

    在TCP的设计中,RST用于关闭异常连接,应对例如服务器意外重启等情况。

    1. A发送RST

    2. B收到RST,直接把连接关闭,无须ACK确认

这种通过握手建立信道,并在信道上进行数据传输的方式这给中间人带来了可乘之机。中间人保存各TCP连接的连接信息(客户端信息、服务端信息、请求方向TCP等待序列号和应答方向TCP等待序列号),然后当过滤到需要劫持的数据包时,找到该TCP数据包所属TCP连接的连接信息,根据所抓取的TCP数据包更新该连接信息中的请求方向TCP等待序列号和应答方向TCP等待序列号。有了这两个序列号,就可以构造有效的数据包对TCP连接的双方进行欺骗了。

攻击分类

RST干扰

不想让你访问某个地址,发现你访问了,抢先向通信的两端各发送一个[RST],从而实现主动切断连接的目的。

发出的[RST]包的前提是知道整个会话当前的序列号和确认号,否则这个RESET包将会被忽略。但很遗憾,相关技术已经申请了专利并得到广泛的应用。

曾经是某W的最爱,后来换用效率更高的路由技术了。

SYN/ACK干扰

中间人冒充你要握手的目标和你进行握手。当你建立连接时,中间人捕获了你的[SYN],并给你回了个[SYN,ACK],然后就和中间人握上了手。而当真正的[SYN,ACK]到达时,包会被丢弃,此后双方由于seq不一致导致无法通信。

FIN干扰

FIN干扰。替换掉你要访问的页面,返回[FIN,ACK]和HTTP 200。

现象分析

后两样攻击都是ISP的最爱,用途有:

  1. 把你劫持到运营商的缓存

    典型现象:当你用HTTP GET请求某个资源(尤其是国外资源)时,对方秒回[FIN,PSH,ACK],HTTP Response为302,指向ISP缓存的地址(一般为纯ip,比如最近热门的联通cdn120.52.72.*)。

    当你想在debian官网下点什么东西时,立马被劫持:

    你以为你访问不存在的文件我就不劫持你了吗?Naive!

    通常来说,ISP这样做都是为了节省出口带宽,减少结算费用。对用户来说,这样有利有弊:利是如果被缓存的文件是最新的,可以加快你的访问/下载速度(CDN啊有木有);弊是如果缓存过期,那你下载的就是过期的文件。比如当你想apt-get update时,就会因为缓存过期导致悲剧:

    所以当你想apt-get点什么东西的时候,最好全局XX,避免被ISP劫持到缓存。

  2. 劫持页面,在DOM中插入script加载乱七八糟的东西

    典型现象:手机上网时弹出运营商的广告,如联通的流量球、购买流量包提醒等等。顺手抓了访问百度百科的包,包的概要信息如下

    查看返回html页面的DOM,发现被插入一行script

    然后这个script会让你加载流量球所需的全部文件,为了加载这个东西,多发送了5个Get请求,加在一起大概耗费了5KB流量,展现在你的眼前是这个样子的

  3. 劫持常用js文件(如jquery),返回iframe,iframe中包含了请求的原js文件和广告的代码

    典型现象:右下角广告。如图

    抓包之,发现我请求的是jQuery,结果返回的是一段DOM,里面加载了放在upaiyun上的一个iframe,iframe里面又加载了广告的脚本

    秒回的是中间人的应答。之后当真正的应答到来时,被当作乱序包丢掉了.....

    至于为什么要用upaiyun?我猜可能是用upaiyun的网站太多,不容易匹配到广告拦截插件的黑名单。

解决方法

忍无可忍,无需再忍。根据我的经验,提供以下三个建议:

  1. 打电话投诉你的ISP

    第二天就抓起电话怒喷运营商,当天他们就派了技术上门,在向其演示劫持现象并展示抓包后,他说会向上面反映巴拉巴拉。几天后,劫持停止。看v2ex的讨论,好像那段时间全国的联通都被劫持,后来可能因为投诉太多就停止了。

  2. 在路由器或浏览器安装广告屏蔽工具

    居家必备。在这里想特别提一下safari也可以加载插件来屏蔽广告,个人觉得AdBlock的效果还不错。当然更彻底的方法是用Surge将相关域名block掉。当然这两项功能都需要IOS9的支持。

  3. 尽量用HTTPS访问网站

    厌倦了访问百度时被加上?hao=xxxxx的尾巴吗?访问https://www.baidu.com/吧。不过现在的Chrome好像都能够自动跳转到HTTPS。