Video Screencast Help
Symantec to Separate Into Two Focused, Industry-Leading Technology Companies. Learn more.

Blind Buffer Overflows In ISAPI Extensions

Created: 25 Jan 2005 • Updated: 02 Nov 2010
Language Translations
Anonymous's picture
0 0 Votes
Login to vote

by Isaac Dawson

Introduction

The topic of blind buffer overflow exploitation or exploitation without access to the binary or system running the application has been rarely discussed. In April 2000 a question was posed to security lists asking the feasibility of remotely exploiting an unknown buffer overflow. Although the question was more OS/Architecture centric, a response by Eeye's Marc Maiffret [ref 1] outlined how one would attempt such an attack against the Microsoft Windows Operating System. Other known attacks such as brute forcing of offsets once the length has been identified has been well documented and this technique is utilized. It is the author's belief that these attacks do happen and will most likely increase the more software products are secured against easily exploitable flaws and as attackers shift focus to exploiting proprietary solutions.

This type of attack has a number of security ramifications which should be discussed. First the attacker does not need access to the binary to identify and exploit a security flaw. In most occasions a flaw is identified by having access to the binary and doing analysis, or debugging the active process on a machine they control. This is important because a number of organizations rely on the fact that their ISAPI extensions are not only closed source, but also not available to the public. Also it is an unknown attack, depending on how the attacker disguises their actions, chances of an Intrusion Detection System (IDS) alerting the organization are much smaller than if an attacker were to choose a known vulnerability.

In this paper we will use different ISAPI extension on a Microsoft Windows 2000, Internet Information Server (IIS) 5.0 web server. A number of different ISAPI extensions were created, each with a different type of stack-based overflow vulnerability to act as demonstrative proprietary applications as seen in the wild. The following examples are overflows using strcpy(), sprintf(), and strcat(). A second set of extensions had also been built with the Microsoft Visual Studio .NET stack protection enabled (/GS option) [ref 2]. The author will demonstrate how to bypass these protection mechanisms and execute arbitrary code completely blind.

The reasons for choosing an ISAPI extension were two fold, the first being that custom web based applications which utilize ISAPI extensions and filters are becoming more and more popular, as well as old extensions still being used today are rarely reviewed for security flaws. These extensions are often exposed to either the public Internet or corporate Extranet's. The second was that in order to exploit these vulnerabilities in a completely blind fashion we need to be able to repeat our attacks a number of times.

Vulnerability requirements

There are very few necessary requirements of the vulnerability for exploitation to be successful. If any type of filtering is being done on our input, output from the extension would be required to display which bytes are denied or modified.

The second and third requirements are that a register must point to our payload and have enough room for single or multi-stage shell code. The shell code utilized for this demonstration is connect-back code. If a firewall were put in place, outbound connections would need to be allowed to our receiving host. If one were to replace the connect-back code with the find socket method of gaining a shell this would no longer be a requirement.   

Configuration

First we need to examine IIS and how ISAPI extensions are handled. IIS 5.0 has three methods of operation for ISAPI extensions. The first is Low mode (In Process), which is handled internally by inetinfo.exe. The second is Medium Pooled (Out-of-Process), which handles DLLs externally by the dllhost.exe process. Medium Pooled contains all DLLs pooled into one instance of dllhost.exe, such as generic ISAPI extensions like asp.dll, printer.dll and so forth. The third method, High (Isolated), uses dllhost.exe but it is handled in a separate dllhost.exe process from all other extensions. Medium Pooled is the default method of operation for ISAPI extensions. Unfortunately if the extension is configured for running in Low mode, the exploitation attempt will cause inetinfo.exe to generate an unhandled exception and terminate in the case that the default exception handler is unable to handle the violation. In this case inetinfo.exe will be restarted 50 times which, if one could determine rather quickly the correct return address and register to use could allow for exploitation to succeed. Since Medium Pooled is the default and thus more common usage we will examine the attack in this configuration first.

 

Figure 1.  Microsoft IIS 5 Process Separation.
Figure 1. Microsoft IIS 5 Process Separation.

When running ISAPI extensions in out-of-process mode, IIS first verifies if the extension has been loaded. If it has not been loaded, dllhost.exe is called to load the DLL.

Vulnerability reconnaissance: identification

