Stack-Based Buffer Overflows

Ah! The classic stack-based buffer overflow. They've been around for eons (in computer time anyway), and they'll be around for years to come. Every time a stack-based buffer overflow is discovered in modern software, it's hard to know whether to laugh or cry ”either way, they're the staple diet of the average bug hunter or exploit writer. Many documents on how to exploit stack-based buffer overruns exist freely on the Internet and are included in earlier chapters in this book, so we won't repeat this information here.

A typical stack-based overflow exploit will overwrite the saved return address with an address that points to an instruction or block of code that will return the process's path of execution into the user -supplied buffer. We'll explore this concept further, but first we'll take a quick look at frame-based exception handlers. Then we'll look at overwriting exception registration structures stored on the stack and see how this lends itself to defeating the stack protection built into Windows 2003 Server.

Frame-Based Exception Handlers

An exception handler is a piece of code that deals with problems that arise when something goes wrong within a running process, such as an access violation or divide by 0 error. With frame-based exception handlers, the exception handler is associated with a particular procedure, and each procedure sets up a new stack frame. Information about a frame-based exception handler is stored in an EXCEPTION_REGISTRATION structure on the stack. This structure has two elements: the first is a pointer to the next EXCEPTION_REGISTRATION structure, and the second is a pointer to the actual exception handler. In this way, frame-based exception handlers are "connected" to each other as a linked list, as shown in Figure 8.1.


Figure 8.1: Frame exception handlers in action

Every thread in a Win32 process has at least one frame-based exception handler that is created on thread startup. The address of the first EXCEPTION_REGISTRATION structure can be found in each thread's Environment Block, at FS:[0] in assembly. When an exception occurs, this list is walked through until a suitable handler (one that can successfully dispatch with the exception) is found. Stack-based exception handling is set up using the try and except keywords under C. Remember, you can get most of the code contained in this book from the Shellcoder's Handbook Web site ( www. wiley .com/compbooks/koziol) , if you do not feel like copying it all down.

 #include <stdio.h> #include <windows.h>     dword MyExceptionHandler(void) {              printf("In exception handler....");              ExitProcess(1);              return 0; }     int main() {               try               {                    __asm {      // Cause an exception                             xor eax,eax                             call eax                     }                 }             __except(MyExceptionHandler())             {                     printf("oops...");              }              return 0; } 

Here we use try to execute a block of code, and in the event of an exception occurring, we direct the process to execute the MyExceptionHandler function. When EAX is set to 0x00000000 and then called, an exception will occur and the handler will be executed.

When overflowing a stack-based buffer, as well as overwriting the saved return address, many other variables may be overwritten as well, which can lead to complications when attempting to exploit the overrun . For example, assume that within a function a structure is referenced and that the EAX register points to the beginning of the structure. Then assume a variable within the function is an offset into this structure and is overwritten on the way to overwriting the saved return address. If this variable was moved into ESI, and an instruction such as

 mov dword ptr[eax+esi], edx 

is executed, then because we can't have a NULL in the overflow, we need to ensure that when we overflow this variable, we overflow it with a value such that EAX+ESI is writable. Otherwise our process will access violate ”we want to avoid this because if it does access violate then the exception handler(s) will be executed and more than likely the thread or process will be terminated , and we lose the chance to run our arbitrary code. Now, even if we fix this problem so that EAX + ESI is writable, we could have many other similar problems we'll need to fix before the vulnerable function returns. In some cases this fix may not even be possible. Currently, the method used to get around the problem is to overwrite the frame-based EXCEPTION_REGISTRATION structure so that we control the pointer to the exception handler. When the access violation occurs we gain control of the process' path of execution: we can set the address of the handler to a block of code that will get us back into our buffer.

