Défis de sécurité des processeurs RISC-V ——Analyse des vulnérabilités de l'architecture et des risques d'attaque

arrière-plan

À l'été 2010, un nouveau jeu d'instructions open source - RISC-V - est né à l'Université de Californie à Berkeley, déclenchant une étincelle dans le domaine des puces.

Au cours des dix années suivantes, la conception de l’architecture RISC-V s’est épanouie comme une étincelle dans le monde universitaire et industriel.

Par exemple, diverses conceptions de processeurs et de puces DSA basées sur l'architecture RISC-V : les processeurs de la série Xuantie d'Alibaba, les processeurs SonicBoom/Rocket-chip de Berkeley, les processeurs de la série riscy-OOO du MIT, etc.

On peut dire que l'architecture RISC-V a brisé le monopole des architectures x86, Arm, Power et autres, et nourrit la prochaine génération d'opportunités de développement des technologies de l'information avec des besoins conceptuels plus avancés. Lors du récent salon Embedded World, Calista Redmond, PDG de la Fondation RISC-V, a déclaré qu'« il existe environ 10 milliards de cœurs RISC-V sur le marché ».

L'arbre est immobile mais le vent ne s'arrête pas. Lorsque RISC-V s'enracine dans de nombreux domaines technologiques de pointe tels que le big data, la 5G, l'Internet des objets, la réalité virtuelle et l'informatique de pointe, l'exhaustivité du jeu d'instructions, la conception de la virtualisation , extension RAS, architecture de sécurité, etc. Le problème se manifeste dans différentes implémentations d'applications. Surtout avec le développement du domaine de la sécurité, la manière d'assurer la sécurité d'un SoC basé sur l'architecture RISC-V est devenue la priorité des développeurs d'architecture RISC-V.

Cet article analyse brièvement la structure de sécurité des processeurs RISC-V, présente les vulnérabilités actuelles de la structure du système liées aux attaques par canal côté cache, aux attaques de mémoire, etc. , effectue des tests POC intégrés sur SonicBoomle noyau RISC-V

Attaque par canal côté cache

Le canal latéral est un moyen de vol d'informations largement utilisé. L'attaquant ne s'attaque pas directement aux éventuelles failles théoriques de l'algorithme, mais obtient des données confidentielles à partir des canaux physiques du système via la mesure du temps, la mesure de la consommation électrique ou encore l'écoute des signaux acoustiques et électriques. signaux. , les gens étudient depuis plus de 20 ans les solutions de défense logicielles et matérielles contre les attaques par canal secondaire traditionnelles.

Cependant, avec le développement rapide des processeurs superscalaires multicœurs, de nouvelles variantes d'attaques par canal secondaire ciblant les systèmes de processeur continuent d'apparaître .

Depuis 2017, plusieurs équipes de recherche ont signalé indépendamment les vulnérabilités « Spectre » et « Meltdown » et leurs variantes dérivées. Elles exploitent chacune des vulnérabilités de haute performance telles que la prédiction de branchement et l'exécution dans le désordre au niveau de la microarchitecture du processeur . utilise l'exécution de flux d'instructions transitoires pour contourner les contrôles de sécurité logiciels et matériels, voler des informations sur les utilisateurs ou accéder à des données à privilèges élevés à un niveau supérieur , a attiré l'attention du monde universitaire et de l'industrie. Ces attaques démontrent les failles des idées de conception de microarchitectures de processeurs modernes, puis exposent certains risques de sécurité fondamentaux.

Analyse de sécurité des attaques par canal côté cache basée sur un flux d'instructions transitoire

Le flux d'instructions transitoires (Transient Instrcution) fait référence aux instructions qui sont dans un état d'exécution spéculatif et il n'est pas encore certain qu'elles puissent être retirées à la fin . Par exemple, la technologie de prédiction de branchement permet au processeur de sélectionner un certain segment de code pour une exécution spéculative avant la résolution de branchement, et les instructions dans l'état d'exécution spéculative peuvent être appelées flux d'instructions transitoires.

Si les résultats ultérieurs de la résolution de branchement indiquent que la prédiction était correcte, le flux d'instructions transitoires peut éventuellement se retirer normalement. Mais si une erreur de prédiction est détectée, le pipeline doit être ramené à l'état dans lequel il était avant l'exécution de ces instructions transitoires. En théorie, les instructions transitoires annulées ne devraient pas avoir d'impact observable sur la microarchitecture, sinon les traces laissées dans le processeur par ces instructions qui n'auraient pas dû être exécutées pourraient provoquer des fuites d'informations.

Malheureusement, il est difficile pour le processeur d'éliminer complètement les traces d'exécution du flux d'instructions transitoires.Un exemple typique est : si l'instruction de chargement de mémoire exécutée dans le flux d'instructions transitoires amène une donnée dans le cache, même si le pipeline renvoie Pendant le lancer, le processeur ignore le résultat de l'accès à la mémoire renvoyé par cette instruction, mais l'état du cache qui a été modifié ne peut pas être annulé . À partir de là, l'attaquant peut déduire l'adresse d'accès à la mémoire du programme victime en surveillant les modifications dans le cache. Si l'adresse elle-même contient des informations sensibles, cela entraînera une fuite d'informations. Le processus ci-dessus constitue un type d'attaque par canal côté cache basé sur un flux d'instructions transitoire.

