Skip to content
KitploitKITPLOIT
StrumentiBlog
Invia
StrumentiBlog
Invia

Strumenti di Hacking, PenTest e Cybersecurity per il tuo Arsenale di Sicurezza!

Kitploit è una directory di strumenti di hacking, cybersecurity e pentesting. Scopri gli ultimi aggiornamenti dei progetti per trovare vulnerabilità, analizzare sistemi, automatizzare i test e rafforzare la tua sicurezza.

··Feed·Contatto·Privacy·© 2026 Kitploit

Directory degli strumenti

Categorie

Vedi tutte le categorie
Loading categories
ExecASLR-ekoparty — Exploit proof-of-concept che aggira ASLR sulle CPU Intel abusando del branch target buffer e dell'esecuzione speculativa per far trapelare indirizzi randomizzati tramite canale laterale. | Kitploit
Strumenti/GitHubGitHub/es0j/execaslr-ekoparty
Analisi delle VulnerabilitàExploitSicurezza HardwareApprendimento e FormazioneRed TeamingAttacco AvversarioBinary Exploitation
GitHubes0j/execaslr-ekoparty

ExecASLR-ekoparty

Exploit proof-of-concept che aggira ASLR sulle CPU Intel abusando del branch target buffer e dell'esecuzione speculativa per far trapelare indirizzi randomizzati tramite canale laterale.

Vedi Repository
7293 anni faRevisionato da Kitploit

Più Popolari

Vedi tutti →

Scopri gli strumenti più utilizzati dalla nostra community.

Esplora tutti gli strumenti

Sfoglia la nostra collezione di strumenti

Vedi tutti gli strumenti →
Condividi

ExecASLR - Abusare dei predittori di branch Intel per bypassare l'ASLR

Cos'è l'ASLR

Address Space Layout Randomization è una mitigazione usata per rendere più difficile lo sfruttamento degli attacchi di corruzione della memoria. In uno scenario di vulnerabilità di buffer overflow, ad esempio, un attaccante che cerca di realizzare un exploit con Return Oriented Programming deve conoscere gli indirizzi dei gadget nella catena. Se il segmento del codice del binario sfruttato viene randomizzato, allora è molto più difficile per un attaccante scegliere l'indirizzo corretto per l'exploit, rendendo lo sfruttamento non fattibile.

Il seguente esempio mostra come un indirizzo viene randomizzato:

root@kitploit:~
#include <stdio.h>
void DoNothing();
void (*codePtr)() = DoNothing;

void DoNothing(){}

int main(int argc,char **argv){

    printf("Destination %p\n",codePtr);
    DoNothing();
}


Ogni esecuzione il valore viene randomizzato:

root@kitploit:~
Destination 0x563714256149
Destination 0x556d8e2f1149
Destination 0x5618c8bdd149
Destination 0x55ee623b0149

Gli ultimi 12 bit 149 sono sempre gli stessi, ma la posizione della funzione può essere approssimativamente ovunque tra 0x550000000000 e 0x570000000000, il che significa che 29 bit sono randomizzati, occupando un possibile spazio di indirizzi di 0x200 0000 0000, ovvero 2,2 TB.

Pipeline della CPU

Il processamento di ogni istruzione è un compito complesso. Alcune fasi del processamento di una singola istruzione sono:

  • Fetch dell'istruzione;
  • Decodifica dell'istruzione;
  • Esecuzione di operazioni nell'Unità Aritmetico-Logica

Per aumentare il throughput delle istruzioni nella CPU, ogni compito dell'istruzione viene eseguito da un'unità specifica del processore. Con tutte le unità che lavorano in parallelo, la CPU può eseguire a velocità di clock molto più elevate; questa è l'idea di una pipeline.

Esecuzione delle istruzioni A, B e C nei cicli 1-5. Nel ciclo 3, ad esempio, le unità di lettura, decodifica ed esecuzione sono attive simultaneamente

Tuttavia, le istruzioni non sono completamente indipendenti tra loro. Ad esempio, la seguente sequenza:

root@kitploit:~
A. add ax,[bx]
B. jz $+1
C. mov dl,[rsi]  
D. nop

