Walkthrough: Seeing a Format String Attack in Action

This walkthrough is completed on a 32-bit machine running Microsoft Windows XP. Although any debugger could be used, the examples given show Microsoft Visual Studio.

Finding the Format String Bug

First Steps:     The first step is to see how this application works. Grab the sample pickle application from this book s companion Web site and follow along.

For this case, the application uses a binary file (appropriately called dill.pickle) to simulate an attacker s untrustworthy input with this pickle.exe application. Several such files are included with pickle.exe. Ordinarily, use a binary editor to create and edit these files. To see how this application works, start by seeing what happens with basic input.

Input:     DILL1.Pickle is the first input file to use, which appears as follows when loaded in abinary editor (the actual contents of the file start with hex 43, which corresponds to the letter C ):

image from book

Processing:    

 C:\>pickle DILL1.Pickle 

Output:    

 Reading pickle file.Done. 

Analysis:     This program doesn t display the input, but apparently it does process the input. Which cases should be tested ? In addition to overflows, look for format string vulnerabilities by including format specifiers in the input. Perhaps the data is interpreted by a function that handles format string specifiers.

Next Steps:     For this example, include %n in the dill.pickle input file and run the program again.

Input:     Change the contents of the input file to include %n , as shown in the following graphic. Note that this is included as DILL2.Pickle.

image from book

Processing:    

 C:\>pickle DILL2.Pickle 

Results:     Aha! This time the program crashes!

Analysis:     In this simple case, it is fairly obvious that adding %n to the input file probably caused the process to crash. Don t jump to conclusions too quickly, however. Although there is compelling evidence, just because the process crashed when the input data included %n doesn t necessarily guarantee this is an exploitable security bug; confirming whether it is a format string bug requires further investigation.

Analyzing Exploitability

Run the program in the debugger to investigate and try to figure out what is happening.

Result:    

image from book

Breaking in the debugger reveals more information, as shown in Figure 9-1.

image from book
Figure 9-1: Debugging Pickle.exe

Analysis:     Per Figure 9-1, the crash occurred on the following instruction:

 00401DBC mov dword ptr [eax],ecx 

It looks like this CPU instruction references CPU registers EAX and ECX. Specifically , it moves the value of ECX to the address specified by EAX. Take a look at the values of these registers. In Figure 9-1, notice EAX is 0x00000000 and ECX is 0x00000004. It might be pretty useful if the input data could somehow influence EAX and ECX.

OK, remember the input was AAAA %n. ECX is 0x00000004. What happens when the input is AAAAAAAA%n ? (This is saved as DILL3.Pickle.) DILL3.Pickle crashes as well. ECX turns out to be 0x00000008 for that case. It happens that ECX is the number of characters before the %n . As an attacker, it looks like the input data can control ECX, and the input causes writing ECX to where EAX points. EAX is zero, so why is this a big deal? It s probably just failing to catch a null pointer, right? No, it is more serious.

Next Steps:     The next step is to experiment and see whether the EAX register can be manipulated.

Input:     Try the following input instead (this walkthrough later covers how to figure out this particular string in DILL4.Pickle):

image from book

Processing:    

 C:\>pickle DILL4.Pickle 

    Result:

image from book
image from book

Observations:     What can the preceding debugger window explain about the bug? This time the crash happens while trying to write the value 0x00000011 (ECX) to memory address 0x41414141 (EAX). The input file started out with AAAA, which is 0x41414141 in hexadecimal.

Analysis:     It looks like EAX can be manipulated based on the malicious input file. What about ECX? Why is ECX 0x00000011 (decimal 17)? Remember, the input was AAAA%x%x%x%x%n . As a format string specifier , %x writes the hexadecimal value of a parameter. Each %x might cause up to 8 bytes to be written out (hexadecimal representation of the 4-byte value on the stack, leading zeros are trimmed , etc.), and the function wasn t finished being processed when the crash occurred.

The malicious data can control EAX, and perhaps also ECX, by writing a lot of bytes out because the more bytes the program writes, the bigger ECX is. To exploit this by getting ECX to be a usefully large value requires a huge string. If it was desirable to write the address 0x11FFFF, for example, the input data would need to be a string that causes the output of that many characters. It turns out there is a shortcut because the format codes have precision fields that expand a number. For example, if the number 0x3 is formatted as %x , it looks like a 3 in the output. If the number 0x3 is specified to a precision of 5, it is formatted to look like 00003. This would be done by using %.5x as a format string specifier.

