360MeshFire Team:CVE-2016-5696 TCP旁路攻击分析与重现

阅读量272887

|

发布时间 : 2016-08-31 18:20:39

http://p1.qhimg.com/t015f81107f9950fa1e.png

0x01  漏洞背景

8月10-12日的USENIX安全大会,加州大学河滨分校与美国陆军研究实验室的6名研究人员揭露了关于RFC 5961的安全漏洞。

为防止 blind in-window”攻击, RFC5961引入Challenge ACK机制,但Linux 内核在实现该RFC文档时引入了漏洞,攻击者可以利用该漏洞通过旁路攻击准确推断TCP连接的端口号、SEQ号和ACK号,从而实现TCP Reset攻击和数据注入攻击。

为了更好的了解CVE-2016-5696,我想我们有必要回溯一下该漏洞的历史缘由。

在RFC5961(August 2010)出现之前,是TCP的荒蛮时代。blind in-window attacks一直是针对TCP协议的主要攻击手段。TCP协议本身在设计时候并没有充分考虑其安全性,TCP的安全性依赖于其连接的不可确定性,能否攻击成功很大程度上取决于攻击者能否预测出TCP序列号和端口号,一旦伪造数据包的序列号落到了窗口上,攻击者就可以去注入恶意链接或者直接重置连接。序列号是一个32位随机数,有40多亿种可能,端口号则有65535种可能,组合起来这种设计能够在一定范围内防御TCP的拒绝服务和注入攻击。

在RFC5961出现之前,Linux的tcp协议栈是这样做的:


当一条已经正常建立的链接收到一个syn包后,会这样做:


if   syn包中的seq在合法窗口中,

接收方reset连接

else

接收方发送ack包


当一条已经正常建立的链接收到一个RST包后,会这样做:


if RST包中的seq在合法窗口中

接收方reset连接

else

接收方发送ack包


如果想防止数据注入,会这样做:


1.seq 在合法范围内

2.ack number必须有效范围内:[SND.UNA-(231-1),SND.NXT]

SND表示发送方,UNA表示已发送未确认收到的seq NXT表示下一个要发送包的seq 

以上这样的逻辑会发现一个很明显的问题,就是我作为一个旁路攻击者,我盲目的猜测一个源地址、一个源端口和一个seq number, 把这个包发给服务器,如果这个链接在服务器上刚好存在,且seq number也正好在合法窗口内的话,那该条连接会被服务器端重置,拒绝攻击成功;

但是如果我没猜中呢?没猜中呢?没猜中呢?

好吧,没猜中那我就继续往后猜呗,我把地址0-255,端口1-65535,seq从0-4G发一遍,我一台机子不够用,我找一百台来发攻击包。这样一来,肯定会有很多条连接中招,然后被reset掉。其实这也就是所谓的blind in-window attacks,合法窗口盲打。

但是,RFC5961出现了。RFC5961的出现,像黑暗中的一道光明,照亮了整个TCP协议栈,也照亮了Linux系统的大好前程。

RFC5961对TCP协议栈的处理提出如下建议:

当一条已经正常建立的链接收到一个syn包后,应该这样做:

不对seq做校验,直接回复一个challenge ack给源端

if收到源端发来的reset包

接收方reset连接、

else

接收方丢弃该syn包

如果源端的确丢失了连接信息,要重新初使化一个新的,那么在收到挑战包后,原端会发送RST包,确认当前连接需要关闭.

当一条已经正常建立的链接收到一个RST包后,应该这样做:

if seq  == 期望接收值 

            接收方断开连接

elif  seq 在有效窗口

            接收方发送挑战包

else

            接收方丢弃该包,不理会

如果想防止假数据注入,应该这样做:

接收端收到ACK包,确认合法性的条件:

1.seq 在合法范围内

2.if ack number in【SND.UNA-MAX.SND.WN, SND.NXT】

                        (注:MAX.SND.WND表示接收方能看到的发送方的最大窗口范围)

            then

                        确认合法数据包

                        elif  ack number在剩下的范围内:[SND.UNA −(231 − 1), SND.UNA − MAX.SND.W ND)

             then

                        需要发送挑战包

http://p0.qhimg.com/t01a9f6b003663c79fe.png

以上是RFC5961对TCP协议栈如何修改所提出的建议。可以明显的看出,当增加了源端确认这样的流程之后, syn包的旁路攻击已经变得不可能,因为你无法收到challeng ack。你只有完全蒙对地址+端口+seq值才能reset掉一条连接,这样大大增加了成本,blind in-window attacks变得非常困难。在防止数据注入方面,RFC5961之前的机制中ACK的范围比较大,相对容易的可以命中有效范围。RFC5961所建议机制虽然不能完全消除,但是大大降低了包的成功注入,提升攻击者成本,本身也是防御的一种。