In questo caso, l'istruzione A nel caso migliore terminerà solo al ciclo 3 in fase di esecuzione. Tuttavia, l'unità di fetch deve decidere qual è la prossima istruzione da recuperare dalla memoria, e se l'istruzione C (mov dl,[rsi]) debba essere saltata. In questo scenario, la CPU ha l'opzione di attendere che l'istruzione A termini, cosa che avverrà solo al terzo ciclo di clock, per poi recuperare l'istruzione corretta dalla memoria, se l'operazione di add restituisce 0, per esempio:

Questo comporta un ritardo nella pipeline perché la CPU deve attendere che l'istruzione venga eseguita. In questo esempio il ritardo è di un singolo ciclo di clock, ma l'istruzione add ax,[bx] richiede un'operazione di memoria che, come visto prima, può richiedere fino a centinaia di cicli per essere completata, causando così un costo prestazionale significativo sul processore.

Un'opzione più veloce sarebbe cercare di "indovinare" il percorso di esecuzione corretto. La CPU può speculare se il branch viene effettuato o meno. Dopo quel punto, l'esecuzione continua dal percorso speculato e i valori vengono committati solo se il percorso si rivela corretto al termine dell'istruzione A. Se il percorso si rivela sbagliato, i risultati vengono scartati e lo stato viene ripristinato a prima del punto di speculazione.

L'unico problema nel ripristinare il percorso intrapreso è che lo stato microarchitetturale della CPU non può essere ripristinato. Quindi se la CPU specula eseguendo l'istruzione C (mov dl,[rsi]), i dati puntati da rsi verranno spostati nella cache. Questo effetto può essere misurato successivamente usando un attacco side-channel (canale laterale).

Predittore condizionale a 2 bit. https://en.wikipedia.org/wiki/Branch_predictor

Spectre V2 (Iniezione del Branch Target)

Non solo le istruzioni condizionali devono essere predette, ma anche i branch indiretti. La CPU deve avere un meccanismo per indovinare le destinazioni di un'istruzione come call [rdi].

La vulnerabilità Spectre v2 mostra che è possibile sfruttare il predittore indiretto per ottenere esecuzione transiente in altri processi: Estratto da https://spectreattack.com/spectre.pdf

Posizionando un'istruzione call nel contesto A allo stesso indirizzo virtuale di un'altra call nel contesto B, l'attaccante può addestrare la CPU a eseguire codice in una posizione scelta dall'attaccante nel contesto B, in un attacco di riuso del codice (code reuse), simile al Return Oriented Programming (ROP).

La vittima designata deve avere un pezzo di codice noto come "spectre gadget" in grado di far trapelare un segreto usando un attacco side-channel. Per un attacco Spectre riuscito, l'attaccante deve anche conoscere la posizione dello spectre gadget. Pertanto, negli attacchi user-user, proteggere la vittima con ASLR era una mitigazione per questo tipo di attacchi. Tuttavia, esistono anche tecniche per estrarre l'ASLR usando attacchi microarchitetturali come Jump Over ASLR. Questa tecnica ha però alcune limitazioni sulla quantità di bit leakati, poiché si basa sulle collisioni nel predittore diretto per bypassare l'ASLR.

I meccanismi interni di questo predittore sono mostrati di seguito:

Estratto da https://spectreattack.com/spectre.pdf

Alcuni di questi componenti sono:

  • Il Branch Target Buffer (BTB);
    • Il BTB è un componente simile a una cache che memorizza le destinazioni per le predizioni. Il BTB memorizza l'indirizzo completo a 64 bit della destinazione e il numero di voci disponibili nel BTB dipende dall'architettura.
  • Il Branch History Buffer (BHB);
    • Il BHB memorizza un "hash" del recente flusso di esecuzione. Ogni istruzione di branch scrive nel BHB. Sulle CPU Skylake e precedenti, il BHB può memorizzare il contesto degli ultimi 29 branch. Su Icelake il BHB memorizza fino a ~100 branch. Nota che il BHB usa solo i 20 LSB dei branch per creare l'hash, di cui 12 non sono randomizzati.
  • Il predittore indiretto;
    • Usa solo i 12 LSB dell'istruzione di branch indiretto per determinare la destinazione completa a 64 bit. Exec ASLR fa trapelare il puntatore a 64 bit dal BTB per recuperare completamente gli indirizzi ASLR.
  • Il predittore di branch diretto
    • Usa i 30 LSB dell'indirizzo sorgente per predire un valore a 32 bit. L'altra metà dell'indirizzo viene riutilizzata dalla sorgente. L'attacco Jump Over ASLR sfrutta le collisioni in questo predittore per trovare un indirizzo sorgente che collida con un altro contesto, quindi è limitato a far trapelare solo 30 LSB dal contesto della vittima.

