Skip to content
KitploitKITPLOIT
OutilsBlog
Soumettre
OutilsBlog
Soumettre

Outils de Hacking, PenTest et Cybersécurité pour votre Arsenal de Sécurité !

Kitploit est un répertoire d'outils de hacking, de cybersécurité et de pentesting. Découvrez les dernières mises à jour des projets pour trouver des vulnérabilités, analyser des systèmes, automatiser les tests et renforcer votre sécurité.

··Flux·Contact·Confidentialité·© 2026 Kitploit

Répertoire d'outils

Catégories

Voir toutes les catégories
Loading categories
ExecASLR-ekoparty — Exploit de preuve de concept qui contourne l'ASLR sur les processeurs Intel en abusant du Branch Target Buffer et de l'exécution spéculative pour divulguer des adresses randomisées via un canal auxiliaire. | Kitploit
Outils/GitHubGitHub/es0j/execaslr-ekoparty
Analyse des VulnérabilitésExploitationSécurité MatérielleApprentissage et ÉducationRed TeamingAttaque AdversarialeExploitation de Binaires
GitHubes0j/execaslr-ekoparty

ExecASLR-ekoparty

Exploit de preuve de concept qui contourne l'ASLR sur les processeurs Intel en abusant du Branch Target Buffer et de l'exécution spéculative pour divulguer des adresses randomisées via un canal auxiliaire.

Voir le dépôt
729il y a 3 ansVérifié par Kitploit

Populaires

Voir tout →

Découvrez les outils les plus utilisés par notre communauté.

Explorer tous les outils

Parcourez notre collection d'outils

Voir tous les outils →
Partager

ExecASLR - Abuser des prédicteurs de branche Intel pour contourner l'ASLR

Qu'est-ce que l'ASLR

L'Address Space Layout Randomization est une mitigation utilisée pour rendre plus difficile l'exploitation des attaques par corruption mémoire. Dans un scénario de vulnérabilité de débordement de tampon, par exemple, un attaquant qui tente de monter une exploitation par Return Oriented Programming a besoin de connaître les adresses des gadgets de la chaîne. Si le segment de code du binaire exploité est randomisé, il est alors beaucoup plus difficile pour un attaquant de choisir la bonne adresse pour l'exploitation, rendant celle-ci irréalisable.

L'exemple suivant montre comment une adresse est randomisée :

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

void DoNothing(){}

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

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


À chaque exécution, la valeur est randomisée :

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

Les 12 derniers bits 149 sont toujours les mêmes, mais la fonction peut se trouver à peu près n'importe où entre 0x550000000000 et 0x570000000000, ce qui signifie que 29 bits sont randomisés, occupant un espace d'adressage possible de 0x200 0000 0000 ou 2,2 To.

Pipeline du CPU

Le traitement de chaque instruction est une tâche complexe. Certaines étapes du traitement d'une seule instruction sont :

  • L'extraction (fetch) de l'instruction ;
  • le décodage de l'instruction ;
  • l'exécution des opérations dans l'unité arithmétique et logique.

Afin d'augmenter le débit d'instructions du CPU, chaque tâche de l'instruction est effectuée par une unité spécifique du processeur. Avec toutes les unités travaillant en parallèle, le CPU peut fonctionner à des fréquences d'horloge bien plus élevées ; c'est l'idée du pipeline.

Exécution des instructions A, B et C sur les cycles 1 à 5. Au cycle 3, par exemple, les unités d'extraction, de décodage et d'exécution sont simultanément actives

Cependant, les instructions ne sont pas complètement indépendantes les unes des autres. Par exemple, la séquence suivante :

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

Dans ce cas, l'instruction A, dans le meilleur des cas, ne se terminera qu'au cycle 3 en exécution. Cependant, l'unité d'extraction doit décider quelle est la prochaine instruction à charger depuis la mémoire, à savoir si l'instruction C (mov dl,[rsi]) doit être ignorée. Dans ce scénario, le CPU a la possibilité d'attendre la fin de l'instruction A, qui n'aura lieu qu'au troisième cycle d'horloge, pour ensuite charger la bonne instruction en mémoire, si par exemple l'opération add retourne 0 :

