Linux内核AF_VSOCK套接字条件竞争误差(CVE-2021-26708)剖析

宣布时间 2021-03-10

误差配景


近期,外洋清静研究职员在oss-security上披露了一个AF_VSOCK套接字条件竞争高危误差CVE-2021-26708(CNVD-2021-10822、CNNVD-202102-529) 。凭证披露细节,该误差是由于过失加锁导致,可以在低权限下触发并自动加载易受攻击驱动� ?榻ㄉ鐰F_VSOCK套接字,进而导致外地权限提升 。该误差补丁已经合并到Linux内核主线中 。


VSOCK先容和架构


VSOCK先容


VM套接字最早是由Vmware开发并提交到Linux内核主线中 。VM套接字允许虚拟机与虚拟机治理程序之间举行通讯 。虚拟机和主机上的用户级应用程序都可以使用VM 套接字API,从而增进guest虚拟机与其host之间的快速有用通讯 。该机制提供了一个vsock套接字地点系列及其vmci传输,旨在与接口级别的UDP和TCP兼容 。VSOCK机制随即获得Linux社区的响应,Redhat在VSOCK中为vsock添加了virtio传输,QEMU/KVM虚拟机治理提供支持,Microsoft添加了HyperV传输 。


VSOCK架构


VM套接字与其他套接字类型类似,例如Berkeley UNIX套接字接口 。VM套接字� ?橹С置嫦蚺牧魈捉幼郑ɡ鏣CP)和无毗连数据报套接字(例如UDP) 。VM套接字协议系列界说为“AF_VSOCK”,并且套接字操作分为SOCK_DGRAM和SOCK_STREAM 。如下图所示:


1.png


VSOCK支持socket API 。AF_SOCK地点簇包括两个要素:CID和port 。CID为ContextIdentifier,上下文标识符;port为端口 。TCP/IP应用程序险些不需要更改就可以适配,每一个地点体现为 �I杏幸徊阄猼ransport,VSOCK transport用于实现guest和host之间通讯的数据通道 。如下图所示:


2.png


Transport凭证传输偏向分为两种(以SOCK_STREAM类型为例),一种为G2H transport,体现guest到host的传输类型,运行在guest中 。另一种为H2G transport,体现host到guest的传输类型 。以QEMU/KVM传输为例,如下图所示:


3.png


该传输提供套接字层接口的驱动分为两个部分:一个是运行在guest中的virtio-transport,用于配合guest举行数据传输;另一个是运行在host中的vhost-transport,用于配合host举行数据传输 。VSOCK transport还提供多传输通道模式,该功效是为了支持嵌套虚拟机中的VSOCK功效 。如下图所示:


4.png


支持L1虚拟机同时加载H2G和G2H两个传输通道,此时L1虚拟机即是host也是guest,通过H2G传输通道和L2嵌套虚拟机通讯,通过G2H传输通道和L0 host通讯 。VSOCK transport还支持外地环回传输通道模式,不需要有虚拟机 。如下图所示:


5.png


该模式用于测试和调试,由vsock-loopback提供支持,并对地点簇中的CID举行了分类,包括两种类型:一种是VMADDR_CID_LOCAL,体现外地环回;一种为VMADDR_CID_HOST,体现H2G传输通道加载,G2H传输通道未加载 。

 

误差剖析与触发历程


误差剖析


该误差触发缘故原由是过失加锁导致条件竞争,凭证补丁可知,保存多处过失加锁,这里以vsock_stream_setsockopt()函数补丁为例,如下图所示:


6.png


补丁很精练,将第1564行代码移动到第1571行,中心就隔着第1569行代码:lock_sock(sk) 。加锁前,vsk->transport已经赋值到transport变量中,这里爆发了一个引用,然后才举行lock_sock(sk)将sk锁定 。可是vsk->transport会在多处被挪用甚至被释放,这就有可能通过条件竞争造成Use After Free 。


触发历程