Il layout classico dell'attacco Spectre v2 si presenta così:

  • L'attaccante posiziona un branch indiretto nella stessa posizione del branch della vittima, ma la destinazione corrisponde a uno spectre gadget nel contesto della vittima.
  • Quando la vittima esegue il branch, viene predetto in modo errato (misprediction) e lo spectre gadget carica un segreto e fa un accesso condizionato a una regione di memoria condivisa come una libreria. Ad esempio, lo spectre gadget che esegue getenv + secret[0]*4096 può far trapelare il valore del segreto alla posizione 0 usando la libc come memoria condivisa.
  • L'attaccante recupera quindi il segreto leakato rilevando quali parti della memoria condivisa sono state spostate nella cache usando un attacco flush+reload.

Exec ASLR

Exec ASLR (noto anche come Reverse Branch Target Buffer Poisoning) è una nuova tecnica per bypassare l'ASLR usando la vulnerabilità Spectre-BTI. Questo attacco abusa del fatto che non solo l'attaccante può inquinare il BTB in uno scenario Spectre-BTI classico, ma anche le vittime possono innescare una predizione errata del branch nel processo dell'attaccante, portando l'attaccante a un salto speculativo verso un indirizzo protetto da ASLR. Poi, usando un side-channel che rivela quale indirizzo viene eseguito, un attaccante può recuperare l'indirizzo di destinazione completo, bypassando l'ASLR per il processo preso di mira.

Il layout dell'attacco Exec ASLR si presenta così:

  • La vittima esegue un branch indiretto, che scrive il suo puntatore di destinazione randomizzato (0x55aabbeef456) nel BTB.
  • L'attaccante posiziona un branch indiretto in un indirizzo allineato ai 12 LSB. Poiché i 12 LSB non sono randomizzati, questa parte è banale.
  • L'attaccante riempie tutte le posizioni possibili della memoria con un "leak gadget" che dice all'attaccante "Sto eseguendo a questo indirizzo!" usando probeArray come side-channel.
  • Quando l'attaccante esegue il branch indiretto, viene predetto erroneamente verso uno dei tanti leak gadget in memoria.
  • L'attaccante usa un attacco flush+reload per recuperare se ProbeArray è stato acceduto o meno durante la speculazione.

In questo tipo di attacco non c'è bisogno di trovare uno spectre gadget né di avere una memoria condivisa per il side-channel: l'unico requisito è un branch indiretto da sfruttare; tutti i gadget necessari di Spectre v2 sono posizionati all'interno del processo dell'attaccante. L'unico nuovo requisito per questo attacco è poter mappare l'indirizzo di destinazione della vittima nel proprio processo, quindi questo attacco non può funzionare contro KASLR, per esempio. Non è nemmeno un attacco di brute force: con un singolo tentativo è possibile testare più indirizzi contemporaneamente, tuttavia c'è un limite a quanti "leak gadget" la memoria può contenere allo stesso tempo. Questo riduce significativamente il tempo per eseguire l'attacco rispetto a Jump Over ASLR, passando da ~100 indirizzi al secondo a poche centinaia di miliardi di indirizzi al secondo.

Leak gadget

Il leak gadget usa probeArray per informare l'attaccante su dove viene eseguito il gadget stesso. Riceve probeArray e l'indice di RIP da far trapelare come argomento e esegue una sorta di programmazione senza branch per decidere se accedere a probeArray[0] o probeArray[4096].

root@kitploit:~
lea rax,[rip - 7]      ;load current address
shr rax,cl             ;selects the bit using cl arg
and rax,1
shl rax,12             ;loads probearray
mov dl,[rsi+rax]       ;or probearray+4096