Ceci implique un retard dans le pipeline, car le CPU doit attendre que l'instruction soit exécutée. Dans cet exemple, le retard est d'un seul cycle d'horloge, mais l'instruction add ax,[bx] nécessite une opération mémoire qui, comme vu précédemment, peut prendre jusqu'à des centaines de cycles pour se terminer, engendrant ainsi un coût de performance significatif sur le processeur.

Une option plus rapide consisterait à essayer de « deviner » le bon chemin d'exécution. Le CPU peut spéculer sur la prise ou non de la branche. Après ce point, l'exécution continue sur le chemin spéculé et les valeurs ne sont validées que si le chemin s'avère correct après la fin de l'instruction A. Si le chemin s'avère incorrect, les résultats sont rejetés et l'état est restauré à celui d'avant le point de spéculation.

Le seul problème lors de l'annulation du chemin emprunté est que l'état microarchitectural du CPU ne peut pas être restauré. Ainsi, si le CPU spécule pour exécuter l'instruction C (mov dl,[rsi]), les données pointées par rsi seront déplacées dans le cache. Cet effet peut être mesuré ultérieurement à l'aide d'une attaque par canal auxiliaire (side channel).

Prédicteur conditionnel à 2 bits. https://en.wikipedia.org/wiki/Branch_predictor

Spectre V2 (Branch Target Injection)

Non seulement les instructions conditionnelles doivent être prédites, mais aussi les branchements indirects. Le CPU doit disposer d'un mécanisme pour deviner les destinations d'une instruction telle que call [rdi].

La vulnérabilité Spectre v2 montre qu'il est possible d'exploiter le prédicteur indirect pour obtenir une exécution transitoire dans d'autres processus : Extrait de https://spectreattack.com/spectre.pdf

En plaçant une instruction call dans le contexte A à la même adresse virtuelle qu'un autre call du contexte B, l'attaquant peut entraîner le CPU à exécuter du code à une position choisie par l'attaquant dans le contexte B, dans une attaque par réutilisation de code, similaire au Return Oriented Programming (ROP).

La victime ciblée doit disposer d'un morceau de code connu sous le nom de « spectre gadget » capable de fuiter un secret à l'aide d'une attaque par canal auxiliaire. Pour qu'une attaque Spectre réussisse, l'attaquant doit également connaître l'emplacement du spectre gadget. Par conséquent, dans les attaques utilisateur-utilisateur, protéger la victime avec l'ASLR était une mitigation pour ce type d'attaque. Cependant, il existe aussi des techniques pour extraire l'ASLR à l'aide d'attaques microarchitecturales comme Jump Over ASLR. Néanmoins, cette technique présente certaines limites quant à la quantité de bits divulgués, car elle repose sur une collision dans le prédicteur direct pour contourner l'ASLR.

Les mécanismes internes de ce prédicteur sont présentés ci-dessous :

Extrait de https://spectreattack.com/spectre.pdf

Certains de ces composants sont :

  • Le Branch Target Buffer (BTB) ;
    • Le BTB est un composant de type cache qui stocke les destinations des prédictions. Le BTB stocke l'adresse complète de 64 bits de la destination et le nombre d'entrées disponibles sur le BTB dépend de l'architecture.
  • Le Branch History Buffer (BHB) ;
    • Le BHB stocke un « hash » du flux d'exécution récent. Chaque instruction de branche écrit dans le BHB. Sur les CPU Skylake et antérieurs, le BHB peut stocker le contexte des 29 dernières branches. Sur Icelake, le BHB stocke jusqu'à ~100 branches. Notez que le BHB n'utilise que les 20 bits de poids faible (20LSBs) des branches pour créer le hash, dont 12 ne sont pas randomisés.
  • Le prédicteur indirect ;
    • N'utilise que les 12 LSBs de l'instruction de branche indirecte pour déterminer la destination complète de 64 bits. Exec ASLR fuite le pointeur de 64 bits du BTB pour récupérer entièrement les adresses ASLR.
  • Le prédicteur de branche directe
    • Utilise les 30 LSBs de l'adresse source pour prédire une valeur de 32 bits. L'autre moitié de l'adresse est réutilisée depuis la source. L'attaque Jump Over ASLR exploite des collisions dans ce prédicteur pour trouver une adresse source qui entre en collision avec un autre contexte ; elle est donc limitée à ne fuiter que les 30 LSBs du contexte victime.

