If you take a look at the source file, the task is to somehow get the ‘trash’ variable to equal the value
This is a little hard, since we aren’t reading into the ‘trash’ variable. So how do we approach this problem. Think about it for a while, then we’ll move further.
Here’s the source code:
Ofcourse some lines have been redacted from the source. They’re really nothing, just some basic obfuscation in order to prevent people from directly getting the flag using ‘strings’ or some similar tool. In order to tackle this problem we need to understand how programs work when executing. We just need the details of how memory is affected when a program is executed. Let’s disassemble the code as it will become easier to follow along with what I’m saying.
These lines are important to understand what actually takes place in memory when a program runs. “push ebp” actually saves the stack frame of the calling function. So basically what we’re doing when running the program is calling the “main()” method. Inorder to know where to continue executing, the “ebp” of the calling function is saved. “mov ebp, esp” is used to set a new stack frame base for the callee function. This is better explained along with the next line which is “sub esp, 0x64” ignoring the previous line. This sequence is what we call a Standard Entry Sequence. It is followed everytime a function is called. We do a “sub” in order to reserve space on the stack for the local variables of the function. Together these memory locations constitue of a ‘stack frame’ also known as a ‘Call Stack’. If you can see from the diagram below, we notice that the local variables are stored in contiguous memory locations. What if we could exploit this feature?
But how do we exploit this? We need a way to overwrite the data of those memory locations. Narrowing it down further, the only way we can attack this(not really) is by providing user input, which is done using the
gets() method. This is currently the only so called ‘attack surface’ for now. Let’s take a look at how this function works using the man pages.
The description looks promising ‘Never use this function’. But why? Scrolling further down, we can see why we should not use this function, ever. Basically there’s no limit to how much gets reads, even if the buffer we read into is of a fixed size. And in our case it’s 64 bytes, so we can bascially read beyond 64 bytes of data. So if we have an input large enough, we should be able to overwrite the data of the ‘trash’ variable. For the sake of completeness, let’s assume we have no idea how far the ‘trash’ variable is located on the stack. We can find this offset by using a special kind of string. I use [pwntools] to genereate this string, though you can do this manually. Let’s do it.
Now let’s open our binary in the debugger and set a breakpoint at the comparision with
0xdeadbeef. Then run it and input our special string.
We see that the ‘trash’ variable is located at an offset of 64 bytes, which is just after the size of our buffer array into which we read our input. So we essentially created what is called a ‘stack overflow’.
Since the value can’t fit in that fixed size buffer, it overflows into the next memory location, thus overwriting the data, which is what we wan’t.
With the offset in hand it’s only a matter of overwriting ‘trash’ with
0xdeadbeef which is fairly simple. Let’s get to it then…
And there you have it. I’ve only showed you how to exploit the 32-bit version of the binary. The 64-bit one can be exploited in the same way with only the offset changing. This is because address spaces are 64-bits. So the offset will be larger compared to a 32-bit binary. Rest of the procedure will be the same. For more information on buffer overflow attacks please do check out the links below.