After cloning the repository and proceeding to the distrib folder, we find a binary alongside a C++ source file, which contents are:
Example usage of given binary:
As we have an access to the source code we can spot some facts:
- binary is presumably compiled with no stack protector, which means there’s no extra code for preventing buffer overflows
- there’s a spawn_shell function, which is never called
- input buffer size is 1024 bytes
Another hint is the title – “1996 – It’s 1996 all over again!” – in 1996 Aleph One wrote an article for Phrack, called “Smashing The Stack For Fun and Profit” which introduced masses to the stack buffer overflow attack.
Notice: Trying to compile it on your own with included Makefile may result in a binary still blocking stack overflows – happened in my case, since my system by default passed stack-protecting flags. Try to hack shipped binary to save your time.
Vector of attack – stack overflow
For introduction to stack overflow attack I delegate you to the article I mentioned above – it’s linked in the resources section.
What we’ll need to perform it:
- (virtual) address of the spawn_shell function
- offset to the return pointer, in bytes
First one can be retrieved by running our binary in gdb:
As you see, it’s 0x400897.
Now we need to figure out the offset. It must be at least [1024 + 8] bytes, since on the stack, there’s a 1024 byte buf array and there must be a pointer to the stack frame which is 8 bytes on x86_64 architecture.
From now, we can either check the value manually or with gdb.
Manual way looks like this:
Let’s write a script that prints 1024 characters and pipe its output to the 1996 binary:
It worked, but still not caused segmentation fault or illegal instruction.
Gradually adding more bytes (more A’s) would reveal, that the offset is 1048 bytes:
gdb way looks like this:
We open the binary via gdb and disassemble main function:
We set a breakpoint at 0x0000000000400954, since after this instruction the stack will be cleared out (watch LiveOverflow’s videos linked in the resources to know how to identify that):
Run the program, type whatever on input so program would stop at our breakpoint.
Then examine registers EBP and ESP:
At this point, EBP should contain the address of the beginning of the stack and ESP should contain the address of the top of the stack.
Let’s substract those addresses to eventually have the offset:
0xffffdaa0 – 0xffffd690 = 0x410 = 1040.
But, as the EBP points at the beginning of a 8 byte address, after which is the return address we want to overwrite, we need to add 8 bytes to our value, so it becomes 1048.
As we now know both offset and spawn_shell address we can feed it to 1996 binary with python:
We wrote spawn_shell address in reversed notation since gdb printed address in big endian notation, not little endian which is used by x86 processor.
Shell has been successfully spawned. We can print the flag:
Calculating stack size was a good exercise for knowing how calling a function works in assembly, that return address and stack frame address are put on the stack and popped in the end, and how to examine those in gdb. All needed resources are linked below.
As I said, 0xffffdaa0 is the address, where stack frame address lies.
Let’s break where we breakpointed before, and print surroundings of this address:
Command I typed means:
print in hexadecimal the next 64 bytes after [top of the stack pointer + 1024 bytes]
So we see the 24 bytes (first row and a half) before the return address and return address itself – 0x00400897 which is value we wrote.
- Smashing stack for fun and profit
- Someone else’s writeup(s)
- OWASP – Buffer overflow attack
- I LiveOverflow – How a CPU works and Introduction to Assembler
- II LiveOverflow – First Stack Buffer Overflow to modify Variable
- III LiveOverflow – Buffer Overflows can Redirect Program Execution
- Virtual address space