In such a situation, with what do we overwrite the pointer to the handler so that we can execute any code we put into the buffer? The answer depends on the platform and service-pack level. On systems such as Windows 2000 and Windows XP without service packs , the EBX register points to the current EXCEPTION_REGISTRATION structure; that is, the one we've just overwritten. So, we would overwrite the pointer to the real exception handler with an address that executes a jmp ebx or call ebx instruction. This way, when the "handler" is executed we land in the EXCEPTION_REGISTRATION structure we've just overwritten. We then need to set what would be the pointer to the next EXCEPTION_REGISTRATION structure to code that does a short jmp over the address of where we found our jmp ebx instruction. When we overwrite the EXCEPTION_REGISTRATION structure then we would do so as depicted in Figure 8.2.

click to expand
Figure 8.2: Overwriting the EXCEPTION_REGISTRATION structure

With Windows 2003 Server and Windows XP Service Pack 1 or higher, however, this has changed. EBX no longer points to our EXCEPTION_REGISTRATION structure. In fact, all registers that used to point somewhere useful are XOR ed with themselves so they're all set to 0x00000000 before the handler is called. Microsoft probably made these changes because the Code Red worm used this mechanism to gain control of IIS Web servers. Here is the code that actually does this (from Windows XP Professional SP1).

 77F79B57   xor         eax,eax 77F79B59   xor         ebx,ebx 77F79B5B   xor         esi,esi 77F79B5D   xor         edi,edi 77F79B5F   push        dword ptr [esp+20h] 77F79B63   push        dword ptr [esp+20h] 77F79B67   push        dword ptr [esp+20h] 77F79B6B   push        dword ptr [esp+20h] 77F79B6F   push        dword ptr [esp+20h] 77F79B73   call        77F79B7E 77F79B78   pop         edi 77F79B79   pop         esi 77F79B7A   pop         ebx 77F79B7B   ret         14h 77F79B7E   push        ebp 77F79B7F   mov         ebp,esp 77F79B81   push        dword ptr [ebp+0Ch] 77F79B84   push        edx 77F79B85   push        dword ptr fs:[0] 77F79B8C   mov         dword ptr fs:[0],esp 77F79B93   push        dword ptr [ebp+14h] 77F79B96   push        dword ptr [ebp+10h] 77F79B99   push        dword ptr [ebp+0Ch] 77F79B9C   push        dword ptr [ebp+8] 77F79B9F   mov         ecx,dword ptr [ebp+18h] 77F79BA2   call        ecx 

Starting at address 0x77F79B57 , the EAX , EBX , ESI , and EDI registers are set to by XOR ing each register with itself. The next thing of note is the call instruction at 0x77F79B73; execution continues at address 0x77F79B7E . At address 0x77F79B9F the pointer to the exception handler is placed into the ECX register and then it is called.

Even with this change, an attacker can of course still gain control ”but without any register pointing to the user-supplied data anymore the attacker is forced to guess where it can be found. This reduces the chances of the exploit working successfully.

But is this really the case? If we examine the stack at the moment after the exception handler is called then we can see that:

 ESP          = Saved Return Address (0x77F79BA4) ESP + 4      = Pointer to type of exception (0xC0000005) ESP + 8      = Address of EXCEPTION_REGISTRATION structure 

Instead of overwriting the pointer to the exception handler with an address that contains a jmp ebx or call ebx , all we need to do is overwrite with an address that points to a block of code that executes the following:

 pop reg      pop reg      ret 

With each POP instruction the ESP increases by 4 , and so when the RET executes, ESP points to the user-supplied data. Remember that RET takes the address at the top of the stack ( ESP ) and returns the flow of execution there. Thus the attacker does not need any register to point to the buffer and does not need to guess its location.

Where can we find such a block of instructions? Well pretty much anywhere , at the end of every function. As the function tidies up after itself, we will find the block of instructions we need. Ironically, one of the best locations in which to find this block of instructions is in the code that clears all the registers at address 0x77F79B79 .

 77F79B79   pop         esi 77F79B7A   pop         ebx 77F79B7B   ret         14h 

The fact that the return is actually a ret 14 makes no difference. This simply adjusts the ESP register by adding 0x14 as opposed to 0x4 . These instructions bring us back to our EXCEPTION_REGISTRATION structure on the stack. Again, the pointer to the next EXCEPTION_REGISTRATION structure will need to be set to code that executes a short jump and two NOP s, neatly side stepping the address we've set that points to the pop , pop , ret block.

