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。如下图所示:

VSOCK支持socket API。AF_SOCK地址簇蕴含两个身分:CID和port。CID为ContextIdentifier,高低文标识符;port为端口。TCP/IP利用法式险些不必要更改就能够适配,每一个地址暗示为

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

该传输提供套接字层接口的驱动分为两个部门:一个是运行在guest中的virtio-transport,用于共同guest进行数据传输;另一个是运行在host中的vhost-transport,用于共同host进行数据传输。VSOCK transport还提供多传输通路模式,该职能是为了支持嵌套虚构机中的VSOCK职能。如下图所示:

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

该模式用于测试和调试,由vsock-loopback提供支持,并对地址簇中的CID进行了分类,蕴含两种类型:一种是VMADDR_CID_LOCAL,暗示本地环回;一种为VMADDR_CID_HOST,暗示H2G传输通路加载,G2H传输通路未加载。
缝隙分析与触发过程
缝隙分析
该缝隙触发原因是谬误加锁导致前提竞争,凭据补丁可知,存在多处谬误加锁,这里以vsock_stream_setsockopt()函数补丁为例,如下图所示:

补丁很简洁,将第1564行代码移动到第1571行,中央就隔着第1569行代码:lock_sock(sk)。加锁前,vsk->transport已经赋值到transport变量中,这里产生了一个引用,而后才进行lock_sock(sk)将sk锁定。但是vsk->transport会在多处被挪用甚至被开释,这就有可能通过前提竞争造成Use After Free。
触发过程
首先找到批改或开释vsk->transport的挪用蹊径,来看关键函数vsock_assign_transport()的实现。对于多传输模式,该函数用于凭据分歧CID分配分歧的传输通路。实现代码如下图所示:

凭据sk->sk_type分为SOCK_DGRAM和SOCK_STREAM,在SOCK_STREAM中,分为三种传输通路。这里能够通过将CID设置为本地环回模式,得到transport_local传输通路。接下来如下图所示:

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

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

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

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

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

最终能够机关一个开释transport的函数蹊径为:vsock_stream_connect-> vsock_assign_transport->virtio_transport_destrcut。
找到了开释蹊径,下一步找使用蹊径,virtio_transport_notify_buffer_size()函数会使用transport。如下图所示:

第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锁定,而后在去开释锁,进行前提竞争。缝隙触发过程如下图所示:

蓝框中是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安全钻延注工控系统安全钻延注云安全钻研。钻研成就利用于产品主题技术钻延注国度沉点科技项目攻关、专业安全服务等。



京公网安备11010802024551号