2

I'm missing a part of the process for preparing a payload for an overflow exploit. To crash a program using an overflow vulnerability, lets say the heap. How can we control the memory address it crashes at and how can some code be sent to control how it executes? E.g. ROP, does the payload contain the shellcode that crashes the program and perfectly replaces the content of the return pointer address of the function with the executable function of the exploit?

I dont get the link between the overflow, controlling the exact memory offset to place the exploit inside the corrupted memory, and the key, how to return the execution to that point, without crashing the application?

  • This is such a broad question. This depends upon the vulnerability, OS, security mitigations, etc. Check out this answer here for how a very basic stack overflow works. This question and answer has a coding example which might be helpful. Try getting the hang of basic stack overflows before venturing into why ROPs are needed. – RoraΖ Dec 2 '15 at 17:45
  • Thanks. I thought ROP is linked to controlling the return pointer, which is used in overflow exploits. – jarryd Dec 2 '15 at 17:48
  • @RoraΖ - great answer in the other post. So my question is what or where is the address of the new return pointer? If we overwrite the return address, what is it returning to, when and how is the payload linked to the new return address? – jarryd Dec 2 '15 at 18:06
  • You mention a heap overflow but are discussing the stack pointer. The heap and the stack are not the same thing. Heap overflows cannot (generally) directly alter the stack. Perhaps you need to better understand how simple stack overflows (ie: not ROP) and heap overflow attacks work. – Neil Smithline Dec 2 '15 at 18:22
  • I think the wrong thing is being focused on. Roraz mentioned a stack overflow, I just gave heap as an example as stack is non executable on OSX. My main question still remains, "how is the payload linked to the new return address?" – jarryd Dec 2 '15 at 18:29
5

Steps to Exploitation


Finding the Vulnerability

This is probably one of the hardest steps. There are various ways to find vulnerabilities, and none of them are quick and easy. This takes time normally on the order of months. But lets say you're doing this on your own so you write your own bad piece of code:

  1 #include <stdio.h>
  2 #include <string.h>
  3 
  4 int function(char **argv) {
  5     char buf[8];
  6 
  7     strcpy(buf, argv[1]);
  8 
  9     return 0;
 10 }
 11 
 12 int main(int argc, char **argv) {
 13   return function(argv);
 14 }

So here the vulnerability is copying an arbitrary string from the command-line arguments. Compile the code with stack mitigations turned off.

gcc overflow.c -o overflow -z execstack

Triggering the Vulnerability

Now you need to trigger the vulnerability. You know what it is. The strcpy blindly copies whatever string is contained in argv[1]. The buffer that the argument is copied into can only contain 8 bytes. Anything past this will overflow the stack. See this answer about how stack overflows work. So you can trigger the vulnerability by running it like so:

./overflow 01234567ABCD1234

Now if you read the link on how stack overflows work you'll know that you've overwritten the saved stack frame pointer with 0x41424344 ("ABCD"). And you've overwritten the return address of the function with 0x31323334 ("1234"). This is great right? You can now control the return address of a function which means you can now tell it where in the program to start executing.

This seems easy, but in reality triggering a vulnerability can be frustrating. Determining the exact path can be straight forward depending on the resources at hand. But nudging and poking execution to take the path that leads to a vulnerability to trigger it can be very difficult.

Exploiting the Vulnerability

You've triggered the vulnerability and the code segfaults. Now what? Now you need to write your shellcode to do something useful. This is purely up to you. Writing shellcode is an art in itself, but exploitation comes down to being able to write your shellcode into memory, and then executing it.

Back to our example. We're overwriting the stack. So keep on writing! After the return address is overwritten write your shellcode onto the stack. So now you have something like:

./overflow 01234567ABCD1234<SHELLCODE>

Remember how memory is oriented, and make sure the shellcode is aligned to the stack. You know the size of your shellcode. We're working in an example where program memory is not randomized so we know the address that we overwrite (You can find this by running your program in gdb and witnessing the address for yourself). With the address and the size you can calculate the address at which your shellcode starts. Rather than using 0x31323334 use 0x<address of shellcode>.

Now you have something like: ./overflow 01234567ABCD<address of shellcode><SHELLCODE>

The function will return to your shellcode, and begin execution.

Reality


That was fun, but in reality there are a lot of things you need to take into account.

Stack Execution

The program was compiled so that the stack was executable. In reality it might not be if DEP is turned on. This is where return oriented programming comes into play. ROPs help execute portions of code at a time in executable portions of memory. Generally a ROP's job is execute instructions that will mark the area of memory the shellcode is in to executable, and then jump to the shellcode.

ASLR

ASLR will randomize the address that a program is executed or a library is loaded. You can see how this works by running the piece of code in the previous link. You'll be able to see how each time a program is run the address changes. This means you can't rely on that pre-calculated address. Generally you'll need a separate vulnerability to leak an address or get the address of a library in order to calculate where you are running in memory.

Stack Overflow

In a stack overflow you just blew away the stack!! You possibly overwrote other return addresses, and key pieces of memory that the program needs to run. Yes the shellcode was run, but what do you do when your shellcode is finished? Cleaning up the stack correctly, or closing the program gracefully is another challenge. Or maybe you just don't care, because now you full access to their box anyway. Just something to keep in mind.

Lots of ways to skin a cat

Gaining execution can occur in lots of different ways. This post only goes over the one example. As you begin to grasp these concepts you can start to get into other techniques for gaining execution after triggering a vulnerability. But that would be well outside the scope of this answer.

I'm sure I'm missing something in all of this. At least this will give you an idea of how difficult exploitation is, and how much you need to take into consideration. Hopefully you can use this as a base, and perhaps later if you have more questions you can ask a more specific question.

  • Great answer, it explained the question exactly. Regarding returning the program to its original state, I will ask another question. – jarryd Dec 2 '15 at 18:49

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.