Aller au contenu

PAC et BTI : la réponse d'ARM à la réutilisation de code

L'authentification de pointeur signe les adresses de retour ; une falsification fait fauter le processus. BTI contraint les branchements indirects.

Publié le 5 min de lecture

Ceci est le troisième tutoriel de l'espace Exploitation ARM64, et il est écrit du point de vue du défenseur. Les guides ret2win et ROP se terminaient tous deux sur les deux mêmes mitigations. Nous expliquons ici ce qu'elles font réellement, comment les activer et confirmer qu'elles ont pris effet, et où se situent leurs limites. PAC et BTI sont la chose la plus importante à comprendre sur la sécurité ARM moderne, car ils s'attaquent à la classe de la réutilisation de code au niveau matériel.

PAC : signer l'adresse de retour

L'authentification de pointeur (ARMv8.3) utilise une clé secrète, propre à chaque processus, pour calculer une courte signature — un PAC — sur un pointeur augmenté d'une valeur de contexte, et stocke cette signature dans les bits de poids fort autrement inutilisés du pointeur. Pour les adresses de retour, -mbranch-protection=pac-ret fait en sorte que le compilateur :

  • dans le prologue, paciasp — signe x30 (lr) avec la clé et sp comme contexte, puis le sauvegarde ;
  • dans l'épilogue, autiasp — authentifie x30 avant ret (ou le retaa combiné).
pac-ret authentifie le registre de lien avant chaque retour
paciasp (prologue)signe x30 avec la clé + sp
le corps s'exécute ; le lr sauvegardé sur la pile porte un PAC valide
autiasp (epilogue)authentifie x30 avant ret
valide ? branche vers l'appelant / falsifié ? faute
ret / faultun lr falsifié échoue à l'authentification et provoque une faute
codewritable memory

Si un attaquant écrase le x30 sauvegardé via un débordement de pile, la signature des bits de poids fort ne correspond plus au pointeur. autiasp corrompt davantage la valeur (il retourne les bits de tête en cas d'échec), si bien que le ret qui suit saute vers une adresse non canonique et le processus provoque une faute. L'attaquant ne peut pas fournir un PAC correct, car il n'a pas la clé.

BTI : des zones d'atterrissage pour les branchements indirects

La branch-target identification (ARMv8.5) protège l'arête avant. Avec -mbranch-protection=bti, les branchements indirects (br, blr) ne peuvent transférer le contrôle qu'à une instruction qui est une zone d'atterrissage valide — un bti (avec les modes c/j/jc) que le compilateur émet aux points d'entrée légitimes.

Un gadget ROP ou JOP situé au milieu d'une fonction ne commence pas par un bti : l'utiliser comme cible de branchement indirect lève donc une exception de Branch Target. Cela ne restreint pas quelle fonction valide vous atteignez (c'est à gros grain), mais cela supprime la vaste réserve de gadgets en milieu de fonction sur laquelle repose la réutilisation de code.

FonctionnalitéArête protégéeInstructionCe qu'elle bloque
PAC (pac-ret)Arrière (retours)paciasp/autiasp/retaaRegistre de lien sauvegardé falsifié
BTIAvant (branchements indirects)bti c/jSauts vers des gadgets en milieu de fonction

-mbranch-protection=standard active les deux.

Activer et vérifier

# Build with both PAC (return addresses) and BTI:
aarch64-linux-gnu-gcc -mbranch-protection=standard -O2 -o app app.c

Confirmez ensuite que le binaire les porte réellement — un drapeau silencieusement abandonné ne protège rien :

$ readelf -n ./app | grep -A2 'GNU'
  Properties: AArch64 feature: BTI, PAC
$ objdump -d ./app | grep -m1 -E 'paciasp|bti c'
   4007c0:  d503233f  paciasp        ; return-address signing in the prologue

Si la ligne de fonctionnalité .note.gnu.property et les instructions paciasp/bti sont présentes, la protection est active. Sur les systèmes réels, chaque bibliothèque du processus doit elle aussi être compilée avec ces fonctionnalités pour que la protection soit complète.

Les limites — un bilan honnête

PAC et BTI augmentent le coût de la réutilisation de code ; ils n'y mettent pas fin :

  • Gadgets de signature. Si le programme contient du code qui signe un pointeur influencé par l'attaquant avec la bonne clé et le bon contexte, un attaquant peut obtenir un pointeur valablement signé sans la clé.
  • Réutilisation de contexte. PAC lie une signature à un contexte (p. ex. sp). Des pointeurs signés peuvent parfois être rejoués là où le même contexte se répète.
  • Fuites. Un bug de divulgation qui laisse fuiter un pointeur valablement signé peut fournir à l'attaquant une valeur authentifiée exploitable — le même schéma fuite-puis-agir que sur x86.
  • Spéculation (PACMAN). Des techniques d'exécution spéculative ont été démontrées pour tester des suppositions de PAC sans déclencher de faute, entamant le coût de la force brute.
  • BTI est à gros grain. Il autorise n'importe quelle zone d'atterrissage valide, si bien que la réutilisation de fonctions entières (ret2libc) vers une entrée marquée bti peut rester possible.

Rien de tout cela ne rend PAC/BTI optionnels — cela en fait une couche forte parmi plusieurs. Combinez-les avec un canari de pile, PIE/ASLR et un travail de sûreté mémoire.

Ce que cela apprend à un défenseur

  • Activez -mbranch-protection=standard et vérifiez la note. C'est la mesure unique au plus fort levier contre la réutilisation de code ARM, facile à activer mais aussi facile à abandonner silencieusement (une dépendance précompilée, un mauvais drapeau). Vérifiez avec readelf -n.
  • Tout ou rien à l'échelle du processus. PAC/BTI ne protègent que les objets compilés avec eux. Une seule bibliothèque héritée sans la fonctionnalité constitue une brèche. Auditez tout l'ensemble chargé.
  • C'est un multiplicateur de coût, pas un remède. Traitez PAC/BTI comme l'ASLR : ils rendent l'exploitation coûteuse et peu fiable, ce qui a de la valeur, mais le correctif durable reste de ne pas avoir le bug de corruption mémoire — voir langages sûrs pour la mémoire.

Points clés à retenir

  • PAC signe l'adresse de retour avec une clé secrète et un contexte ; l'épilogue l'authentifie, si bien qu'un lr sauvegardé falsifié provoque une faute au lieu de retourner.
  • BTI force les branchements indirects sur des zones d'atterrissage bti, invalidant les gadgets en milieu de fonction sur l'arête avant.
  • -mbranch-protection=standard active les deux ; vérifiez avec readelf -n (BTI, PAC) et en trouvant paciasp/bti c.
  • Ce sont une couche forte aux limites réelles (gadgets de signature, fuites, PACMAN, BTI à gros grain) — à combiner avec les mitigations classiques et la sûreté mémoire.

Ceci clôt l'arc Exploitation ARM64 : le registre de lien et ret2win, le ROP à travers le registre de lien et les défenses PAC/BTI qui le brisent. Pour la vue inter-architecture, comparez avec l'espace Techniques d'exploitation x86-64.

Guides associés