到这里,我们应该觉得RFC5961应该提出了很多不错的建议,可为什么旁路攻击又变得可能了呢?

在RFC5961中给出了一段关于挑战包节流机制的建议,以防止过多的挑战包影响系统性能,也正是这个建议,导致linux kernel在严格遵守协议做实现后,引发这个漏洞:

包的检查严格了,会导致触发较多挑战包。为了减少CPU和带宽损耗,REF5961建议引入挑战包的节流机制:限制单位时间间隔中,挑战包的个数。

在linux  kernel 3.6中,缺省设置是每秒100个,管理员可以修改。

REC5961中建议:挑战包的节流机制应该保守些, 

linux  kernel 3.6中,挑战包的计数器是一个被所有TCP连接共用的全局变量sysctl_tcp_challenge_ack_limit。

为什么windows, freebsd, MAC OS X, 没有这个问题呢?

因为这些系统都没有严格遵守RFC5961的要求,要么,没有完全实现挑战机制,要么,没有实现挑战包的节流机制。

BINGO, 好孩子linux,独中。


0x02 攻击原理

为防止这种 “blind in-window attacks”, 在RFC5961中引入了挑战包机制,REF5961本身作为改进建议,协议的设计没有问题。该漏洞的根源在于RFC5961引入的Challenge ACK挑战包节流机制的速率限制,在linux kernel实现时出现了漏洞。由于所有连接共用一个计数器,那么通过伪造包和真实通信包去挑战,根据真实通信返回的挑战包的数量,通过旁路攻击的方式来推断探测包的命中与否,一旦攻击包命中则可以逐步准确的推断出连接的端口号、SEQ号和ACK号,从而可以实现Reset攻击和数据注入攻击。这就是下面的攻击方法

攻击方法:

要想Reset攻击,需要知道的条件:

    1、两IP是否有连接

    2、seq 有效范围

要想注入攻击 ,需要知道的条件:

    1、两IP间是否有连接

    2、seq number

    3、ACK number 

下面以注入攻击为例,进行分析:

client与server存在TCP连接,

    1创建真实连接,到server端(因为server端口大多公开,不需要猜测)

    2伪造1个包发到server端,目的是为了触发对端的挑战机制。

    3再使用真实连接,故意发100个包,触发挑战条件,如果收到100个,说明步骤2发送的包未触发挑战条件,如果收到99个,说明步骤2发送的包,触发了挑战条件,可以进行进一步推断。

详细攻击步骤:

    1.推断两个IP间是否存在连接

    假设两端IP固定,服务端的端口公开,只需要确定源端口,就能确定一个连接。

    1.1伪造一个syn-ack 包,伪造client端IP,源端口号为猜测值

    1.2 使用真实连接,发送100个设计过的RST包(seq落在有效范围,且不等于正确值),如果收到99个包,说明基于该端口的TCP连接存在。

    1.3 采用折半算法,收紧范围,最终确定准确的端口号

    2 推断seq number

    2.1伪造一个RST包,seq为猜测值

    2.2 使用真实连接,发送100个设计过的RST包(seq落在有效范围,且不等于正确值),如果收到99个包,说明seq num在范围内

    2.3 采用折半算法,收紧范围,最终确定准确的seq值 

    3 推断ack number

原理:推断ACK值,可以先推断出挑战范围的左边界:SND.UNA −(231 − 1), 然后再 +(231 – 1),就可以得到准确的ACK值了。

    3.1确定左边界的范围,尝试0, 1G,2G, 3G四个节点,确定左边界落在哪个象限中。

具体实施:

    3.1.1 发送伪造ACK包,ack分别为0、1G、2G、3G,seq为有效范围内的值

    3.1.2 发送100个设计过的RST包, 如果收到99个包,说明ack在挑战范围

    3.13 通过四个节点的命中挑战的情况,就可以判断出挑战范围的左边界。

    3.2 再使用折半逼近的方法,确定左边界的精确值。 

 具体实施:

    3.2.1 伪造一个ACK包,ack为猜测值, seq为有效范围内的值,

    3.2.2 发送100个设计过的RST包,如果收到99个包,说明 ack在挑战范围内; 如果收到100个包,则说明ack在invalid 范围内。

    3.2.3 根据判断结果,逐渐逼近至挑战范围的左边界。

    3.3 左边界+(231 – 1) = ack

攻击示意图:

http://p5.qhimg.com/t01cbf0eb562750d8f7.png

现实挑战:

时间同步

    由于计数器是按秒复位,上一秒的计数器,在进入下一秒后会复位。

    所以攻击端,必须与被攻击机器的时间尽可能保持同步,才能使攻击成立。

RST限制

    通常 firewall都有限制RST的频率,防止攻击,那么就不能使用RST做尝试推断了。

    可以使用ACK包进行挑战,ACK挑战条件:

    1、seq在有效范围 

    2、ACK在挑战范围,

    ACK的挑战值很容易确定:

    四个节点:0,1G,2G,3G,必定有1到2个命中范围,在已知一个seq有效值的情况下,可以推断出ACK挑战值。

