0x01简介

实验所属系列:网络安全事件

相关课程及专业: Linux基础

预备知识

概述

脏牛(Dirty COW,编号:CVE-2016-5195)是2016年10月18日被曝出的存在于Linux内核中的一款0day漏洞。因为此漏洞是在Linux内核的内存子系统在处理写时拷贝(Copy-on-Write)时发生的,而又给Linux内核的使用带来烦恼,所以将其命名为“Dirty COW”。

这个漏洞10月18号由Phil Oester提交,被Linux的创始人Linus亲自修复,并且在当天将Linux内核补丁提交至:https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/

10月20号,漏洞的发现者Phil Oester将漏洞的部分细节提交到github上,Phil Oester表示,Dirty COW漏洞是一个古老的漏洞,发现于2007年,并一直存在于后续的Linux版本当中。

漏洞编号:CVE-2016-5195

漏洞类型:内核竞态条件漏洞

漏洞危害:本地提权

影响范围:Linux kernel>2.6.22 (released in 2007)

需了解以下技术:

写时拷贝(Copy on Write,COW)

竞态条件

页式内存管理

缺页中断处理

Dirty COW网站:

https://dirtycow.ninja/

Poc项目地址:

PoCs · dirtycow/dirtycow.github.io Wiki · GitHub https://github.com/dirtycow/dirtycow.github.io/wiki/PoCs

参考文档:

CVE-2016-5195 Dirtycow: Linux内核提权漏洞分析:http://bobao.360.cn/learning/detail/3132.html

https://github.com/dirtycow/dirtycow.github.io/wiki/VulnerabilityDetails

https://access.redhat.com/security/cve/CVE-2016-5195

Protect from CVE-2016-5195 (DirtyCow) on Centos 7/RHEL7/cPanel/CloudLinux:

https://gryzli.info/2016/10/21/protect-cve-2016-5195-dirtycow-centos-7rhel7cpanelcloudlinux/

实验目的

1)漏洞检测复现

2)漏洞形成原因

3)漏洞修复建议

实验环境

操作系统: Centos 7 1511

IP随机

0x02实验步骤

实验步骤一

漏洞检测复现

1、编译poc 文件

切换到cow用户

当前系统内核版本和系统版本:

我们根据代码中的说明进行编译和测试:

输入如下指令进行编译:gcc -lpthread dirtycOw.c -o dirtycOw

没有报错证明编译成功。

测试:

新建一个只有读权限的文件,利用poc对文件进行越权写入

接下来就是见证奇迹的时刻:执行时间大约需要1分钟左右

查看文件:

我们发现本来只有只读权限的文件,里面的内容被改变了。

如果你得到的是如下结果,说明你的系统没有受到这个漏洞影响

实验步骤二

漏洞形成原因

1、写时拷贝技术

在Linux程序中,fork()会产生一个和父进程完全相同的子进程,但子进程在此后多会exec系统调用,出于效率考虑,linux中引入了“写时复制“技术,也就是只有进程空间的各段的内容要发生变化时,才会将父进程的内容复制一份给子进程。

Linux内核的内存子系统在处理写时拷贝(Copy-on-Write)时存在条件竞争漏洞,导致可以破坏私有只读内存映射。一个低权限的本地用户能够利用此漏洞获取其他只读内存映射的写权限,有可能进一步导致提权漏洞。

竞态条件(race condition)是指设备或系统出现不恰当的执行时序,而得到不正确的结果。

2、触发原理:

调用write系统调用向/proc/self/mem文件中写入数据时,进入内核态后内核会调用get_user_pages函数获取要写入内存地址。get_user_pages会调用follow_page_mask来获取这块内存的页表项,并同时要求页表项所指向的内存映射具有可写的权限。

第一次获取内存的页表项会因为缺页而失败。get_user_page调用faultin_page进行缺页处理后第二次调用follow_page_mask获取这块内存的页表项,如果需要获取的页表项指向的是一个只读的映射,那第二次获取也会失败。这时候get_user_pages函数会第三次调用follow_page_mask来获取该内存的页表项,并且不再要求页表项所指向的内存映射具有可写的权限,这时是可以成功获取的,获取成功后内核会对这个只读的内存进行强制的写入操作。

这个实现是没有问题的,因为本来写入/proc/self/mem就是一个无视映射权限的强行写入,就算是文件映射到虚拟内存中,也不会出现越权写:

如果写入的虚拟内存是一个VM_PRIVATE的映射,那在缺页的时候内核就会执行COW操作产生一个副本来进行写入,写入的内容是不会同步到文件中的