Important  

Testers shouldn t have to exploit these bugs to get them fixed. Unfortunately, many development organizations judge the code innocent until proven guilty, whereas the savvy tester views it as buggy until proven otherwise .

Digging Deeper: Working Around Exploitability Problems

In this section, we present a more complete set of details of how to exploit format string overflows on x86 architectures for the VC runtime included with Microsoft Visual Studio. Feel free to follow along, and you might also pick up a few additional tips and tricks for working with the debugger and assessing false claims made by others.

Problem: Getting ECX to a Useful Value

Next Steps:     The next step is to experiment with precision fields in format string specifiers in an attempt to evaluate how they can reduce the size of the input required to elevate the resulting ECX to a useful value, such as a function return address, an exception handler, or a function pointer on the stack. Options for exploiting these are similar to the steps for controlling the instruction pointer EIP discussed in Chapter 8, Buffer Overflows and Stack and Heap Manipulation.

That brings up a question: What is a useful value? Remember that ECX is the value that is written to memory when %n is processed (in this case). Although clever people can make skilled use of small values, this walkthrough shows how to construct useful input when you want the program to run code given the input data, which is typically located on the stack or in the heap, and not in small addresses.

Input:     Try %.32x as an experiment.

image from book

Processing:    

 C:\>pickle DILL5.Pickle 

    Result:

image from book

Press [Break] and figure out what happened .

image from book

Observations:     Same place in the code as before. Oh, no! This input does not result in 0x41414141 being where the program writes data in memory any more. EAX and ECX are different register values from before.

Analysis:     A larger value for ECX was expected, and it is good this input got it. Maybe EAX was pulled off of a different place on the stack. After all, this data isn t all that different from the previous input.

Next Steps:     The next strategy is to use the debugger to look around a bit to figure out where the input data is, where EAX came from, and to get some questions answered about how this application works.

Action:     Next, look at the stack in memory to figure out what happened. Do this by looking at memory referenced by the ESP processor register.

Results:    

image from book

Observations:     Does any of this data look familiar? No immediate clues seem evident.

Next Steps:     Find the data. It should be here somewhere.

Action:     The stack fills from higher memory addresses to lower ones, so scroll down to look for the input data.

Results:     Here is a memory view of the input data on the stack.

image from book

Observations:     It turns out the input data is down a little ways (see it?). Also, notice how 0x00000A28 is almost right next to it (at offset 0x0011F6D4 above).

Analysis:     The 0x00000A28 is not part of the original input data specified, so there is no way to control it directly. Maybe there is a way to move the stack pointer back into a copy of the input data. Doesn t a regular %d or %x pop the value off of the call stack and insert it into the output? Wouldn t that advance the stack pointer? It is worth a try. For each %x processed, the stack pointer moves 4 bytes. Notice in the preceding graphic that AAAA (0x41414141) is 12 bytes after where the 0x00000A28 is in memory.

Next Steps:     The plan is to attempt to adjust EAX so it becomes 0x41414141 after the crash (specifically, that it is the 0x41414141 from the input data).

Action:     Since the target is 12 bytes farther and each %x advances the stack pointer by 4 bytes, try adding %x%x%x to the input data.

image from book

Processing:    

 C:\>pickle DILL6.Pickle 

Result:

image from book

Observations:     Back in business! OK, now the value 0x00000030 is written to address 0x41414141, which is from the malicious input data.

Overwriting the Stack Return Address

Analysis:     Now that the input data can bump up the written value some, where would an interesting place to write the four bytes be? Well, there are a lot of interesting possibilities, but only one that works well is needed. For this example, suppose it is desirable to overwrite the return function pointer for the current function to try to exploit this. If the malicious input data overwrites the return address with an address of where the input data itself is stored, the supplier of the input data can run his or her own code.

Next Steps:     The next task is to find out where the return pointer is in memory. To determine where the return address is, try to see what happens in the case when the program gets expected data. The most reliable way to figure out where the return address is stored on the call stack is to insert some data that doesn t cause a crash so you can analyze what normally happens past the point where the crash occurs.

