Hacking

Tricks for exploit development

Stephen Bradshaw
March 1, 2011 by
Stephen Bradshaw

This is the second article in a series about using the OllyDbg, a 32 bit assembler level analyzing user mode debugger for Windows.

In part one of this tutorial we covered:

Earn two pentesting certifications at once!

Earn two pentesting certifications at once!

Enroll in one boot camp to earn both your Certified Ethical Hacker (CEH) and CompTIA PenTest+ certifications — backed with an Exam Pass Guarantee.

In this article we will cover the following subjects:

Methods for directing code execution in the debugger

When you are writing an exploit you are going to need to be able to execute the code in your target application in a variety of different ways, to give you the appropriate amount of control to monitor the code and memory closely when needed. You may want to run normally at one point, to go step by step through each individual instruction at another, and sometimes to have it run quickly to a particular point allowing you to take control once that point is reached.

Luckily, this is all possible via the use of a debugger by using breakpoints as well as the various methods for stepping through code.

This section has been broken up into the following subsections:

  • Step in, Step over
  • Where did I come from? Exposing your history
  • Animation
  • Setting breakpoints
  • Running the code
  • Exception-al debugging
  • Run tracing to find the cause of an exception

Step in, Step over

Let's start by learning how to step through code. If you haven't already, start up OllyDbg and open vulnserver.exe. Execution should automatically pause at the program entry point. In the top left hand pane of the CPU view you should see the instruction "PUSH EBP" highlighted.

Take note of the top entry on the stack (bottom right hand pane) as well as the value of the ESP and EBP registers in the top right hand pane, then try hitting the F8 key, just once. The F8 key is a shortcut key for the "Step over" operation, which allows you to advance forward one instruction, while NOT following any function calls.

The significance of that will become clear in a moment, but for now, you should have noticed that since executing that PUSH EBP instruction, the value held by EBP has been added to the top of the stack and the value of the ESP register has decreased by four. In addition, the instruction following "PUSH EBP" in the top left hand pane, namely "MOV EBP, ESP", should now be highlighted, and two registers, ESP and EIP have their values highlighted in red to indicate that they have changed.

Take note of the values of the EBP and ESP registers, and hit F8 once more. The EBP register will change to match that of the ESP register, and the EBP and EIP registers values will be highlighted in red. What this red highlighting of values is indicating is that this particular value changed during the last operation.

Press F8 two more times until the "CALL DWORD PTR DS:[<&msvcrt.__set_app_type>]" instruction is highlighted. Now press F8 once more and execution should advance to the following instruction of "CALL vulnserv.00401020".

What happened here? We just ran a CALL instruction, which is intended to be used to temporarily redirect code execution to another location within the programs memory space, yet the debugger didn't move to this new section of code, as we might have expected it to do. What actually happened here is that the F8 or "Step over" key, indeed "stepped over" this CALL instruction. It ran the code specified by this CALL instruction and paused the program in the debugger once more after it was done and execution had returned to the instruction immediately after the CALL. So what do we do if we want to actually follow the debugger into the code specified by one of these CALL statements?

We use the "Step into" command, which uses F7 as its shortcut key. Use the F7 key now, with your debugger selecting the instruction "CALL vulnserv.00401020", and see what happens. The debugger will follow through to the instruction in vulnserver at the memory address 00401020, and will then pause. You can now follow along with the code referenced by that CALL instruction.

So the difference between the "Step over" and "Step into" commands is that one steps over CALL statements (preventing you from having to go through the code there if you don't want to) and the other steps into them, allowing the "CALL"ed code to be viewed.

Where did I come from? Exposing your history

OllyDbg keeps a history of the last 1000 commands that were displayed in the CPU window, so if you have stepped into a CALL statement, or followed a JMP and you want to remind yourself of the previous code location, you can use the plus (+) and minus (-) keys to navigate through the history. Try the minus key now, the CPU view should then display the CALL instruction that you just executed. Use the plus key to come back to the current instruction.

Note that this little trick only allows you to view instructions that have actually been displayed and tracked in the debugger. You can't let the program run, generate a crash and then use the minus key to check the instruction just before the crash occurred, nor can you let your program run until it hits a breakpoint and then step back from there. If this type of functionality interests you, you can use the Run trace capabilities of OllyDbg, which I will cover later in this section.

Animation

If you want to step through your code in a controlled fashion, but don't like the thought of having to hammer quickly at the F7 or F8 buttons, you can take advantage of OllyDbg's animation capabilities. Press Ctrl-F7 for "Step into" animation and Ctrl-F8 for "Step out" animation. This will step through the code rather quickly, allowing you to pause once more by hitting the Esc key. You can then use plus (+) and minus (-) keys to step through the history of your animated session. Try hitting Ctrl-F7 now to do some step in animation, and then hit Esc when you are done. Now use the plus (+) and minus (-) keys to navigate through your execution history for a bit, until you are comfortable with how this works.

Setting breakpoints

Up until now we have maintained more or less manual control over how the program is executed, with us having to either approve each instruction in advance, or having to let the instructions run in a semi-automated fashion with us hitting Esc (hopefully at the right time) when we want the program to stop.

What if we want to stop the program at a particular point in the middle of its execution though? The step by step method will be too slow (there are a lot of instructions in even the simplest program), and the animation method will be too imprecise. Well, to allow us to stop at a point of our choosing, we can use breakpoints, which are essentially markers on particular instructions in the code that tell the debugger to pause execution when one of those instructions are about to be executed by the CPU.

Let's try setting one now. First of all, use the View->Executable modules menu option to bring up a list of executable modules loaded with vulnserver.exe. Double click on the vulnserv entry to open this view in the CPU window (we are doing this because it's possible that our previous animation session brought another module to the fore). Now right click in the top left hand pane of the CPU view and select Search for->All referenced text strings. See the following screen shot.

