Informative Information for the Uninformed | ||||||||||||||
|
||||||||||||||
Analyzing MadwifiThe 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 Continuing. 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 (gdb) 0x008f3190 in sta_add () 2: x/i $eip 0x8f3190 <sta_add+865>: lea eax,[esi+63] (gdb) 0x008f3193 in sta_add () 2: x/i $eip 0x8f3193 <sta_add+868>: mov DWORD PTR [esp],eax (gdb) 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 (gdb) 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.
|