To successfully attack the application we must be able to determine where the buffer becomes overflowed. This can be achieved in a number of ways. In some cases detailed error messages will be returned by the server. Other times the connection will simply be dropped or no data will be returned after the server header responds with a 500 Server Error. One should be aware that the connection might drop for other reasons as well, such as a function which checks the length of the request and calls an exit procedure if the length check fails. Other times this application behavior will signify that an overflow has occurred.

In the example of an ISAPI extension, detailed error information is returned to the client. If we are able to overflow the application to a point where dllhost.exe crashes (usually due to overwriting the Structured Exception Handler [ref 3]) our next request will result in ?Error in Remote Procedure Call' message being returned by the server. This can happen if we overwrite the Structured Exception Handler and the overflow causes an Access Violation. In this scenario, dllhost.exe will crash and be re-spawned. During this re-spawn if we request the extension and dllhost.exe has not had time to restart, we are given the remote procedure call error message.

When the compiler flag for Stack Protection is set we are presented with another interesting way of remotely identifying the overflow. If our request does not cause dllhost.exe to fail (for instance by overwriting the Structured Exception Handler), the thread will be paused and not respond with any data. Since our application is blocking on the recv() call our program will pause until the "Error Buffer Overflow Detected" message box has been closed on the remote host and the application is ultimately terminated. It should be noted that although the "Buffer Overflow Detected" message box is displayed, the application will continue to create new threads and process our requests.

Unless the developer has created a custom handler for the stack overflow protection, we can create new requests and continue to attempt to determine the length of the buffer we are overflowing without the process crashing. This scenario is the same for the isolated mode for ISAPI extensions. When attacking In-process (inetinfo.exe) we first identify the buffer overflow length, and then we continue to increase our string length by one until inetinfo.exe crashes. Upon crashing the ISAPI extension the entire web service is terminated instead of just the thread handling ISAPI extension due to all handling being done directly by the inetinfo.exe process. It should also be noted that if we are running in inetinfo.exe upon successful exploitation we will gain system level privileges. Since we are overwriting the Structured Exception Handler, and causing a violation, execution attempts to jump to the corrupted exception handler and fails. The default exception handler then terminates the inetinfo.exe process since no other handlers could repair the application.

Vulnerability reconnaissance: determining length

Determining the length of the buffer that we are overflowing is a relatively easy and quick process. Using the identifiable change in application behavior we can send strings increasing in length by one until we have an exact location of where the fault/overwrite occurs. There are a number of unanswered questions however using this methodology:

  • What behavior change are we actually causing?
  • Is it overwriting a saved return address on the stack?
  • Is it overwriting an exception handler?

Unfortunately there is no direct way to determine what is causing the data to not be returned from our request. We can only assume it is a buffer overflow if the server responds with no data. We are also unable to distinguish if we are able to overwrite the return address for the function we are overflowing, until we attempt to actually overwrite the return address with a valid jmp or call address which we can use to change execution flow.

Overwriting the exception handler is something that we can remotely identify. In Low mode the inetinfo.exe process will crash and be restarted by iisreset.exe. In Medium Pooled and Isolated a server error message will be displayed stating that there was in error in the remote procedure call.

If a buffer overflow were found, the following steps would allow us to determine at what length the overflow was happening and whether or not stack protection was compiled into the application.

    1. Call the extension with a known good value which we can use to determine normal application behavior.
    2. Increase our string by one each request until a change in behavior is identified, such as the resultant string no longer being displayed. This is common when an overflow has occurred within an ISAPI extension in all modes of operation.
    3. Once the buffer overflow has been identified, we can determine if stack protection is in use due to the current threads activity being suspended by the buffer overflow detection handler. This suspension of return data signifies that we've not only discovered a buffer overflow, but that stack protection is in fact in use. If this suspension does not occur, attempt to exploit from the length at which no data was returned and skip step four.
    4. If stack protection is in use, continue increasing our string length by one until another behavioral change occurs. In the case of overwriting the SEH, dllhost.exe will be unable to handle the exception and dllhost.exe or inetinfo.exe will crash and re-spawn. At this point exploitation should be attempted using this length.

Once this relative length has been determined we can begin exploitation attempts.

Exploitation

