Lab 2: Sample Code
Lesson:
Sample Code:
#include
#include
void do_something(char *Buffer)
{
char MyVar[128];
strcpy(MyVar,Buffer);
}
int main (int argc, char **argv)
{
do_something(argv[1]);
}
This applications takes an argument (argv[1] and passes the argument to function do_something(). In that function, the argument is copied into a local variable that has a maximum of 128 bytes. So… if the argument is longer than 127 bytes (+ a null byte to terminate the string), the buffer may get overflown.
We can follow the pointer by typing ‘d 0x0022FED8’ (pointer) to actualy see where that data is stored… in the .data portion of our code
NOTE: In the case of Dev-C++, this is 0x98 bytes. So you will see a SUB ESP,0x98 instruction. That way, there will be space available for this variable.
do_something()
The disassembly of the function looks like this: (GET ACTUAL CODE)
00401290 /$ 55 PUSH EBP
00401291 |. 89E5 MOV EBP,ESP
00401293 |. 81EC 98000000 SUB ESP,98
00401299 |. 8B45 08 MOV EAX,DWORD PTR SS:[EBP+8] ; |
0040129C |. 894424 04 MOV DWORD PTR SS:[ESP+4],EAX ; |
004012A0 |. 8D85 78FFFFFF LEA EAX,DWORD PTR SS:[EBP-88] ; |
004012A6 |. 890424 MOV DWORD PTR SS:[ESP],EAX ; |
004012A9 |. E8 72050000 CALL ; \strcpy
004012AE |. C9 LEAVE
004012AF . C3 RETN
NOTE don’t worry about the code too much. You can clearly see the function prolog (PUSH EBP and MOV EBP,ESP), you can also see where space gets allocated for MyVar (SUB ESP,98), and you can see some MOV and LEA instructions (which basically set up the parameters for the strcpy function… taking the pointer where argv[1] sits and using it to copy data from, into MyVar.
NOTE Again In the case of Dev-C++, this is 0x98 bytes. So you will see a SUB ESP,0x98 instruction. That way, there will be space available for this variable.
??????????????????????????????? Address [ebp-88]?????????????????????
MOV BYTE PTR DS:[EDI],DL
DL is LOWER EDX
ABABAB00
EDI = 0022FE54
EDX got moved to EDI
Padding to put Ptr to Argv[1] ???
Above EDI ??? 003d2480 = edi
Above EIP
77BD6F05 = MOV ECX,DWORD PTR SS:[ESP+C] > C = 12 mov “pointer to argv[1]” to ecx
77BD6F3C = MOV EDX,DWORD PTR DS:[ECX]
2ND TIME
77BD6F29 = MOV DWORD PTR DS:[EDI],EDX
???? ALREADY AT BYTE SITE
77BD6F80 = MOV BYTE PTR DS:[EDI],DL
> POINTS TO AAAA
do_something()
If there would not have been a strcpy() in this function, the function would now end and "unwind" the stack.
Basically, it would just move ESP back to the location where saved EIP was, and then issues a RET instruction A ret, in this case, will pick up the saved EIP pointer from the stack and jump to it Thus, it will go back to the main function, right after where do_something() was called.
The epilogue instruction is executed by a LEAVE instruction (which will restore both the frame pointer and EIP).
strcpy()
This function will read data, from the address pointed to it by [Buffer], and store it in, reading all data until it sees a null byte (string terminator)
While it copies the data, ESP stays where it is
The strcpy() does not use PUSH instructions to put data on the stack
It basically reads a byte and writes it to the stack, using an index (for example ESP, ESP+1, ESP+2, etc)
So after the copy, ESP still points at the beginning of the string
That means…
If the data in [Buffer] is somewhat longer than 0x98 bytes, the strcpy() will overwrite saved EBP and eventually saved EIP (and so on)
After all, it just continues to read & write until it reaches a null byte in the source location (in case of a string)
ESP still points at the begin of the string. The strcpy() completes as if nothing is wrong After the strcpy(), the function ends, this is where things get interesting… The function epilogue kicks in Basically, it will move ESP back to the location where saved EIP was stored, and it will issue a RET It will take the pointer (AAAA or 0x41414141 in our case, since it got overwritten), and will jump to that address
Controlling EIP
So you control EIP Long story short, by controlling EIP, you basically change the return address that the function will uses in order to “resume normal flow” Of course, if you change this return address by issuing a buffer overflow, it’s not a “normal flow” anymore
Back to Easy RM2MP3 Converter:
In the upper left corner, you have the CPU view, which shows assembly instructions and their opcodes
NOTE the window is empty because EIP currently points at 41414141 and that’s not a valid address
In the upper right windows, you can see the registers
In the lower left corner, you see the memory dump of 00446000
In the lower right corner, you can see the contents of the stack (so the contents of memory at the location where ESP points at)
In both cases, we can see that the instruction pointer contains 41414141, which is the hexadecimal representation for AAAA
A quick note before proceeding: On intel x86, the addresses are stored little-endian (so backwards)
The AAAA you are seeing is in fact AAAA (or, if you have sent ABCD in your buffer, EIP would point at 44434241 (DCBA)
So, it looks like part of our m3u file was read into the buffer and caused the buffer to overflow
We have been able to overflow the buffer and write across the instruction pointer, so we may be able to control the value of EIP
Since our file does only contain A’s, we don’t know exactly how big our buffer needs to be in order to write exactly into EIP
In other words, if we want to be specific in overwriting EIP (so we can feed it usable data and make it jump to our evil code, we need to know the exact position in our buffer/payload where we overwrite the return address (which will become EIP when the function returns)
This position is often referred to as the “offset”
Determining location of EIP:
We know that the buffer is somewhere between 20000 and 30000
We could try and figure out where it is in there by cutting the number in half and replacing with “B” (0x42), and keep repeating until we can narrow down to the exact location
However, Metasploit has a nice tool (pattern_create.rb) to assist us with calculating the offset
NOTE: It will generate a string that contains unique patterns
Using this pattern (and the value of EIP after using the pattern in our malicious .m3u file), we can see how big the buffer should be to write exactly into EIP
Time to test your skillz
Assignment:
Step 1 – Create example.c
Step 2 – Compile code
Step 3 – Access file from target machine
Step 4 – Launch Immunity Debugger
Step 5 – Launch example_[sid].exe in debugger
Step 6 – Run program
Step 7 – Create buffer overflow in example_[sid].exe
Feel free to go back and forth through this lab as we talk about the theory in class. It will help seeing what is happening in the CPU, the different registers, in the memory dump, and in the stack.
As we go through the labs, you can press [F2] to set break points in the program. When you run the program, it will stop at each of these break points.
When the software is paused, you can press [F7] to step through each step through each step, including stepping into each method called.
You can press [F8] to step through each step, except this will step over a function call, and simply go to the next line after the method has executed.
Answers:
View associated .txt file
Last updated
Was this helpful?