Uninformed: Informative Information for the Uninformed

Vol 8» 2007.Sep

Analyzing Madwifi

The madwifi source code shows that most of the crashes occur while iterating over the scan cache stored in a variable known as scan_state. To add an entry to the scan cache a function called sta_add parses management frames into a structure called sta_entry.

struct sta_entry {
  struct ieee80211_scan_entry base;
  TAILQ_ENTRY(sta_entry) se_list;
  LIST_ENTRY(sta_entry) se_hash;
  u_int8_t        se_fails;               /* failure to associate count */
  u_int8_t        se_seen;                /* seen during current scan */
  u_int8_t        se_notseen;             /* not seen in previous scan */
  u_int32_t se_avgrssi;           /* LPF rssi state */
  unsigned long se_lastupdate;    /* time of last update */
  unsigned long se_lastfail;      /* time of last failure */
  unsigned long se_lastassoc;     /* time of last association */
  u_int se_scangen;               /* iterator scan gen# */

The sta_add function is too long to print here but can be found in the net80211/ieee80211_scan_sta.c source file. In this function, an assignment is performed that sets the copy destination for all the beacon data into the base variable from sta_entry.

ise = &se->base;

The ieee80211_scan_entry structure is defined as the follows. Note that the Extended Rate buffer is defined as an array with a size of IEEE80211_RATE_MAXSIZE + 2. This is much like other buffer overflows where programmers reserve fixed sized buffers in memory to hold variable length data from packets.

 * Scan cache entry format used when exporting data from a policy
 * module; this data may be represented some other way internally.
struct ieee80211_scan_entry {
  u_int8_t se_macaddr[IEEE80211_ADDR_LEN];
  u_int8_t se_bssid[IEEE80211_ADDR_LEN];
  u_int8_t se_ssid[2 + IEEE80211_NWID_LEN];
  u_int8_t se_rates[2 + IEEE80211_RATE_MAXSIZE];
  u_int8_t se_xrates[2 + IEEE80211_RATE_MAXSIZE];
  u_int32_t se_rstamp;            /* recv timestamp */
  union {
    u_int8_t data[8];
    u_int64_t tsf;
  } se_tstamp;                    /* from last rcv'd beacon */
  u_int16_t se_intval;            /* beacon interval (host byte order */
  u_int16_t se_capinfo;           /* capabilities (host byte order) */
  struct ieee80211_channel *se_chan;/* channel where sta found */
  u_int16_t se_timoff;            /* byte offset to TIM ie */
  u_int16_t se_fhdwell;           /* FH only (host byte order) */
  u_int8_t se_fhindex;            /* FH only */
  u_int8_t se_erp;                        /* ERP from beacon/probe resp*/
  int8_t se_rssi;                 /* avg'd recv ssi */
  u_int8_t se_dtimperiod;         /* DTIM period */
  u_int8_t *se_wpa_ie;            /* captured WPA ie */
  u_int8_t *se_rsn_ie;            /* captured RSN ie */
  u_int8_t *se_wme_ie;            /* captured WME ie */
  u_int8_t *se_ath_ie;            /* captured Atheros ie */
  u_int se_age;                   /* age of entry (0 on create) */

IEEE80211_RATE_MAX_SIZE is defined in ieee80211.h as the following:

#define    IEEE80211_RATE_MAXSIZE  15    /* max rates we'll handle */

The author was initially puzzled because all research to this point showed that the Extended Rate buffer was the culprit but the madwifi source code had a check for a maximum length before the copy happened. At this point, the corruption must have occurred before the sta_add function or the length check did not work as expected. To figure out what might be missing, the author set a break point at the beginning of sta_add and walked through the code. Single-stepping showed that the memcpy was called at 0x008f3188. This was verified by looking at the size and the source being passed to the memcpy. Since the Extended Rate element in a script-generated packet it is noticeably larger than in a typical packet, a conditional breakpoint can be set when the size argument is pushed to the stack for the memcpy. The following debugger output shows how the system behaves when this breakpoint is set:

(gdb) break *0x008f3188 if $eax > 100
Breakpoint 2 at 0x8f3188
(gdb) c

Breakpoint 2, 0x008f3188 in sta_add ()
2: x/i $eip  0x8f3188 <sta_add+857>:    mov    DWORD PTR [esp+8],eax
(gdb) stepi
0x008f318c in sta_add ()
2: x/i $eip  0x8f318c <sta_add+861>:    mov    DWORD PTR [esp+4],edx
0x008f3190 in sta_add ()
2: x/i $eip  0x8f3190 <sta_add+865>:    lea    eax,[esi+63]
0x008f3193 in sta_add ()
2: x/i $eip  0x8f3193 <sta_add+868>:    mov    DWORD PTR [esp],eax
0x008f3196 in sta_add ()
2: x/i $eip  0x8f3196 <sta_add+871>:    call   0x1933c8 <memcpy>
(gdb) x/20x $esp
0xc82badc:      0x03aeb643      0x1e36a046      0x000000f2      0x00000080
0xc82baec:      0x0c82bb24      0x0c82bb04      0x0c82bc8c      0x03800004
0xc82bafc:      0x0393d72c      0x0393d704      0x1e36a00a      0x0380246c
0xc82bb0c:      0x008f2e35      0x00000014      0x00000302      0x0c82bc8c
0xc82bb1c:      0x00000080      0x1e36a138      0x0c82bb84      0x008e8cb9
(gdb) x/20x 0x1e36a046
0x1e36a046:     0x4141f032      0x41414141      0x41414141      0x41414141
0x1e36a056:     0x41414141      0x41414141      0x41414141      0x41414141
0x1e36a066:     0x41414141      0x41414141      0x41414141      0x41414141
0x1e36a076:     0x41414141      0x41414141      0x41414141      0x41414141
0x1e36a086:     0x41414141      0x41414141      0x41414141      0x41414141

Based on the location of the memcpy call, it is necessary to calculate the relative address within the binary which can be accomplished by doing 0x8f3196 0x8e7000 0x1000 = 0xB196. The code found within the driver shows that although there is a length check in the open source driver, it's not actually present in the OS X binary driver.

__text:0000B177                 mov     ecx, [ebp+scanparam]
__text:0000B17A                 mov     edx, [ecx+28h]
__text:0000B17D                 test    edx, edx
__text:0000B17F                 jz      short loc_B19D
__text:0000B181                 movzx   eax, byte ptr [edx+1]
__text:0000B185                 add     eax, 2
__text:0000B188                 mov     [esp+48h+var_40], eax
__text:0000B18C                 mov     [esp+48h+var_44], edx
__text:0000B190                 lea     eax, [esi+63]
__text:0000B193                 mov     [esp+48h+ic], eax
__text:0000B196                 call    near ptr _memcpy ; xrate memcpy

In this example, the copy size is 0xf2 and the ``Extended Rate'' buffer is being copied. Verifying that there is actually no length check means that adjacent data found within a ieee80211_scan_entry is being corrupted, such as another sta_entry structure.

This is where the first of two serious problems manifests itself. It is possible to overwrite fields in a structure, but not typical control structures like stack or heap frames that are typically used to gain code execution. This makes direct code execution more difficult.