Linux内核AF_PACKET原生套接字缝隙(CVE-2020-14386)阐发

宣布时候 2020-09-22

缝隙背景


克日,Openwall社区上公然了一个Linux内核AF_PACKET原生套接字内存粉碎缝隙。按照细节描写,该缝隙呈现在net/packet/af_packet.c中,由整数溢出致使越界写,能够经由进程它停止权限晋升。该缝隙风险评级为高,编号为CVE-2020-14386。


受影响产物和减缓办法


1、受影响产物


该缝隙影响Linux刊行版高于4.6的内核版本,包罗:

  • Ubuntu Bionic (18.04) and newer

  • Debian 9

  • Debian 10

  • CentOS 8/RHEL 8

2、减缓办法


(1)修补体系

下游内核补丁以下:


(2)封闭CAP_NET_RAW功效

针对RHEL8,详细封闭步骤以下:

# echo"user.max_user_namespaces=0" > /etc/sysctl.d/userns.conf

# sysctl -p/etc/sysctl.d/userns.conf


(3)针对一些受影响的容器产物,一样采用封闭CAP_NET_RAW功效停止减缓

Kubernetes Pod宁静战略:设置装备摆设Pod宁静战略以删除运转容器中的CAP_NET_RAW功效,参考链接:


相干观点


1、AF_PACKET套接字


收集和谈栈中,原始套接字是一个特别的套接字范例,从完成上能够分为两类,一类为链路层原始套接字;另外一类为收集层原始套接字。链路层原始套接字可间接用于领受和发送链路层的MAC帧,在发送时须要挪用者自行机关和封装MAC首部。链路层原始套接字挪用socket()函数建立。第一个参数指定地点簇范例为AF_PACKET,第二个参数套接字范例为SOCK_RAW或SOCK_DGRAM,当范例指定为SOCK_RAW时,套接字领受和发送的数据都是从MAC首部起头的。在发送时须要由挪用者从MAC首部起头机关和封装报文数据。


2、PACKET_MMAP


仅依托AF_PACKET过滤数据包长短常低效的,内核又供给了PACKET_MMAP撑持。PACKET_MMAP在内核空间平分配一块环形内核缓冲区,用户空间经由进程mmap将该内核缓冲区映照出来。收到的数据包拷贝到环形内核缓冲区中,用户层能够间接操纵数据,经由进程内核空间和用户空间同享的缓冲区起到减多数据拷贝的感化,进步处置效力。


PACKET_MMAP完成进程


经由进程setsockopt()函数设置环形缓冲区,option参数设置为PACKET_RX_RING或PACKET_TX_RING。为了便利内核与用户层办理和交互环形缓冲区中的数据帧,内审界说了TPACKET_HEADER布局体,该布局体存储着一些元信息如套接字地点信息、时候戳和环形缓冲区办理信息等。若是经由进程setsockopt()函数设置了PACKET_VNET_HDR选项,还需增加一个virtio_net_hdr布局体。一个数据帧包罗两个局部,第一局部为TPACKET_HEADER,第二局部为Data,并且要保障页面临齐,以下图所示:

今朝TPACKET_HEADER存在三个版本,每一个版本长度略有差别。对v1和v2,收发环形缓冲区用tpacket_req布局体办理,该布局体包罗四个数据域:别离为内存块的巨细和数目、每一个数据帧的巨细和数据帧总数。以下图所示:



捕获的frame被分别为多个block,每一个block是一块物理上持续的内存地区,有tp_block_size/tp_frame_size个frame,block的总数是tp_block_nr。比方,tp_block_size = 4096,tp_frame_size = 2048,tp_block_nr = 4,tp_frame_nr = 8。获得的缓冲区布局以下图所示:



每一个frame必须放在一个block中,每一个block保管整数个frame,也便是说一个frame不能逾越两个block。在用户层映照环形缓冲区能够间接利用mmap()函数。固然环形缓冲区在内核中是由多个block构成的,可是映照后它们在用户空间中是持续的。


缝隙阐发


该缝隙详细呈现在tpacket_rcv()函数中,该函数是基于PACKET_MMAP的数据包领受函数。详细功效完成以下代码所示:



行2226到行2228,若是sk_type为SOCK_DGRAM,表现不须要自行机关MAC首部,由内核添补,则macoff即是netoff,巨细为TPACKET_ALIGN(tp_hdr_len)+ 16 + tp_reserve。若是sk_type为SOCK_RAW,则进入行2230,表现须要自行机关MAC首部。行2231到行2233,起首计较netoff,巨细为TPACKET_ALIGN(tp_hdrlen +(maclen < 16 ?16 : maclen)) + tp_reserve。行2234到行2237,若是设置了PACKET_VNET_HDR选项,还需加上一个virtio_net_hdr布局体的巨细,而后设置do_vnet为真。行2238,计较macoff


因为macoff、netoff和maclen被界说为unsigned short范例,最大值为0xffff。而tp_reserve被界说为unsigned int范例,最大值为0xffffffff,并且巨细能够经由进程setsockopt()函数停止设置,以下代码所示:



是以,在计较netoff时,能够经由进程节制tp_reserve形成整数溢出,进而计较犯毛病的macoff。当履行到以下代码时:



行2287,挪用virtio_net_hdr_from_skb()函数从sk_buff中拷贝数据,该函数第二个参数为h.raw + macoff – sizeof(struct virtio_net_hdr),h.raw为tpacket_rcv_uhdr范例的指针,指向环形缓冲区的frame,因为macoff是可控的,能够让maoff小于sizeof(struct virtio_net_hdr),致使向前越界写,最多可写入sizeof(struct virtio_net_hdr)个字节。按照供给的PoC,调试代码以下图所示:



rdx中寄存着TPACKET_ALIGN(tp_hdrlen+(maclen < 16 ? 16 : maclen)),巨细为0x50。rbp+0x4e4处寄存着po->tp_reserve,巨细为0x0000ffb4。相加后,整数上溢后,rdx为0x0004。当履行到越界拜候时,详细以下:



R9寄存着h.raw指针,rdx寄存着macoff,virtio_net_hdr布局体巨细为0xa。以下图所示:



产生内存拜候毛病,形成体系瓦解。


参考链接:


[1] http://blog.csdn.net/sinat_20184565/article/details/82788387

[2] http://www.openwall.com/lists/oss-security/2020/09/03/3

[3] http://elixir.bootlin.com/linux/v5.6/source/Documentation/networking/packet_mmap.txt

[4] http://sysdig.com/blog/cve-2020-14386-falco/

[5] http://bugzilla.redhat.com/show_bug.cgi?id=1875699#c9