Controllare il BHB e il codice della vittima designata

Come visto in precedenza, il BHB viene usato per selezionare una voce del BTB. Per trovare una collisione nel BTB e sfruttare questa vulnerabilità, un attaccante deve conoscere gli ultimi N branch eseguiti (29 se < Skylake). Nei nostri test, abbiamo usato un ciclo for per impostare lo stato del BHB a un valore noto prima della chiamata indiretta. Ecco un esempio vulnerabile di codice vittima:

root@kitploit:~
#include <stdio.h>

void DoNothing();
void (*codePtr)() = DoNothing;

void DoNothing(){
    return;
}

int main(){
    printf("Destination = %p\n",codePtr);
    while(1){
	for(int i=0;i<200;i++){}
    	codePtr();
    }
}

Per garantire che lo stato del BHB sia lo stesso in entrambi i contesti, l'attaccante copia i byte corrispondenti al ciclo for e alla call della vittima sotto forma di shellcode. Lo shellcode viene copiato in tutte le 256 posizioni possibili che possono corrispondere all'allineamento dei 20 LSB richiesto affinché lo stato del BHB sia lo stesso.

root@kitploit:~
Victim Code

0x5594c566a152 <+28>:    mov    eax,0xc8
0x5594c566a157 <+33>:    dec    eax
0x5594c566a159 <+35>:    jne    0x1157 <main+33>
0x5594c566a15b <+37>:    nop
0x5594c566a15c <+38>:    nop
0x5594c566a15d <+39>:    nop
0x5594c566a15e <+40>:    lea    rdi,[rip+0x2ecb]
0x5594c566a165 <+47>:    call   QWORD PTR [rdi]

--> Executes to
0x5594c5669135:    ret

root@kitploit:~
Attacker Code

… //eax=200 rsi=probeArray, cl=0 
0x6a157    dec    eax
0x6a159    jne    0x455555500157
…
0x6a165    jmp    QWORD PTR [rdi]

--> Misspredicts to
0x5594c566a135:    lea rax,[rip - 7]
0x5594c566a13c:    shr rax,cl
0x5594c566a13f:    and rax,1
0x5594c566a133:    shl rax,12
0x5594c566a137:    mov dl,[rsi+rax]

Sfide

C'è un problema quando si cerca di posizionare un gadget in tutte le posizioni possibili contemporaneamente. Nei nostri test, l'ASLR colloca la destinazione da qualche parte tra 0x550000000000 e 0x570000000000. Ciò significa che ci sono 2,2 TB di possibili indirizzi virtuali da mappare, ovvero 537 milioni di leak gadget. Ma il sistema ha solo 8 GB di RAM. Oltre al fatto che sarebbe possibile mappare 2 TB di RAM usando il COW, non abbiamo avuto molto successo con questo approccio. Suppongo che crei troppa pressione sul Translation Lookaside Buffer (TLB), rendendo la speculazione verso un indirizzo non tradotto troppo lenta. Nei test abbiamo creato una pagina in memoria da 1 GB e l'abbiamo riempita con i gadget che leakano. Poi abbiamo spostato la pagina attraverso l'intervallo di 2 TB usando la syscall remap. Un altro problema osservato è stato il fatto che l'indirizzo speculato probabilmente non era presente nel TLB, poiché non era mai stato effettivamente eseguito. Ma il manuale Intel afferma:

  • "Il processore può memorizzare nella cache le traduzioni richieste per i prefetch e per gli accessi che sono il risultato di esecuzione speculativa che non si verificherebbero mai realmente nel percorso di codice eseguito" - ISA

Pertanto, per aumentare le probabilità di un pagewalk "indotto dalla speculazione", abbiamo cercato di rendere il più difficile possibile la risoluzione dell'indirizzo corretto. Questo viene fatto usando una catena di puntatori (pointer chain) per l'indirizzo di destinazione. L'idea è che il frontend della CPU speculi sulla destinazione del branch e l'unità di riordino esegua il leak gadget prima di completare la lettura della catena di puntatori.

