CVE-2022-0847-DirtyPipe分析
字数 1799 2025-08-29 08:31:53
DirtyPipe漏洞分析与利用教学文档
1. 漏洞概述
CVE-2022-0847 (DirtyPipe) 是Linux内核中的一个漏洞,允许低权限用户覆盖任意只读文件的内容,包括setuid文件。该漏洞存在于Linux内核的管道(pipe)机制中,影响5.8及以上版本的Linux内核。
2. 漏洞背景知识
2.1 Linux管道机制
Linux管道是一种进程间通信机制,由pipe_inode_info结构体表示:
struct pipe_inode_info {
struct mutex mutex;
wait_queue_head_t rd_wait, wr_wait;
unsigned int head;
unsigned int tail;
unsigned int max_usage;
unsigned int ring_size;
unsigned int nr_accounted;
unsigned int readers;
unsigned int writers;
unsigned int files;
unsigned int r_counter;
unsigned int w_counter;
struct page *tmp_page;
struct pipe_buffer *bufs;
// ...其他字段省略
};
其中关键字段:
bufs: 指向pipe_buffer结构体数组的指针,默认16个元素head: 生产者指针tail: 消费者指针
2.2 pipe_buffer结构
struct pipe_buffer {
struct page *page; // 指向数据页
unsigned int offset, len; // 数据在页中的偏移和长度
const struct pipe_buf_operations *ops;
unsigned int flags; // 包含PIPE_BUF_FLAG_CAN_MERGE等重要标志
unsigned long private;
};
关键标志:
PIPE_BUF_FLAG_CAN_MERGE: 允许将新数据合并到现有缓冲区中
2.3 splice系统调用
splice()是一种零拷贝技术,允许在两个文件描述符之间移动数据而不需要在用户空间和内核空间之间复制数据。关键特性:
- 可以直接让pipe_buffer指向原始文件的page cache
- 省去了内存拷贝过程,提高效率
3. 漏洞成因分析
漏洞的根本原因在于splice系统调用在给pipe_buffer赋值时没有正确初始化flags字段,导致之前设置的PIPE_BUF_FLAG_CAN_MERGE标志未被清除。
具体流程:
-
正常pipe写入流程:
- 写入数据时会申请一个新page
- 把数据拷贝到page里
- 让pipe_buffer指向这个page
- 如果
PIPE_BUF_FLAG_CAN_MERGE被置位,数据会接着上一次的数据在同一个page中写入
-
splice零拷贝流程:
- 直接让pipe_buffer指向目标文件的page cache
- 但未清除之前设置的
PIPE_BUF_FLAG_CAN_MERGE标志
-
漏洞触发条件:
- 先让所有pipe_buffer的
PIPE_BUF_FLAG_CAN_MERGE被置位 - 调用splice让pipe_buffer指向目标文件的page cache
- 再向pipe写入数据会直接修改page cache里的内容
- 先让所有pipe_buffer的
4. 关键代码分析
4.1 pipe_write函数
static ssize_t pipe_write(struct kiocb *iocb, struct iov_iter *from) {
// ...省略...
if (chars && !was_empty) {
struct pipe_buffer *buf = &pipe->bufs[(head - 1) & mask];
if ((buf->flags & PIPE_BUF_FLAG_CAN_MERGE) &&
offset + chars <= PAGE_SIZE) {
// 如果CAN_MERGE被置位,会在现有page中追加数据
ret = copy_page_from_iter(buf->page, offset, chars, from);
buf->len += ret;
// ...省略...
}
}
// ...省略...
}
4.2 copy_page_to_iter_pipe函数
static size_t copy_page_to_iter_pipe(struct page *page, size_t offset,
size_t bytes, struct iov_iter *i) {
struct pipe_inode_info *pipe = i->pipe;
struct pipe_buffer *buf = &pipe->bufs[i_head & p_mask];
buf->ops = &page_cache_pipe_buf_ops;
get_page(page);
buf->page = page; // 直接指向page cache
buf->offset = offset;
buf->len = bytes;
// 注意:没有初始化flags字段!
// ...省略...
}
5. 漏洞利用细节
5.1 利用步骤
- 创建一个pipe但不指定O_DIRECT选项,使buffer的
PIPE_BUF_FLAG_CAN_MERGE被置位 - 填充pipe的所有缓冲区,确保所有buffer都设置了
PIPE_BUF_FLAG_CAN_MERGE - 清空pipe,但保留flags设置
- 使用splice将目标文件的page cache引入pipe缓冲区
- 向pipe写入数据,由于
PIPE_BUF_FLAG_CAN_MERGE仍被设置,数据会直接写入page cache
5.2 持久性分析
- 修改的是文件的page cache,不是直接修改磁盘内容
- 由于修改的page未被标记为dirty,writeback机制会忽略这些修改
- 重启系统或手动清除缓存(
echo 1 > /proc/sys/vm/drop_caches)会恢复原始文件内容 - sync命令无法将这种修改写回磁盘
6. 漏洞影响范围
- 影响Linux内核5.8及以上版本
- 允许低权限用户修改只读文件
- 可被用于提权,如修改/etc/passwd或setuid程序
7. 防御措施
- 升级到已修复的内核版本
- 临时缓解:
- 限制对pipe的访问权限
- 监控可疑的文件修改行为
8. 相关资源
- Linux内核源码:重点关注pipe.c和splice.c
- 官方补丁:检查对splice和pipe flags的处理
- 漏洞验证工具:可参考公开的PoC代码
9. 深入理解建议
- 阅读Linux内核关于pipe和splice的文档
- 分析page cache和writeback机制
- 研究其他类似的内核漏洞模式
- 实践编写简单的PoC验证漏洞
10. 总结
DirtyPipe漏洞展示了Linux内核中管道机制与零拷贝技术交互时的安全隐患。通过精心构造的pipe操作序列,攻击者可以绕过正常的文件权限检查,直接修改只读文件的内容。理解这一漏洞需要深入掌握Linux内核的内存管理、文件系统和进程通信机制。