Double click on the entry that has the text "Welcome to Vulnerable Server! Enter HELP for help." to go to the related instruction in the CPU view (see the selected entry in the screenshot above). Now, with this instruction highlighted in the CPU View, hit the F2 key, which is used to both set and clear breakpoints. If you open the View->Breakpoints menu option, or hit Alt-B, you should now also see your breakpoint listed in the Breakpoints view.

You will note that the memory address for that instruction will be highlighted in red in the top left pane of the CPU View. So, a breakpoint has been set, now let's see how we can trigger it, as well as how we can let code run normally in the debugger.

Note: When you reach the point in your exploit writing process where you are inserting your own code into the program (e.g. when using shellcode in an exploit you are writing, or even when you are writing your own shellcode) you can alternatively use the opcode "xCC" to insert your own breakpoints directly into the code. We will look at how this is used in more detail in a later section.

Running the code

To allow code to run within the debugger, hit the F9 or Run key. The program will now essentially run normally, until either a breakpoint is hit or an exception occurs. (Strictly speaking, you can manually pause the program in the debugger too, but that's often not particularly useful.)

Generally, when you want to interact with a program in a normal way while you are debugging it, for example if you want to send data to a program to cause an exception, the F9 Run option is what you need to use to allow that to happen.

Now that our program is running normally within the debugger, let's try and trigger our breakpoint. In this case, we have set a breakpoint on the instruction that references a text block that is displayed by vulnserver in response to a connection from a client, so in order to hit the breakpoint, we need to initiate a client connection. Since we are currently in Run mode, the program will operate as normal within the debugger until the code referenced by the breakpoint is about to be executed.

Save the following into a file basicclient.pl.

#!/usr/bin/perl

use IO::Socket;

if ($ARGV[1] eq '') {

die("Usage: $0 IP_ADDRESS PORTnn");

}

$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;

Now, run the script, feeding the script the appropriate IP address and TCP port number for vulnserver.exe as shown below.

C:_Work>perl basicclient.pl 127.0.0.1 9999

Your code should now stop at your configured breakpoint, allowing you to take control of execution once more, so you can step through future code. Hit F2 again to clear the breakpoint, and then F9 to start the program running once more.

I mentioned at the start of this section that a program that is let to run will only stop when a breakpoint is hit or an exception occurs. Let's try causing an exception next, to see how the program reacts.

Exception-al debugging

When an exception occurs in a running program in a debugger, the debugger will halt execution and allow you to view the state of the CPU and memory inside the program at the time the exception occurred. This is exactly the kind of information we need to see if we want to be able to write a reliable exploit for the given vulnerability.

To trigger an exception, I am going to make use of a vulnerability in vulnserver.exe. If you wish to find out how the vulnerability was discovered, you can visit the following links:

/intro-to-fuzzing/

/fuzzer-automation-with-spike/

The following code will cause an exception in vulnserver.exe. Save as trun.pl

#!/usr/bin/perl

use IO::Socket;

if ($ARGV[1] eq '') {

die("Usage: $0 IP_ADDRESS PORTnn");

}

$baddata = "TRUN ."; # sets variable $baddata to "TRUN ."

$baddata .= "A" x 5000; # appends (.=) 5000 "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

Run the script as follows (ensure you provide the correct IP address and port number if you are not running this from the same host running vulnserver.exe).

C:_Work>perl trun.pl 127.0.0.1 9999

If everything goes according to plan, your debugger should stop, with the screen looking like the screenshot below. If this does not work, restart vulnserver.exe in the debugger (Debug->Restart menu option), hit F9 to let the program run, and try again.

The text in the bottom left hand corner of the screen shows the following.

And the register values are as follows. Note the value of the EIP register.

This is how the debugger will behave when an exception occurs. You will note that you can see CPU register values, as well as stack and memory data, as it looks at the time of the crash. You can't however, see the instructions that the CPU was attempting to execute, because the Instruction pointer (EIP) is pointing to a memory address that does not appear to contain any code (41414141). Hmmmmm....

At this point, you could use Shift+F7/F8/F9 to pass the exception to the program to handle, but let's not do that right now (we will try this in an upcoming section focusing on the SEH Chain).

Run tracing to find the cause of an exception

We have now seen how an exception looks when hit within a debugger, but we still haven't seen how we can identify the particular section of code where the exception occurs. To do this, we can use the Run trace functionality of OllyDbg.

Run tracing essentially allows us to log instructions that are performed by the debugger, so that when an event such as an exception occurs we will have a command history that we can look back through to find out how it happened.

While this is undoubtedly a great feature, there are some caveats to using it – mainly that it's much slower than just running code normally and it can use a lot of memory if you leave it running for too long. Consequently, if we want to use Run trace to efficiently identify the cause of an exception, we should attempt to get code execution as close to the point of the crash as we reasonably can before we turn it on.

Let's see if we can use Run trace to find the code that leads to the exception examined in the previous section.

By examining the trun.pl script that we used to create the exception, we see that the data we are sending to the application is a string beginning with the text "TRUN". The appropriate line from the script that sets this string is shown below. (The entire script is provided in the previous section).

$baddata = "TRUN ."; # sets variable $baddata to "TRUN ."

Let's have a look in the debugger to see if we can find references to this string, as such references may indicate code segments that deal with this particular data. If we find any such references, this will hopefully provide us with a good area from which to start our Run trace.

Restart vulnserver.exe in the debugger, and hit F9 to let the program run. Now, right click in the disassembler pane and select the Search for->All referenced text strings option. This function essentially searches for any and all references to text strings within the code.

In the Text strings window that appears, about midway down you should see a reference to an ASCII text string "TRUN ". See the screenshot below, where I have selected this particular entry. Although this particular instruction may not necessarily be placed immediately before code that will create the exception we viewed in the previous section, it seems like a good place to start looking. We will set a breakpoint at this location in the code and see if that breakpoint is hit before the exception occurs. This would indicate that the marked instruction is located at an earlier point in the execution path than the code associated with the exception, and may be a good place to start our Run trace from. If the breakpoint is not hit, we can keep looking through the code to see if we can identify some other position from which it might be suitable to start our Run trace.

Double click on the "TRUN" entry in the Text strings window to be taken to the corresponding instruction in the disassembler pane. Now hit F2 to set a breakpoint on this location. The address should turn red, as shown below.

Now we are almost ready to start a Run trace to see if we can identify which instructions lead up to our exception. The program is running normally in the debugger, with a breakpoint (hopefully) set at a point somewhere in the code not too far before the exception occurs. If everything goes to plan, code execution should pause at this breakpoint when we use the trun.pl script to generate the exception, and this should then allow us to start a Run trace which will identify the instructions that lead up to our exception.

Before we try this though, we will tweak the Run trace options just a little in order to make the Run trace process slightly more efficient. Open the Options menu, select the Debugging options item, and hit the Trace tab. Enable the Always trace over system DLLs and Always trace over string commands options and hit OK. See the screenshot below.

This will prevent our debugger from stepping into system routines and string commands, which in many cases is not needed and will just clutter our Run trace log unnecessarily.

Now run the trun.pl script to generate the exception.

C:_Work>perl trun.pl 127.0.0.1 9999

Execution should pause at our breakpoint. Success! Now let's start a Run trace. These can be initiated by using the Ctrl-F11 key for a step into Run trace, or Ctrl-F12 for a step over Run trace. As with the other debugger code execution methods, step into means to follow code within CALL statements, and step over means to execute over them.

Press Ctrl-F11 to execute a step into Run trace. You should be greeted very quickly by the following popup, indicating that the exception has been reached. The speed at which this has occurred suggests that the location for our breakpoint has been well chosen – we have discovered the code that leads up to the exception but we haven't been left waiting for an extended period of time while the trace ran.

Hit OK in the popup to dismiss it. Now, to actually see the contents of the Run trace log, open the View menu, and select Run trace. You should see something like the following, showing the exception at the bottom of the window (entry 0), with all of the other instructions since the Run trace started listed from bottom to top in order of most to least recently executed.

At this point, if you wanted to examine the cause of the exception in more detail, you now know exactly what leads up to it. You have the option of taking the address of any of the instructions above, setting a breakpoint at that location, retriggering the exception and then stepping through the code to see exactly how the exception occurred.

Remember, if you want to use Run trace to effectively find exception prone code, place a breakpoint as near as possible to the point where you think the exception occurs (some trial and error may be involved in getting the breakpoint in the right place), then trigger the exception and Run trace from the breakpoint.

Before you proceed, clean up the breakpoint you added in this section. Open the View menu and select Breakpoints or hit Alt-B to open the Breakpoints window, and use the Delete key to remove any existing breakpoints listed.

The SEH Chain

"What is the SEH Chain?" I hear you ask. It's essentially a per-thread structure in memory that provides a linked list of error handler addresses that can be used by Windows programs to gracefully deal with exceptions, like the one we generated in the previous section.

Once an exception is detected within a program, Windows will attempt to manage that exception by using the error handling routines in Windows to select an error handler from the SEH Chain. That error handler will be a memory address that contains code that will do something useful, like throw up an error dialog box to indicate that the program has crashed. Entries in this SEH Chain are stored on the stack.

We can look at the SEH address in one of two ways - we can scroll through the stack pane in OllyDbg until we see the "SE handler" text next to a stack entry, or we can use the View ->SEH chain menu option.

To find the entry on the stack, you can generally just scroll down to the very bottom of the stack pane in OllyDbg, as this is likely where the Operating System supplied SEH chain entry will be located. Using the "SEH chain" menu option will show you all entries in the SEH chain if there is more than one.

Why, as exploit writers, do we care what the SEH chain contains? We care because the SEH chain contains addresses where code execution is redirected once an error occurs, allowing us to use any exception as an opportunity to redirect code execution to a location of our choice by overwriting these SEH entries with our own provided values. Since these entries in the chain are stored on the stack, they are ideally placed in order to be overwritten by a stack based overflow.

There are some caveats you need to be aware of when writing SEH exploits, but this is something I will go into in more detail in a future tutorial, for now we will just cover how to use the debugger to determine if an SEH overwrite has occurred.

If you haven't already, restart the program in the debugger and hit F9 to get it running. Now execute the trun.pl script created in the "Exception-al debugging" component of the previous section to get the exception back into the debugger, and view the SEH handlers, by using the stack and View->SEH chain methods.

What you should see now is an intact SEH chain - one that hasn't been overwritten. You can tell this because the handler address is a valid one within ntdll (from the SEH chain window – see screenshot above) and because the SEH entry near the bottom of the stack is preceded by an entry with the data FFFFFFFF (this stack entry should be marked as "Pointer to next SEH record"). Since the SEH chain is a linked list, each entry in the list contains a location for the next entry, with the reference for the last SEH entry pointing to FFFFFFFF to indicate that it is the final entry. The following screenshot shows the SEH entry on the stack, after scrolling down to the bottom of the stack pane.

The following shows a zoomed in view of the stack pane from the above.

Let's compare that to an SEH chain that has been overwritten. Restart vulnserver.exe in OllyDbg and use F9 to start it running.

Save the following as gmon.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 5000; # appends (.=) 5000 "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

And run it

C:_Work>perl gmon.pl 127.0.0.1 9999

An exception should occur. Now check the bottom of the stack as well as the SEH chain window.

You should see that the SEH entry has been overwritten with 41414141, as has the pointer to the next SEH record. We have definitely overwritten the SEH entry here.

A zoomed in view of the stack is shown below.

Now try and pass the exception through to the program to be handled using Shift+F7/F8/F9 key combination. You should get an access violation with EIP pointing to 41414141. Windows tried to handle the previous exception by passing control to our overwritten SEH entry of 41414141.

The text in the bottom left hand corner shows the following.

And the registers are set to values as shown below (note the value of EIP)

When this happens within a program you are debugging, you should know that you have a decent chance of writing an SEH exploit for that vulnerability, although as I mentioned earlier there are a few caveats which you have to watch out for that I will discuss in a future article.

Searching for commands

One of the simplest ways in which to perform software exploitation is to use instructions located in specific areas of memory to redirect code execution to areas of memory that we can control. In the case of the vulnerabilities shown so far in this tutorial we have managed to set the EIP register to locations of our choosing both via directly overwriting a RETN address on the stack and by using an overwritten SEH entry to redirect execution via the windows error handling routines. If we want to use this control over EIP to redirect to our own code inserted within the application, the simplest way to proceed is to find instructions that can perform this redirection at known locations within memory.

The best place to look for these instructions is within the main executables code or within the code of an executable module that is reliably loaded with the main executable. We can see which modules we have to choose from by allowing the program to run normally and then checking the list of executable modules in OllyDbg.

Note: This method of checking for loaded modules may result in us missing some delay loaded dlls. If you're concerned about this you can run the executable through its paces a little to give the best opportunity for those dlls to get called before you check which modules are loaded. Just remember that if you choose an address from a delay loaded module you need to ensure that that module is loaded before any exploit that relies on it is triggered.

Let's try this now. Restart vulnserver in the debugger and press F9 to let it run. Now open the View menu and select the Executable modules option (or hit Alt-E). This will show you a list of executable modules currently loaded with the main executable.

At this point you will most likely note that there are quite a few modules to choose from. Which ones should you start with first? The best way to know how to proceed here is to start with the goal in mind. We want a module that offers a location in memory where we can reliably find a particular instruction we can use to redirect code for our exploit. Basically, this boils down to the module including the instruction we want and there not being any characteristics of the module that will prevent that instruction from being used. Such characteristics could include a restricted value in the address, or various exploit protections such as SafeSEH or ASLR being enabled in the module.

I usually use the following process to order the loaded modules in terms of least to most usable, and I search the most usable modules for my desired instruction first.

Operating System supplied modules go to the bottom of the list. You can tell which modules are OS supplied by checking the full path of the module. Usually, they are located in the Windowssystem32 folder. These modules are best avoided for a number of reasons. First, they change between OS versions, as well as service packs and sometimes even hotfixes, meaning that an instruction that you find in one version of the module may not be there in another version, meaning that your exploit will only work on very specific systems (ones that have the same OS version, service pack and perhaps even hotfixes as your sample system.) Second, these OS supplied modules are almost certainly going to be compiled using compiler based exploit prevention features, such as SafeSEH and ASLR. Basically, you should only be looking at OS supplied modules if you have no better options.

A module you can consider using is the main executable itself, which is quite often not subject to any compiler based exploit protections, especially when the application has been written by a third party developer and not Microsoft. Using the main executable has one major flaw however, in that it almost always starts with a zero byte. This is a problem because the zero byte is a string terminator in C/C++, and using zero bytes as part of strings used to overflow a buffer will often result in the string being terminated at that point, potentially preventing the buffer from being appropriately overflowed and breaking the exploit.

The best choice of module to use is one that comes with the application itself (assuming a third party application). These modules are often compiled without exploit protections and since they come with the application being exploited the module structure usually remains the same amongst copies of the application with the same version number. This gives you the ability to write an exploit which will most likely work across a number of different Windows versions, as long as the version of the program to be exploited remains the same.

In this case we have one module, essfunc.dll, which should be suitable for our uses. Double click on it to open it in the CPU view. The title bar should now end with the text "module essfunc" if you have properly selected the essfunc module. Let's see how we can find useful instructions within this module.

The first thing we need to know is what command we want to search for. In the case of exploits, the most commonly used instructions are those that either JMP or CALL a particular register, or a POP, POP RET instruction. The JMP or CALL commands are used when a particular register points to a location in memory which we control, and the POP, POP, RET instructions are used when we write SEH exploits on Windows systems XP SP2 and up.

Assuming we want to search for a "JMP ESP" instruction, we can use one of two commands accessible via right clicking in the top left hand pane of the CPU View of the appropriate module.

The first command, accessible by right clicking on the top left pane and selecting Search for->Command allows us to search for one command at a time. After the first result is shown, highlighted in the CPU view, you can see subsequent results by clicking Ctrl-L or selecting Search for->Next from the right click menu.

Try this now. Select the Search for->Command option from the right click menu in the top left pane of the CPU view (make sure essfunc is the currently selected module – it should mention this in the title bar as shown in the screenshot below).

Then, in the Find command window that appears, type "JMP ESP" and hit Find.

This will take you to the first instance of that command within the currently selected module. Now hit Ctrl-L to find the next instance.

The second command, accessible by right clicking on the top left pane and selecting Search for->All

commands, will open a new window listing all instances of that command within the currently selected module.

Try this now. Select the Search for->All commands option from the right click menu in the top left pane of the CPU view. In the Find all commands box, type "JMP ESP" and hit Find. A new window will then appear showing the complete list of JMP ESP commands found in the currently selected module.

This demonstrates how we can look for a single instruction, but what do we do if we want to look for multiple commands in sequence, such as a POP, POP, RET? Well for this we can either use the right click options Search for->Sequence of commands or Search for->All sequences, to either search for individual instances or all instances of a particular command sequence.

Let's try searching for all instances of POP, POP, RET sequences in essfunc.dll. Make sure that module is still selected in the CPU View, then right click on the top left hand pane and select Search for->All sequences. Then, in the find sequence of commands window that appears type the following:

POP r32

POP r32

RETN

Where "r32" is shorthand for any 32 bit register (e.g. ESP, EAX, etc). See the following screenshot.

Hit Find and you will be greeted with a "Found sequences" window showing you all the locations where that sequence of commands exists within the current module. Your currently selected position in the code will also be shown in the list in red to show you where those discovered commands are in relation to your current position.

Double click on any of the entries in the list and you will be taken to that location in the CPU view, so you can see that all commands you elected to search for are actually present. (In the screenshot below, for example, we can see POP EBX, POP EBP, RETN).

Searching through memory

In some circumstances when writing an exploit you will need to determine if data you have sent to an application has been captured and stored somewhere in memory. This type of condition is especially useful when you have found an exploitable vulnerability but you don't have room to insert your full payload into the same set of data that causes the exception. By placing your payload data into another accessible area in the program's memory, you can then use special purpose shellcode to find that payload elsewhere in memory and redirect execution to it.

You can search memory for particular values very easily in OllyDbg. Essentially, you need to insert the appropriate data into the program, pause the debugged program at an appropriate point in its execution, then open the OllyDbg Memory map view and use the Search function to see if the appropriate data is present. If your goal is to see if data stored in a programs memory can be utilized in a particular exploit, the best way to go about this is to send the data and pause the program in the debugger by actually causing the appropriate exception. Let's see how this works.

This code below will create the same exception we used in trun.pl above, but before the crash is triggered it will also send some data (a giant string of "B" characters) to the application using the GDOG command from vulnserver. Save the following as sendextradata.pl.

#!/usr/bin/perl

use IO::Socket;

if ($ARGV[1] eq '') {

die("Usage: $0 IP_ADDRESS PORTnn");

}

$memdata = "GDOG "; #sets variable $memdata to "GDOG "

$memdata .= "B" x 5000; # appends (.=) 5000 "B" characters to $memdata

$baddata = "TRUN ."; # sets variable $baddata to "TRUN ."

$baddata .= "A" x 5000; # appends (.=) 5000 "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($memdata); # send $memdata variable via $socket

$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

Start up vulnserver.exe in OllyDbg and hit F9 to allow the program to Run. Then run this script as follows.

C:_Work>perl sendextradata.pl 127.0.0.1 9999

An exception should be triggered in the debugger. To now search for the additional data we sent to the program (that big string of upper case "B" characters), open the View menu and select Memory, or hit Alt-M to bring up the Memory map in OllyDbg.

Now right click on the Memory map and select Search from the Menu, or hit Ctrl-B.

This will bring up the search window. Enter a large string of upper case B characters in the ASCII box, and be sure to tick the Case sensitive option near the bottom of the window. See the below screenshot.

Now hit OK, and you should soon be greeted by a memory Dump window, showing a large number of B characters in a row. Expand the window a little to see just how many there are. See the screenshot below. In this particular shot, you can also see the beginning of the TRUN command that we used to cause the exception.

Here we have confirmed that we have a way of inserting additional data into the application in such a way that it is still available in memory at the time we caused this particular exception. This means that if we needed to provide additional content to the application in order to complete an exploit for this vulnerable bug, we could use the method demonstrated in the sendextradata.pl script to provide that data.

Working in the memory dump

The memory dump windows in OllyDbg are extremely useful when writing exploits, as they provide multiple ways in which to view and access the data stored in a programs memory. This section will look at a number of ways in which we can work in the memory dump windows in order to efficiently manipulate and process this data.

This section is made up of the following subsections:

  • Different memory dump views
  • Following in memory dump
  • Copying data from the memory dump

Different memory dump views

During exploit writing it can often be useful to be able to represent data in the debugger in a variety of different formats, and sometimes to copy that data out of the debugger for processing with other programs. Luckily, OllyDbg provides a number of different view formats for memory dump windows (both the one in the bottom left hand pane of the CPU view and the ones accessed by double clicking on entries in the Memory map). This gives us options in how we can view a programs executable code, the data generated by the program, and data supplied by a programs users, all of which will be stored in memory. In addition, the memory dump windows, as well as many of the other views in OllyDbg, allow data to be easily copied out to the clipboard, and sometimes written to a file on disk, to allow it to be easily used in other programs.

The different view modes in the memory dump windows are accessed via the right click menu. Let's examine some of the views that will be most useful to us as exploit writers.

One of the most commonly used display modes for memory in OllyDbg, especially within the CPU view, is the "Hex/ASCII (8 bytes)" view, which shows both the Hex and ASCII representation of the data, with 8 bytes per row. You can see a screenshot of this view mode below, along with the menu option used to select it (right click, Hex->Hex/ASCII (8 bytes)). There is also a "Hex/ASCII (16 bytes)" view mode which is essentially the same but 16 bytes of data are shown per row as opposed to 8, and you can also choose to view the text in Unicode instead of ASCII. This screenshot is of a memory dump window opened from the Memory map, but remember this view mode also applies to the memory pane in the CPU view.

The "Text" view represents the data as pure text, using either the ASCII (single byte) or Unicode (double byte) encoding format. The previous view showed the ASCII (or Unicode) representation of data as well, but did it alongside the Hex representation. If you need to show just the text (perhaps because you want to copy only the text and not the hex data), this is the mode you can choose. To select the "Text" view, right click and select the appropriate entry from the Text submenu. See the following screenshot to see how the ASCII (32 chars) text view looks, as well as the menu entry you can choose to select it.

The "Disassemble" view is the last one we will examine. This interprets the data from memory as assembly code, much as shown in the top left hand pane of the CPU view. The screenshot below shows the "Disassemble" view along with the menu option used to select it (right click and choose Disassemble from the menu).

When you open a Memory dump window from the Memory map, OllyDbg will often choose one of the above views for you automatically based on the type of content it believes is contained within that area of memory. For example, it will use the "Disassemble" view for areas that are believed to contain executable code.

Following in memory dump

Directly clicking on various memory pages from the memory map allows us to go to any area in memory we like, but what if we want to view the contents of memory at locations stored in one of the CPU registers, or in entries from the stack? What if we want to view the code from the disassembler pane in the stack?

Well we can do this by right clicking on the appropriate register, stack entry or disassembled instruction in the CPU view, and selecting the "Follow in Dump" option. The memory dump pane in the CPU view will then show the section of memory beginning with the address represented by the item you selected.

Try this now, by restarting vulnserver in the debugger, right clicking on the first entry in the disassemble pane, and selecting Follow in Dump->Selection.

The memory dump pane will then display the memory address of the instruction you just selected, with the bytes making up that instruction being highlighted.

You can also select multiple instructions and try the same thing – the data representing all of the instructions you selected will be highlighted in the memory dump.

You can also use the Follow in Dump option on the stack. Right click on an address of a stack entry (left hand column in the stack pane), to view that section of the stack in the memory dump. Right click on the value of a stack entry (second column in the stack pane), to view the memory location that entry contains. Please note that in the case of stack entries, the Follow in Dump option will only be available if the stack entry contains a valid memory address.

The Follow in Dump option also works with CPU registers. Like with stack entries, the option will only appear if the register contains a valid memory address.

Try the Follow in Dump option yourself on the stack and on the CPU registers so you get a feel for how it works.

One of the most important uses of this Follow in Dump option when writing exploits is to assist in finding stack entries or CPU registers that point to areas of memory we control when an exception occurs. This will give a pointer to how we can direct code execution into those memory areas. For example, if we see that when an exception occurs, the ESP register points to memory containing data we sent to the application, we could use a "JMP ESP" instruction to redirect code execution to that location.

Another potential use of this option is to allow us to easily view, select and copy instructions being executed by the CPU in different ways – a capability given to us by the flexibility of the display options of the memory dump view as opposed to some of the other views in OllyDbg. This can be very useful when we want to confirm shellcode we have sent to an application has not been mangled during transfer.

Copying data from the memory dump

There are two different ways in which selected data can be copied out of the memory dump that I would like to cover here.

The first method copies the data in a textual format, much as it is displayed in the debugger itself. When using this method, the output depends on the view mode you have chosen. If you are using the "Hex/ASCII (8 bytes)" view mode, you will get the address, the hex dump, and the ASCII representation, but if you are using the "Disassembly" view, you will get the address, the hex dump, the disassembly and a comment. As I said, it's basically a text representation of what you see on screen.

To copy in this way, select some data using the mouse, then right click and select Copy->To clipboard or Copy->To file, depending on where you want the data to go. To get the data into the right format, you should obviously set your view mode, as described earlier, to the appropriate setting. You will find that most of the display windows in OllyDbg also offer some variation of this option, so if you want a text copy of pretty much anything you see on screen, you can probably get it via selecting the data and right clicking.

As an example of what this looks like, here is a sample of text copied from the memory dump in disassemble mode.

00401130 > $ 55 PUSH EBP

00401131 89E5 MOV EBP,ESP

00401133 83EC 18 SUB ESP,18

00401136 C70424 0100000>MOV DWORD PTR SS:[ESP],1

The second method for copying data is the binary copy. This copy method is only available in the memory dump and the disassembler views, and essentially copies to the clipboard a space delimited representation of the hex values for each byte selected. This is available by selecting some data in the memory dump, right clicking and selecting Binary->Binary copy. The following shows the text generated by performing a binary copy on the same data that was used to produce the example text based copy above.

55 89 E5 83 EC 18 C7 04 24 01 00 00 00

Accessing the data in these formats can prove to be very useful when writing exploits. I often use the binary copy format to feed the data into a script that checks to see if shellcode sent to a program has been mangled.

Editing code, memory and registers

One particularly cool feature of OllyDbg allows you to edit the values of memory, registers and the stack while a program is being debugged. This is extremely useful when writing exploits or shellcode as it allows you to quickly tweak data in memory after an exception if you realize you have made a mistake in any data or shellcode already provided to the application. It also provides a really quick way of finding opcodes for particular instructions if you need to add some custom shellcode to an exploit.

To demonstrate how this can work, we will use the following skeleton exploit code – save as skeletonexploit.pl.

#!/usr/bin/perl

use IO::Socket;

if ($ARGV[1] eq '') {

die("Usage: $0 IP_ADDRESS PORTnn");

}

$baddata = "TRUN ."; # sets variable $baddata to "TRUN ."

$baddata .= "x90" x 2006; # append 2006 x90 bytes to $baddata

$baddata .= pack('V1', 0x625011AF); # address of "JMP ESP" instruction from essfunc, little endian

$baddata .= "xcc" x 100; # append 100 xcc INT3 breakpoints 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

This code is essentially the beginnings of an exploit for the vulnerability associated with the first exception we looked at earlier in this guide. When use against vulnserver running in the debugger, it should redirect code execution to the first of 100 "xCC" characters sent to the program.

Don't worry about how I wrote this code for the moment, or how the redirection to the INT3 instruction actually happened, I will cover that in a future article. For now, just restart Vulnserver in the debugger, use F9 to start it running, then run the script as follows:

C:_Work>perl skeletonexploit.pl 127.0.0.1 9999

Your debugger should pause execution, with the disassemble pane showing something like the below.

All those "xCC" characters we sent to the program using the skeletonexploit.pl script are shown right there in the debugger, and our program is actually running them as code. If we wanted to, we could replace the "xCC" characters in our skeletonexploit.pl script with code that would perform some other function, but at the moment what we want to do is use this opportunity to show how we can edit data directly within the debugger. The "xCC" is of course the opcode for an INT3 debugger trap instruction, as mentioned during the earlier section of this tutorial on assembly, and that is why the debugger paused when it executed the first of these instructions.

The first code editing method we will try is direct assembling, where we select an instruction and provide some assembly code to be placed in that location. If the assembly code we provide takes up more space than the instruction we selected, additional following instructions will also be overwritten, and if the final instruction overwritten has leftover bytes (e.g. if the final instruction is 4 bytes long but we only need two of those bytes to complete the instruction we add), any spare space will be filled with NOPs if the Fill with NOP's checkbox is left selected.

To try this, double click on the currently selected INT3 instruction (make sure you click on the instruction itself, not the address, opcode or comment columns, as this will have a different effect). In the Assemble window that appears, type the following.

ADD EAX, 5

See the following screenshot.

Hit the Assemble button, and then Cancel to get rid of the next Assemble window that appears. You should now see something like the following in your disassembler pane. The new instruction is shown in red.

If you were curious to know the opcodes that compromise this assembly instruction, you would now know what they were – "x83xC0x05" – check the second column from the left in the disassembler pane. This has even been separated in the display somewhat, with a space between the "C0" and the "05", which indicates "x83xC0" is the "ADD EAX", and "x05" is the value to add (in hex format). The ability to quickly discover opcodes for given assembly instructions, so you can use them in your exploits, is one of the ways this code editing feature benefits us as exploit writers.

Take a note of the value stored in the EAX register, and then hit the F7 key to step through execution of this newly added instruction. You should see then that the value of EAX changes by 5, and the EAX register will be colored red to indicate that its value has changed during the execution of the last instruction. See the following screenshot.

The second code editing method we will try to use is a direct binary edit of data in the disassemble pane. This can be a quick way for us to translate opcode values to their equivalent assembly language instructions, and can also allow us to modify values of existing instructions.

Let's try this now. Left click to select the next instruction in the disassembler pane following the "ADD EAX, 5" instruction we just added (this instruction should have its address highlighted with a black background to indicate it will be the next instruction executed by the CPU). The right click, and from the menu that appears select Binary->Edit, or you can hit Ctrl-E.

In the Edit code box that appears, replace the existing hex data of "CC" with "54", as shown below. From our assembly lesson earlier in this tutorial, we know that the opcode of "x54" translates to a "PUSH ESP" instruction.

Hit OK. Sure enough a PUSH ESP instruction should appear in our disassembler pane. Now select the next INT3 instruction, immediately following the one we just edited, and perform another binary edit, this time replacing "CC" with "C3". As we learned earlier, this opcode represents a "RETN". Hit OK. Your disassembler pane should resemble the following screenshot.

Now press F7 to execute the PUSH ESP instruction. You should notice the value of ESP will get pushed onto the stack. Now press F7 again. The RETN instruction will execute, which will POP the top entry off the stack and redirect execution to

That was all very well and good, but I'm sure you are wondering now why should you bother with binary editing when direct assembling seems so much easier. Well there is at least one area in which the use of opcodes is much more efficient — relative JMPs and CALLs.

Let's say we want to add code to perform a JMP 8 bytes ahead. If you remember from our earlier lesson though, JMP instructions were specified using an absolute address in assembly. To save us from having to calculate the address 8 bytes ahead, we can just directly enter the opcode.

Select the top "INT3" and the "ADD, EAX, 5" commands in the disassembler pane, then right click and select Binary->Edit from the menu. The following will appear.

Edit the hex values in the bottom box to "xEBx08x90x90". This is "xEBx08" for the "JMP 8", with two NOPs added to the end. See below.

Hit OK. Your disassembler pane will show as below.

OK, it has taken the opcodes we provided and interpreted this as a "JMP SHORT" and provided an absolute address in memory to jump to – we did not need to calculate the address ourselves in order to add this code to the debugger.

As well as being useful for relative JMPs and CALLs, we can also use this ability of OllyDbg to disassemble binary data any time we have a need to find the assembly equivalent of particular byte values. This can be useful when we need to find which instructions will bypass character restrictions, in order to achieve goals like decoding shellcode or forming a NOP slide.

While I have demonstrated the editing of data only in the disassembler pane, it also works elsewhere in the CPU view, allowing you to edit register values, flag values, stack values as well as any data you can access from the memory dump pane. In the memory dump and disassembler panes, you even have the ability to do binary copies and pastes if the need arises. These features can really come in handy when you are writing and testing custom shellcode.

One important caveat to mention regarding editing data in the debugger in this way is that any changes made in this manner do not apply outside the debugging session. This is useful as supporting functionality when actually creating an exploit, but cannot be used in the actual finished product.

Help in calculating relative address differences

If you need to calculate distance for a relative JMP or CALL statement, or determine the size of an area in memory to see if it's big enough for shellcode, you may be able to make use of OllyDbgs relative memory addressing feature.

Double click on an address entry anywhere in OllyDbg, and the address column will change from displaying absolute memory addresses to displaying the relative offset of each entry from the address initially clicked upon.

The screenshot below shows the disassembler pane after I double clicked on the second address from the top – the one holding the first red NOP instruction. That particular address was then marked with the "==>" indicator, and the other addresses are marked with their relative distance from that position. The entry highlighted in grey is where the top JMP SHORT instruction will land (8 bytes from the start of the instruction following the JMP).

This relative addressing trick also works in the memory dump views and the stack pane. Try it out when you need to calculate memory differences!

Plugins

OllyDbg features a plugin architecture that allows third parties to add functionality to the debugger by providing the necessary code in a dll file which can be placed in the OllyDbg directory.

There are a number of OllyDbg plugins available for download from the OpenRCE site at the following URL: http://www.openrce.org/downloads/browse/OllyDbg_Plugins

In this section, I will briefly cover the use of one plugin that is particularly useful when writing SEH exploits – OllySSEH.

This plugin is available here

[download]

and allows you to easily see which modules loaded with an application can be used to provide overwrite addresses when writing an SEH exploit.

To install the plugin, simply take the OllySSEH.dll file from the ollyssehProjectRelease directory in the zip file, and copy it to the OllyDbg main program directory. Then restart OllyDbg.

Once installed, the functionality provided by the plugin can be accessed via the Plugins menu, SafeSEH->Scan /SafeSEH Modules option.

To use the plugin effectively, you start your target program and use the F9 key to allow it to run in the browser. Modules set to load with the program will be loaded into memory and the debugger, allowing them to be scanned by the plugin. Once the program is running, use the Plugins menu, SafeSEH->Scan /SafeSEH Modules option to scan the modules.

Doing this for vulnserver.exe shows the following output.

The entries in red, which have a SEH mode of "/SafeSEH OFF" are most suitable for use with SEH exploits – any address from these modules can be used as an SEH overwrite address.

These OllyDbg plugins will not work in Immunity Debugger nor will they work in OllyDbg version 2. Immunity Debugger has its own Python engine, and a number of Python based plugin scripts have been provided that allow similar types of information to be gathered.

You should keep your eye out for other useful OllyDbg plugins, which may help in your exploitation work.

What should you learn next?

What should you learn next?

From SOC Analyst to Secure Coder to Security Manager — our team of experts has 12 free training plans to help you hit your goals. Get your free copy now.

What should you learn next?

What should you learn next?

From SOC Analyst to Secure Coder to Security Manager — our team of experts has 12 free training plans to help you hit your goals. Get your free copy now.

The OllyDbg program will be useful as we work through future articles exploiting vulnerabilities in the Vulnserver program.

Stephen Bradshaw
Stephen Bradshaw

Stephen Bradshaw is security researcher for InfoSec Institute and an IT Security Specialist in Australia, with a focus on the areas of penetration testing and incident detection and response.