root@kitploit:~
Improved Caller - Frontend fetched instructions
mov rcx,%1       ;mask arg for gadget
lea rsi,[%2]     ;probe array ptr arg
lea rdx,[%0]
mov rdx,[rdx]    ;pointer chain
mov rdx,[rdx]
mov rdx,[rdx]
...
mov rdx,[rdx]
mov rdx,[rdx]
check [rdx]      ;mispredicts to gadget

lea rax,[rip - 7];speculative execution
shr rax,cl
and rax,1
shl rax,12
mov dl,[rsi+rax]

root@kitploit:~
Improved Caller - Reorder unit scheduled instructions
mov rcx,%1    ;mask arg for gadget
lea rsi,[%2]  ;probe array ptr arg
lea rdx,[%0]
mov rdx,[rdx] ;pointer chain
mov rdx,[rdx]
; <pagewalk occurs some where here>
... ;Out of order + speculation
lea rax,[rip - 7]
shr rax,cl
and rax,1
shl rax,12
mov dl,[rsi+rax]
...
mov rdx,[rdx]
mov rdx,[rdx]
check [rdx] ;the execution path reverted

Questa tecnica di esecuzione fuori ordine (Out of Order) + speculativa ha mostrato un miglioramento nei tassi di misprediction desiderati nell'attacco.

Scheduling e STIBP

Per eseguire un attacco Spectre V2, l'attaccante deve eseguire codice nello stesso core della vittima, così da condividere la stessa Branch Prediction Unit (BPU). Nelle CPU <= Skylake abbiamo osservato che è possibile ottenere la co-residenza sul core usando l'hyperthreading. Le CPU Icelake e Cascade Lake implementano una mitigazione chiamata Single Thread Indirect Branch Predictor, che separa la BPU tra i thread. Pertanto è necessario eseguire vittima e attaccante sullo stesso thread e usare usleep per alternare tra processo vittima e processo attaccante; questo renderebbe l'attaccante più lento su queste CPU, se non fosse per il fatto che le cache (e probabilmente anche il TLB) sono molto buone in quelle generazioni, permettendo di mettere in cache molti più gadget contemporaneamente.

Risultati

Questa tecnica è stata testata su tutte le CPU Intel disponibili su Google Cloud, sia sulle generazioni N1 che N2:

  • Sandy Bridge
  • Ivy Bridge
  • Haswell
  • Broadwell
  • Skylake
  • Cascadelake
  • Icelake

Oltre ad alcune differenze negli exploit per Cascade e Ice Lake, tutti i test sono in grado di recuperare gli indirizzi con una precisione >99% in meno di 10 secondi.

Mitigazioni

Le mitigazioni sono le stesse di Spectre V2 per mitigare gli attacchi user-user. La Indirect Branch Prediction Barrier (IBPB) consente il flush della BPU e può essere usata nei context switch. In Linux, l'IBPB può essere usata tramite la syscall prctl con l'opzione PR_SET_SPECULATION_CTRL. Non ho idea di quale sia la mitigazione equivalente per Windows, per favore fatemelo sapere.


Paper:

https://cos.ufrj.br/uploadfile/publicacao/3061.pdf

Talk di Ekoparty:

https://www.youtube.com/watch?v=Qj4z-KvnkxU

Slide:

https://docs.google.com/presentation/d/10t-oo-c26x9ydx1_FYgmhy204rxfmQ92eboPlCnA2y4/edit?usp=sharing

Riferimenti:

https://googleprojectzero.blogspot.com/2018/01/reading-privileged-memory-with-side.html https://eprint.iacr.org/2013/448.pdf https://spectreattack.com/spectre.pdf https://www.cs.ucr.edu/~nael/pubs/micro16.pdf http://download.vusec.net/papers/bhi-spectre-bhb_sec22.pdf https://www.kernel.org/doc/html/latest/userspace-api/spec_ctrl.html Intel® 64 and IA-32 Architectures Software Developer’s Manual Volume 3. Santa Clara, USA: Intel Corporation, 2016, iSBN 325384-060US

Scarica lo strumento
Operazione \ ciclo di clock12345
FetchABC
DecodificaABC
EsecuzioneABC
Operazione \ ciclo di clock123456
FetchABD
DecodificaABD
EsecuzioneABD
Operazione \ ciclo di clock123456
FetchAB(S) C
DecodificaAB(S) C
EsecuzioneAB(S) C