La configuration classique d'une attaque Spectre v2 ressemble à ceci :

  • L'attaquant place une branche indirecte à la même position que la branche de la victime, mais la destination correspond à un spectre gadget dans le contexte de la victime.
  • Lorsque la victime exécute la branche, elle fait une mauvaise prédiction et le spectre gadget charge un secret puis effectue un accès conditionnel à une région mémoire partagée, telle qu'une bibliothèque. Par exemple, le spectre gadget qui exécute getenv + secret[0]*4096 peut fuiter la valeur du secret à la position 0 en utilisant la libc comme mémoire partagée.
  • L'attaquant récupère ensuite le secret fuité en détectant quelles parties de la mémoire partagée ont été déplacées dans le cache à l'aide d'une attaque flush+reload.

Exec ASLR

Exec ASLR (également connu sous le nom de Reverse Branch Target Buffer Poisoning) est une nouvelle technique pour contourner l'ASLR en utilisant la vulnérabilité Spectre-BTI. Cette attaque exploite le fait que non seulement l'attaquant peut polluer le BTB dans un scénario Spectre-BTI classique, mais que les victimes peuvent aussi déclencher une mauvaise prédiction de branche dans le processus de l'attaquant, amenant l'attaquant à sauter spéculativement vers une adresse protégée par l'ASLR. En utilisant ensuite un canal auxiliaire qui révèle quelle adresse est en cours d'exécution, un attaquant peut récupérer l'adresse de destination complète, contournant ainsi l'ASLR pour le processus ciblé.

La configuration de l'attaque Exec ASLR ressemble à ceci :

  • La victime exécute une branche indirecte, qui écrit son pointeur de destination randomisé (0x55aabbeef456) dans le BTB.
  • L'attaquant place une branche indirecte à une adresse alignée sur 12 LSBs. Comme les 12 LSBs ne sont pas randomisés, cette partie est triviale.
  • L'attaquant remplit tous les emplacements possibles de la mémoire avec un « leak gadget » qui indique à l'attaquant « Je m'exécute à cette adresse ! » en utilisant probeArray comme canal auxiliaire.
  • Lorsque l'attaquant exécute la branche indirecte, elle fait une mauvaise prédiction vers l'un des nombreux leak gadgets présents en mémoire.
  • L'attaquant utilise une attaque flush+reload pour déterminer si ProbeArray a été accédé ou non pendant la spéculation.

Dans ce type d'attaque, il n'est pas nécessaire de trouver un spectre gadget ni de disposer d'une mémoire partagée pour le canal auxiliaire ; la seule exigence est d'exploiter une branche indirecte. Tous les gadgets requis pour un Spectre v2 sont placés dans le processus de l'attaquant. La seule nouvelle exigence pour cette attaque est de pouvoir mapper l'adresse de destination de la victime dans votre propre processus ; par conséquent, cette attaque ne peut pas fonctionner contre KASLR, par exemple. Ce n'est pas non plus une attaque par force brute : en un seul essai, il est possible de tester plusieurs adresses en même temps. Cependant, il existe une limite quant au nombre de « leak gadgets » que la mémoire peut contenir simultanément. Cela réduit considérablement le temps nécessaire pour réaliser l'attaque par rapport à Jump Over ASLR, passant d'environ 100 adresses par seconde à quelques centaines de milliards d'adresses par seconde.

Leak gadget

Le leak gadget utilise probeArray pour indiquer à l'attaquant où le gadget lui-même est exécuté. Il reçoit probeArray et l'index du RIP à fuiter comme arguments et effectue une sorte de programmation sans branche pour décider si probeArray[0] ou probeArray[4096] doit être accédé.

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

Contrôle du BHB et du code de la victime cible

Comme vu précédemment, le BHB est utilisé pour sélectionner une entrée du BTB. Afin de trouver une collision dans le BTB et d'exploiter cette vulnérabilité, un attaquant doit connaître les N dernières branches prises (29 si < skylake). Lors de nos tests, nous avons utilisé une boucle for pour définir l'état du BHB à une valeur connue avant l'appel indirect. Voici un exemple vulnérable de code de victime :

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();
    }
}