如果写入的虚拟内存是一个VM_SHARE的映射,那mmap能够映射成功的充要条件就是进程拥有对该文件的写权限,这样写入的内容同步到文件中也不算越权了。

但是,在上述流程中,如果第二次获取页表项失败之后,另一个线程调用madvice(addr,addrlen, MADV_DONTNEED),其中addr~addr+addrlen是一个只读文件的VM_PRIVATE的只读内存映射,那该映射的页表项会被置空。这时如果get_user_pages函数第三次调用follow_page_mask来获取该内存的页表项。由于这次调用不再要求该内存映射具有写权限,所以在缺页处理的时候内核也不再会执行COW操作产生一个副本以供写入。所以缺页处理完成后后第四次调用follow_page_mask获取这块内存的页表项的时候,不仅可以成功获取,而且获取之后强制的写入的内容也会同步到映射的只读文件中。从而导致了只读文件的越权写。

实验步骤三

漏洞修复建议

Redhat检查漏洞脚本:

wget https://access.redhat.com/sites/default/files/rh-cve-2016-5195_2.sh

bash rh-cve-2016-5195_2.sh

1、升级内核修复

CentOS 6/7 系列操作系统:

1) 检查是否有内核升级包:yum check-update |grep kernel

2) 升级内核:yum update kernel

3) 然后重启系统

4) 查看版本:uname -a

Ubuntu 系列操作系统:

Ubuntu 系统必须在/etc/apt/sources.list中开启–security后缀的源地址才能收到更新。

untu 12.04 LTS (precise)

deb http://security.ubuntu.com/ubuntu/ precise-security main

Ubuntu 14.04 LTS (trusty)

deb http://security.ubuntu.com/ubuntu/ trusty-security main

更新包列表:

sudo apt-get update

升级软件包:

sudo apt-get upgrade

重启系统

2、编译修复

可以通过 git 分支获取修复代码,自行编译进行修复,但存在一定的风险。https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=19be0eaffa3ac7d8eb6784ad9bdbc7d67ed8e619

3、免重启临时修复

由于该漏洞位于 Linux 内核,发行版官方的内核更新只有在重启后才能生效。如果线上业务不能中断,可以采用基于 systemtap 的热修补方法

systemtap 是一款内核调试、性能分析工具,其原理是插入新的内核模块并根据需要修改逻辑,实现功能。该工具同样可以被用于安全应急响应。

1) 安装systemtap, kernel-devel and kernel-debuginfo 软件包(实验环境已经安装好)

切换回root用户:

2) 创建stap文件

代码如下:

probe kernel.function(“mem_write”).call ? {

$count = 0

}

probe syscall.ptrace { // includes compat ptrace as well

$request = 0xfff

}

probe begin {

printk(0, “CVE-2016-5195 mitigation loaded”)

}

probe end {

printk(0, “CVE-2016-5195 mitigation unloaded”)

作用是阻止写入 /proc/self/mem

3) 生成stap模块

stap -p4 -g /root/CVE-2016-5195.stap

4) 安装这个模块

模块路径为上一步生成的文件路径

staprun -L /root/.systemtap/cache/06/stap_0613bdc7535b2c7fcbf11d33c024acbb_65596.ko

安装模块时可能会出现bash断开,出现”mitigation loaded”后按回车即可

模块已经被安装了。

PS:这个方法只能临时生效,重启之后需要重新加载,所以还是建议使用update进行修复。

切换到cow用户,重新验证一次POC。

已经被修复了。

PS:还可以根据长亭科技

http://mp.weixin.qq.com/s?__biz=MzIwNDA2NDk5OQ==&mid=2651370571&idx=1&sn=68acf07ca2683a9c98fa52e900d97db3&chksm=8d39c5c3ba4e4cd58c21d0a21ca337ded2132625987e174d286f8d175034267bd09807ea9a11&scene=4#wechat_redirect)提供另外一种办法来修复。

因为阻止写入 /proc/self/mem有较为明显的副作用:无法调试程序,还可能导致 RHEL/CentOS 6 和 Ubuntu 14.04 上使用的 upstart 服务管理机制不能正常工作。

所以可以将stap文件修改成如下:

然后再生成模块进行加载即可。

还可以根据safe3提供的模块进行安装:

https://github.com/Safe3/clean-cow

PS:再次提醒,这个方法只能临时生效,重启之后需要重新加载,所以还是建议使用update进行修复。