SecurityInsider
Le blog des experts sécurité Wavestone

HITB2017 - Fault injection attacks on Secure Boot




Durant cette intervention Niek Timmers et Albert Spruyt de la société Riscure ont présenté les possibilités d’attaques par injection de fautes et leurs utilisations afin de contourner les mécanismes de secure boot. 


Présentation des attaques par injection de fautes

Les attaques par injections de fautes consistent à modifier l’état de la mémoire afin de provoquer un changement du chemin d’exécution vers un chemin souhaité. Au cours du fonctionnement d’un système informatique des fautes peuvent survenir de manière occasionnelle et non contrôlée ; ces fautes provoquent en général le bug des applications ou sont simplement invisibles pour l’utilisateur. Cependant, dans certains cas, l’injection d’une faute peut permettre à un attaquant de provoquer un comportement inattendu pouvant être exploité en sa faveur. Dans l’exemple si dessous, la fonction secure_boot n’est normalement (d’un point de vue logiciel) appelée que si le test d’intégrité (effectué via l’appel à la fonction check_integrity) a réussi :


Néanmoins, si un attaquant est en capacité de modifier la valeur de la variable « test » directement en mémoire, la fonction secure_boot sera exécutée et l’attaquant aura réussi à contourner les vérifications effectuées en amont.
Afin de permettre l’injection de fautes, différentes méthodes peuvent être utilisées et sont présentées dans la suite de l’article.

Avantage des attaques par injection de fautes

Les attaques sur le matériel possèdent de nombreux avantages sur les attaques logicielles, notamment :

  • L’absence de maturité dans ce domaine : alors que depuis de nombreuses années, les éditeurs sont sensibilisés aux dangers des attaques logicielles (débordement de tampon, injection SQL…) et prennent en compte ce type de vulnérabilités, peu d’attention est actuellement portée à la sécurité contre les attaques matérielles ; ainsi, les cibles sont en général moins bien protégées contre ce type d’attaques.
  • Aucune vulnérabilité logicielle n’est nécessaire : les attaques par injection de fautes ne nécessitent pas la présence de vulnérabilités liées au développement logiciel des solutions.

En revanche, ces attaques sont généralement plus complexes à mettre en œuvre pour les raisons suivantes :
  • Les attaques matérielles sont plus intrusives (que les attaques logicielles) : les attaques étant directement mises en place sur le socle matériel, elles sont naturellement plus intrusives que les attaques logicielles ; elles peuvent notamment nécessiter / provoquer l’altération / la destruction des composants.
  • Un accès physique à l’équipement ciblé est nécessaire : afin de pouvoir injecter des fautes sur le composant, il est en effet nécessaire de pouvoir le manipuler.
  • Des outils (matériels) peuvent être nécessaires : comme expliqué dans la section suivante, de nombreuses méthodes d’attaques requièrent l’utilisation de composants et outils dédiés à l’analyse matérielle et à l’injection de fautes.


Méthodes d’injection de fautes matérielles

Modification d’horloge

Ces attaques consistent en la modification de la fréquence de l’horloge permettant de définir la fréquence d’exécution des instructions du processeur. En effet, l’horloge permet de cadencer l’exécution des instructions ; à chaque réception du signal, le processeur exécute l’instruction qu’il reçoit. Cependant, si le processeur reçoit un nouveau signal sans avoir pu exécuter l’instruction précédente, il abandonne l’exécution courante et commence à exécuter l’instruction suivante. De cette manière, une augmentation temporaire de la fréquence d’horloge peut permettre le « saut » d’une instruction.

Modification de la tension

Cette méthode consiste à diminuer la tension appliquée au module de lecture / écriture (mémoire) afin de provoquer l’apparition d’une faute. En effet, afin de lire ou d’écrire une valeur en mémoire un niveau de tension minimum est requis par le module ; si la tension n’est pas suffisante l’action n’est pas réalisée :

Figure 3 : Exemple d’injection de faute via la diminution de la tension d’alimentation d’un composant

Ainsi, si un attaquant réussi à suffisamment diminuer la tension (au moment de la lecture d’une valeur souhaitée en mémoire) une valeur erronée sera renvoyée et le flot d’exécution normal pourrait être alors contourné.

Injections électromagnétiques

Cette méthode consiste à injecter des fautes via la production d’un champ électromagnétique à proximité des composants afin d’altérer les valeurs stockées ou transmises (registres, bus…) :
Figure 3 : Exemple d’installation visant à injecter des fautes via l’utilisation d’un champ électromagnétique

Injections au laser

Cette méthode consiste à injecter des fautes via l’utilisation d’un laser pointé sur le matériel. En effet, l’éclairage d’un laser a le même effet que plusieurs types de radiations et peuvent ainsi être utilisés afin de modifier directement l’état physique des composants (registres, mémoire…) :

Figure 3 : Utilisation d’un laser afin de provoquer des fautes matérielles

Injection de fautes en pratique

Où injecter les fautes ?

Les méthodes présentées précédemment permettent ainsi de provoquer des fautes matérielles. De façon générale, ces fautes peuvent être injectées :
  • Via les bus de communication ;
  • Directement dans les registres ;

Les fautes sur le matériel peuvent ensuite affecter le flot d’exécution ou la logique mise en place par le développeur. Le code assembleur de l’exemple donné en introduction pourrait être le suivant :


L’instruction « mov ecx, eax » s’écrit avec la séquence d’octets suivante :


Si le second bit de poids le plus faible est modifié la séquence devient :


Cette séquence correspond à l’instruction « mov ebx, eax », le flow d’exécution altéré est alors :