Every Win32 process and each thread within that process is given at least one frame-based handler, either at process or thread startup. So when it comes to exploiting buffer overflows on Windows 2003 Server, abusing frame-based handlers is one of the methods that can be used to defeat the new stack protection built into processes running on this platform.

Abusing Frame-Based Exception Handling on Windows 2003 Server

Abusing frame-based exception handling can be used as a generic method for bypassing the stack protection of Windows 2003. (See the section "Stack Protection and Windows 2003 Server" for more discussion on this). When an exception occurs under Windows 2003 Server, the handler set up to deal with the exception is first checked to see whether it is valid. In this way Microsoft attempts to prevent exploitation of stack-based buffer overflow vulnerabilities where frame-based handler information is overwritten; it is hoped that an attacker can no longer overwrite the pointer to the exception handler and have it called.

So what determines whether a handler is valid? The code of NTDLL.DLL 's KiUserExceptionDispatcher function does the actual checking. First, the code checks to see whether the pointer to the handler points to an address on the stack. This is done by referencing the Thread Environment Block's entry for the high and low stack addresses at FS:[4] and FS:[8] . If the handler falls within this range it will not be called. Thus, an attacker can no longer point the exception handler directly into their stack-based buffer. If the pointer to the handler is not equal to a stack address, the pointer is then checked against the list of loaded modules, including both the executable image and DLLs, to see whether it falls within the address range of one of these modules. If it does not, then somewhat bizarrely, the exception handler is considered safe and is called. If, however, the address does fall into the address range of a loaded module, it is then checked against a list of registered handlers.

A pointer to the image's PE header is then acquired by calling the RtlImage_NtHeader function. At this point a check is performed; if the byte 0x5F past the PE header ”the most significant byte of the DLL Characteristics field of the PE header ”is 0x04 , then this module is "not allowed." If the handler is in the address range of this module, it will not be called. The pointer to the PE header is then passed as a parameter to the RtlImageDirectoryEntryToData function. In this case, the directory of interest is the Load Configuration Directory. The RtlImageDirectoryEntryToData function returns the address and size of this directory. If a module has no Load Configuration Directory, then this function returns 0, no further checks are performed, and the handler is called. If, on the other hand, the module does have a Load Configuration Directory, the size is examined; if the size of this directory is 0 or less than 0x48 , no further checking is performed and the handler is called. Offset 0x40 bytes from the beginning of the Load Configuration Directory is a pointer that points to a table of Relative Virtual Addresses (RVAs) of registered handlers. If this pointer is NULL , no further checks are performed and the handler is called. Offset 0x44 bytes from the beginning of the Load Configuration Directory is the number of entries in this table. If the number of entries is 0, no further checks are performed and the handler is called. Providing that all checks have succeeded, the base address of the load module is subtracted from the address of the handler, which leaves us with the RVA of the handler. This RVA is then compared against the list of RVAs in the table of registered handlers. If a match is found, the handler is called; if it is not found, the handler is not called.

When it comes to exploiting stack-based buffer overflows on Windows 2003 Server, overwriting the pointer to the exception handler leaves us with several options:

  1. Abuse an existing handler that we can manipulate to get us back into our buffer.

  2. Find a block of code in an address not associated with a module that will get us back to our buffer.

  3. Find a block of code in the address space of a module that does not have a Load Configuration Directory.

Using the DCOM IRemoteActivation buffer overflow vulnerability, let's look at these options.

Abusing an Existing Handler

Address 0x77F45A34 points to a registered exception handler within NTDLL.DLL . If we examine the code of this handler, we can see that this handler can be abused to run code of our choosing. A pointer to our EXCEPTION_ REGISTRATION structure is located at EBP+0Ch .

 77F45A3F   mov ebx,dword ptr [ebp+0Ch]      ..      77F45A61   mov esi,dword ptr [ebx+0Ch]      77F45A64   mov edi,dword ptr [ebx+8]      ..      77F45A75   lea ecx,[esi+esi*2]      77F45A78   mov eax,dword ptr [edi+ecx*4+4]      ..      77F45A8F   call eax 

