Stack Canaries
Last updated
Last updated
Impara e pratica l'Hacking su AWS:HackTricks Training AWS Red Team Expert (ARTE) Impara e pratica l'Hacking su GCP: HackTricks Training GCP Red Team Expert (GRTE)
StackGuard inserisce un valore speciale noto come canary prima dell'EIP (Extended Instruction Pointer), specificamente 0x000aff0d
(rappresentante null, newline, EOF, carriage return) per proteggersi dagli overflow di buffer. Tuttavia, funzioni come recv()
, memcpy()
, read()
, e bcopy()
rimangono vulnerabili, e non protegge l'EBP (Base Pointer).
StackShield adotta un approccio più sofisticato rispetto a StackGuard mantenendo uno Stack di Ritorno Globale, che memorizza tutti gli indirizzi di ritorno (EIP). Questa configurazione assicura che eventuali overflow non causino danni, poiché consente di confrontare gli indirizzi di ritorno memorizzati con quelli effettivi per rilevare eventuali occorrenze di overflow. Inoltre, StackShield può controllare l'indirizzo di ritorno rispetto a un valore di confine per rilevare se l'EIP punta al di fuori dello spazio dati previsto. Tuttavia, questa protezione può essere aggirata attraverso tecniche come Return-to-libc, ROP (Return-Oriented Programming), o ret2ret, indicando che StackShield non protegge nemmeno le variabili locali.
-fstack-protector
:Questo meccanismo posiziona un canary prima dell'EBP, e riorganizza le variabili locali posizionando i buffer a indirizzi di memoria più alti, impedendo loro di sovrascrivere altre variabili. Copia in modo sicuro gli argomenti passati nello stack sopra le variabili locali e utilizza queste copie come argomenti. Tuttavia, non protegge gli array con meno di 8 elementi o i buffer all'interno di una struttura dell'utente.
Il canary è un numero casuale derivato da /dev/urandom
o un valore predefinito di 0xff0a0000
. È memorizzato in TLS (Thread Local Storage), consentendo a spazi di memoria condivisi tra thread di avere variabili globali o statiche specifiche del thread. Queste variabili vengono inizialmente copiate dal processo genitore, e i processi figlio possono modificare i loro dati senza influenzare il genitore o i fratelli. Tuttavia, se viene utilizzato un fork()
senza creare un nuovo canary, tutti i processi (genitore e figli) condivideranno lo stesso canary, rendendolo vulnerabile. Sull'architettura i386, il canary è memorizzato a gs:0x14
, e su x86_64, a fs:0x28
.
Questa protezione locale identifica le funzioni con buffer vulnerabili agli attacchi e inietta codice all'inizio di queste funzioni per posizionare il canary, e alla fine per verificare la sua integrità.
Quando un server web utilizza fork()
, consente un attacco di forza bruta per indovinare il byte del canary uno alla volta. Tuttavia, utilizzando execve()
dopo fork()
sovrascrive lo spazio di memoria, annullando l'attacco. vfork()
consente al processo figlio di eseguire senza duplicazione fino a quando tenta di scrivere, momento in cui viene creata una duplicazione, offrendo un approccio diverso alla creazione di processi e alla gestione della memoria.
Nei binari x64
, il cookie del canary è un qword di 0x8
byte. I primi sette byte sono casuali e l'ultimo byte è un byte nullo.
Nei binari x86
, il cookie del canary è un dword di 0x4
byte. I primi tre byte sono casuali e l'ultimo byte è un byte nullo.
Il byte meno significativo di entrambi i canary è un byte nullo perché sarà il primo nello stack proveniente da indirizzi inferiori e quindi le funzioni che leggono stringhe si fermeranno prima di leggerlo.
Leak del canary e successiva sovrascrittura (ad es. overflow del buffer) con il proprio valore.
Se il canary è forked nei processi figlio potrebbe essere possibile forzarne uno byte alla volta:
Se c'è qualche leak interessante o vulnerabilità di lettura arbitraria nel binario potrebbe essere possibile effettuare un leak:
Sovrascrittura dei puntatori memorizzati nello stack
Lo stack vulnerabile a un overflow dello stack potrebbe contenere indirizzi a stringhe o funzioni che possono essere sovrascritti per sfruttare la vulnerabilità senza dover raggiungere il canary dello stack. Controlla:
Pointer RedirectingModifica sia del canary master che del canary del thread
Un buffer overflow in una funzione thread-safe protetta con canary può essere utilizzato per modificare il canary master del thread. Di conseguenza, la mitigazione è inutile poiché il controllo viene effettuato con due canary che sono gli stessi (anche se modificati).
Inoltre, un buffer overflow in una funzione thread-safe protetta con canary potrebbe essere utilizzato per modificare il canary master memorizzato nel TLS. Questo perché potrebbe essere possibile raggiungere la posizione di memoria in cui è memorizzato il TLS (e quindi il canary) tramite un bof nello stack di un thread. Di conseguenza, la mitigazione è inutile poiché il controllo viene effettuato con due canary che sono gli stessi (anche se modificati). Questo attacco è eseguito nel writeup: http://7rocky.github.io/en/ctf/htb-challenges/pwn/robot-factory/#canaries-and-threads
Controlla anche la presentazione di https://www.slideshare.net/codeblue_jp/master-canary-forging-by-yuki-koike-code-blue-2015 che menziona che di solito il TLS è memorizzato da mmap
e quando viene creato uno stack di thread viene generato anche da mmap
secondo questo, che potrebbe consentire l'overflow come mostrato nel writeup precedente.
Modifica dell'entry GOT di __stack_chk_fail
Se il binario ha Partial RELRO, è possibile utilizzare una scrittura arbitraria per modificare l'entry GOT di __stack_chk_fail
in una funzione fittizia che non blocca il programma se il canary viene modificato.
Questo attacco è eseguito nel writeup: https://7rocky.github.io/en/ctf/other/securinets-ctf/scrambler/
Impara e pratica l'Hacking su AWS:HackTricks Training AWS Red Team Expert (ARTE) Impara e pratica l'Hacking su GCP: HackTricks Training GCP Red Team Expert (GRTE)