Si une faute est injectée et que l’instruction « mov ecx, eax » est modifiée par l’instruction « mov ebx, eax » la valeur de retour de la fonction check_integrity ne sera pas placée dans le registre ecx (qui contiendra alors une valeur dépendante des instructions exécutée en amont). Ainsi, la comparaison effectuée n’est plus dépendante du résultat de l’appel à la fonction check_integrity et la fonction secure_boot pourrait alors exécutée de façon malveillante (contournement du mécanisme de vérification d’intégrité).

Remarque : il est à noter que dans le cas si dessus, une modification de la fréquence de l’horloge au moment de l’exécution de l’instruction « jz loc_exit » pourrait permettre de sauter cette instruction et ainsi de provoquer l’appel à la fonction secure_boot.

Cas pratique - Mécanismes de secure boot

Le mécanisme de « secure boot » permet d’assurer l’intégrité et l’authenticité d’un système démarrant sur un équipement. Ainsi, l’utilisation de fonctions cryptographiques permettant d’assurer ces fonctions est nécessaire à la mise en place d’un « secure boot ». Les éléments constituant ainsi que les étapes de démarrage d’un système de type secure boot peuvent être schématisés de la façon suivante :

Figure 3 : Éléments et étapes de démarrage d’un système de secure boot

Différentes méthodes utilisées afin de contourner des mécanismes de type secure boot ont ensuite été présentées :

Erreurs lors de la comparaison des condensats cryptographiques

Le package « mbeb TLS » offre différentes routines cryptographiques afin de simplifier la tâche des développeurs sur plateforme ARM. Le code suivant est un extrait d’une fonction de vérification de condensat cryptographique (RSASSA-PKCS1-v1_5) :


Le code assembleur une fois décompilé est le suivant:

Figure 3 : Code assembleur de la fonction « mbedtls_rsa_rsassa_pkcs1_v15_verify »

L’analyse du code ci-dessus permet d’identifier rapidement différents registres dont une modification du contenu pourrait affecter le résultat de la vérification finale, notamment :
  • Registres R2 et R7 (offset 0x132DA) : La modification de R7 en amont ou celle de R2 suite à cette instruction peut permettre de changer le flow d’exécution via la modification de la longueur sur laquelle s’effectue la comparaison en mémoire.
  • Registres R1, R2 et R5 (offsets 0x132E0 & 0x132E2) : la modification de ces registres contenant les adresses des chaines à comparer peut permettre de contourner la vérification effectuée par la fonction memcmp (en particulier si un attaquant peut modifier l’instruction « mov R0, R5 » vers l’instruction « mov R0, R1 » car dans ce cas R1 et R2 pointeront vers la même adresse).
  • Registre R0 (offset 0x132E6) : la valeur présente dans R0 lors de cette instruction influe directement sur le saut effectué.

Exploitation des boucles infinies pour l’injection de fautes

Les boucles infinies peuvent parfois être utilisées afin d’éviter le démarrage des modules comme dans l’exemple ci-dessous :


Le code désassemblé est le suivant :

Figure 3 : Code assembleur de la fonction utilisant une boucle infinie

Suite à la comparaison « cmp R0, #9 », une instruction de branchement est exécutée afin d’entrer ou non dans la boucle infinie. L’analyse des adresses des instructions montre que le bloc relatif à la boucle infinie est placé juste avant (en termes d’adresses et de bloc d’exécution) le bloc exécutant la fonction de boot. Ainsi, l’injection d’une faute sur l’instruction de branchement à l’offset 0x854 peut permettre à un attaquant de continuer l’exécution et de démarrer sur une partition malveillante. Par ailleurs, aucune contrainte de temps n’est imposée à l’attaquant grâce à la présence de la boucle infinie.

Attaques combinées

Les attaques matérielles peuvent être combinées aux attaques logicielles afin d’augmenter les possibilités d’exploitation. Un attaquant peut notamment injecter des fautes afin de construire son propre exploit. En effet, le code suivant n’est pas vulnérable à des attaques de type débordement de tampon :


Le code assembleur est alors :


Si l’attaquant est en capacité d’injecter une faute et de modifier (pour une valeur supérieure) le contenu du registre R2, il est alors possible d’exploiter une vulnérabilité de type débordement de tampon (dans le tas) :



Conclusion

Les possibilités d’exploitation de ces attaques sont variées et peuvent notamment permettre de :
  • Contourner les mécanismes de type secure boot ;
  • Extraire les clés cryptographiques des équipements de type HSM (Hardware Security Module) ;
  • etc


De plus, peu de technologies résistent actuellement ; certains mécanismes peuvent cependant être utilisés afin de limiter la surface d’attaque ou de diminuer leur probabilité de réussite :
  • Mettre en place des mécanismes d’authentification de code : cette méthode permet de détecter les erreurs et la modification du firmware, il est à noter que ce mécanisme pourrait lui-même faire l’objet d’une attaque par injection de fautes.
  • Diminuer la surface de code : la diminution du nombre d’instructions exécutées permet de réduite la probabilité de réussite des attaques par injection de fautes via la limitation du temps et du nombre de possibilités d’exploitation.
  • Diminuer les privilèges des exécutables : cette pratique permet de limiter les conséquences de l’exploitation d’une faute via la diminution des privilèges d’exécution.

Bien que ce type d’attaques soit souvent efficace, il est cependant important de mitiger leur probabilité. En effet, leur mise en place nécessite souvent des compétences spécifiques, de nombreuses heures d’études et de tests ainsi que du matériel actuellement relativement onéreux. 

Mahdi BRAIK

Sources



Aucun commentaire:

Enregistrer un commentaire