The pointer to our EXCEPTION_REGISTRATION structure is moved into EBX . The dword value pointed to 0x0C bytes past EBX is then moved into ESI . Because we've overflowed the EXCEPTION_REGISTRATION structure and beyond it, we control this dword. Consequently, we "own" ESI . Next, the dword value pointed to 0x08 bytes past EBX is moved into EDI. Again, we control this. The effective address of ESI + ESI * 2 (equivalent to ESI * 3 ) is then loaded into ECX . Because we own ESI we can guarantee the value that goes into ECX . Then the address pointed to by EDI , which we also own, added to ECX * 4 + 4 , is moved into EAX . EAX is then called. Because we completely control what goes into EDI and ECX (through ESI ) we can control what is moved into EAX , and therefore can direct the process to execute our code. The only difficulty is finding an address that holds a pointer to our code. We need to ensure that EDI+ECX*4+4 matches this address so that the pointer to our code is moved into EAX and then called.

The first time svchost is exploited, the location of the Thread Environment Block (TEB) and the location of the stack are always consistent. Needless to say, with a busy server, neither of these may be so predictable. Assuming stability, we could find a pointer to our EXCEPTION_REGISTRATION structure at TEB+0 ( 0x7FFDB000 ) and use this as our location where we can find a pointer to our code. But, as it happens, just before the exception handler is called, this pointer is updated and changed, so we cannot use this method. The EXCEPTION_REGISTRATION structure that TEB +0 does point to, however, at address 0x005CF3F0 , has a pointer to our EXCEPTION_REGISTRATION structure, and because the location of the stack is always consistent the first time the exploit is run, then we can use this. There's another pointer to our EXCEPTION_REGISTRATION structure at address 0x005CF3E4 . Assuming we'll use this latter address if we set 0x0C past our EXCEPTION_REGISTRATION structure to 0x40001554 (this will go into ESI ) and 0x08 bytes past it to 0x005BF3F0 (this will go into EDI ), then after all the multiplication and addition we're left with 0x005CF3E4 . The address pointed to by this is moved into EAX and called. On EAX being called we land in our EXCEPTION_REGISTRATION structure at what would be the pointer to the next EXCEPTION_REGISTRATION structure. If we put code in here that performs a short jmp 14 bytes from the current location then we jump over the junk we've needed to set to get execution to this point.

We've tested this on four machines running Windows 2003 Server, three of which were Enterprise Edition and the fourth a Standard Edition. All were successfully exploited. We do need to be certain, however, that we are running the exploit for the first time ”otherwise it's more than likely to fail. As a side note, this exception handler is probably supposed to deal with Vectored handlers and not frame-based handlers, which is why we can abuse it in this fashion.

Some of the other modules have the same exception handler and can also be used. Other registered exception handlers in the address space typically forward to __except_handler3 exported by msvcrt .dll or some other equivalent.

Find a block of code in an address not associated with a module that will get us back to our buffer

As with other versions of Windows, at ESP + 8 we can find a pointer to our EXCEPTION_REGISTRATION structure. So, if we could find a

 pop reg      pop reg      ret 

instruction block at an address that is not associated with any loaded module, this would do fine. In every process, at address 0x7FFC0AC5 on a computer running Windows 2003 Server Enterprise Edition, we can find such an instruction block. Because this address is not associated with any module, this "handler" would be considered safe to call under the current security checking and would be executed. There is a problem, however. Although I have a pop , pop , ret instruction block close to this address on my Windows 2003 Server Standard Edition running on a different computer ”it's not in the same location. Because we can't guarantee the location of this pop , pop , ret instruction block, using it is not an advisable option. Rather than just looking for a pop , pop , ret instruction block we could look for:

 call dword ptr[esp+8] 

or, alternatively:

 jmp dword ptr[esp+8] 