Action:     Scroll down to the next return ( ret ) instruction and set another breakpoint.

Results:    

image from book

Action:     Next, restart the process but use normal, expected input (DILL1.Pickle) with the breakpoint set on 0x00401FEA as shown in the preceding graphic.

Results:    

image from book

Observations:     Looking at ESP reveals where the return value is stored .

image from book

Analysis:     OK. The exploit can overwrite the value at 0x0011F6A8, and when the function returns it will jump into the input data. Nice! The location of the return address to overwrite (0x0011F6A8) is 1,177,256 in decimal, so something around %.1177256x should work, right? Yeah, wishful thinking.

Problem: Limits on Output per Format Specifier

The particular compiler used in generating pickle.exe has a maximum precision of 512, meaning that when printf( %.512x ) and printf( %.513x ) are used, both statements return the information that only 512 characters were written and increment ECX only by as much. Ouch. How can the exploit work around this limitation? It turns out that although each format specifier contains a maximum, there is no overall count limit (except for the fact the specifier is a signed integer). So printf( %.512x%.512x ) returns the information that 1,024 characters were written. Also, there is an upper bound on how much the input data can chain together, but the limit is fairly high. How many will be needed? Dividing 1,177,256 by 512 is roughly 2,300. Yikes ”2,300 copies of %.512x is 13,800 bytes long. Pickle.exe won t take a string that long, so at first glance it appears this might not be exploitable.

But these bugs deserve more attention than a single passing glance. If the runtime changes (or is a different platform/compiler), this might be more easily exploitable ”don t say a bug is not exploitable based on the way the application appears to function today. Often, as in the pickle.exe case, buffers cannot accommodate the operation and other failures happen to destroy the chance of malicious input doing more than crashing the process.

Important  

Even if the software development team cannot immediately figure out how to exploit the issue, the team should still fix it. It s still a bug!

There is almost always a way for determined individuals to exploit format string bugs. Consider what happens if, instead of overwriting at 0x0011F6A8, the input data instead overwrites at 0x0011F6A9? Take a look at how this might work.

In practice, the actual data might reside elsewhere, but just suppose for a minute the exploit data/code was hypothetically to reside at 0x0011FEC4 over a span of at least 256 bytes (0x0011FEC4 through 0x0011FFC3) (assume the following B characters are the exploit data for the time being):

image from book

If the exploit could overwrite at address 0x0011F6A9 with a small count 0x000011FF, the stack would look like this:

image from book

In this case, perhaps the return address would be pulled from 0x0011F6A8 as of 0x0011FF01, which is within the input data. Then the exploit would still be able to run the input data as code. Typically, in practice it doesn t matter what comes immediately after the return address. Furthermore, even if the exploit did need to preserve the byte at 0x0011F6AC, the exploit code could just reconstruct it.

Next Steps:     The input data must be bigger so data is sitting at some address 0x00####01. Given that, the next task is to figure out how many %.###x format specifiers are needed and what the values of # is to write the correct amount.

Result:     Each %.512x can deliver up to 0x200. The exploit needs to reach approximately 0x11F6 (remember from earlier where the input data actually is?), so 0x1200 divided by 0x200 is 9. The exploit can probably get by using 8 because of the additional characters in the string counting as well. So insert 8 %. 512x strings.

Now the input data is ready to have the exploit code added, right? Wrong ”there is another problem.

Problem: No Null Bytes Allowed

Analysis:     Remember how many strings in the C language often end with a null character (byte, in this case)? The address to overwrite (0x0011F6A9) has a null byte in it, and it is currently located at the beginning of the input data. The effective exploit must place it after the %n , after the payload, and after the %x values, or the printf function will think the string ends with the null byte and never process the %n .

Next Steps:     Change the input file to include the dummy payload of B characters and get things working so the positioning is correct and the B characters can be replaced with a real proof-of-concept payload later.

Action:     For now, if the input contains a string of 128 B s as the payload (this will change later), the input file looks as follows:

image from book

Note that the final exploit will probably need additional %x specifiers, positioned such that the string of B s is in the target location for the payload and the %x values are situated in more uninteresting locations yet are still processed as part of the format string.

Processing:    

 C:\>pickle DILL7.Pickle 

Results:    

image from book