Now armed with knowledge about the length of the possible overflow, whether or not stack protection is in use, and knowing that if the fault occurs our process will be restarted, we can begin our attack. It does not really matter which mode the extension is running in, the code does not need to be changed to exploit any of the modes. The method the author used is a matrix of arrays of service pack independent jmp / call addresses for each register which are known to be loaded by dllhost.exe and inetinfo.exe. In this case addresses from resutils.dll, version.dll, clusapi.dll, and txfaux.dll can be used. By determining which DLLs are mapped by both the dllhost.exe and inetinfo.exe processes we can search for service pack independent jmp/call location addresses.

It is also possible to increase the number of DLLs loaded by requesting different file types, for instance calling an ASP page will load the asp.dll into dllhost.exe, giving us more potential jmp / call address locations. The type of mode the extensions are running in will determine which DLLs addresses are accessible. Although loading other DLLs is possible, only DLLs which exist in both processes should be used for reliability purposes. In process mode has the most DLLs loaded. If using Isolated mode, we can only use the aforementioned DLLs. Since asp.dll runs in Medium Pooled, we can not use addresses from this DLL if we are attacking an ISAPI extension which is running in Isolated mode. Alternatively the attacker can employ more advanced methods of service pack / patch level fingerprinting, if available, to reduce the amount of addresses required for the return address or SEH overwrite.

When first attempting to write the exploit framework for unknown vulnerabilities, a few problems were identified in the theory of blind exploitation. The shell code that was first used was so large it was not only overwriting return address but also the SEH and causing an Access Violation (AV). When this AV occurred and the corrupted SEH was called, dllhost.exe would terminate and be re-spawned. To get around this problem a Multi-Stage shell code method should be used.

The exploit code was arranged to work in all methods of exploitation; it is IIS Mode independent. The same code can be used to exploit all modes of IIS (Low/Medium/High). Code with stack protection can be exploited exactly the same way as a regular stack overflow, the only difference is that the register which points to our string is changed. In our vulnerable functions all stack overflows were exploited by calling or jumping to an address which contains a jmp/call esp.

In stack protection bypass mode our code used an address which contains a jmp/call ebx. Since our exploit code was also designed with a brute force method, all registers could be attempted so this information would not need to be known to an attacker. The suggestion is to start with the esp register for stack overflows, then move to all registers if the esp register fails to execute code. It is the same for the SEH exploitation except one would start with the ebx register then move to all registers if that attempt failed.

 

Figure 2. Stack layout before return address overwrite.
Figure 2. Stack layout before return address overwrite.

The following steps are required for exploiting a stack based buffer overflow without stack protection.

  1. Make multiple requests to the web server with each request containing secondary stage shell code. This shell code would stay in memory until other threads used the same address for writing data to it.
  2. Once the second stage of shell code is loaded in memory, we begin to brute force the identified overwrite length with valid jmp/call addresses. For each length every jmp / call address is changed in our exploit buffer. In all of our test examples the esp register was used for our jump due to it pointing to our string after the return address overwrite -- although a number of times the ebx register worked as well.
  3. Once the return address overwrite is successful with a valid address and our first stage shell code is called, our code searches memory for a specific "egg" which marks our second stage shell code.
  4. If the egg is found in memory, a function was designed to create a check sum by adding all bytes together of the shellcode and matching the final two bytes of the secondary shell code, which was calculated prior to exploitation. This calculation will determine if the shell code has been modified or destroyed in memory.
  5. If the checksum fails, continue searching memory from the address after the malformed egg. If the checksum succeeds then jump execution to the valid secondary shell code.
  6. After jumping to the second stage code, copy the shell code out of the unstable area of memory into a secure, known location such as the Process Environment Block (PEB) [ref 4].

The second stage shell code contains the actual code to execute a command shell or fulfill the purpose the exploiter needs to meet. The purpose of the multiple requests in step one is to ensure that the second stage of our shell code exists within the memory of the process we are trying to exploit. For each register roughly two jmp / call addresses from different DLLs should be utilized to increase a successful chance for exploitation. For each length this is equal to sixteen (16) attempts, eight (8) registers multiplied by two (2) jmp / call addresses.

 

Figure 3. Stack layout after overwrite.
Figure 3. Stack layout after overwrite.

When the overflowed function exits and calls our now corrupted return address, execution will point into our first stage shell code. The purpose of the first stage code is to setup an exception handler to trap invalid memory read attempts and begin searching for our egg to identify the second stage shell code. Once this egg has been identified a jump to the address of the egg is executed.

