2021年10月12日,日本安全厂商 Flatt security 披露了 Linux 内核提权漏洞CVE-2021-34866
。11月5日,@HexRabbit 在 Github 上公布了此漏洞的利用方式,并写文分析,技术高超,行文简洁。但作为初次研究相关内容,笔者做了一些较基础的内容补充。
eBPF
eBPF 是一种在访问内核服务和硬件的新技术。在这项技术诞生之前,如果需要在 Linux 内核执行定制的代码有两种方式,一是提交代码到原生内核项目中,不用赘述其难度;二是使用内核模块,以扩展的方式添加代码到内核执行,可以在运行时动态加载和卸载。但内核模块也有明显的缺点:需要对每个内核版本进行适配;如果代码有问题容易导致内核崩溃。
eBPF 可以较好地解决在内核空间实现自定义代码的问题。eBPF 是 Linux 内核中高度灵活和高效的类似虚拟机的技术,有自己的字节码语法和特定的编译器,允许以安全的方式在各个挂钩点执行字节码。它可用于许多 Linux 内核子系统,最突出的是网络、跟踪和安全。
在实现上,通过调用bpf_prog_load()
可以创建 eBPF 程序。其中需传入一个包含 eBPF 指令的结构体bpf_insn
。
Verifier
安全性是 eBPF 的突出特点。如果需要加载一个 eBPF 程序到内核中,需要通过Verifier
的检查。它会检查 eBPF 程序中是否有死循环、程序大小、越界、参数错误等。Verifier
中的主要检查函数是bpf_check()
,在这函数中,有一个针对 helper 函数参数检查的函数check_map_func_compatibility
,就是此次漏洞所在的函数。
Helper 函数
eBPF 程序并不能调用任意的内核函数,这会导致 eBPF 程序与特定的内核版本绑定。因此 eBPF 提供的是一些常用且稳定的 API,这些 API 被称为 helper
函数,用于 eBPF 与内核交互数据。所有的 helper 的名称和作用在bpf.h
中有声明。每种 eBPF 程序类型的有不同的可用的 helper 函数。下面列举出我们后面分析相关的 helper 函数原型和作用。
/* long bpf_ringbuf_output(void *ringbuf, void *data, u64 size, u64 flags)
* Description
* Copy *size* bytes from *data* into a ring buffer *ringbuf*.
* If **BPF_RB_NO_WAKEUP** is specified in *flags*, no notification
* of new data availability is sent.
* If **BPF_RB_FORCE_WAKEUP** is specified in *flags*, notification
* of new data availability is sent unconditionally.
* If **0** is specified in *flags*, an adaptive notification
* of new data availability is sent.
*
* An adaptive notification is a notification sent whenever the user-space
* process has caught up and consumed all available payloads. In case the user-space
* process is still processing a previous payload, then no notification is needed
* as it will process the newly added payload automatically.
* Return