Observations:     When you break and look in memory, you see that the input data is stored at the following locations:

image from book

Analysis:     The payload needs to overwrite with 0x000011F7, but ECX is only at 0x00001090.

Next Steps:     ECX will have to wait a minute; first the exploit needs to get EAX to the target AAAA. Once EAX is read from the input data from the correct location and set to 0x41414141 (AAAA), the exploit can replace that with the address to jump to. To do that, first figure out how EAX is assigned.

Action:     Still at the break in the debugger, scan upward in the Disassembly window, and look for the place EAX likely is assigned.

Observations:    

image from book

In the preceding graphic, notice that EAX appears to be changed at the instruction at 0x00401DAB.

Action:     Remove the breakpoint from 0x00401DBC and put a breakpoint at address 0x00401DAB. The goal in doing so is to determine from where EAX actually is read so the correct adjustments can be made to read in EAX from the end of the input data where the AAAA is located. Restart the pickle.exe application with a breakpoint set at 0x00401DAB.

Result:     Execution should break at the following line of assembly:

image from book

Observation:     The Memory window shows this:

image from book

Analysis:     The target address is the four A characters (at address 0x0011F7A2), and the current input is writing to 0x0011F6F0. 0x0011F7A2 minus 0x0011F6F0 is 0xB2 (decimal 178). For each %x , the stack pointer advances 4 bytes but requires 2 in the form of a larger input string, so there is a net stack pointer gain of 2 bytes per %x . Dividing 178 by 2 is 89, so the input needs 89 %x specifiers added to hit the target.

Next Steps:     Change the input file to get back on track.

Action:     When you insert 89 %x codes into the input data to align the address, it might look as follows:

image from book

Processing:    

 C:\>pickle DILL8.Pickle 

Running hits the breakpoint:

image from book

Check your math:

image from book

OK! Running now results in the following:

image from book

Observations:     A quick look at ECX is encouraging ”adding the %x values has given more than enough byte count:

image from book

Another Format String Specifier Challenge

Next Steps:     Set up EAX and ECX for a successful exploit now (by using the input data).

Action:     The address the input needs to overwrite was one byte past the place where the return value was stored on the call stack, at 0x0011F6A9. Edit the pickle input data to replace the AAAA with the right bits (included as DILL9.Pickle):

image from book

ECX was 0x00001345 and we need it to be 0x000011F7 (the payload will be in the data at 0x00 11F7 01), a difference of 0x14E, or decimal 334. Subtracting 334 from 512 is 178, so one of the %.512x format specifiers becomes %.178x (in DILL10.Pickle):

image from book

Processing:    

 C:\>pickle DILL10.Pickle 

Result:     When you run this, the program crashes trying to write to memory address 0x00F611A9. Oops! The intention was to write to address 0x0011F6A9.

Caution  

Addresses on some systems are stored backward (using little-endian notation). It can be easy to make mistakes when working with them. Have a tablet ready to write down addresses and don t be afraid to double-check everything. It would be a shame, for example, if you failed to recognize an exploitable security issue because of a simple mathematical mistake.

Action:     Fix the input file and rerun. Now we have (DILL11.Pickle):

image from book

Processing:    

 C:\>pickle DILL11.Pickle 

Result:     Nothing unusual happens ”which is weird.

Next Steps:     Debug what happened.

Results:     Run the program with a debug breakpoint set on 0x00401FEA (the ret instruction), but this time set it on the second 0x00401FEA break so you can see that the stack arrangement has shifted somewhat.

image from book

Analysis:     The stack layout must have changed, and the exploit didn t actually overwrite the return address. The real place to write to is 0x0011F684 + 1, or 0x0011F685.

Next Steps:     Change the input file and rerun.

Action:     Change the address written to from 0x0011F6A9 to 0x0011F685. The input file now looks as follows (included as DILL12.Pickle):

image from book

Processing:    

 C:\>pickle DILL12.Pickle 

Result:    

image from book

Analysis:     Hmm. Where did that come from? If you could overwrite an exception handler, you d be set; but there isn t one handy. If you spend time reading through the runtime library code behind the printf statement and figuring out how it works, you might find that the printf function is overwriting the right data in memory but is continuing to process the string, choking on the 0x85 byte. Analysis of the exit conditions for the loop that interprets the format string shows that terminating conditions are essentially when the string encounters an error writing the output or the null terminator of the input string.

