After understanding the basics of how the memory diagram looks and how the stack works, I moved on to the exploitation part.
This is the program that I used to experiment:
#include <stdio.h>
void forbidden() {
printf("The forbidden function\n");
}
void allowed() {
char buffer[10];
scanf("%s", buffer);
printf("You are now inside the allowed function: %s \n", buffer);
}
int main() {
allowed();
return 0;
}
Stack Protection
There are a couple of safety measures in place to prevent stack overflow based attacks, which I had to deal with first:
-
ASLR: It stands for Address Space Layout Randomization, it randomly arranges the address space positions of key data areas which makes it difficult to locate various parts of the program.
To disable it I had to set the value of randomize_va_space = 0:
echo "0" | sudo dd of=/proc/sys/kernel/randomize_va_space
Make sure you change it back to its original state after you’re done compiling the code.
-
Stack Canaries: According to Wikipedia:
Stack canaries, named for their analogy to a canary in a coal mine, are used to detect a stack buffer overflow before execution of malicious code can occur. This method works by placing a small integer, the value of which is randomly chosen at program start, in memory just before the stack return pointer
To disable this I simply had to compile my code with -fno-stack-protector flag.
Compiling the Code
After disabling these security features, I compiled my code:
gcc -m32 -g -fno-stack-protector unsafe.c -o unsafe -z execstack
-fno-stack-protector
: to disable stack canaries-m32
: to compile it to a 32-bit executable-g
: to make sure our code is visible in gdb-z execstack
: to make the stack executable, that means if I somehow inject any command into the stack then it will be treated as a command and will be executed, despite being on the stack.
P.S. I was facing some issues after compiling this code on my system, so I used a VM to compile this code and transferred it to my host system.
Finding the Offset
In my code I had allotted 10 bytes to our buffer. So, I can try with 14 or 18 bytes of input to overflow the buffer.
18 bytes it is! Now I know that we can overwrite the buffer after 18 bytes.
Ohh, by the way, you may have noticed we the function ‘forbidden’ is not being called in our code. So, in order to call that function, I used the base pointer of the function ‘forbidden’ to overwrite the $eip. That way, our instruction pointer will point to the beginning of the forbidden
function, thus making a function call.
Finding the Required Address and Overwriting $eip
Finding the $ebp of forbidden
using objdump -d ./unsafe
The highlighted address is the address we need. I used the filled the first 18 bytes to overflow the stack, then used 4 bytes of NOP (\x90) for padding and finally overwrote the $eip with the $ebp of the forbidden
function.
As you can see, there’s the output of the printf from the forbidden
function “The forbidden function”
In the above image, I had provided the input python2 -c 'print "A"*18+"\x90"*4+"BBBB"'
. So, the NOPs overwrite the $ebp and “BBBB” overwrites the $eip (\x42 is the Hex code of B). And replacing BBBB with the address of the forbidden
function (“\x8b\x84\x04\x08”) executes the forbidden function.
Thank you for reading.