包丢失

包的丢失,会影响推断:

    引入多次确认机制。

窗口变化

    tcp 窗口,不是固定,是变化的,一定程序上增加了推断难度。


0x03 攻击效果

有一组公开的数据,

1.断开连接攻击:

http://p0.qhimg.com/t01cddb4aa6d999006c.png

2.数据注入攻击

http://p7.qhimg.com/t013276c0ccb7f2ee32.png

可以看到成功率非常高,时间基本控制在1分钟左右。


0x04 POC

截止到8月30日,网络上已经出现两个POC:

rover: 第三方,只是开放了推断端口号部分,python实现。

mountain-goat: 官方曝出,只开放到推断seq 范围,C实现 。

8月底,360MeshFire网络安全团队完整实现了端口号、seq和ack的推断,并完成了reset攻击和inject数据攻击。

360MeshFire Team POC演示视频: https://yunpan.cn/cMW9EbkGBscuz (提取码:b46b)

附演示视频截图:

http://p4.qhimg.com/t019d79f562c7921efd.png

POC结论:

1.针对任意TCP连接,精准的猜测连接是否存在、源端口、seq、ack是可行的,成功率极高。

2.只要服务端的Linux kernel为漏洞影响版本,可触发挑战包机制,就会受到该漏洞的影响。

3.一旦推测出四元组信息,seq,ack的情况下,就可以实现双向的数据劫持注入和定向TCP连接的拒绝服务:包括HTTP中注入钓鱼代码,伪造客户端发送写命令(TELNET,HTTP等)

4.如果在网络带宽足够、程序实现优化(C实现)等情况下,可以快速精准地推断出以上元素数据,达到paper所述的攻击成功率。

5.由于安卓4.4版本以上均存在该问题,且大量APP中建立的是与服务器的长连接,导致可被利用的场景,可能会有更多的变数。


0x05 影响范围

Linux  Kernel3.6 以上版本, 包括目前最高4.6版本,都受该漏洞影响。

安卓

release version , name  ,kernel version 

4.4   Kit Kat         |3.10

5.x   Lollipop        |3.16.1

6.0   Marshmallow     |3.18.10

Centos 7

release version ,kernel version 

7.0   3.10

7.1   3.10

7.2   3.10

Utuntu

release version ,kernel version 

13.04   Raring Ringtail  3.8

13.10   Saucy Salamander 3.11

14.04   Trusty Tahr      3.13

14.10   Utopic Unicorn   3.16

15.04   Vivid Vervet     3.19

15.10   Wily Werewolf    4.2

16.04   Xenial Xerus     4.4

16.10   Yakkety Yak      4.6 

Redhat Enterprise release

release version ,kernel version 

RHEL 7.2        3.10.0-327

RHEL 7.1        3.10.0-229

RHEL 7.0 GA   3.10.0-123

RHEL 7.0 Beta   3.10.0-54.0.1


0x06 修复方案

1.升级Linux内核到4.7版本以上

2.修改挑战包计数器值,步骤如下:

1)修改挑战包的节流设置/etc/sysctl.conf:

net.ipv4.tcp_challenge_ack_limit = 9999999

该值取100-300间随机数,可以过滤那些只会使用工具的黑客,增加一定难度,同时对性能不会产生什么影响。

该值取一个大值,将直接解决此问题,但因此导致的性能、带宽损耗需要评估。

2 )sysctl -p (激活)

3. 对业务来讲,使用加密传输,可以有效防止数据的劫持和注入。

    协议栈解决漏洞思路:

    不采用全局挑战包计数器的实现方案,每个连接维护一个挑战包计数器。

    linux 4.0部分引入了这个思想,但只对SYN包的挑战进行了单独计算,所以面对攻击无效。


0x07 参考资料

sec16_TCP_pure_offpath.pdf

数据注入演示视频

14亿Android设备受Linux TCP漏洞的影响

How to mitigate Linux “Off-path” TCP exploits

知乎tombkeeper与曹跃关于geekpwn上任意tcp远程支持的讨论

本文由360 MeshFire Team原创发布

转载,请参考转载声明,注明出处: https://www.anquanke.com/post/id/84479

安全客 - 有思想的安全新媒体

分享到:微信
+10赞
收藏
360 MeshFire Team
分享到:微信

发表评论

内容需知
  • 投稿须知
  • 转载须知
  • 官网QQ群8:819797106
  • 官网QQ群3:830462644(已满)
  • 官网QQ群2:814450983(已满)
  • 官网QQ群1:702511263(已满)
合作单位
  • 安全客
  • 安全客
Copyright © 北京奇虎科技有限公司 360网络攻防实验室 安全客 All Rights Reserved 京ICP备08010314号-66