More Info  

For more details about how the loop works for this case, see the printf function in printf.c and the output function in output.c included with Visual C++. Refer to Chapter 17 for suggestions about how to approach analyzing cases for which code is not available. Remember that exhaustively checking the behavior of all possible input bytes 0x00 through 0xFF for differences in behavior might be an effective approach as well.

Next Steps:     Change the data so the 0x85 byte is never processed by the format string interpreter.

Action:     Maybe the exploit could pull off something fancy and skip over these bytes with a few %x format strings, but for now just insert null bytes prior to the address.

Fixing the input data requires injecting two null bytes and another %x to compensate, and decrementing the 178 number in the %.178x to compensate. Although a number of changes need to be made to the input data, your approach should be to make one or two changes at a time so consequences are clear and the intended effect can be verified . First, insert the %x and the two null bytes (DILL13.Pickle):

image from book

Now put a breakpoint back on the return address at 0x00401FEA and run again to make sure the right things happen.

Result:     When you run, you get the following:

image from book

Stepping in the debugger looks like this:

image from book

Observation:     Hmm. That s not the input data.

Analysis:     What happened? Well, there is more than one printf . The first one isn t the issue.

Action:     In the debugger, continue execution. The breakpoint is triggered again. Step again.

Result:     This time, you get this:

image from book

Analysis:     That still is not the input data. This input data causes the exploit to run code at 0x0011FF62. Remember, the input data is positioned at 0x0011F7## in memory.

Next Steps:     Reduce the %178.x in the input data until the exploit runs the input data as code.

Analysis:     The input data is at 0x0011F762. So 0x000011FF (taken from 0x0011FF62 ” remember we overwrote the first three bytes) minus 0x0011F7 is just 8, and %178.x would become %170.x (DILL14.Pickle):

image from book

Action:     In the debugger, run with the breakpoint set on the return address at 0x00401FEA. The second time the breakpoint is triggered. Stepping you see the following.

Result:    

image from book

Analysis:     That s in the input data! Yes! The program is attempting to process the %x%x%x as code. That s data from the input file. The input can include any code here and it will be run.

Note  

Running interesting code at 0x0011F762 requires moving some of the %x specifiers elsewhere within the input data to make room. Remember this.

Next Steps:     OK, the next step is to develop an interesting payload. Because this is a proof of concept, running the calculator should be sufficient.

Building a Simple Payload

When you look up WinExec in kernel32.dll (using Depends.exe, which comes with Visual Studio), you see the entry point at the following offset within kernel32.dll:

image from book
More Info  

For more information about Depends.exe, see http://www.dependencywalker.com/ . Microsoft provides the Depends.exe utility along with Windows XP Service Pack 2 (SP2) support tools at http://www.microsoft.com/downloads/details.aspx?FamilyId=49AE8576-9BB9-4126-9761-BA8011FABF38&displaylang=en .

Look back in the debugger with pickle.exe running, and notice kernel32.dll is actually loaded at 0x7C800000:

image from book

Analysis:     The WinExec entry point is actually located at 0x7C800000 + 0x0006114D, or 0x7C86114D in memory, in the target pickle.exe process for this particular computer.

Note  

If you are following along in the walkthrough, the location of the WinExec application programming interface (API) and kernel32.dll might be different on your machine. For the walkthrough to continue to work, find the correct address to substitute. A number of ways to create payloads are not dependent on the location of WinExec or other API entry points, but the details are beyond the scope of this book.

The Compiler Is the Payload Coder s Friend

Next Steps:     The payload exploit needs to be coded in assembly. Writing code in assembly can be time-consuming ; usually writing C is much faster. One way to save some time is to write the payload in C and then look at the assembly the compiler creates from the C code.

Tip  

When writing exploit code, taking advantage of a good optimizing compiler and analyzing the code it generates can be very useful.

Action:     Create a simple WinExec call and compile it. WinExec takes two parameters, as follows:

image from book

Analysis:     Pay attention to how the compiler does things. How does the compiled code actually call WinExec ? First, it pushes the second parameter onto the call stack; then it pushes an offset to the string containing the command to run, followed by a call to WinExec .

