内容简介:全文完LWN文章遵循CC BY-SA 4.0许可协议。极度欢迎将文章分享到朋友圈
The TCP SACK panic
By Jake Edge
June 19, 2019
Selective acknowledgment (SACK) 是TCP里面用到的一个机制,帮助减少丢包重传导致的拥塞。网络的这一端点就可以利用这个机制,来告诉对端自己收到了哪部分数据,然后对端就知道只需要传送缺失的这部分数据即可。不过,最近在 Linux 的SACK实现里面发现了一个bu,可能会被远程攻击者利用,通过发送特制的SACK信息而让接收方的Linux进入panic状态。
TCP发送的数据都是被分割为很多个片段(根据网络对端或流经节点里所支持的maximum segment size, MSS,最大分片长度)。这些数据包成功到达对端之后,对方会恢复acknowledge包来确认说收到这些数据了。
以前,这些acknowledgement包(ACKs)只包含有限信息,如果碰到数据丢失(例如由于拥塞控制而丢弃)的话,哪怕收到后续的数据,也只会ack到第一个丢失的数据包为止。发送方就只能重发从这个包开始的所有数据(事实上跳过丢失的包之后的数据在接收方已经收到了)。这样发送方需要重新传送很多对方其实已经收到的数据包,因为发送方不知道后续这些包对方已经收到了,因此只能盲目重传。
举个简化的例子,发送方A可能发送了序号20~50的数据包,而23和37这两个包在中途就被丢弃了。接收方B只能对20~22这几个包进行ACK,所以A需要重新发送23~50这些包。
很容易想到的一点,如果链路本身拥塞很严重导致丢包,那么又重复发送了这么多数据包肯定会加剧拥塞。
Selective acknowledgment就是创建出来消除这些重复的网络数据的。它诞生于1996年,RFC 2018号。这里的核心想法就是接收端B能够ACK 20~22, 24~36,38~50,这样A只需要重发第23和37这两个包。这里应该很容易理解,就像某个人读了一个30个字的一段话给你听,但是你没有听清第三个字,那你肯定不会让对方从第三个字开始把后面所有的字都再念一遍。
为了实现这个机制,网络子系统的代码里面需要做一些记录工作,本文所提到的bug就是出现在这里。
kernel有个名为struct sk_buff(通常叫做SKB)的数据结构,这就是用来存放各种类型的网络数据的,会用于transmit queue(发送队列),receive queue(接收队列),SACK queue(SACK队列)等等等等。可以读一下网络模块的代码维护者David Miller的一篇很好的综述文章(http://vger.kernel.org/~davem/skb_data.html ),帮助理解SKB在kernel里是怎么使用的。TCP会把data stream分成32KB(powerPC上是64KB)的fragment,然后记录下来。而kernel在做fragment分割的代码,和SACK处理代码,这里合作时出现了问题。
struct tcp_skb_cb是一个control buffer(用于控制的缓存)结构,记录一个TCP packet的各种信息,包括它被分割成多少个segment/fragment。这个主要是用于general segmentation offload (GSO)功能,用来把packet的segmentation分段动作尽量放在网络协议栈的更低层级来做,甚至可能的话放到网络硬件(例如网卡)里去做。这里的segment的数据是存在tcp_gso_segs成员变量里的,这是一个2个byte大的ussigned int数。如果segment的数量不超过64K的话,肯定是足够了。
不过,如果网络传输双方都打开了SACK机制(网络连接建立时协商的结果)的话,可能就不够了。攻击者可以使用一个很小的MSS值(例如48 byte,这样真正的用户数据就只能用8 byte空间了),然后仔细选择ack哪些segment,从而让tcp_gso_segs值溢出。在kernel里,为何能更加高效的处理多段未被ack的数据segment,可能会选择对多个SKB进行拼接合并,这个动作就可能被恶意攻击者利用来让tcp_gso_segs溢出。
如果发生溢出的话,会触发tcp_shifted_skb()里的BUG_ON()调用,导致kernel panic。这是Jonathan Looney(来自Netflix)发现的SACK相关的bug里面最严重的一个了。同时还报告了其他两个Linux bug,同样都会导致SACK变慢,或者占用大量资源,从而利用来做拒绝服务攻击(DoS)。此外Looney还在FreeBSD 12,在用RACK TCP stack的时候,发现了一个类似的SACK拖慢系统的问题。RACK是一年前Netflix刚刚贡献给FreeBSD的。
这个SACK panic问题被命名为CVE-2019-11477,很明显,这是Linux的一个相当严重的问题。CVE-2019-11478是另外一个DoS攻击漏洞,就是通过特制的SACK序列来导致TCP传输队列数据进行分片fragmentation,从而占用大量资源。CVE-2019-11479里面则指出如果Linux的MSS设置为48,就会导致在发送很少的用户数据的时候就能占用大量的CPU, memory,以及bandwidth。针对性的解决方案,是提供了一个sysctl knob开关给系统管理员,允许设置kernel能接受的最小的MSS值,缺省值仍然配成是48,主要是出于兼容性考虑,不过管理员可以很轻松的改变这个配置了。
这个问题已经完全搞清楚了,Netflix的报告里也提供了修复问题的kernel patch,它们在6月17日被分别合入了Linux 5.1.11, 4.19.52, 4.14.127, 4.9.182, 4.4.182这几个stable update(稳定版kernel发布)里。多数的发行版里都已经更新了kernel,用户也请尽量更新。
目前无法更新的用户,还有一些其他方法来避免收到攻击。可以利用iptables来限制MSS的选值,等等其他方式。不过这些方法都需要关闭MTU probing(通过设置net.ipv4.tcp_mtu_probing为0),否则无法彻底避免CVE-2019-11477和CVE-2019-11478。当然这两个CVE攻击也可以通过关闭SACK来避免(设置/proc/sys/net/ipv4/tcp_sack为0)。为了避免CVE-2019-11479攻击,管理员只需要利用Netflix列出的几种方法来把MSS值太小的给过滤掉。Red Hat的vulnerability report也跟Netflix的报告一样,提供了很多细节信息。这样的远程kernel crash攻击确实是一个让人很不爽的漏洞,可能会有大范围的受害者。不过,只有在网络连接的端点会受影响,这样稍微限制了一下影响范围。至少服务器和桌面PC都能通过更新来解决,而网络链路的各个中间节点,就不是那么好解决了。
全文完
LWN文章遵循CC BY-SA 4.0许可协议。
极度欢迎将文章分享到朋友圈
长按下面二维码关注:Linux News搬运工,希望每周的深度文章以及开源社区的各种新近言论,能够让大家满意~
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
图片转BASE64编码
在线图片转Base64编码工具
HEX CMYK 转换工具
HEX CMYK 互转工具