Pour garantir que l'état du BHB est le même dans les deux contextes, l'attaquant copie les octets correspondant à la boucle for et à l'appel de la victime sous forme de shellcode. Le shellcode est copié à toutes les 256 positions possibles pouvant correspondre à l'alignement sur 20 LSBs requis pour que l'état du BHB soit identique.

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]

Défis

Il y a un problème lorsqu'on essaie de placer un gadget à toutes les positions possibles en même temps. Dans nos tests, l'ASLR place la destination quelque part entre 0x550000000000 et 0x570000000000. Cela signifie qu'il y a 2,2 To d'adresse virtuelle possible à mapper, soit 537 millions de leak gadgets. Mais le système ne dispose que de 8 Go de RAM. Bien qu'il soit possible de mapper 2 To de RAM en utilisant COW, nous n'avons pas eu beaucoup de succès avec cette approche. Je suppose que cela crée trop de pression sur le Translation Lookaside Buffer (TLB), rendant la spéculation vers une adresse non traduite beaucoup trop lente. Lors des tests, nous avons créé une page de 1 Go en mémoire et l'avons remplie avec les gadgets de fuite. Ensuite, nous avons décalé la page sur la plage de 2 To à l'aide de l'appel système remap. Un autre problème observé était le fait que l'adresse spéculée n'était probablement pas présente dans le TLB, car elle n'avait jamais réellement été exécutée. Mais le manuel Intel indique :

  • « Le processeur peut mettre en cache les traductions requises pour les préchargements et pour les accès résultant d'une exécution spéculative qui ne se produiraient jamais réellement dans le chemin de code exécuté » - ISA

Par conséquent, afin d'augmenter les chances d'une « pagewalk induite par la spéculation », nous avons essayé de rendre la résolution de l'adresse correcte aussi difficile que possible. Cela est fait en utilisant une chaîne de pointeurs pour l'adresse de destination. L'idée est que le front-end du CPU va spéculer sur la destination de la branche et que l'unité de réorganisation exécutera le leak gadget avant de terminer la lecture de la chaîne de pointeurs.

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

Cette technique d'exécution hors ordre + spéculative a montré une amélioration des taux de mauvaise prédiction souhaités dans l'attaque.

Ordonnancement et STIBP

Pour réaliser une attaque Spectre V2, l'attaquant doit exécuter du code sur le même cœur que la victime, afin de partager la même unité de prédiction de branche (BPU). Sur les CPU <= Skylake, nous avons observé qu'il est possible d'obtenir une co-résidence de cœur en utilisant l'hyperthreading. Les CPU Icelake et Cascade Lake implémentent une mitigation appelée Single Thread Indirect Branch Predictor, qui sépare la BPU entre les threads. Il est donc nécessaire d'exécuter la victime et l'attaquant sur le même thread et d'utiliser usleep pour alterner entre le processus victime et le processus attaquant, ce qui rendrait l'attaquant plus lent sur ces CPU sans le fait que les caches (et probablement aussi le TLB) sont très bons sur ces générations, permettant de mettre en cache beaucoup plus de gadgets en même temps.

Résultats

Cette technique a été testée sur tous les CPU Intel disponibles sur Google Cloud, à la fois sur les générations N1 et N2 :

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

Mis à part quelques différences dans les exploits pour Cascade Lake et Ice Lake, tous les tests parviennent à récupérer les adresses avec une précision > 99 % en moins de 10 s.

Mitigations

Les mitigations sont les mêmes que pour Spectre V2 afin d'atténuer les attaques utilisateur-utilisateur. La barrière de prédiction de branche indirecte (IBPB) permet de vider la BPU et peut être utilisée lors des changements de contexte. Sous Linux, l'IBPB peut être utilisée via l'appel système prctl avec l'option PR_SET_SPECULATION_CTRL. Je n'ai aucune idée de la mitigation équivalente pour Windows, merci de me le dire.


Article :

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

Présentation Ekoparty :

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

Diapositives :

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

Références :

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

Télécharger l’outil
Opération \ cycle d'horloge12345
ExtractionABC
DécodageABC
ExécutionABC
Opération \ cycle d'horloge123456
ExtractionABD
DécodageABD
ExécutionABD
Opération \ cycle d'horloge123456
ExtractionAB(S) C
DécodageAB(S) C
ExécutionAB(S) C