Linux内核TCP协议多个SACK功效拒绝服务误差剖析

宣布时间 2019-06-21
误差配景


2019年6月18日,Redhat宣布清静通告,Linux内核TCP/IP协议栈保存3个清静误差(CVE-2019-11477/CVE-2019-11478/CVE-2019-11479),这些误差与最大分段大�。∕SS)和TCP选择性确认(SACK)功效相关,允许远程攻击者举行拒绝服务攻击。


要害看法



数据包重传确认机制


TCP数据包传输历程中,来自滑动窗口的数据包丧失可能对TCP吞吐量爆发影响。TCP使用累积确认(ACK)计划解决该问题,其中不吸收不在滑动窗口左边沿的吸收段,这会强制发送方期待往返时间以找出每个丧失的数据包,或者不须要地重新传输已准确吸收的段,从而降低整体吞吐量。


选择性确认(SACK)是一种在多个扬弃的段的情形下解决此行为的战略。通过选择性确认,数据吸收方可以向发送方通知已乐成抵达的所有段,因此发送方只需重新传输现实丧失的段。详细选择性确认历程,如下图所示。


尊龙凯时 - 人生就是搏!


最大分段大�。∕aximum Segment Size)


MSS(Maximum Segment Size,最大报文段大�。┑目捶ㄊ侵窽CP层所能够吸收的最大分段巨细,该值只包括TCP段的数据部分,不包括Option部分。另外,在TCP首部有一个MSS选项,在三次握手历程中,TCP发送端使用该选项告诉对方自己所能接受的最大分段巨细。


TSO(TCP Segmentation Offload)


TSO是一种使用网卡来对大数据包举行自动分段,降低CPU负载的手艺。其主要是延迟分段。


GSO(Generic Segmentation Offload)


GSO是协议栈是否推迟分段,在发送到网卡之前判断网卡是否支持TSO,若是网卡支持TSO则让网卡分段,不然协议栈分完段再交给驱动。若是TSO开启,GSO会自动开启。


误差原理


CVE-2019-11477


凭证补丁可知,该误差是由一个16bit无符号数溢出导致的,该无符号数保存如下结构体中。


尊龙凯时 - 人生就是搏!


该tcp_skb_cb结构体存放着TCP每个数据包的控制信息,凭证注释可知,tcp_gso_segs/size只用于写行列历程中。


Linux内核TCP/IP协议栈实现中,每个数据缓冲区是由一个sk_buff结构体统一管理的。在一个完整的数据缓冲区中skb_end后面紧随着一个skb_shared_info结构体数据,skb_shared_info结构体如下所示:


尊龙凯时 - 人生就是搏!


结构体最后一个成员是frags[MAX_SKB_FRAGS]数据。MAX_SKB_FRAGS声明如下所示:

尊龙凯时 - 人生就是搏!


PAGE_SIZE为4KB情形下(即一个内存页面为4KB大�。�,MAX_SKB_FRAGS取值为65536/4096 + 1即17,因此一个skb中最多容纳17个数据分片。关于x86系统,每个数据分片最多可以纪录32KB数据的巨细。
数据分片skb_frag_struct结构体如下所示:


尊龙凯时 - 人生就是搏!


在整个协议栈操作历程中,数据包既要举行IP被分片的,又要举行TCP分段。传输数据时,协议栈会凭证GSO值,MSS值以及滑动窗口三者之间的巨细关系判断是否举行分片。并通过tcp_set_skb_tso_segs()函数设置GSO,详细实现如下图所示:


尊龙凯时 - 人生就是搏!


若是skb->len大于mss_now,行1207,将tcp_gso_segs设置为skb->len/mss_now。行1208,将tcp_gso_size设置为mss_now。


若是启用了SACK,在爆发丢包后,吸收端会返回SACK块,SACK块中纪录着丧失包的序列编号。发送端会剖析SACK块中纪录的丧失包序列编号,并重新传输,并且在一个滑动窗口中可能包括多个SACK块,SACK块中也可能包括多个skb行列。在TCP重传数据包历程中,可以将多个skb行列合并到一个skb行列中举行重传。


tcp_shift_skb_data()函数实现这个功效。实验将跨越多个skb的SACK块折叠为一个skb。要害代码如下:


尊龙凯时 - 人生就是搏!


skb_shift()和tcp_shifted_skb()两个函数主要实现该功效。重传历程中多个skb行列合并到一个skb行列中,若是填充17个分片到最大容量, 17*32*1024/8=69632,已经大于65535,导致无符号整数溢出。


在skb_shift ()函数中,tcp_gso_segs溢出后,进入tcp_shifted_skb()函数后,如下所示:


尊龙凯时 - 人生就是搏!


行1299,判断tcp_gso_segs和pcount的巨细,若是tcp_gas_segs小于pcount,BUG_ON断言触发导致内核瓦解。


凭证补丁可知,skb_shift()被tcp_skb_shift()取代,只是加了两个判断,如下所示:

尊龙凯时 - 人生就是搏!


补丁中划分判断了skb->len+shift_len不可大于65535*8字节和tcp_skb_pcount(to) + pcount不可大于65535。第一个判断,skb->len是体现sk_buff结构体中体现payload长度,shift_len体现要合并到skb中的payload。


CVE-2019-11478


该误差也是整数溢出,在数据包重新传输历程中,将传输行列分段为多个细小的skbs,膨胀skb中写行列内存爆发溢出。在处置惩罚SACK块中包括的skb并将其合并后,凭证GSO判断举行是否分片,若是需要,挪用tcp_fragement()函数举行分片。凭证补丁可知:


尊龙凯时 - 人生就是搏!


补丁在tcp_fragment()函数中加入了最小空间判断。Sk是sock结构体类型,每一个tcp链接对应一个。以是所有要发送的skb数据巨细都要累加到sk->sk_wmem_queued中,sk->sk_wmem_queued体现为该套接字TCP写行列缓冲区巨细。通常在使用时间需要判断该值是否够用。如下所示:


尊龙凯时 - 人生就是搏!


凭证注释可知,判断最新排队skb包所需的最小可写空间。补丁中,判断剩余发送缓存为大于即是目今发送行列占用空间的一半,即尚有1/3以上的空余空间时,并且小于sk->sk_sndbuf发送上限才可以正常发送,不然就判断TCP写行列太大。


CVE-2019-11479


该误差由于太过消耗资源导致拒绝服务。若是恶意数据包将MSS选项设置成较小值,这将迫使协议栈破费很是高的网络或CPU资源发送数据包开销。Linux内核中将MSS_NOW硬编码为48。凭证补丁可知:

尊龙凯时 - 人生就是搏!


举行了max最大值判断,而不再是牢靠硬编码。这里的sysctl_tcp_min_snd_mss被设置为65535,如下所示:


尊龙凯时 - 人生就是搏!


阻止了攻击者使用极小MSS值。


影响版本及补丁修复


实时更新最新补丁或禁用SACK和过滤极小MSS的数据包。


CVE-2019-11477

影响版本:

  • Linux 2.6.29 ~ 4.19.13(stable kernel releases 4.4.182,  4.9.182, 4.14.127, 4.19.52, 5.1.11除外)

  • RHEL 8 (kernel, kernel-rt),RHEL 7 (kernel, kernel-rt),RHEL 6

禁用sack:

  • sudo sysctl -w net.ipv4.tcp_sack=0

补�。�

  • https://git.kernel.org/pub/scm/linux/kernel/git/davem/net.git/commit/?id=3b4929f65b0d8249f19a50245cd88ed1a2f78cff

CVE-2019-11478

影响版本:

  • Linux 2.6.29 ~ 4.19.13(stable kernel releases 4.4.182,  4.9.182, 4.14.127, 4.19.52, 5.1.11除外)

  • RHEL 8 (kernel, kernel-rt),RHEL 7 (kernel, kernel-rt),RHEL 6,RHEL 5

禁用sack:

  • sudo sysctl -w net.ipv4.tcp_sack=0

补�。�

  • https://git.kernel.org/pub/scm/linux/kernel/git/davem/net.git/commit/?id=f070ef2ac66716357066b683fb0baf55f8191a2e

CVE-2019-11479

影响版本:

  • Linux 2.6.29 ~ 4.19.13(stable kernel releases 4.4.182,  4.9.182, 4.14.127, 4.19.52, 5.1.11除外)

  • RHEL 8 (kernel, kernel-rt),RHEL 7 (kernel, kernel-rt),RHEL 6,RHEL 5

过滤下令:

  • sudo iptables -A INPUT -p tcp -m tcpmss --mss 1:500 -j DROP

关闭tcp_mtu_probing:

  • sysctl net.ipv4.tcp_mtu_probing

补�。�

  • https://git.kernel.org/pub/scm/linux/kernel/git/davem/net.git/commit/?id=5f3e2bf008c2221478101ee72f5cb4654b9fc363

  • https://git.kernel.org/pub/scm/linux/kernel/git/davem/net.git/commit/?id=967c05aee439e6e5d7d805e195b3a20ef5c433d6