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.

pic1

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

pic2 pic3 pic4 pic5

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.

pic6

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.

pic7 pic8 pic9

??????????????????????????????? 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()

pic10 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). pic11 pic12 pic13

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 pic14 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) pic15 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 pic16 pic17 pic18 pic18

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?