Contrairement aux attaques par canal secondaire traditionnelles, les attaques par canal secondaire basées sur des flux d'instructions transitoires exploitent des failles de sécurité courantes au niveau de la microarchitecture matérielle pour voler des informations sensibles. Ce type d'attaque ne peut pas être complètement défendu par des moyens logiciels . Ce type d’attaque présente généralement les trois caractéristiques suivantes :

  • Impact à grande échelle : la plupart des processeurs modernes disposent de structures de cache et prennent en charge des technologies hautes performances telles que les superscalaires hors service , de sorte que les conceptions architecturales des grands fabricants sont affectées par cette attaque.
  • Coûts de défense élevés : les méthodes de protection auront un impact plus important sur les performances globales du système .
  • Dépendance matérielle : la mise en œuvre de ce type d'attaque dépend de l'environnement d'exécution du programme et de la conception matérielle spécifique . Différents plans d'attaque peuvent être dérivés pour différentes micro-architectures.

Méthodes de défense contre les attaques par canal côté cache

Les attaques « fantôme » et « meltdown » ont attiré une large attention et des recherches dans le monde universitaire et l'industrie. Voici, à titre de référence , une brève introduction à certaines des idées de défense actuellement utilisées par le monde universitaire pour les attaques par canal côté cache :

1. Empêcher la propagation de valeurs spéculatives dans le pipeline

Une attaque par canal secondaire basée sur un flux d'instructions transitoire consiste essentiellement à ce que la valeur située sur le mauvais chemin de spéculation dans le processeur soit utilisée pour l'exécution d'instructions ultérieures et soit finalement obtenue par l'attaquant via des moyens de canal secondaire.

Par conséquent, une solution simple consiste à empêcher la propagation de valeurs spéculatives contenant des données privées sur le pipeline . En fait, la première solution de l'industrie consistant à utiliser des instructions de barrière de type lfence était également basée sur cette idée, mais le simple fait d'utiliser des instructions de barrière pour bloquer toutes les instructions ultérieures entraînerait une perte énorme dans les performances d'exécution spéculative.

Par conséquent, il est devenu une direction de recherche importante pour diviser et marquer dynamiquement les instructions dangereuses dans le matériel d'une manière plus fine, réduisant ainsi la gamme d'instructions dont l'exécution est restreinte et réduisant la perte de performances de la solution. (MICRO'19), NDA (MICRO' 19), la méthode de conception de SpectreGuard (DAC'19) et d'autres articles.

2. Différer les modifications de l'état du cache par des instructions d'accès mémoire spéculatives