Next Steps:     Now that the compiler-generated code has clarified how to call WinExec , the details of the pickle.exe proof-of-concept exploit can begin to take shape.

Draft Exploit Based on WinExec Disassembly

 __asm{       push 1                       //SW_NORMAL 00401017 6A 01            push 1       mov ecx, 55555555h           //ECX to get a ptr to the string 00401019 B9 55 55 55 55   mov           ecx,55555555h       sub ecx, 01010101h           //To get the null 0040101E 81 E9 01 01 01 01 sub           ecx,1010101h       push ecx                     //Shove ECX as a ptr to the string. 00401024 51               push          ecx       add ecx, 08h                 //Index to the end of the string. 00401025 83 C1 08         add           ecx,08h       //mov [ecx], 01h             //End of string should be 0x01 already       dec [ecx]                    // so that when it is decremented                                    // it will be null. 00401028 FE 09            dec           byte ptr [ecx]       mov eax, 7C86114Dh           //Call WinExec... 0040102A B8 4D 11 86 7C   mov           eax,7C86114Dh       call eax 0040102F FF D0            call         eax       int 3                       //Break in the debugger. 00401031 CC               int          3    }; 

Filling in the Details: What Do You Want to Run Today?

Observation:     For the draft exploit to actually work, the preceding 0x55555555 will be replaced with the real address of the string calc.exe . Because that is an address with a null character in it (on the stack) the exploit code can simply add 0x01010101 to the address, which will be subtracted at run time.

Note  

Remember that running this on a different machine requires replacing the WinExec address as well (in bold text in the following code example). There are more portable ways to accomplish the same thing, but because this is proof-of-concept code, this simple approach is sufficient.

Action:     Actually producing the exploit requires getting the opcodes (from the draft exploit section earlier) and inserting the address of WinExec (0x7C86114D). The bold type indicates which bytes change:

 0x6A 0x01 0xB9 0x55 0x55 0x55 0x55 0x81 0xE9 0x01 0x01 0x01 0x01 0x51 0x83 0xC1 0x08 0xFE 0x 09 0xB8 0x4D 0x11 0x86 0x7C 0xFF 0xD0 0xCC 

Next Steps:     Where should the input data store the string calc.exe ?

Action:     The input file needs to be rearranged a bit so that the section of B bytes (payload) starts at 0x0011F762, the address the exploit can run code at, so that replacing the string of B bytes with the payload will actually result in the payload running. For this walkthrough, put the calc.exe string right before the payload. The string calc.exe plus a null terminator is 9 bytes. Remember the earlier issue of having to move some of the %x specifiers for the pay-load. To get the extra 9 bytes for calc.exe and the null terminator, 5 more %x specifiers need to be moved (for a total of 30). Changing the input data to move the %x specifiers and add in the calc.exe string with its 0x01 trailing byte (more on this later) results in the following (DILL15.Pickle):

image from book

Running with the preceding input and the breakpoints set at 0x00401FEA reveals where data winds up in memory. The second time the breakpoint triggers, step once and look at where EIP points.

image from book

Analysis:     In the Memory window, see where calc.exe is located. It is at 0x0011F758. 0x0011F758 plus 0x01010101 is 0x0112F859, so we replace the 0x55555555 with 0x0112F859. Note that the 0x01 will be decremented to a null terminator ( dec [ecx] ) by the payload prior to calling WinExec ”this is necessary because the string precedes the %n and theformat string interpretation loop in pickle.exe terminates when it encounters a null terminator.

Making the adjustments to the payload code results in the following:

 0x6A 0x01 0xB9 0x59 0xF8 0x12 0x01 0x81 0xE9 0x01 0x01 0x01 0x01 0x51 0x83 0xC1 0x08 0xFE 0x09 0xB8 0x4D 0x11 0x86 0x7C 0xFF 0xD0 0xCC 

Next Steps:     Determine at which offset within the input data to insert the preceding payload code.

Preparation:     Remember how calc.exe was positioned right before the code to run in the malicious input data? The string calc.exe is located at 0x0011F758 in memory, and code runs at 0x0011F762. 0x0011F762 minus 0x0011F758 is 0x0A, so the trick is to insert the payload 10 bytes after the start of calc.exe in the input data.

Action:     Make a new input file to reflect the latest changes. This looks as follows (DILL16.Pickle):

image from book

Next Steps:     The payload constructed will work in theory, but the next step is to confirm it works in practice.

Testing the Payload

Action:     With the breakpoint still set at 0x00401FEA, run the new payload:

image from book

The second time the breakpoint is triggered, stepping in the debugger reveals the following:

image from book

Result:     That is not the exploit data! What happened this time?

Analysis:     Look at the address where code is going to run. It is 0x0011F562. The payload from the data is at 0x0011F762, 0x00000200 bytes later. Apparently, not all of the opcodes in the payload data were considered characters that could be printed (remember this latest input replaced B characters, which were all printable), so ECX was incremented to 0x000011F5 instead of 0x000011F7.

Next Steps:     Fix the problem and try again.

Action:     This is easy to fix; just change the %.170x to %.172x (this is DILL17.Pickle):

image from book

Try running again with the change. Again, after the second time at the breakpoint at 0x00401FEA, step in the debugger.

Result:     Much better. The intended payload is running!

image from book

Next Steps:     Step through and confirm the payload works as expected.

Action:     Each of the following instructions includes an explanation of how it works and where to look to confirm the instruction operated successfully.

Observation:    

 0011F762 6A 01        push       1 

Analysis:     This instruction pushes the second parameter for the WinExec call on the stack.

Action:     To confirm it operated correctly, look at ESP in memory when it is done.

image from book

    Observation:

 0011F764 B9 59 F8 12 01   mov          ecx,112F859h 

Analysis:     This sets ECX to 0x0112F859. Remember what this number is? This is the offset to the string calc.exe , 0x0011F758, plus 0x01010101. The reason for adding the 0x01010101 is there cannot be any null (0x00) bytes in this part of the exploit in the input data.

Action:     To confirm, look at the value of ECX.

image from book

Observation:    

 0011F769 81 E9 01 01 01 01 sub         ecx,1010101h 

Analysis:     To get the real offset to calc.exe the exploit subtracts the 0x01010101.

image from book

Action:     To confirm this worked properly, ECX should now point to calc.exe :

image from book

Observation:    

 0011F76F 51               push        ecx 

Analysis:     This instruction pushes the first parameter for the WinExec call (pointer to calc.exe ) on the stack.

Action:     To confirm it operated correctly, look at ESP in memory when done, confirming that ESP points to the location of calc.exe (0x0011F758).

image from book

Observation:    

 0011F770 83 C1 08        add        ecx,8 

Analysis:     Now the payload needs to convert the 0x01 byte at the end of calc.exe into a null terminator in memory. One way to do this is to first increment ECX by the length of calc.exe (8 bytes) so it is pointing at the 0x01 byte.

Action:     To confirm this worked correctly, check that ECX points to the 0x01 byte following calc.exe .

Observation:    

 0011F773 FE 09             dec        byte ptr [ecx] 

Analysis:     Now this subtracts one from (decrements) whatever ECX points to in memory. In this case, it is the 0x01 byte following calc.exe causing it to become a null- terminated string in memory.

Action:     Verify this has the right effect (null terminating the calc.exe string) in memory:

image from book

Observation:    

 0011F775 B8 4D 11 86 7C mov        eax,7C86114Dh 

Analysis:     Depends.exe and the debugger together indicate that WinExec is loaded at 0x7C86114D on this particular machine, and although there are complex ways of handling scenarios where this is less clear, for now the walkthrough just points EAX at 0x7C86114D. (Remember, this will be different on different computers.)

Note  

Again, attackers don t always need to know the base address and offset to the function on a victim s machine; a number of ways not detailed here can be used to create robust machine-independent exploits.

Observation:    

 0011F77A FF D0            call        eax 

Analysis:     This actually makes the WinExec call.

Action:     Verify that when stepped over in the debugger, calc.exe launches.

Observation:    

 0011F77C CC             int        3 

Analysis:     This instruction is present to throw an exception so the debugger will trap it here. Real-world exploits probably wouldn t leave it this way, but it is fine for this simple proof of concept.

The payload works fine as is, but can be compressed considerably as well.

image from book


Hunting Security Bugs
Hunting Security Bugs
ISBN: 073562187X
EAN: 2147483647
Year: 2004
Pages: 156

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net