首先找到修改或释放vsk->transport的挪用路径,来看要害函数vsock_assign_transport()的实现 。关于多传输模式,该函数用于凭证差别CID分派差别的传输通道 。实现代码如下图所示:


7.png


凭证sk->sk_type分为SOCK_DGRAM和SOCK_STREAM,在SOCK_STREAM中,分为三种传输通道 。这里可以通过将CID设置为外地环回模式,获得transport_local传输通道 。接下来如下图所示:


8.png


若是vsk->transport不为空,则进入if语句 。先判断vsk->transport是否即是new_transport,若是即是直接返回,在触发历程中,要包管能走到vsock_deassign_transport()函数,该函数是析构函数,用于释放transport 。如下代码所示:


9.png


行411,挪用vsk->transport->destruct(),要明确使用transport类型,前文已经确定使用transport_local 。Transport_local为全局变量,会在vsock_core_register()函数中被初始化 。该函数被挪用情形如下图所示:


10.png


*_init()函数用来初始化transport的回调函数,凭证第二部分先容,vhost_vsock_init()、virtio_vsock_init()和vsock_loopback_init()函数为QEMU/KVM情形下的支持函数 。我们发明transport->destruct()函数的最后实现都是统一个函数 。如下图所示:


11.png


该destruct()函数释放vsk->trans,如下图所示:


12.png


而vsk->trans指针是指向transport的 。结构体vsock_sock界说如下所示:


13.png


最终可以结构一个释放transport的函数路径为:vsock_stream_connect-> vsock_assign_transport->virtio_transport_destrcut 。


找到了释放路径,下一步找使用路径,virtio_transport_notify_buffer_size()函数会使用transport 。如下图所示:


14.png


第492行,通过vsk->trans获取指向transport的指针,第497行,解引用vvs指针,对vvs->buf_alloc举行赋值 。而挪用virtio_transport_notify_buffer_size()函数最终会被vsock_stream_setsockopt()函数挪用 。最终可以结构一个使用transport的函数路径为:vsock_stream_setsockopt-> vsock_update_buffer_size->virtio_transport_notify_buffer_size 。


接下来就是营造一个抢锁的条件竞争情形,很显着必需是connect()系统挪用先抢到锁对transport举行释放,然后再挪用setsockopt()才华触发误差 。有开发职员提出使用userfaultfd机制先将lock_sock锁定,然后在去释放锁,举行条件竞争 。误差触发历程如下图所示:


15.png


蓝框中是connect()挪用历程,最后挪用virtio_transport_destruct()函数释放vsk->trans 。红框中是setsockopt()挪用历程,挪用virtio_transport_notify_buffer_size()函数使用vvs,该值是0xffff888107a74500,在0xffff888107a74500+0x28处会写入4字节 。


参考链接:

[1]https://github.com/torvalds/linux/commit/d021c344051af91f42c5ba9fdedc176740cbd238

[2]https://static.sched.com/hosted_files/devconfcz2020a/b1/DevConf.CZ_2020_vsock_v1.1.pdf

[3]https://github.com/jordan9001/vsock_poc

[4]https://terenceli.github.io/%E6%8A%80%E6%9C%AF/2020/04/18/vsock-internals


尊龙凯时起劲防御实验室(ADLab)


ADLab建设于1999年,是中国清静行业最早建设的攻防手艺研究实验室之一,微软MAPP妄想焦点成员,“黑雀攻击”看法首推者 。阻止现在,ADLab已通过CVE累计宣布清静误差近1100个,通过 CNVD/CNNVD累计宣布清静误差1000余个,一连坚持国际网络清静领域一流水准 。实验室研究偏向涵盖操作系统与应用系统清静研究、智能终端清静研究、物联网智能装备清静研究、Web清静研究、工控系统清静研究、云清静研究 。研究效果应用于产品焦点手艺研究、国家重点科技项目攻关、专业清静服务等 。


微信图片_20210310102858.jpg