漏洞通告|CVE-2022-23222 eBPF验证器过滤不严导致的漏洞分析及利用
发布时间:
2023-08-04 16:20
01 背景
CVE-2022-23222 这是一个位于eBPF验证器中的漏洞,漏洞允许eBPF程序在未经验证的情况下对特定指针进行运算,通过精心构造的代码,可以实现任意内核内存读写,而这将会造成本地提权以及容器逃逸的风险。
02 危害
随着漏洞PoC公开,这个漏洞的利用成本也无限趋近于0。但是这个漏洞的利用并不容易,需要本地允许非特权用户执行BPF程序,而这在大多数发行版中是默认禁止的。同时,由于漏洞在5.8.0版本内核中引入,而大多数生产环境并没有使用这么高的内核版本,所以,问题并不算很严重。
03 漏洞原理
那么,验证器是如何知道我们是否判断以及什么时候判断了结果是否为NULL呢?我们来看相关的实现代码。

这是eBPF中寄存器的完整类型列表,通过代码我们可知它通过“*_OR_NULL”类型来表示一个未经NULL判断的指针类型。当寄存器的类型是“*_OR_NULL”时,他只能进行非常有限的操作。只有当类型为“*_OR_NULL”的寄存器做完NULL比较后,才可能变为普通的指针,也就是“PTR_TO_*”类型。
出现漏洞的代码位于 linux/kernel/bpf/verifier.c

其中adjust_ptr_min_max_vals()是eBPF验证其用于检验指针加减运算的函数。这段代码使用switch来过滤不支持加减运算的指针类型,比如各种“*_OR_NULL”类型。但是这段代码里却少了很多类型的判断。这意味着,我们可以对这些少了的类型做加减运算,其中就包括一部分“*_OR_NULL”类型。
04 EXP

大致流程如下:
首先构造一个bpf map并利用漏洞泄露array_map的地址,然后构造任意内存地址读写原语。
紧接着,创建用于提权的进程,查找task_struct结构,对task_struct中的uid,gid,euid,egid进行覆盖,创建root shell。
接下来以注释的方式仔细分析其中的关键部分:



提权过程:

而父进程将在记录子进程pid后继续执行find_cred。


05 总结
此漏洞本质上是eBPF验证器过滤不严,导致验证器的逻辑与实际执行时不一致,从而突破了验证器的安全检查,并最终导致内核任意内存读写。而提权的方法也不只有公开的exp一种,甚至完全可以利用漏洞构造出内核shellcode执行。
从这方面看来,漏洞还是很严重的,不过由于此漏洞利用需要允许非特权用户执行eBPF代码,而大多数发行版默认并不开启此功能,此漏洞的危害终究有限。
06 容器环境
由于此漏洞为内核漏洞,不可避免会影响到容器环境。经过测试,直接利用公开的exp只能实现容器内非特权用户到容器内root的提升,无法直接逃逸。但是通过构造内核shellcode或者篡改task_struct中的nsproxy结构来逃离namespace隔离都是可以实现容器逃逸的。不过容器环境中还有一点不同,默认情况下,非特权容器没有加载eBPF程序的权限,也就又进一步缩小了容器被攻击的可能性。
07 防范
升级内核到安全版本,禁止非特权用户执行eBPF程序,命令如下:
sudo sysctl kernel.unprivileged_bpf_disabled=1
08 参考资料
https://www.openwall.com/lists/oss-security/2022/01/14/1
https://github.com/tr3ee/CVE-2022-23222
https://github.com/torvalds/linux





