Assignment: ROP

Changelog:

Your Task

  1. Download the target executable program dumbledore-rop.exe, which is the same program that we used for the OVER assignment, compiled and linked differently. Your job is to exploit a buffer overflow using an ROP chain, most likely with assistance from the tool ROPgadget (linked below) to construct it.

    Your goal is to construct a program input such that the program’s output ends with:

    Thank you, YOUR NAME.
    I recommend that you get a grade of A on this assignment.
    

    If your attack program is in a file called attack.py3 or attack.py, your exploit must work when run as follows, which is similar to OVER, but without the dependency on libc (dumbledore-rop.exe is statically linked, so it does not depend on libc):

    • from the directory containing dumbledore-rop.exe, running the shell command:

       python3 attack.py3 > attack.txt
       setarch -RL env - ./dumbledore-rop.exe < attack.txt
      

    or a similar program named attack.c or attack.cc or attack.py2 (which we will run differently, as in the OVER assignment).

Tools

  1. For this assignmentusing the tool ROPgadget, available here. Follow the installation instructions in “Install” section of the README.md.

Some notes about running/installing it:

Addt’l Resources

  1. The slides on return-oriented programming.

  2. The “definitive” article on return-oriented programming, especially section 4.

Hints

Using ROPgadget

  1. Given a binary foo.exe, running

    ROPgadget.py --binary foo.exe
    

    will output a list of gadgets discovered in the executable.

    ROPgadget.py also has many additional options which you can find by running

    ROPgadget.py --help
    

The ROPchain option

  1. ROPgadget has an --ropchain option that attempts to construct a generic ROP chain that executes a shell (that is, a program that accepts commands to run, like what you get when you open a terminal on Linux). So running

    ROPgadget.py --binary foo.exe --ropchain
    

    will output the list of gadgets, followed by a Python 2 script given the gadgets found in foo.exe.

    (In a Unix-like shell, you can use something like

    some_command > output.txt
    

    to run the command some_command, redirecting its output to output.txt)

  2. Although you could construct a custom ROP chain that outputs the text you want, it would probably be easier to use this generic ROP chain, and then provide a shell command that will output the text you desire.

    In the “executing a command with a shell” section below, we describe how to construct suitable input for the shell that this exploit would start.

  3. The Python 2 script that ROPgadget constructs is incomplete; it places the chain in a variable p, but does not:

    • output that chain
    • add padding such that the beginning of the chain will overwrite a return address
    • add shell commands after the chain so some useful command will run afterwards
    • add padding before the shell command to deal with stdio buffering (see hint below)

The executable is offset

  1. The dumbledore-rop.exe we supply is position-independent, so it can be loaded at a different locations. (If we did not disable address space layout randomization (ASLR), this would allow the executable to fully take advantage of ASLR.)

    By default, ROPgadget.py assuems that executables are loaded at the addresses encoded in them (the one shown by objdump), which isn’t the same as the address that’s chosen by the OS, even with ASLR disabled.

    You can change this default by giving ROPgadget an offset with the --offset option:

    ROPgadget.py --binary foo.exe --offset 0x123456 ...
    

    (where 0x123456 is the difference between the addresses shown in objdump and the actual address to which the executable is loaded).

  2. If the you run the debugger with the same settings as the executable (such as by running gdb with setarch x86_64 -RL gdb), you should get addresses in the debugger which are consistent with the addresses from testing.

Forbidden bytes

  1. It’s possible that some gadgets that ROPgadget.py finds will have an address which when written out as bytes contain 0x10, which is the newline character. Since this causes the program to stop reading input, you would not be able to input these addresses. You can have ROPgadget.py ignore these gadgets by using the --badbytes option

Positioning the ROP chain

  1. You need to make sure the ROP chain is positioned properly over the return address. If it is not, you are likely to get a segfault, or the normal output.

    Positioning the beginning of the ROP chain should be exactly like positioning the overwritten return address in OVER (though the program is slightly different). You can either:

    • examine the objdump output to determine what’s on the stack in the vulnerable function, and therefore the distance between the return address and the beginning of the buffer; or
    • try inputting different amounts of data and see what length is required to get a segfault. This should indicate where the return address starts relative to the buffer.

Executing a command with a shell

Padding before shell comamnd to handle buffers

  1. To execute a command in the shell that ROPgadget.py’s exploit code would start, you will need to output a shell command after the exploit string. When you do this, you will need additional padding before this shell command to deal with gets’s buffering.

    stdio.h functions like gets default to reading a lot of extra data from a file to avoid making many calls to the OS to get data. This data is buffered for future gets, getchar, etc. calls (making them faster). But when the exploit code generated by ROPgadget.py starts the shell, these buffers are discarded.

  2. To work around stdio buffering, you can have some unused bytes between the attack string and the shell commands, such as several thousand newlines. (Note that the python expression "\n" * 10000 will produce a string containing 10000 newlines.) This will act similarly to a NOP sled.

A suitable shell command

  1. The echo command is useful for outputting text of your choice:

    echo "Thank you, YOUR NAME."
    echo "I recommend that you get a grade of A on this assignment."
    

    (Originally the command above missed the word that, which wasn’t consistent with the “Your Task” section. We’ll accept either output.)

No output?

  1. If your ROP chain causes the program to not produce any output, this is probably because you need to add padding between your exploit and the shell commands. You can check if a shell is actually running by seeing if the debugger gives a message like “process 13977 is executing new program: /bin/dash”.

    See the “padding before shell command to handle buffers” section above.