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 关键配置步骤

  1. 设置BusyBox SUID位
sudo chown root:root ./bin/busybox
sudo chmod u+s ./bin/busybox
  1. 编译POC
gcc ./dirty.c -static -o dirty -lpthread -lcrypt
  1. 准备文件系统
find . -print0 | cpio --null -ov --format=newc > ../rootfs.cpio
  1. 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 缺页中断处理流程

  1. 访问不存在或只读的页时触发缺页中断
  2. 内核检查线性地址是否合法
  3. 对于合法地址,分配物理页并建立映射
  4. 对于COW情况,创建页的副本并建立新映射

3.3 关键系统调用

  • mmap():创建内存映射
    • MAP_PRIVATE标志会触发COW机制
  • madvise():向内核提供内存使用建议
    • MADV_DONTNEED:表示近期不会访问该内存,内核可释放相关资源

4. 漏洞原理分析

4.1 漏洞触发条件

  1. 进程通过mmap创建文件的私有映射(MAP_PRIVATE)
  2. 尝试写入该映射区域,触发COW机制
  3. 另一个线程调用madvise(MADV_DONTNEED)解除映射
  4. 竞争条件导致可以绕过COW,直接修改原始只读页

4.2 详细调用流程

  1. 第一次缺页处理

    • get_user_pages() -> follow_page_mask()失败
    • faultin_page() -> handle_mm_fault() -> do_cow_fault()
    • 创建COW页,设置pte为dirty/read-only
  2. 第二次缺页处理

    • do_wp_page()检查到已COW,复用页面
    • 返回VM_FAULT_WRITE,清除FOLL_WRITE标志
  3. 竞争窗口

    • 其他线程调用madvise(MADV_DONTNEED)清空页表项
  4. 第三次缺页处理

    • 因页表项为空再次触发缺页
    • 由于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 利用步骤

  1. 打开目标文件(如/etc/passwd)
  2. 创建私有内存映射(mmap with MAP_PRIVATE)
  3. fork子进程:
    • 父进程:不断尝试写入映射区域(ptrace)
    • 子进程:循环调用madvise(MADV_DONTNEED)
  4. 竞争成功时,修改会写入原始文件而非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. 补丁分析

官方补丁主要修改:

  1. 引入FOLL_COW标志专门标记COW情况
  2. 不再清除FOLL_WRITE标志
  3. follow_page_pte()中增加对COW页的特殊处理

补丁代码片段:

+       if ((flags & FOLL_WRITE) && !can_follow_write_pte(pte, flags)) {
+               pte_unmap_unlock(ptep, ptl);
+               return NULL;
+       }

7. 漏洞修复与防御

  1. 及时更新内核版本
  2. 对于不能立即更新的系统:
    • 限制/proc/self/mem的访问
    • 使用内核模块监控可疑的madvise调用
  3. 启用SELinux等强制访问控制机制

8. 总结

Dirty COW漏洞展示了Linux内核中:

  • COW机制的实现缺陷
  • 内存管理子系统中的竞争条件
  • 权限检查不完整带来的安全问题

该漏洞利用难度中等,但影响广泛,是研究Linux内核安全机制的经典案例。

9. 参考资源

  1. Linux内核源码(4.4.0版本)
  2. 《深入理解Linux内核》内存管理章节
  3. 官方漏洞公告和补丁
  4. Dirty COW漏洞的多种利用技术分析

附录:完整测试步骤

  1. 准备漏洞环境(QEMU+易受攻击内核)
  2. 编译POC代码
  3. 在虚拟机中执行POC
  4. 验证/etc/passwd文件是否被修改
  5. 尝试使用新创建的用户切换root权限

注意:此漏洞仅可用于合法安全研究和授权测试,未经授权攻击他人系统是违法行为。

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位 : 编译POC : 准备文件系统 : QEMU启动命令 : 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() 中的这段逻辑: 当COW完成后( VM_FAULT_WRITE ),清除 FOLL_WRITE 标志,使得后续操作可以绕过写权限检查。 5. 漏洞利用(POC分析) 5.1 利用步骤 打开目标文件(如/etc/passwd) 创建私有内存映射( mmap with MAP_PRIVATE ) fork子进程: 父进程:不断尝试写入映射区域( ptrace ) 子进程:循环调用 madvise(MADV_DONTNEED) 竞争成功时,修改会写入原始文件而非COW副本 5.2 关键代码 6. 补丁分析 官方补丁主要修改: 引入 FOLL_COW 标志专门标记COW情况 不再清除 FOLL_WRITE 标志 在 follow_page_pte() 中增加对COW页的特殊处理 补丁代码片段: 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权限 注意:此漏洞仅可用于合法安全研究和授权测试,未经授权攻击他人系统是违法行为。