Heap Overflow
Reading time: 7 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.
Informazioni di base
Un heap overflow è simile a un stack overflow ma nell'heap. Fondamentalmente significa che è stato riservato dello spazio nell'heap per memorizzare alcuni dati e i dati memorizzati erano più grandi dello spazio riservato.
Negli stack overflow sappiamo che alcuni registri come il puntatore di istruzione o il frame dello stack verranno ripristinati dallo stack e potrebbe essere possibile abusarne. Nel caso degli heap overflow, non ci sono informazioni sensibili memorizzate per impostazione predefinita nel chunk dell'heap che può essere sovrascritto. Tuttavia, potrebbero esserci informazioni sensibili o puntatori, quindi la criticità di questa vulnerabilità dipende da quali dati potrebbero essere sovrascritti e da come un attaccante potrebbe abusarne.
tip
Per trovare gli offset di overflow puoi utilizzare gli stessi schemi degli stack overflow.
Stack Overflows vs Heap Overflows
Negli stack overflow l'organizzazione e i dati che saranno presenti nello stack al momento in cui la vulnerabilità può essere attivata sono abbastanza affidabili. Questo perché lo stack è lineare, sempre in aumento in memoria collidente, in specifici punti dell'esecuzione del programma la memoria dello stack di solito memorizza un tipo simile di dati e ha una struttura specifica con alcuni puntatori alla fine della parte dello stack utilizzata da ciascuna funzione.
Tuttavia, nel caso di un heap overflow, la memoria utilizzata non è lineare ma i chunk allocati sono solitamente in posizioni separate della memoria (non uno accanto all'altro) a causa di bins e zone che separano le allocazioni per dimensione e perché la memoria precedentemente liberata viene utilizzata prima di allocare nuovi chunk. È complicato sapere quale oggetto andrà a collidere con quello vulnerabile a un heap overflow. Quindi, quando viene trovato un heap overflow, è necessario trovare un modo affidabile per fare in modo che l'oggetto desiderato sia il prossimo in memoria rispetto a quello che può essere sovrascritto.
Una delle tecniche utilizzate per questo è Heap Grooming che viene utilizzata ad esempio in questo post. Nel post viene spiegato come, quando nel kernel iOS una zona esaurisce la memoria per memorizzare chunk di memoria, essa si espande di una pagina del kernel, e questa pagina viene suddivisa in chunk delle dimensioni previste che verrebbero utilizzate in ordine (fino alla versione 9.2 di iOS, poi questi chunk vengono utilizzati in modo randomizzato per rendere più difficile l'esploitazione di questi attacchi).
Pertanto, nel post precedente in cui si verifica un heap overflow, per forzare l'oggetto sovrascritto a collidere con un oggetto vittima, vengono forzati diversi kallocs
da diversi thread per cercare di garantire che tutti i chunk liberi siano riempiti e che venga creata una nuova pagina.
Per forzare questo riempimento con oggetti di una dimensione specifica, l'allocazione out-of-line associata a un mach port iOS è un candidato ideale. Modificando la dimensione del messaggio, è possibile specificare esattamente la dimensione dell'allocazione kalloc
e quando il corrispondente mach port viene distrutto, l'allocazione corrispondente verrà immediatamente rilasciata a kfree
.
Quindi, alcuni di questi segnaposto possono essere liberati. La lista libera kalloc.4096
rilascia elementi in ordine last-in-first-out, il che significa fondamentalmente che se alcuni segnaposto vengono liberati e l'exploit tenta di allocare diversi oggetti vittima mentre cerca di allocare l'oggetto vulnerabile all'overflow, è probabile che questo oggetto sia seguito da un oggetto vittima.
Esempio libc
In questa pagina è possibile trovare un'emulazione di base di Heap overflow che mostra come sovrascrivere il bit prev in use del chunk successivo e la posizione della dimensione prev consente di consolidare un chunk utilizzato (facendolo pensare non utilizzato) e poi allocarlo di nuovo potendo sovrascrivere dati che vengono utilizzati in un puntatore diverso.
Un altro esempio da protostar heap 0 mostra un esempio molto basilare di un CTF in cui un heap overflow può essere abusato per chiamare la funzione vincitrice per ottenere la flag.
Nell'esempio protostar heap 1 è possibile vedere come abusando di un buffer overflow è possibile sovrascrivere in un chunk vicino un indirizzo dove dati arbitrari dell'utente verranno scritti.
Esempio ARM64
Nella pagina https://8ksec.io/arm64-reversing-and-exploitation-part-1-arm-instruction-set-simple-heap-overflow/ puoi trovare un esempio di heap overflow dove un comando che verrà eseguito è memorizzato nel chunk successivo rispetto al chunk sovrascritto. Quindi, è possibile modificare il comando eseguito sovrascrivendolo con un exploit semplice come:
python3 -c 'print("/"*0x400+"/bin/ls\x00")' > hax.txt
Altri esempi
- Auth-or-out. Hack The Box
- Utilizziamo una vulnerabilità di Integer Overflow per ottenere un Heap Overflow.
- Corrompiamo i puntatori a una funzione all'interno di un
struct
del chunk sovrascritto per impostare una funzione comesystem
e ottenere l'esecuzione di codice.
Esempio del Mondo Reale: CVE-2025-40597 – Uso improprio di __sprintf_chk
Nel firmware 10.2.1.15 di SonicWall SMA100, il modulo reverse-proxy mod_httprp.so
alloca un chunk heap di 0x80 byte e poi concatena diverse stringhe al suo interno con __sprintf_chk
:
char *buf = calloc(0x80, 1);
/* … */
__sprintf_chk(buf, /* destination (0x80-byte chunk) */
-1, /* <-- size argument !!! */
0, /* flags */
"%s%s%s%s", /* format */
"/", "https://", path, host);
__sprintf_chk
fa parte di _FORTIFY_SOURCE. Quando riceve un parametro size
positivo, verifica che la stringa risultante rientri nel buffer di destinazione. Passando -1
(0xFFFFFFFFFFFFFFFF), gli sviluppatori hanno effettivamente disabilitato il controllo dei limiti, riportando la chiamata rinforzata a una classica sprintf
non sicura.
Fornire un'intestazione Host:
eccessivamente lunga consente quindi a un attaccante di superare il chunk di 0x80 byte e sovrascrivere i metadati del chunk heap successivo (tcache / fast-bin / small-bin a seconda dell'allocatore). Un crash può essere riprodotto con:
import requests, warnings
warnings.filterwarnings('ignore')
requests.get(
'https://TARGET/__api__/',
headers={'Host': 'A'*750},
verify=False
)
L'exploitation pratica richiederebbe heap grooming per posizionare un oggetto controllabile subito dopo il chunk vulnerabile, ma la causa principale evidenzia due importanti insegnamenti:
- _FORTIFY_SOURCE non è una soluzione miracolosa – un uso improprio può annullare la protezione.
- Passa sempre la dimensione corretta del buffer alla famiglia
_chk
(o, ancora meglio, usasnprintf
).
Riferimenti
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.