Ce type de solution consiste à introduire un tampon supplémentaire dans l'architecture du cache, et les instructions d'accès à la mémoire qui n'ont pas encore échappé à l'exécution spéculative ne peuvent entrer que dans le tampon au lieu du cache . Si la supposition est correcte, la valeur du tampon sera entrée dans le cache ; sinon, si la supposition est fausse, la valeur du tampon sera rejetée, empêchant ainsi l'état du cache d'être modifié par le résultat de l'accès à la mémoire sur le chemin de supposition incorrect. Travaux représentatifs tels que les méthodes de conception dans InvisiSpec (MICRO'18), SafeSpec (DAC'19).

3. Assurer l'isolation des données entre différents domaines de sécurité au niveau du cache

Cette direction modifie également l'architecture du cache pour empêcher les attaquants de mesurer efficacement les informations du cache, mais l'essentiel est de fournir une isolation des données entre les attaquants et les parties attaquées. Les instructions de base incluent le partitionnement du cache et la randomisation de la carte d'adresses du cache .

Parmi eux, la technologie CAT (Cache Allocation Technology) d'Intel fournit également un mécanisme de partitionnement du cache dans LLC (CAT a été initialement introduit pour résoudre le problème des voisins bruyants des systèmes multicœurs), et certaines recherches ont emprunté ou utilisé cette structure.

4. Identifiez dynamiquement les attaquants des canaux secondaires

Les attaquants par canal côté cache sont généralement en concurrence avec l'attaquant pour accéder au même emplacement physique du cache, ils afficheront donc un modèle d'accès à la mémoire (modèle) spécifique. Si le matériel peut identifier les modèles d'accès à la mémoire d'attaque par canal côté potentiels et donner un avertissement en temps opportun, la victime peut prendre des mesures pour éviter les fuites de données. Par exemple, la méthode de conception dans Cyclone (MICRO '19).

L'attaque par canal côté cache réapparaît

Voici un fragment du programme d'attaque Spectre-v1 (Bounds Check Bypass) à titre d'exemple :

if (idx < array1_sz){
        secret = array1[idx]
        dummy = array2[secret * L1_BLOCK_SZ_BYTES];
}

Cette attaque cible les instructions de branchement conditionnel (telles que les instructions bge dans RISC-V). Le processus d'attaque comprend les étapes suivantes :

  • Pendant la phase d'entraînement, le segment de code ci-dessus est exécuté à plusieurs reprises avec l'index de données idx qui n'a pas franchi la frontière, de sorte que la direction du saut de branche enregistré dans le PHT (Pattern History Table) soit le segment de code dans le if. Étant donné que la valeur idx ne dépasse jamais la plage d'index du tableau array1 à ce stade, aucun problème de sécurité ne se posera.

  • Dans la phase d'attaque, le segment de code ci-dessus est exécuté avec un index de tableau hors limites idx. Du point de vue d'un développeur de logiciel, puisque le jugement conditionnel if stipule que la valeur idx ne peut pas dépasser la plage d'index array1, l'accès aux données dans le segment de code ne doit pas être exécuté à ce moment-là et le programme doit être sécurisé. Cependant, étant donné que le prédicteur de branchement a déjà été entraîné, le matériel entrera toujours de manière spéculative dans l'exécution du segment de code et accédera au secret de données privées ailleurs dans l'espace d'adressage mémoire avec l'index de tableau hors limites idx. Dans la fenêtre d'exécution spéculative, l'attaquant construit ensuite une adresse d'accès mémoire via la valeur secrète dans le flux d'instructions transitoires pour accéder à un tableau auxiliaire array2.

  • Pendant la phase de récupération du processeur, une erreur de prédiction de branchement est découverte et le processeur annule le pipeline et ignore les résultats d'accès à la mémoire des instructions ci-dessus. Cependant, certaines données du tableau auxiliaire array2 ont été introduites dans le cache pendant la phase d'attaque, et l'adresse du cache est liée au secret des données privées . Le contenu qui a été introduit dans le cache ne sera pas révoqué en raison de la restauration du pipeline.

  • Dans la phase de reconstruction des données, l'attaquant n'a besoin que de mesurer les accès aux données à différentes positions dans le tableau array2 du cache , puis il peut déduire où dans le tableau2 le segment de code ci-dessus a accédé pendant la phase d'attaque, puis en déduire la valeur spécifique. de secret, complétant ainsi le vol de données.

On peut voir que le processus d'attaque ci-dessus contourne la vérification des limites définie par le logiciel et obtient des données privées ailleurs dans l'espace d'adressage mémoire sans lever aucune exception de programme.

L'attaque est reproduite ci-dessous sur un vrai processeur. L'environnement SoC utilisé est Chipyard, le cœur du processeur est SonicBoom et le FPGA utilise VCU118 ou VC707. Pour le code source de l'attaque, veuillez vous référer au code POC fourni par SonicBOOM https://github.com/riscv-boom/boom-attacks.

L'adresse attaquée stocke : !"ThisIsTheBabyBoomerTest

L'attaquant réel a obtenu : !"ThisIsTheBabyBoomerTest

On peut voir que le programme d'attaque Spectre-v1 a été reproduit avec succès sur SonicBoom.

Sécurité des données de la mémoire

Selon les dernières statistiques de MITRE, 40 % des vulnérabilités du Top 2022 CWE des 25 faiblesses logicielles les plus dangereuses (2022 CWE Top 25 Most Dangerous Software Weaknesses) sont liées à la modification/au contrôle de la mémoire. Ces dernières années, le langage C/C++ a été largement utilisé dans la programmation système moderne. En raison des caractéristiques des langages tels que C/C++ qui peuvent faire fonctionner le système de mémoire, il a considérablement amélioré la vitesse d'exécution et l'efficacité du programme, mais cela a également introduit de nombreux problèmes de sécurité de la mémoire. Actuellement, les compilateurs et les environnements d'exécution traditionnels n'effectuent pas de contrôles de sécurité statiques ou dynamiques sur les pointeurs de programmes C/C++. Le résultat final est que les programmes programmés en C/C++ sont très vulnérables aux attaquants.

Selon le type d'accès illégal au pointeur, la sécurité de la mémoire est principalement divisée en deux catégories : la sécurité de la mémoire spatiale et la sécurité temporelle (sécurité spatiale et sécurité temporelle).

  • Violation de la sécurité de l'espace : lorsque le pointeur accède à un objet en dehors de la portée du programme, la sécurité de l'espace mémoire sera violée. L'exemple le plus courant consiste à utiliser un débordement de tampon sur la pile pour écraser l'adresse de retour d'une fonction avec une valeur conçue par l'attaquant pour guider le sens du flux d'exécution du programme ; ou à utiliser directement le débordement pour réécrire des variables ou des informations importantes. .

  • Violation de la sécurité temporelle : la sécurité temporelle de la mémoire est violée lorsqu'une référence à un objet est utilisée en dehors d'une plage de temps spécifiée, généralement après que la mémoire de l'objet instancié a été réaffectée sans initialisation stricte de la mémoire . Par exemple, une violation de la sécurité temporelle (Utiliser après libération) provoquée par le déréférencement d'un pointeur vers une mémoire invalide (généralement non allouée ou libérée).

La sécurité de la mémoire peut être subdivisée en

  • Débordement de tampon,
  • Double gratuit,
  • Déréférencement de pointeur nul,
  • Invalide gratuit,
  • Accéder à la mémoire non initialisée (Lecture de la mémoire non initialisée),
  • Utiliser après gratuité (Utiliser après gratuité) et plusieurs autres sous-catégories.

Les attaques de vulnérabilité de mémoire sont principalement divisées en trois étapes :

  • Implantation de code : implanter un code d'attaque (shellcode) dans le programme cible ;

  • Attaque par débordement : le débordement de tampon est obtenu en saisissant une chaîne spéciale comme paramètre, et l'adresse de retour après le débordement pointe vers l'adresse de départ du code d'attaque ;

  • Détournement de système : détourner et contrôler le système en exécutant un code d'attaque ;

Technologie de protection des logiciels de sécurité de la mémoire

Après avoir compris les attaques de vulnérabilité de mémoire et diverses vulnérabilités de mémoire, introduisons les technologies de protection logicielle existantes :

Le plus typique est le mécanisme de protection de l'exécution des données (DEP) .Son principe de base est de marquer la page mémoire où se trouvent les données comme non exécutables.Lorsque le programme déborde et est transféré avec succès au shellcode , le programme essaie d'exécuter instructions sur la page de données, et le processeur lancera une exception au lieu d'exécuter des instructions malveillantes. En activant DEP, vous pouvez empêcher efficacement les pages de données (telles que la page de tas par défaut, diverses pages de pile et les pages de pool de mémoire) d'exécuter du code.

La technologie ASLR (Address Space Layout Randomization) est un mécanisme de protection qui interfère avec le positionnement du shellcode en n'utilisant plus d'adresse de base fixe lors du chargement d'un programme. Y compris la randomisation d'image, la randomisation de pile et la randomisation de PEB (Process Environment Block, bloc d'environnement de processus) et TEB (Thread Environment Block, bloc d'environnement de thread)

Un autre mécanisme de défense efficace est le mécanisme Stack Canary. Son principe est d'insérer les informations du cookie en bas de la pile lorsque la fonction est exécutée. Au retour de la fonction, elle vérifiera si les informations du cookie sont légales. Si elles ne sont pas légales, le programme cessera de fonctionner.

Le mécanisme ASLR, DEP (NX/XD) ou Stack Canary mis en œuvre via le système de mémoire virtuelle empêche les attaquants d'injecter et d'exécuter du code d'attaque arbitraire à volonté. Par conséquent, ces mécanismes de prévention protègent dans une certaine mesure le fonctionnement sûr du programme.

Cependant, face à des codes et méthodes d’attaque plus complexes, ces mécanismes de protection seront confrontés aux conséquences d’une défaillance. Par exemple, dans la programmation orientée retour (ROP), l'exécution de code arbitraire est obtenue en détruisant les pointeurs de code (tels que les adresses de retour) et en enchaînant l'exécution de plusieurs gadgets. Les séquences de ces binaires bruts sont combinées pour mettre en œuvre le code d'attaque malveillant envisagé par l'attaquant.

En plus d'ajouter certaines technologies de protection logicielle aux langages de programmation d'origine, de nombreux langages de programmation axés sur la sécurité ont vu le jour ces dernières années. Par exemple, le langage Rust a reçu beaucoup d’attention pour sa capacité à atteindre des performances comparables à celles du C/C++ tout en garantissant une sécurité élevée. Pour cette raison, cet article présente brièvement le langage de sécurité Rust.

Sécurité

Rust accorde la priorité à la sécurité et à la vitesse. Il ne dispose pas de mécanisme de récupération de place, mais il parvient à assurer la sécurité de la mémoire. Ce qui distingue Rust du C et du C++, ce sont ses solides garanties de sécurité.

Rust est totalement sécurisé en mémoire à moins d'être explicitement sélectionné à l'aide du mot-clé "unsafe".

La conception de base de Rust est un ensemble strict de règles de sécurité appliquées via des contrôles au moment de la compilation. Pour prendre en charge davantage de contrôles de bas niveau, Rust permet aux programmeurs de contourner ces vérifications du compilateur pour écrire du code dangereux. Environ 70 % des problèmes de sécurité dans CVE fournis par MSRC (Microsoft Security Response Center) sont des problèmes de sécurité de la mémoire. Ils pensent que si les programmes actuellement considérés comme causant des problèmes de sécurité sont écrits en Rust, alors 70 % des problèmes de sécurité de la mémoire n'existeront plus.

Un document de recherche présenté lors de la conférence internationale ACM SIGPLAN (PLDI'20) a souligné que "le mécanisme de code sécurisé du langage Rust peut très efficacement éviter les problèmes de sécurité de la mémoire. Tous les problèmes de sécurité de la mémoire rencontrés dans les versions stables sont liés à un code non sécurisé".

performance

Si vous souhaitez utiliser Rust pour remplacer C ou C++ comme langage de programmation principal, vous devez prendre en compte les performances et les capacités de contrôle du langage de programmation.

Rust dispose également d'un environnement d'exécution configurable, et la bibliothèque standard de Rust s'appuie sur la libc pour prendre en charge sa plate-forme, tout comme les langages C et C++, et la bibliothèque standard de Rust est également facultative. Les programmes compilés par Rust s'exécutent sur des plates-formes qui n'ont pas de système d'exploitation. Il peut également être exécuté.

Technologie de protection matérielle de sécurité de la mémoire

Voici une brève introduction à quelques idées académiques actuelles sur la technologie de protection matérielle de la sécurité de la mémoire à titre de référence :

1. Atténuation basée sur Tripwire

Ce type de solution place principalement la zone rouge entre les blocs de mémoire alloués. Lorsque le pointeur accède à la zone rouge, une erreur correspondante se produit. Des exemples typiques sont AddressSanitizer de Google (ATC'12) et le vérificateur de mémoire de Valgrind (PLDI'07).

L'innovation de Tripwire est qu'ils sont activés immédiatement lorsqu'une intrusion dans la mémoire se produit - en s'appuyant sur la mémoire fantôme pour effectuer la vérification, contrairement aux mesures logicielles telles que Canary (Stack Canary) qui s'appuient sur un logiciel pour effectuer des vérifications explicites.

Bien que Tripwire offre une sécurité renforcée, il entraîne une surcharge de hautes performances prohibitive et ne convient donc pas au déploiement sur des appareils sensibles aux performances. La figure ci-dessous est l'organigramme comportemental de REST (ISCA'18). REST n'est qu'une très grande valeur aléatoire intégrée dans le programme. Il s'agit d'une détection matérielle de la zone rouge - remplaçant la zone rouge par un jeton pour éliminer la dépendance à l'égard de la mémoire fantôme. Les instructions de chargement/stockage détecteront si la zone d'adresse est isolée.

2. Gros pointeurs

En plus du mécanisme Tripwire, les gros pointeurs sont également une direction populaire dans l'industrie. Ils conservent principalement les métadonnées de l'objet - faisant généralement référence à la base, aux limites, etc. de l'objet, insérées dans le pointeur et indexées avec l'adresse du pointeur. Généralement, il sera vérifié lors de la compilation statique et de l'instrumentation dynamique au moment de l'exécution pour la protection de la sécurité de la mémoire.

Hardbound (SOSP'08) utilise l'espace fantôme pour stocker les autorisations du pointeur. L'adresse pointée par le pointeur est de base plus la taille de l'intervalle lié entier. Softbound (PLDI'09) et Watchdog (ISCA'12) pour chaque chien de garde Générer un unique identifiant pour chaque allocation de mémoire, associez ces identifiants à des pointeurs et vérifiez que l'identifiant est présent à chaque accès mémoire. Pour fournir une détection complète, Watchdog utilise des identifiants basés sur des identifiants presque entièrement sur la détection matérielle. La figure ci-dessous est un aperçu de la conception HardBound. Pour la signification spécifique, veuillez vous référer au document, qui ne sera pas décrit dans cet article.

3. Capacité

La capacité et le pointeur Fat sont similaires dans une certaine mesure. Ils sont protégés par l'ajout de métadonnées au pointeur. Cependant, la différence entre Capability et le pointeur Fat est qu'il s'agit d'un pointeur Fat qui ne peut pas être modifié. Chaque capacité ne peut être créée et transférée (référence du module enfant) et est supprimé. Les autorisations de lecture, d'écriture, d'exécution et autres de chaque pointeur sont également attachées au pointeur. Le pointeur ne fonctionne plus seulement comme un index, mais plutôt comme un « objet » avec autorisation de récupération.

Il existe également certaines implémentations matérielles basées sur la capacité, et la conception représentative est CHERI (ISCA'14, Capability Hardware Enhanced RISC Instructions). Les métadonnées (limites et autorisations) sont maintenues en ligne avec les adresses, éliminant ainsi les recherches coûteuses de tables fantômes en échangeant les régions, le stockage et la bande passante mémoire. Ce qui suit explique brièvement certains avantages de la conception CHERI :

  • Un modèle de protection abstrait de CHERI a été proposé et implémenté respectivement dans MIPS 64 bits, RISC-V 32/64 bits et ARMv8-A ISA 64 bits.
  • Possède un modèle ISA formel, prend en charge la simulation Qemu-CHERI et plusieurs prototypes FPGA
  • Vérifiez que le modèle de sécurité ISA est établi via un logiciel
  • De nombreux systèmes prenant en charge les logiciels : CHERI Clang/LLVM/LLD, CHERI BSD, C/C++ APP
  • Morello : La carte de démonstration de qualité industrielle de l'architecture CHERI ISA est actuellement la seule implémentation physique de l'architecture prototype CHERI.

contrôler l’intégrité du flux

Les attaques de détournement de flux de contrôle atteignent l'objectif de l'attaque en construisant des vecteurs d'attaque spécifiques, en exploitant des vulnérabilités logicielles telles que le débordement de mémoire et en falsifiant illégalement les données de contrôle au cours du processus, modifiant ainsi le flux de contrôle du processus et exécutant des programmes d'attaque pré-construits. Une attaque réussie par débordement de tampon peut être utilisée comme une condition préalable efficace pour modifier le flux de contrôle en cours d'exécution du système informatique. Par exemple, elle peut modifier l'adresse de retour du programme, l'EBP de la fonction appelante ou modifier le pointeur de fonction et GOT. table, etc., conduisant ainsi le programme aux préréglages de l'attaquant. Les programmes d'attaque sont utilisés pour exécuter du code malveillant, provoquant l'effondrement de l'ensemble du programme, ou permettant aux attaquants de prendre le contrôle d'une partie du système, provoquant une crise de sécurité pour l'ensemble du système informatique. . Par conséquent, la défense contre les attaques par débordement de tampon revêt une importance pratique extrêmement importante.

W ⊕ , il s'agit d'une attaque de réutilisation de code. L'attaque initiale de réutilisation de code est une attaque de retour à la libc. Étant donné que la bibliothèque libc du système contient souvent des sous-programmes pour exécuter des appels système et d'autres fonctions qui peuvent être utiles à l'attaquant, il est plus probable qu'ils recherchent du code pour assembler l'attaque. . .

Dans une attaque de retour à la libc, l'attaquant détourne le flux de contrôle du programme en exploitant une vulnérabilité de débordement de tampon, sélectionne une fonction de bibliothèque disponible et écrase l'adresse de retour par son emplacement d'entrée, puis écrase les paramètres conçus avec un écrasement détaillé de l'emplacement de la pile. Passé à la fonction pour terminer l'attaque.

Attaque ROP

L'attaque de retour à la libc contourne la méthode de défense W⊕X, mais ses défauts sont également évidents : les programmes d'attaque qu'elle peut exécuter ne sont pas assez flexibles et sont facilement ciblés pour la défense, c'est pourquoi ROP a vu le jour.

ROP (Return-Oriented Programming) permet aux attaquants d'exécuter du code en présence de défenses de sécurité telles que la protection de l'espace exécutable et la signature de code. Dans cette technique, un attaquant peut contrôler la pile d'appels pour détourner le flux de contrôle du programme, puis exécuter une séquence d'instructions machine soigneusement sélectionnée et déjà présente dans la mémoire de la machine, appelée « gadget ».

Chaque gadget se termine généralement par une instruction de retour (ret) et se trouve dans un sous-programme du programme existant et/ou du code de la bibliothèque partagée. Ces gadgets sont enchaînés pour permettre à un attaquant d'effectuer des actions arbitraires sur une machine défendue.

ROP écrase toujours la pile avec l'adresse de retour et les paramètres, mais l'adresse de retour fournie par l'attaquant peut pointer vers n'importe quel point de la base de code, en particulier n'importe quel fragment de code (gadgets) qui se termine par l'instruction ret.


Même si les fonctions de bibliothèque disponibles existantes sont totalement inexploitables, ROP peut toujours utiliser une approche patchwork pour assembler un programme d'attaque complet, d'autant plus que le jeu d'instructions de l'architecture x86 est très dense et que presque n'importe quelle séquence d'octets aléatoire peut être interprétée comme une séquence d'octets valide. Jeu d'instructions x86.

L’objectif de l’attaque ROP est de trouver un ensemble de gadgets répondant aux exigences suivantes :

  • Doit se terminer par ret
  • Ne modifie pas explicitement le pointeur de pile
  • Possède des fonctions de base telles que des fonctions arithmétiques et logiques
  • Avec des fonctions complexes telles que l'accès à la mémoire et les appels système
  • Avec fonctions de branchement et de boucle conditionnelles (modifier la valeur esp via la logique)
  • Tant qu'un ensemble de gadgets répondant aux conditions ci-dessus peut être trouvé dans le programme exécutable, une machine Turing-complète orientée retour est générée déguisée.

De nombreuses défenses contre la ROP :

  • DynIMA (Dynamic Integrity Measurement and Attestation, mesure d'intégrité dynamique) - détecte chaque séquence d'instructions exécutées consécutivement se terminant par ret
  • DROP (Detecting ROP Malicious Code, détection du code malveillant de programmation orientée retour) - détecte l'adresse de retour apparue dans la pile qui pointe toujours vers un espace mémoire spécifique
  • Noyaux « sans retour » – une optimisation au niveau du compilateur qui élimine toutes les instructions ret du programme, rendant impossible aux attaquants d'utiliser des attaques ROP.
  • ……

Attaque JOP

Les attaques ROP sont puissantes, mais s'appuyer sur les instructions stack et ret pour détourner le flux de contrôle constitue également son inconvénient. Sur cette base, les attaquants ont proposé la méthode d'attaque JOP/COP.

JOP/COP (Jump/Call-Oriented Programming, Jump/Call-Oriented Programming) abandonne toute dépendance vis-à-vis du flux de contrôle de la pile et de la découverte et de la liaison des gadgets, et utilise simplement une série d'instructions de saut indirectes, qui contournent complètement toutes les défenses réussies. contre le ROP.

Dans ROP, un programme orienté saut se compose d'un ensemble d'adresses de gadgets et de valeurs de données chargées en mémoire. Les adresses de gadgets sont analogues aux opcodes dans une nouvelle machine orientée saut. Dans ROP, ces données sont stockées sur la pile, de sorte que le pointeur de pile agit comme un « compteur de programme » dans les programmes orientés retour.


JOP ne se limite pas à utiliser esp pour référencer l'adresse de son gadget, et le flux de contrôle n'est pas non plus piloté par des instructions ret. Au lieu de cela, JOP utilise une table de répartition pour contenir les adresses et les données des widgets.


Le « compteur de programme » est n'importe quel registre qui pointe vers la table de répartition. Le flux de contrôle est piloté par un gadget de planification spécial qui exécute une séquence de gadgets. A chaque appel, le planificateur avance le compteur de programme virtuel et démarre les widgets associés. Une fois l'exécution du gadget terminée, le répartiteur est renvoyé.


Ci-dessus, un exemple de programme orienté sauts. L'ordre des sauts est représenté par les chiffres 1->6. Ici, edx est utilisé comme pc, et le répartiteur passe à l'adresse suivante dans un tableau consécutif d'adresses de gadgets en ajoutant simplement 4 (donc f(pc) = pc + 4). La fonction « attaque »

  • Déréférencer eax
  • Ajoutez la valeur de l'adresse ebx à eax
  • Le résultat est stocké à l'adresse ecx,
    les registres esi et edi sont utilisés pour rendre le contrôle au planificateur

moyens défensifs

Control-flow Enforcement Technology (CET) est une extension de jeu d'instructions proposée par Intel spécifiquement pour les débordements de pile avancés tels que ROP/COP/JOP. Il est principalement divisé selon les deux aspects suivants :

  • a. Shadow Stack (SS, shadow stack) est une méthode de défense principalement contre le ROP. Le but est de protéger l'adresse de retour de la pile. Cette défense crée une deuxième pile indépendante de la pile de données du programme. Lorsque la pile fantôme est activée, l'instruction CALL pousse l'adresse de retour sur les piles de données et la pile fantôme.
  • b. Le suivi indirect des branches (IBT, indirect branch tracking) est une méthode de défense principalement contre les COP/JOP. La méthode consiste à ajouter une nouvelle instruction ENDBRANCH, qui est utilisée pour identifier l'adresse cible effective des appels indirects et des sauts dans le programme. Son opcode est exprimé en NOP pour les anciennes architectures qui ne sont pas compatibles avec cette instruction, et n'empêchera pas l'exécution du programme. Pour les processeurs prenant en charge CET, l'opcode est toujours NOP et n'apporte pas de charge matérielle supplémentaire.

    Pour les attaques contre l'intégrité du flux de contrôle, ARM propose deux solutions de défense : BTI et PAC :
    a. Code d'authentification du pointeur (PAC, code d'authentification du pointeur) : le jeu d'instructions ARMV8.3-A ajoute un mécanisme d'authentification du pointeur (PA). En utilisant la valeur de un registre servant de pointeur pour vérifier son contenu avant d'accéder aux données ou au code est destiné à lutter contre les attaques ROP. Le principe de base du mécanisme est d'utiliser des instructions spéciales pour obtenir un texte chiffré de 64 bits grâce à un algorithme de chiffrement basé sur un contexte de 64 bits, la valeur originale du pointeur et une clé de 128 bits, de le tronquer et de l'utiliser comme un PAC. Utilisez des instructions spéciales pour vérifier la valeur du pointeur avant d'utiliser le pointeur. Une fois la vérification réussie, le bit haut du pointeur sera restauré à son état d'origine. Sinon, un code d'erreur sera rempli dans le bit haut du pointeur. et une exception sera déclenchée lors de la phase d'adressage.
  • b. Branch Target Identification (BTI, Branch Target Identification) : BTI est une fonctionnalité de sécurité ajoutée dans Armv8.5 pour limiter les attaques. Elle est principalement utilisée pour se défendre contre les attaques JOP (Jump-Oriented Programming). Le principe de fonctionnement est que le processeur configurera une instruction BTI pour l'instruction BR ou BLR lors de la compilation, c'est-à-dire en définissant la cible de saut spécifiée pour l'instruction de saut indirect (un mot très approprié est utilisé en anglais, landing pads, landing point). . Si la cible d'un saut indirect n'est pas le point d'atterrissage spécifié, une exception de cible de saut est déclenchée. L'utilisation de points d'atterrissage limite le nombre de cibles possibles pour les sauts indirects, ce qui rend difficile l'enchaînement de gadgets pour former de nouveaux programmes.

L'attaque de mémoire réapparaît

Voici un exemple de fragment de programme d'attaque par débordement de tampon :

void shellcode() {
  .......
}
int main(int argc, char *argv[]) {
  char buf[8];
  if(argc < 2) {
    printf("Pass an argument, champ!\n");
  }
  strcpy(buf,argv[1]);
  printf(buf);
}
  • La première étape de l'implantation du code : implanter le code d'attaque (shellcode) dans le programme cible ;

  • Attaque par débordement de deuxième étape : obtenez un débordement de tampon en saisissant une chaîne spéciale en tant que paramètre, et l'adresse de retour après le débordement pointe vers l'adresse de départ du code d'attaque (adresse de départ du shellcode) ;

  • La troisième étape du piratage du système : détourner et contrôler le système en exécutant le code d'attaque ;

L'environnement SoC est Chipyard, le cœur du processeur est SonicBoom et le FPGA utilise VCU118/VC707

Ceci est un exemple du résultat de sortie d'un programme d'attaque par débordement de pile. Le débogage GDB trouve l'adresse du programme RA (adresse de retour), l'écrase par débordement de pile et fait passer le programme au programme d'attaque établi. Le résultat final est la sortie Vous gagnez ! En plus de changer le shell du programme.

Cadre de sécurité Arm - un cadre de référence important pour la conception de la sécurité RISC-V

En réponse aux menaces de sécurité ci-dessus, de nombreux développeurs du domaine RISC-V ont créé leurs propres solutions, mais ces solutions de sécurité ne sont pas encore standardisées. L'architecture Arm comporte des contre-mesures de sécurité hautement standardisées. Vous pouvez tirer des enseignements de l'architecture de sécurité Arm pour fournir des idées sur l'orientation future de l'expansion de la sécurité de RISC-V. Ici, nous introduisons principalement quatre types de contre-mesures de sécurité :

  • Technologies d'exécution défensive : il s'agit d'une classe de technologies de protection contre les attaques de flux de contrôle, les attaques d'accès aux données et les attaques par canal secondaire. Les logiciels sont rarement parfaits et certaines vulnérabilités des programmes peuvent être réparées artificiellement grâce à une programmation défensive, mais cette approche peut ne pas être applicable à tous les programmes de code. À l'heure actuelle, la solution à ces problèmes de sécurité passe principalement par trois moyens techniques de modification du matériel, du compilateur et du moteur d'exécution à des fins de protection. Ce type de contenu sera expliqué en détail ultérieurement et ne sera pas expliqué ici.
  • Technologies d'isolation : des limites de sécurité bien définies sont l'un des principes les plus fondamentaux de l'ingénierie de sécurité. Cette défense de sécurité est principalement un type de technologie d'isolation de défense conçue pour TEE. Les produits représentatifs incluent Arm TrustZone, Intel SGX, Apple Secure Enclave, Penglai Enclave, Keystone Enclave, etc.
  • Services de sécurité de plate-forme commune : pour des raisons de sécurité, tous les appareils nécessitent une racine de confiance (RoT) et sont vérifiés par rapport à une liste de contrôle détaillée d'exigences de sécurité. Sans l’utilisation de cadres accessibles au public, le marché ne peut pas déterminer si un produit est suffisamment résistant aux attaques.
  • API de sécurité standard : avec le développement rapide de l'informatique et de la technologie, de plus en plus de clients utilisent des interfaces API personnalisées par les développeurs pour effectuer le développement itératif de divers microservices. Pour cette raison, l'interface API peut non seulement connecter les fonctions du service, mais également effectuer la transmission de données, et la protection de la sécurité de l'API devient de plus en plus importante. Il y a eu de nombreux accidents de fuite de données dus à des attaques ou à des vulnérabilités de l'API. Par exemple, l'incident de fuite d'informations de Huazhu en Chine en 2018 a provoqué la fuite d'une grande quantité d'informations personnelles des utilisateurs, et Instagram en 2019 en raison de vulnérabilités de l'interface de l'API. et des photos ont été divulguées.
    Comment établir une architecture de sécurité informatique complète et efficace sur la chaîne écologique RISC-V et réduire la fragmentation de la conception technique est un besoin très difficile et urgent dans le développement écologique actuel de RISC-V.

Opportunités et défis

Avec la popularisation de la technologie 5G, l’ère du véritable Internet of Everything viendra, et des vulnérabilités sans fin auront un impact de plus en plus répandu. Les lacunes existantes en matière de protection de la sécurité seront encore amplifiées. Dans un environnement où tout peut être attaqué, différents maillons faibles deviendront les cibles de premier choix des attaquants. En regardant le développement de X86, Arm, MIPS et d'autres architectures, la sécurité des puces est toujours « déconnectée » au début de la conception de l'architecture, et des « correctifs » sont ajoutés dans les étapes ultérieures du cycle de conception. L'architecture RISC-V sera-t-elle implémentée plus tôt ? Déployer et construire un « fossé » sécurisé ? Les milieux industriels et universitaires ont prouvé l'efficacité et la commodité du jeu d'instructions open source RISC-V à travers divers exemples (processeurs hautes performances, accélérateurs efficaces, etc.), mais actuellement les extensions de sécurité dans le domaine RISC-V sont toujours activement promues. . Alors que de plus en plus de puces RISC-V sont utilisées dans les systèmes critiques, comment injecter davantage de réflexion et de stratégies dans les méthodes de sécurité et le cycle de vie du développement de la sécurité, et comment passer d'une protection ponctuelle à une protection globale systématique. est un nouveau défi pour les développeurs d'architecture.

Je suppose que tu aimes

Origine blog.csdn.net/weixin_45264425/article/details/132698911
conseillé
Classement