I'd like a share a little trick I figured out today. I'm sure it has been done before and probably many times but its new to me so I'll describe it here. I asked myself if anything cool could be achieved by overwriting the Saved Frame Pointer instead of the Return Address. I'm not sure what sort of security-related attacks could be leveraged by doing this, but I did find that its possible to affect the program flow in the calling function. This is just another trick to change the flow of execution and even works on a non-executable stack.
The vulnerable program source code
The attack program source code
EBP and Local Variables
The extended base pointer is a register used by the CPU as base for the current stack frame. As we discussed earlier, the EBP for the function that called the current one is stored in the current function's Saved Frame Pointer. The EBP points to this SFP; in this way, there is a chain of saved EBPs for each stack frame. Check out the EIP Redirection page for more details about this. The location of the current stack frame's SFP is stored in the Extended Base Pointer.
Keeping track of the chain of the program's stack frames is not the only purpose of the EBP/SFPs. As the name implies, the Extended Base Pointer register holds an address that is used as the "base" of the current stack frame (this address is the frame's SFP). The code in the current function references local variables using EBP as a base. For example, the C code for setting a variable equal to 3 is myVar = 3;. This might be assembled into something like: MOV SS:[EBP - 4], 0x03. This means that the local variable myVar is referred to as "the address on the stack stored in EBP, minus 4 bytes" by the assembled code. Another variable in the same function called myVar2 might be located at SS:[EBP - 8]. We can see that if an attacker could change the value of EBP, they could also change where the code thinks local variables are.
Using SFP to Modify EBP
Imagine that there is function called FunctionA that is not vulnerable to any sort of buffer overflow. Inside it is a local integer variable that is used by the function later on. Before the variable is used, another function called FunctionB is called. This one happens to be vulnerable to a buffer overflow. Our goal is to trick FunctionA into using a variable that we define instead of the one that it declared.
We wait until the execution reaches FunctionB and we are able to input our malicious buffer. At the beginning we'll have the value of the fake variable that FunctionA will be tricked into using. From there, we pad the buffer with junk until the saved frame pointer is reached. Now we push the address on the stack that is directly below our fake variable. The reason we do this is that FunctionA references its real variable as SS:[ebp - 4]. Therefore, our injected EBP must be 4 bytes beyond our fake variable. By setting it up in this way, if we can manage to get that address into the Extended Base Pointer while FunctionA is executing; our fake variable will be referenced instead of the real one. We are not overwriting the value in the real variable. Instead, we are changing FunctionA's perception of its location to an address inside the buffer that we control.
Now its time to put our attack into a context. At the top of the source code, you'll notice the vulnerable test() function. All it does is declare a 5 byte buffer and fail to protect it while its being filled with user input. The main() function declares a local integer variable (4 bytes) and feeds the decimal value 3 into it. Main() then calls the test() function which asks the user for input but doesn't do anything with it. After test() returns, a loop is entered. The loop keeping looping as long as the integer c is greater than zero. Each time it repeats, the value of c is decreased by one and "Later" is displayed on the screen. Overall, this thing prints that word c times. Because the value of this integer was set to 3 earlier, this means that "Later" is printed 3 times by this loop.
We will trick this program into thinking that a variable we create using the vulnerable buffer is actually c. By filling this fake variable with the decimal value 500,000,001, we'll make this loop run and print "Later" 500 million and one times instead of the expected three.
Buiding the Attack
As before, we'll be writing a program in C that will feed a malicious string into the input of the vulnerable program. Because our ultimate goal is to overwrite the test() function's SFP, we'll need to pad the string appropriately to get there. The first 4 bytes of the attack string will be the value of our fake variable that main() will eventually use in its loop. These bytes need to equal the goal of 500,000,001 so we'll feed "\x01\x65\xCD\x1D" first. The next four do not matter in this case, but take note that they will become main()'s fake SFP. From here use OllyDbg to find the distance between this fake main() SFP and test()'s SFP. If you are unsure where the test() function's SFP is, you can always step the program until inside test() and check the EBP register. You'll find that the buffer begins at 0x0027FF00 and the test() function's SFP is located at 0x0027FF18. After converting to decimal and subtracting the 8 bytes we already used up so far, it means we'll need to add 16 bytes of padding to reach test()'s SFP.
Next we'll actually overwrite the SFP. Have a look at the main() function and you'll find that the loop references the variable c as SS:[EBP - 4]. This means that if we want it to reference that 4 byte value at the beginning of the buffer instead, we need main()'s EBP to point to the address 4 bytes beyond the beginning of our attack buffer. We've already pointed out that any value we can sneak into test()'s SFP will end up in the EBP when main() returns. So we end the attack string with the following 3 bytes: "\x04\xFF\x27". The value is chosen because 0x0027FF04 - 4 is the address of our fake variable. Writing all four bytes is, again, not necessary because gets() automatically sticks a null byte at the end of the string inputted.
By crafting the above buffer and ensuring that the last 3 bytes overwrite test()'s SFP, we directly control EBP when test() returns back to main(). Because main() uses EBP to reference an important variable c and we control EBP; we make main() think that c is actually where our old overwritten buffer began. The data is still there because there was no reason for the program to erase/modify it. Because c is referenced as SS:[EBP - 4] in main(), we need that overwritten Stack Frame Pointer to point to the address four bytes below the fake variable location on the stack. When main uses our fake EBP to reference variable c is it will subtract 4 from it and find our fake variable value. This was filled in with the decimal value of 500,000,001. Main() runs the loop to print "Later" a number of times determined by c. So by doing all of this trickery we can make the program display "Later" 500,000,001 times instead of 3.