in the address space of the vulnerable process. As it happens, no such instruction at a suitable address exists, but one of the things about exception handling is that we can find many pointers to our EXCEPTION_REGISTRATION structure scattered all around ESP and EBP . Here are the locations in which we can find a pointer to our structure:

 esp+8   esp+14   esp+1C   esp+2C   esp+44   esp+50       ebp+0C   ebp+24   ebp+30   ebp-4   ebp-C   ebp-18 

We can use any of these with a call or jmp . If we examine the address space of svchost we find

 call dword ptr[ebp+0x30] 

at address 0x001B0B0B . At EBP + 30 we find a pointer to our EXCEPTION_REGISTRATION structure. This address is not associated with any module, and what's more, it seems that nearly every process running on Windows 2003 Server (as well as many processes on Windows XP) have the same bytes at this address; those that do not have this "instruction" at 0x001C0B0B . By overwriting the pointer to the exception handler with 0x001B0B0B we can get back into our buffer and execute arbitrary code. Checking 0x001B0B0B on four different Windows 2003 Servers, we find that they all have the "right bytes" that form the call dword ptr[ebp+0x30] instruction at this address. Therefore, using this as a technique for exploiting vulnerabilities on Windows 2003 Server seems like a fairly safe option.

Find a block of code in the address space of a module that does not have a Load Configuration Directory

The executable image itself ( svchost.exe ) does not have a Load Configuration Directory. svchost.exe would work if it weren't for a NULL pointer exception within the code of KiUserExceptionDispatcher() . The RtlImageNtHeader() function returns a pointer to the PE header of a given image but returns for svchost . However, in KiUserException_Dispatcher() the pointer is referenced without any checks to determine whether the pointer is NULL .

 call    RtlImageNtHeader test    byte ptr [eax+5Fh], 4 jnz     0x77F68A27 

As such, we access violate and it's all over; therefore, we can't use any code within svchost.exe. comres.dll has no Load Configuration Directory, but because the DLL Characteristics of the PE header is 0x0400 , we fail the test after the call to RtlImageNtHeader and are jumped to 0x77F68A27 ”away from the code that will execute our handler. In fact, if you go through all the modules in the address space, none will do the trick. Most have a Load Configuration Directory with registered handlers and those that don't fail this same test. So, in this case, this option is not usable.

Since we can, most of the time, cause an exception by attempting to write past the end of the stack, when we overflow the buffer we can use this as a generic method for bypassing the stack protection of Windows 2003 Server. Although this information is now correct, Windows 2003 Server is a new operating system, and what's more, Microsoft is committed to making a more secure OS and rendering it as impervious to attacks as possible. There is no doubt that the weaknesses we are currently exploiting will be tightened up, if not altogether removed as part of a service pack. When this happens (and I'm sure it will), you'll need to dust off that debugger and disassembler and devise new techniques. Recommendations to Microsoft, for what it's worth, would be to only execute handlers that have been registered and ensure that those registered handlers cannot be abused by an attacker as we have done here.

A Final Note about Frame-Based Handler Overwrites

When a vulnerability spans multiple operating systems ”such as the DCOM IRemoteActivation buffer overflow discovered by the Polish security research group , The Last Stages of Delirium ”a good way to improve the portability of the exploit is to attack the exception handler. This is because the offset from the beginning of the buffer of the location of the EXCEPTION_REGISTRATION structure may vary. Indeed, with the DCOM issue, on Windows 2003 Server this structure could be found 1412 bytes from the beginning of the buffer, 1472 bytes from the beginning of the buffer on Windows XP, and 1540 bytes from the beginning of the buffer on Windows 2000. This variation makes possible writing a single exploit that will cater to all operating systems. All we do is embed, at the right locations, a pseudo handler that will work for the operating system in question.



The Shellcoder's Handbook. Discovering and Exploiting Security
Hacking Ubuntu: Serious Hacks Mods and Customizations (ExtremeTech)
ISBN: N/A
EAN: 2147483647
Year: 2003
Pages: 198
Authors: Neal Krawetz

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net