During these trails it was identified that our second stage shell code was being modified or incomplete in memory. Since our exploit needs to verify that the shell code will be intact, I implemented a check sum to verify that the shell code was valid. By pre-calculating the sum of all bytes of the shell code together we are left with a two byte check sum value. The first stage shell code will re-enact this same process then compare the final two bytes of the secondary shell code with the newly created sum to verify they are the same.

If the check sum fails the first stage shell code will continue searching for the same egg from that location until it finds a valid egg, or until it reaches the end of the memory segment.

Since the area of memory used by our second stage shell code is unstable and may be overwritten or destroyed, another step would be to copy the shell code to a more secure or safe area of memory. Since Microsoft Windows 2000's PEB (Process Environment Block) is always located at 0x7FFDF000 across service packs (also true for NT Operating Systems), a location in this block was used to execute the final stage of the code. An address in the PEB, 0x7FFDFBC0, should be sufficient to copy and then execute the connect back code segment.

Bypassing Visual Studio stack protection

If this ISAPI extension were compiled with /GS from Visual Studio .NET, our attack methodology remains nearly the same. The only difference is that the length is different and a different register points to our overflow string. It should be noted that these examples were tested on IIS 5.0 and will not work with on Windows XP/2003 due to the latest version of ntdll.dll clearing the registers after the SEH has been corrupted. In the author's examples (strcpy, strcat, sprintf based overflows) it was found that it was possible to overwrite the SEH with an address to a jmp ebx operation. The ebx register points to the four byte address before the SEH (after the return address and before the SEH). This address is actually the pointer to the next SEH record. After doing a short jmp, we can jump over the SEH address (the one we overwrite) and into our shell code, which can be either a smaller first stage shell code, or the entire payload. A short jmp must be used to jump over our SEH address otherwise the CPU will attempt to execute this address as if it were code and not data.

 

Figure 4. Stack layout of the structured exception handler before overwrite.
Figure 4. Stack layout of the structured exception handler before overwrite.

If we were to overwrite only the return address, we would also overwrite the stack protection cookie. When the function attempts to check this stack canary, it would fail and result in the buffer overflow protection mechanisms being executed. This is all possible because we are causing an exception before the overflowed function returns. The exception occurs due to an invalid read attempt because the ISAPI extension is trying to read data that was also overwrote by our overflow string. When it can't read the data it is expecting, the access violation occurs and the next Exception Handler (which is now under our control) is called. Since we overwrote the exception handler with an address to a call ebx instruction, the CPU reads the ebx register which points to the old next SEH record but now points to our short jump. The short jump is called and it jumps over our call ebx address and directly into our first stage shell code.  

 

Figure 5. Stack layout of the structured exception handler after overwrite.
Figure 5. Stack layout of the structured exception handler after overwrite.

 

Conclusion

One can now see that even when an attacker does not have access to the binary, source or platform information, exploitation may in some specific scenarios allow for remote code execution. The result of which is companies need to review the source of these proprietary applications or even third party developed code to verify it has been authored to meet accepted secure programming practices and guidelines.

 

References

[ref 1] http://cert.uni-stuttgart.de/archive/vuln-dev/2000/04/msg00020.html

[ref 2] http://msdn.microsoft.com/library/default.asp?url=/library/en-us/
vccore/html/vclrfGSBufferSecurity.asp

[ref 3] http://msdn.microsoft.com/library/default.asp?url=/library/en-us
/vccore98/html/_core_exception_handling_topics_.28.seh.29.asp

[ref 4] The PEB contains process information such as process heap information, the base address, module information and function pointers to RtlEnterCriticalSection and RtlLeaveCriticalSection.

Sample code

Sample code using our exploit framework can be downloaded from the SecurityFocus website; please see the README file within the archive for information on usage.

About the author

Prior to working in the security industry Isaac Dawson worked in the telecommunication industry. Currently, he has been in the security industry for four years doing penetration testing and application based assessments. His primary knowledge is in exploiting Windows based vulnerabilities as well as Web based applications.

Author Credits

The author would like to thank Ian Melvin, Shane Macaulay, Dave Aitel, and Ollie Whitehouse for their assistance with this paper.

This article originally appeared on SecurityFocus.com -- reproduction in whole or in part is not allowed without expressed written consent.