0

For demonstration / educational purposes I want to write a simple proof of concept application which uses a buffer overflow to execute code from within this app which is not called normally. I thought something like this:

#include <iostream>
#include <cstring>

void vulnerableFunction(const char* input) {
    char buffer[10];  
    strcpy(buffer, input);
}

void printSomething(){
 std::cout << "This should not be executed" << std::endl;
}

int main() {
    const char* input = "This is a long string that will cause a buffer overflow";
    vulnerableFunction(input);
    return 0;
}

With the input string I want to overwrite the IP and point it to the memory location of the printSomething() method and execute it like this.

So first question: Is this even possible like this?

I analyzed it with the Immunity Debugger and was able to exactly create a string which overwrites the EIP. So I know the offset, the thing missing is to find the exact location of the printSomething() method call. Does anybody have a idea how I could do this?

4
  • With such a simple program you will have a hard time getting it to execute that code - the compiler will see that there is no way to call the function and simply remove it. godbolt.org/z/qnYoW87WW With clang your "vulnerable" function isn't even called as it has no sideeffect - so main just returns without doing any calls. Commented May 2, 2024 at 20:52
  • See the as-if rule. The code you write is only a description of what you want to do. The compiler is then free to rearrange the code, optimize it, etc., as long as the observable results are the same. Commented May 2, 2024 at 22:03
  • When I compile without any optimization (-O0) vulnerableFunction is called. Commented May 3, 2024 at 12:37
  • @PaulMcKenzie yes all of this is technically UB, but of course it is - buffer overflows are inherently UB but that doesn't prevent there from being a practical and meaningful answer. Commented May 3, 2024 at 17:29

1 Answer 1

0

Yes you can do this. Finding the location of printSomething could be achieved with a couple different methods depending on the state of ASLR but the easiest way that'll work no matter what is to just use the existing C operator to fetch the address of the function:

&printSomething

You will then need to append the value of that address to your string that you pass to the vulnerable function, e.g. using memcpy

char input[64] = "AAAAA...A"; //64 As
void* printSomethingAddress = &printSomething;
memcpy(input + ipOffset,&printSomethingAddress,(sizeof(void*)));
vulnerableFunction(input);

ipOffset here is the distance from the beginning of the buffer you strcpy to in vulnerableFunction to the location of the saved instruction pointer.

A couple things to be aware of:

  • That ipOffset distance may change based on subtle differences in the code and compilation environment. Use a debugger and a disassembler to help you calculate the correct offset when recompiling your program.

  • Set optimization high enough and some of your functions are likely to go away without warning because the compiler detects that the code is "dead" - you can mitigate this by making sure to use -O0 and also by introducing "side-effects" that have a meaningful effect on the program state.

  • If you compile with stack cookies you're likely to cause the program to just abort - so make sure you skip em.

  • If you have any null bytes in the address of printSomething itself your strcpy might end unexpectedly in the middle of the address. The bad news is you almost certainly will have null bytes, but the good news is that those bytes tend to be MSB's rather than LSB's, and assuming you're compiling this on a little-endian platform that's unlikely to be a problem since the original return address and &printSomething likely share those MSB's. As long as you copy at least 4 bytes of &printSomething to the overflown buffer, you're probably fine. However if by some unfortunate happenstance printSomething happens to be 256-byte aligned (aka LSB is 0x00) then you could have trouble. You can check this with a debugger.

Of course, all of this is technically undefined behavior but that's never stopped anyone before - at a practical level anyway. A good hacker thrives on turning undefined behavior into hacker-controlled behavior.

Sign up to request clarification or add additional context in comments.

1 Comment

Thank you for this detailed answer. I'm a step closer now. At the moment I'm struggling with getting the correct memory location in to the stack. The input I use use is 16x'A' (fill buffer + EBP on stack) + 4 Bytes Memory address of the printSomething() Method. So something like AAAAAAAAAAAABBBBZZZ where A is in the buffer, B overwriting EBP and Z overwriting the return adress / EIP. How can I enter the memory address so it's not interpreted as single chars? So for example AAA..\x20\x10\xA3\x00 I want 00A31020 on the stack but I have just \x20 in there (interpreted as 4 bytes instead of one).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.