Auparavant, l'équipe CertiK avait découvert une série de vulnérabilités de déni de service dans la blockchain Sui. Parmi ces vulnérabilités, une nouvelle vulnérabilité à fort impact se démarque. Cette vulnérabilité peut empêcher les nœuds du réseau Sui de traiter de nouvelles transactions, et l'effet équivaut à un arrêt complet de l'ensemble du réseau.
Lundi dernier, CertiK a reçu une prime de bogue de 500 000 $ de SUI pour avoir découvert cette faille de sécurité majeure. CoinDesk, un média faisant autorité dans l'industrie américaine, a rendu compte de l'événement, puis les principaux médias ont également publié des informations connexes à la suite de son rapport.
Cette vulnérabilité de sécurité est clairement appelée "Hamster Wheel": sa méthode d'attaque unique est différente des attaques actuellement connues. L'attaquant n'a qu'à soumettre une charge utile d'environ 100 octets pour déclencher une boucle infinie dans le nœud de vérification Sui. , ce qui le rend insensible à de nouvelles opérations.
De plus, les dommages causés par l'attaque peuvent continuer après le redémarrage du réseau et se propager automatiquement dans le réseau Sui, rendant tous les nœuds incapables de traiter de nouvelles transactions comme des hamsters tournant sans fin sur la roue. Nous appelons donc ce type d'attaque unique une attaque de "roue de hamster".
Après avoir découvert le bogue, CertiK l'a signalé à Sui via le programme de prime de bogue de Sui. Sui a également répondu efficacement dès la première fois, confirmé la gravité de la vulnérabilité et pris activement les mesures correspondantes pour réparer le problème avant le lancement du réseau principal. En plus de corriger cette vulnérabilité particulière, Sui a également mis en place des atténuations préventives pour réduire les dommages potentiels que cette vulnérabilité pourrait causer.
Pour remercier l'équipe CertiK pour sa divulgation responsable, Sui a décerné à l'équipe CertiK une prime de 500 000 $.
Les détails de cette vulnérabilité critique seront divulgués au niveau technique ci-dessous, et la cause profonde et l'impact potentiel de la vulnérabilité seront clarifiés.
Explication de la vulnérabilité
Le rôle clé des validateurs dans Sui
Pour les chaînes de blocs basées sur le langage Move telles que Sui et Aptos, le mécanisme de protection pour empêcher les attaques de charge utile malveillantes est principalement une technologie de vérification statique. Grâce à la technologie de vérification statique, Sui peut vérifier la validité de la charge utile soumise par l'utilisateur avant l'émission ou la mise à niveau du contrat. Le validateur fournit une série de vérificateurs pour s'assurer de l'exactitude de la structure et de la sémantique. Ce n'est qu'après avoir réussi la vérification du contrôle que le contrat entrera dans la machine virtuelle Move pour être exécuté.
Menaces de charges utiles malveillantes sur la chaîne de déplacement
La chaîne Sui fournit un nouvel ensemble de modèles de stockage et d'interfaces au-dessus de la machine virtuelle Move d'origine. Sui dispose donc d'une version personnalisée de la machine virtuelle Move. Afin de prendre en charge de nouvelles primitives de stockage, Sui introduit en outre une série de méthodes d'inspection supplémentaires et personnalisées pour la vérification de la sécurité des charges utiles non approuvées, telles que la sécurité des objets et les fonctions d'accès au stockage global. Ces contrôles personnalisés correspondent aux caractéristiques uniques de Sui, c'est pourquoi nous appelons ces contrôles personnalisés des validateurs Sui.
Ordre de vérification des chargements de Sui
Comme le montre la figure ci-dessus, la plupart des vérifications du vérificateur effectuent une vérification de sécurité structurelle par rapport au CompiledModule (représentant l'exécution de la charge utile du contrat fournie par l'utilisateur). Par exemple, utilisez le "vérificateur de doublons" pour vous assurer qu'il n'y a pas d'entrées en double dans la charge utile d'exécution ; utilisez le "vérificateur de limite" pour vous assurer que la longueur de chaque champ dans la charge utile d'exécution est dans la limite d'entrée maximale autorisée.
En plus des vérifications structurelles, les vérifications statiques du vérificateur nécessitent encore des méthodes d'analyse plus complexes pour assurer la robustesse des charges utiles non fiables au niveau sémantique.
En savoir plus sur l'interpréteur abstrait de Move :
Analyse linéaire et itérative
L'interpréteur abstrait fourni par Move est un cadre spécialement conçu pour effectuer une analyse de sécurité complexe sur le bytecode par le biais d'une interprétation abstraite. Ce mécanisme rend le processus de vérification plus granulaire et précis, et chaque validateur est autorisé à définir son état abstrait unique pour l'analyse.
Au démarrage, l'interpréteur abstrait construit un graphe de flux de contrôle (CFG) à partir des modules compilés. Chaque bloc de base dans ces CFG maintient un ensemble d'états, un "état de pré-commande" et un "état de post-commande". "L'état de pré-commande" fournit un instantané de l'état du programme avant l'exécution du bloc de base, tandis que "l'état de post-commande" fournit une description de l'état du programme après l'exécution du bloc de base.
Lorsque l'interpréteur abstrait ne rencontre pas de saut en arrière (ou de boucle) dans le graphe de flux de contrôle, il suit un principe d'exécution linéaire simple : chaque bloc de base est analysé tour à tour, et l'instruction précédente est calculée selon la sémantique de chaque instruction du bloc État séquentiel et état post-séquentiel. Le résultat est un instantané précis de l'état de chaque bloc de base pendant l'exécution d'un programme, aidant à vérifier les propriétés de sécurité du programme.
Flux de travail de l'interpréteur abstrait Move
Cependant, le processus devient plus compliqué lorsqu'il y a des boucles dans le flux de contrôle. L'apparition d'un cycle signifie que le graphe de flux de contrôle contient un front de saut en arrière. La source du front de saut en arrière correspond à l'état suivant du bloc de base courant, et le bloc de base cible (tête de boucle) du saut- back edge est une analyse préalable Par conséquent, l'interpréteur abstrait doit soigneusement fusionner les états des deux blocs de base liés au jumpback.
Si l'état fusionné s'avère différent de l'état de préordre existant du bloc de base de tête de boucle, l'interpréteur abstrait met à jour l'état du bloc de base de tête de boucle et redémarre l'analyse à partir de ce bloc de base. Ce processus d'analyse itératif se poursuivra jusqu'à ce que le pré-état de la boucle se stabilise. En d'autres termes, ce processus est répété jusqu'à ce que l'état de préordre du bloc de base de tête de boucle ne change plus entre les itérations. Atteindre un point fixe indique que l'analyse du cycle est terminée.
Validateur Sui IDLeak :
Analyse d'interprétation abstraite personnalisée
Contrairement à la conception originale de Move, la plate-forme blockchain de Sui introduit un modèle de stockage global unique centré sur les "objectifs". Une caractéristique notable de ce modèle est la suivante : toute structure de données avec un attribut clé (stocké sur la chaîne en tant qu'index) doit avoir un type ID comme premier champ de la structure. Le champ ID est immuable et ne peut pas être transféré à d'autres objets, car chaque objet doit avoir un ID unique au monde. Pour garantir ces propriétés, Sui a construit un ensemble de logique d'analyse personnalisée au-dessus de l'interpréteur abstrait.
Le vérificateur IDLeak, également appelé id_leak_verifier, fonctionne en conjonction avec l'interpréteur abstrait pour l'analyse. Il a son propre AbstractDomain unique, appelé AbstractState. Chaque AbstractState consiste en AbstractValue correspondant à plusieurs variables locales. L'état de chaque variable locale est surveillé par AbstractValue pour déterminer si une variable d'ID est toute nouvelle.
Dans le processus d'emballage de la structure, le validateur IDLeak permet uniquement d'emballer un tout nouvel identifiant dans une structure. En faisant abstraction de l'analyse d'interprétation, les validateurs IDLeak peuvent suivre de manière exhaustive l'état du flux de données local pour s'assurer qu'aucun ID existant n'est transféré vers d'autres objets struct.
Problème d'incohérence de maintenance de l'état du validateur Sui IDLeak
Le validateur IDLeak est intégré à l'interpréteur abstrait Move en implémentant la fonction AbstractState::join. Cette fonction joue un rôle essentiel dans la gestion des états, en particulier dans la fusion et la mise à jour des valeurs d'état.
Examinez ces fonctions en détail pour comprendre leur fonctionnement :
Dans AbstractState::join, la fonction prend un autre AbstractState en entrée et tente de fusionner son état local avec l'état local de l'objet courant. Pour chaque variable locale dans l'état d'entrée, il compare la valeur de cette variable à sa valeur actuelle dans l'état local (par défaut, AbstractValue::Other s'il n'est pas trouvé). Si les deux valeurs ne sont pas égales, il définira un indicateur "changé" comme base pour savoir si le résultat final de la fusion de l'état a changé, et mettra à jour la valeur de la variable locale dans l'état local en appelant AbstractValue::join.
Dans AbstractValue::join, la fonction compare sa valeur avec une autre AbstractValue. S'ils sont égaux, il renverra la valeur transmise. S'il n'est pas égal, renvoie AbstractValue::Other.
Cependant, cette logique de maintenance d'état contient un problème d'incohérence caché. Bien que AbstractState::join renverra un résultat indiquant que l'état fusionné a changé (JoinResult::Changed) en fonction de la différence entre les nouvelles et les anciennes valeurs, la valeur de l'état mis à jour fusionné peut rester inchangée.
Ce problème d'incohérence est causé par l'ordre des opérations : la décision de changer l'état dans AbstractState::join se produit avant la mise à jour de l'état (AbstractValue::join), et cette décision ne reflète pas le résultat réel de la mise à jour de l'état.
De plus, dans AbstractValue::join, AbstractValue::Other joue un rôle décisif dans le résultat de la fusion. Par exemple, si l'ancienne valeur est AbstractValue::Other et la nouvelle valeur est AbstractValue::Fresh, la valeur d'état mise à jour est toujours AbstractValue::Other, même si l'ancienne et la nouvelle valeurs sont différentes, l'état lui-même reste inchangé après la mise à jour.
Exemple : incohérence des connexions avec état
Ceci introduit une incohérence : le résultat de la fusion de l'état d'un bloc de base est jugé comme "modifié", mais la valeur de l'état fusionné elle-même n'a pas changé. Dans le processus d'analyse de l'interprétation abstraite, de telles incohérences peuvent avoir de graves conséquences. Nous passons en revue le comportement d'un interpréteur abstrait lorsqu'un cycle se produit dans un graphe de flux de contrôle (CFG) :
Lorsqu'une boucle est rencontrée, l'interpréteur abstrait emploie une méthode d'analyse itérative pour fusionner l'état du bloc de base cible de saut en arrière et le bloc de base courant. Si l'état fusionné change, l'interpréteur abstrait ré-analysera à partir de la cible de saut.
Cependant, si l'opération de fusion de l'analyse d'interprétation abstraite marque par erreur le résultat de la fusion d'état comme "changement", alors qu'en fait la valeur de la variable interne de l'état n'a pas changé, cela conduira à une réanalyse sans fin, résultant en un boucle infinie.
Exploitation supplémentaire des incohérences
Déclenche une boucle infinie dans le validateur Sui IDLeak
En exploitant cette incohérence, un attaquant peut construire un graphe de flux de contrôle malveillant qui trompe les validateurs IDLeak dans une boucle infinie. Ce graphe de flux de contrôle soigneusement construit se compose de trois blocs de base : BB1 et BB2, BB3. Il convient de noter que nous avons intentionnellement introduit un front de saut arrière de BB3 à BB2 pour construire une boucle.
Un statut CFG+ malveillant peut conduire à une boucle infinie interne dans le validateur IDLeak
Le processus commence par BB2, où la valeur abstraite d'une variable locale particulière est définie sur :: Autre. Après avoir exécuté BB2, le flux est transféré à BB3 où la même variable est définie sur ::Fresh. À la fin de BB3, il y a un bord de saut arrière, sautant à BB2.
Les incohérences susmentionnées jouent un rôle clé dans l'interprétation abstraite de cet exemple. Lorsque le front de saut arrière est traité, l'interpréteur abstrait tente de connecter l'état de post-ordre de BB3 (avec la variable "::Fresh") avec l'état de pré-ordre de BB2 (avec la variable "::Other"). La fonction AbstractState::join remarque la différence entre les anciennes et les nouvelles valeurs et définit le drapeau "change" pour indiquer que BB2 doit être réanalysé.
Cependant, le comportement dominant de "::Other" dans AbstractValue::join signifie qu'après la fusion de AbstractValue, la valeur réelle de la variable d'état BB2 est toujours "::Other", et le résultat de la fusion d'état n'a pas changé .
Ainsi, une fois que ce processus cyclique démarre, c'est-à-dire que les validateurs continuent à réanalyser BB2 et tous ses nœuds de bloc de base successeurs (BB3 dans ce cas), il continue indéfiniment. La boucle infinie consomme tous les cycles CPU disponibles, ce qui la rend incapable de traiter et de répondre aux nouvelles transactions, ce qui persiste lors des redémarrages du validateur.
En exploitant cette vulnérabilité, les nœuds de validation fonctionnent sans fin comme des hamsters sur une roue dans une boucle infinie, incapables de traiter de nouvelles transactions. Nous appelons donc ce type d'attaque unique une attaque de "roue de hamster".
L'attaque de la "roue de hamster" peut effectivement paralyser les validateurs Sui, ce qui peut à son tour faire tomber l'ensemble du réseau Sui.
Après avoir compris la cause de la vulnérabilité et le processus de déclenchement, nous avons construit un exemple concret en utilisant la simulation de bytecode Move suivante et avons réussi à déclencher la vulnérabilité dans la simulation dans l'environnement réel :
Cet exemple montre comment déclencher la vulnérabilité dans un environnement réel grâce à un bytecode soigneusement construit. Plus précisément, un attaquant pourrait déclencher une boucle infinie dans le validateur IDLeak, consommant tous les cycles CPU d'un nœud Sui avec une charge utile d'environ 100 octets seulement, empêchant efficacement le traitement de nouvelles transactions et provoquant un déni de service sur le réseau Sui.
L'attaque "Hamster Wheel" continue de nuire au réseau Sui
Le programme de primes de bogues de Sui a des règles strictes sur l'évaluation des niveaux de vulnérabilité, principalement basées sur le degré de dommage à l'ensemble du réseau. Les vulnérabilités qui répondent à la cote « critique » doivent arrêter l'ensemble du réseau et empêcher efficacement les nouvelles confirmations de transaction, et nécessitent un hard fork pour résoudre le problème ; vulnérabilités (moyennes) » ou « à haut risque (élevé) ».
La vulnérabilité "roue de hamster" découverte par l'équipe CertiK Skyfall peut arrêter l'ensemble du réseau Sui et nécessite la publication officielle d'une nouvelle version pour la mise à niveau et la réparation. Sui a finalement classé la vulnérabilité comme "critique" en fonction de sa criticité. Afin de mieux comprendre l'impact sérieux causé par l'attaque "roue de hamster", il nous est nécessaire de comprendre l'architecture complexe du système backend de Sui, en particulier l'ensemble du processus de publication ou de mise à niveau des transactions sur la chaîne.
Aperçu des interactions pour soumettre des transactions dans Sui
Initialement, les transactions des utilisateurs sont soumises via RPC frontal et transmises aux services principaux après une vérification de base. Le service backend Sui est chargé de valider davantage les charges utiles des transactions entrantes. Après avoir vérifié avec succès la signature de l'utilisateur, la transaction est convertie en un certificat de transaction (contenant les informations de transaction et la signature de Sui).
Ces certificats de transaction sont un élément fondamental du fonctionnement du réseau Sui et peuvent être diffusés entre différents nœuds de vérification du réseau. Pour les transactions de création/mise à niveau de contrat, avant qu'elles ne puissent être téléchargées dans la chaîne, le nœud de vérification appellera le vérificateur Sui pour vérifier et vérifier la validité de la structure/sémantique du contrat de ces certificats. C'est lors de cette étape critique de vérification que la vulnérabilité "boucle infinie" peut être déclenchée et exploitée.
Lorsque la vulnérabilité a été déclenchée, le processus de vérification a été interrompu indéfiniment, entravant ainsi la capacité du système à traiter de nouvelles transactions et provoquant l'arrêt complet du réseau. Pour ajouter l'insulte à l'injure, la situation a persisté après le redémarrage des nœuds, ce qui signifiait que les mesures d'atténuation traditionnelles étaient loin d'être adéquates. Une fois la vulnérabilité déclenchée, il y aura des "dommages continus" et laissera un impact durable sur l'ensemble du réseau Sui.
La solution de Sui
Après les commentaires de CertiK, Sui a confirmé la vulnérabilité en temps opportun et a publié un correctif pour corriger la faille critique. Le correctif assure la cohérence entre les changements d'état et les drapeaux après changement, en supprimant l'effet critique de l'attaque "roue de hamster".
Pour supprimer l'incohérence susmentionnée, le correctif de Sui inclut un petit mais critique ajustement de la fonction AbstractState::join. Ce correctif supprime la logique consistant à déterminer le résultat de la fusion d'états avant d'exécuter AbstractValue::join. Au lieu de cela, exécutez d'abord la fonction AbstractValue::join pour la fusion d'états et définissez la fusion en comparant le résultat final de la mise à jour avec la valeur d'état d'origine (ancienne _value) Un indicateur pour les modifications.
De cette manière, le résultat de la fusion d'états sera cohérent avec le résultat de la mise à jour réelle, et une boucle infinie ne se produira pas pendant le processus d'analyse.
En plus de corriger cette vulnérabilité spécifique, Sui a également déployé des atténuations pour réduire l'impact des futures vulnérabilités du validateur. Selon la réponse de Sui dans le rapport de bogue, l'atténuation implique une fonctionnalité appelée Denylist.
"Cependant, les validateurs ont un fichier de configuration de nœud qui leur permet de rejeter temporairement certaines classes de transactions. Cette configuration peut être utilisée pour désactiver temporairement le traitement des versions et des mises à niveau de packages. En raison de ce bogue, exécute Sui avant de signer une version ou une mise à niveau de package tx validateurs, tandis que la liste de refus empêchera le validateur de s'exécuter et supprimera le tx malveillant, le refus temporaire de ces types de tx est une atténuation efficace à 100 % (bien que cela perturbe temporairement le service pour les personnes essayant de publier ou de mettre à niveau le code).
Soit dit en passant, nous avons ce fichier de configuration de liste de refus TX depuis un certain temps déjà, mais nous avons également ajouté un mécanisme similaire pour les certificats en tant qu'atténuation de suivi de votre vulnérabilité de "boucle de validation" signalée précédemment. Avec ce mécanisme, nous aurons plus de flexibilité contre cette attaque : nous utiliserons la configuration de la liste de refus de certificats pour que les validateurs oublient les mauvais certificats (casser la boucle infinie), et la configuration de la liste de refus TX pour interdire la publication/les mises à niveau, empêchant ainsi la création de nouvelles transactions d'attaques malveillantes. Merci de nous avoir fait penser à ça !
Un validateur a un nombre limité de "ticks" (différents du gaz) pour la vérification du bytecode avant de signer une transaction, et si tout le bytecode émis dans une transaction ne peut pas être vérifié dans ce nombre de ticks, le validateur refusera de signer la transaction, empêchant l'empêcher de s'exécuter sur le réseau. Auparavant, la mesure ne s'appliquait qu'à un ensemble sélectionné de passes de validation complexes. Pour lutter contre cela, nous étendons le comptage à chaque validateur pour garantir une contrainte sur le travail qu'un validateur effectue pendant le processus de validation de chaque tick. Nous avons également corrigé un bogue potentiel de boucle infinie dans le validateur de fuite d'ID. "
--Explication des développeurs Sui sur les corrections de bugs
Dans l'ensemble, Denylist permet aux validateurs d'éviter temporairement d'exploiter les vulnérabilités des validateurs et de prévenir efficacement les dommages potentiels causés par certaines transactions malveillantes en désactivant le processus de publication ou de mise à niveau. Lorsque les mesures d'atténuation de Denylist entrent en vigueur, les nœuds s'assurent qu'ils peuvent continuer à fonctionner en sacrifiant leurs propres fonctions de contrat de publication/mise à jour.
Résumer
Dans cet article, nous partageons les détails techniques de l'attaque "Hamster Wheel" découverte par l'équipe CertiK Skyfall, expliquant comment ce nouveau type d'attaque exploite des vulnérabilités clés pour provoquer un arrêt complet du réseau Sui. En outre, nous avons également étudié attentivement la réponse rapide de Sui pour résoudre ce problème critique, et partagé le correctif de vulnérabilité et les méthodes d'atténuation ultérieures pour des vulnérabilités similaires.
Voir l'original
Le contenu est fourni à titre de référence uniquement, il ne s'agit pas d'une sollicitation ou d'une offre. Aucun conseil en investissement, fiscalité ou juridique n'est fourni. Consultez l'Avertissement pour plus de détails sur les risques.
Détails techniques et analyse approfondie de la dernière vulnérabilité "roue de hamster" de Sui
Auparavant, l'équipe CertiK avait découvert une série de vulnérabilités de déni de service dans la blockchain Sui. Parmi ces vulnérabilités, une nouvelle vulnérabilité à fort impact se démarque. Cette vulnérabilité peut empêcher les nœuds du réseau Sui de traiter de nouvelles transactions, et l'effet équivaut à un arrêt complet de l'ensemble du réseau.
Lundi dernier, CertiK a reçu une prime de bogue de 500 000 $ de SUI pour avoir découvert cette faille de sécurité majeure. CoinDesk, un média faisant autorité dans l'industrie américaine, a rendu compte de l'événement, puis les principaux médias ont également publié des informations connexes à la suite de son rapport.
Cette vulnérabilité de sécurité est clairement appelée "Hamster Wheel": sa méthode d'attaque unique est différente des attaques actuellement connues. L'attaquant n'a qu'à soumettre une charge utile d'environ 100 octets pour déclencher une boucle infinie dans le nœud de vérification Sui. , ce qui le rend insensible à de nouvelles opérations.
De plus, les dommages causés par l'attaque peuvent continuer après le redémarrage du réseau et se propager automatiquement dans le réseau Sui, rendant tous les nœuds incapables de traiter de nouvelles transactions comme des hamsters tournant sans fin sur la roue. Nous appelons donc ce type d'attaque unique une attaque de "roue de hamster".
Après avoir découvert le bogue, CertiK l'a signalé à Sui via le programme de prime de bogue de Sui. Sui a également répondu efficacement dès la première fois, confirmé la gravité de la vulnérabilité et pris activement les mesures correspondantes pour réparer le problème avant le lancement du réseau principal. En plus de corriger cette vulnérabilité particulière, Sui a également mis en place des atténuations préventives pour réduire les dommages potentiels que cette vulnérabilité pourrait causer.
Pour remercier l'équipe CertiK pour sa divulgation responsable, Sui a décerné à l'équipe CertiK une prime de 500 000 $.
Les détails de cette vulnérabilité critique seront divulgués au niveau technique ci-dessous, et la cause profonde et l'impact potentiel de la vulnérabilité seront clarifiés.
Explication de la vulnérabilité
Le rôle clé des validateurs dans Sui
Pour les chaînes de blocs basées sur le langage Move telles que Sui et Aptos, le mécanisme de protection pour empêcher les attaques de charge utile malveillantes est principalement une technologie de vérification statique. Grâce à la technologie de vérification statique, Sui peut vérifier la validité de la charge utile soumise par l'utilisateur avant l'émission ou la mise à niveau du contrat. Le validateur fournit une série de vérificateurs pour s'assurer de l'exactitude de la structure et de la sémantique. Ce n'est qu'après avoir réussi la vérification du contrôle que le contrat entrera dans la machine virtuelle Move pour être exécuté.
Menaces de charges utiles malveillantes sur la chaîne de déplacement
La chaîne Sui fournit un nouvel ensemble de modèles de stockage et d'interfaces au-dessus de la machine virtuelle Move d'origine. Sui dispose donc d'une version personnalisée de la machine virtuelle Move. Afin de prendre en charge de nouvelles primitives de stockage, Sui introduit en outre une série de méthodes d'inspection supplémentaires et personnalisées pour la vérification de la sécurité des charges utiles non approuvées, telles que la sécurité des objets et les fonctions d'accès au stockage global. Ces contrôles personnalisés correspondent aux caractéristiques uniques de Sui, c'est pourquoi nous appelons ces contrôles personnalisés des validateurs Sui.
Ordre de vérification des chargements de Sui
Comme le montre la figure ci-dessus, la plupart des vérifications du vérificateur effectuent une vérification de sécurité structurelle par rapport au CompiledModule (représentant l'exécution de la charge utile du contrat fournie par l'utilisateur). Par exemple, utilisez le "vérificateur de doublons" pour vous assurer qu'il n'y a pas d'entrées en double dans la charge utile d'exécution ; utilisez le "vérificateur de limite" pour vous assurer que la longueur de chaque champ dans la charge utile d'exécution est dans la limite d'entrée maximale autorisée.
En plus des vérifications structurelles, les vérifications statiques du vérificateur nécessitent encore des méthodes d'analyse plus complexes pour assurer la robustesse des charges utiles non fiables au niveau sémantique.
En savoir plus sur l'interpréteur abstrait de Move :
Analyse linéaire et itérative
L'interpréteur abstrait fourni par Move est un cadre spécialement conçu pour effectuer une analyse de sécurité complexe sur le bytecode par le biais d'une interprétation abstraite. Ce mécanisme rend le processus de vérification plus granulaire et précis, et chaque validateur est autorisé à définir son état abstrait unique pour l'analyse.
Au démarrage, l'interpréteur abstrait construit un graphe de flux de contrôle (CFG) à partir des modules compilés. Chaque bloc de base dans ces CFG maintient un ensemble d'états, un "état de pré-commande" et un "état de post-commande". "L'état de pré-commande" fournit un instantané de l'état du programme avant l'exécution du bloc de base, tandis que "l'état de post-commande" fournit une description de l'état du programme après l'exécution du bloc de base.
Lorsque l'interpréteur abstrait ne rencontre pas de saut en arrière (ou de boucle) dans le graphe de flux de contrôle, il suit un principe d'exécution linéaire simple : chaque bloc de base est analysé tour à tour, et l'instruction précédente est calculée selon la sémantique de chaque instruction du bloc État séquentiel et état post-séquentiel. Le résultat est un instantané précis de l'état de chaque bloc de base pendant l'exécution d'un programme, aidant à vérifier les propriétés de sécurité du programme.
Flux de travail de l'interpréteur abstrait Move
Cependant, le processus devient plus compliqué lorsqu'il y a des boucles dans le flux de contrôle. L'apparition d'un cycle signifie que le graphe de flux de contrôle contient un front de saut en arrière. La source du front de saut en arrière correspond à l'état suivant du bloc de base courant, et le bloc de base cible (tête de boucle) du saut- back edge est une analyse préalable Par conséquent, l'interpréteur abstrait doit soigneusement fusionner les états des deux blocs de base liés au jumpback.
Si l'état fusionné s'avère différent de l'état de préordre existant du bloc de base de tête de boucle, l'interpréteur abstrait met à jour l'état du bloc de base de tête de boucle et redémarre l'analyse à partir de ce bloc de base. Ce processus d'analyse itératif se poursuivra jusqu'à ce que le pré-état de la boucle se stabilise. En d'autres termes, ce processus est répété jusqu'à ce que l'état de préordre du bloc de base de tête de boucle ne change plus entre les itérations. Atteindre un point fixe indique que l'analyse du cycle est terminée.
Validateur Sui IDLeak :
Analyse d'interprétation abstraite personnalisée
Contrairement à la conception originale de Move, la plate-forme blockchain de Sui introduit un modèle de stockage global unique centré sur les "objectifs". Une caractéristique notable de ce modèle est la suivante : toute structure de données avec un attribut clé (stocké sur la chaîne en tant qu'index) doit avoir un type ID comme premier champ de la structure. Le champ ID est immuable et ne peut pas être transféré à d'autres objets, car chaque objet doit avoir un ID unique au monde. Pour garantir ces propriétés, Sui a construit un ensemble de logique d'analyse personnalisée au-dessus de l'interpréteur abstrait.
Le vérificateur IDLeak, également appelé id_leak_verifier, fonctionne en conjonction avec l'interpréteur abstrait pour l'analyse. Il a son propre AbstractDomain unique, appelé AbstractState. Chaque AbstractState consiste en AbstractValue correspondant à plusieurs variables locales. L'état de chaque variable locale est surveillé par AbstractValue pour déterminer si une variable d'ID est toute nouvelle.
Dans le processus d'emballage de la structure, le validateur IDLeak permet uniquement d'emballer un tout nouvel identifiant dans une structure. En faisant abstraction de l'analyse d'interprétation, les validateurs IDLeak peuvent suivre de manière exhaustive l'état du flux de données local pour s'assurer qu'aucun ID existant n'est transféré vers d'autres objets struct.
Problème d'incohérence de maintenance de l'état du validateur Sui IDLeak
Le validateur IDLeak est intégré à l'interpréteur abstrait Move en implémentant la fonction AbstractState::join. Cette fonction joue un rôle essentiel dans la gestion des états, en particulier dans la fusion et la mise à jour des valeurs d'état.
Examinez ces fonctions en détail pour comprendre leur fonctionnement :
Dans AbstractState::join, la fonction prend un autre AbstractState en entrée et tente de fusionner son état local avec l'état local de l'objet courant. Pour chaque variable locale dans l'état d'entrée, il compare la valeur de cette variable à sa valeur actuelle dans l'état local (par défaut, AbstractValue::Other s'il n'est pas trouvé). Si les deux valeurs ne sont pas égales, il définira un indicateur "changé" comme base pour savoir si le résultat final de la fusion de l'état a changé, et mettra à jour la valeur de la variable locale dans l'état local en appelant AbstractValue::join.
Dans AbstractValue::join, la fonction compare sa valeur avec une autre AbstractValue. S'ils sont égaux, il renverra la valeur transmise. S'il n'est pas égal, renvoie AbstractValue::Other.
Cependant, cette logique de maintenance d'état contient un problème d'incohérence caché. Bien que AbstractState::join renverra un résultat indiquant que l'état fusionné a changé (JoinResult::Changed) en fonction de la différence entre les nouvelles et les anciennes valeurs, la valeur de l'état mis à jour fusionné peut rester inchangée.
Ce problème d'incohérence est causé par l'ordre des opérations : la décision de changer l'état dans AbstractState::join se produit avant la mise à jour de l'état (AbstractValue::join), et cette décision ne reflète pas le résultat réel de la mise à jour de l'état.
De plus, dans AbstractValue::join, AbstractValue::Other joue un rôle décisif dans le résultat de la fusion. Par exemple, si l'ancienne valeur est AbstractValue::Other et la nouvelle valeur est AbstractValue::Fresh, la valeur d'état mise à jour est toujours AbstractValue::Other, même si l'ancienne et la nouvelle valeurs sont différentes, l'état lui-même reste inchangé après la mise à jour.
Exemple : incohérence des connexions avec état
Ceci introduit une incohérence : le résultat de la fusion de l'état d'un bloc de base est jugé comme "modifié", mais la valeur de l'état fusionné elle-même n'a pas changé. Dans le processus d'analyse de l'interprétation abstraite, de telles incohérences peuvent avoir de graves conséquences. Nous passons en revue le comportement d'un interpréteur abstrait lorsqu'un cycle se produit dans un graphe de flux de contrôle (CFG) :
Lorsqu'une boucle est rencontrée, l'interpréteur abstrait emploie une méthode d'analyse itérative pour fusionner l'état du bloc de base cible de saut en arrière et le bloc de base courant. Si l'état fusionné change, l'interpréteur abstrait ré-analysera à partir de la cible de saut.
Cependant, si l'opération de fusion de l'analyse d'interprétation abstraite marque par erreur le résultat de la fusion d'état comme "changement", alors qu'en fait la valeur de la variable interne de l'état n'a pas changé, cela conduira à une réanalyse sans fin, résultant en un boucle infinie.
Exploitation supplémentaire des incohérences
Déclenche une boucle infinie dans le validateur Sui IDLeak
En exploitant cette incohérence, un attaquant peut construire un graphe de flux de contrôle malveillant qui trompe les validateurs IDLeak dans une boucle infinie. Ce graphe de flux de contrôle soigneusement construit se compose de trois blocs de base : BB1 et BB2, BB3. Il convient de noter que nous avons intentionnellement introduit un front de saut arrière de BB3 à BB2 pour construire une boucle.
Un statut CFG+ malveillant peut conduire à une boucle infinie interne dans le validateur IDLeak
Le processus commence par BB2, où la valeur abstraite d'une variable locale particulière est définie sur :: Autre. Après avoir exécuté BB2, le flux est transféré à BB3 où la même variable est définie sur ::Fresh. À la fin de BB3, il y a un bord de saut arrière, sautant à BB2.
Les incohérences susmentionnées jouent un rôle clé dans l'interprétation abstraite de cet exemple. Lorsque le front de saut arrière est traité, l'interpréteur abstrait tente de connecter l'état de post-ordre de BB3 (avec la variable "::Fresh") avec l'état de pré-ordre de BB2 (avec la variable "::Other"). La fonction AbstractState::join remarque la différence entre les anciennes et les nouvelles valeurs et définit le drapeau "change" pour indiquer que BB2 doit être réanalysé.
Cependant, le comportement dominant de "::Other" dans AbstractValue::join signifie qu'après la fusion de AbstractValue, la valeur réelle de la variable d'état BB2 est toujours "::Other", et le résultat de la fusion d'état n'a pas changé .
Ainsi, une fois que ce processus cyclique démarre, c'est-à-dire que les validateurs continuent à réanalyser BB2 et tous ses nœuds de bloc de base successeurs (BB3 dans ce cas), il continue indéfiniment. La boucle infinie consomme tous les cycles CPU disponibles, ce qui la rend incapable de traiter et de répondre aux nouvelles transactions, ce qui persiste lors des redémarrages du validateur.
En exploitant cette vulnérabilité, les nœuds de validation fonctionnent sans fin comme des hamsters sur une roue dans une boucle infinie, incapables de traiter de nouvelles transactions. Nous appelons donc ce type d'attaque unique une attaque de "roue de hamster".
L'attaque de la "roue de hamster" peut effectivement paralyser les validateurs Sui, ce qui peut à son tour faire tomber l'ensemble du réseau Sui.
Après avoir compris la cause de la vulnérabilité et le processus de déclenchement, nous avons construit un exemple concret en utilisant la simulation de bytecode Move suivante et avons réussi à déclencher la vulnérabilité dans la simulation dans l'environnement réel :
Cet exemple montre comment déclencher la vulnérabilité dans un environnement réel grâce à un bytecode soigneusement construit. Plus précisément, un attaquant pourrait déclencher une boucle infinie dans le validateur IDLeak, consommant tous les cycles CPU d'un nœud Sui avec une charge utile d'environ 100 octets seulement, empêchant efficacement le traitement de nouvelles transactions et provoquant un déni de service sur le réseau Sui.
L'attaque "Hamster Wheel" continue de nuire au réseau Sui
Le programme de primes de bogues de Sui a des règles strictes sur l'évaluation des niveaux de vulnérabilité, principalement basées sur le degré de dommage à l'ensemble du réseau. Les vulnérabilités qui répondent à la cote « critique » doivent arrêter l'ensemble du réseau et empêcher efficacement les nouvelles confirmations de transaction, et nécessitent un hard fork pour résoudre le problème ; vulnérabilités (moyennes) » ou « à haut risque (élevé) ».
La vulnérabilité "roue de hamster" découverte par l'équipe CertiK Skyfall peut arrêter l'ensemble du réseau Sui et nécessite la publication officielle d'une nouvelle version pour la mise à niveau et la réparation. Sui a finalement classé la vulnérabilité comme "critique" en fonction de sa criticité. Afin de mieux comprendre l'impact sérieux causé par l'attaque "roue de hamster", il nous est nécessaire de comprendre l'architecture complexe du système backend de Sui, en particulier l'ensemble du processus de publication ou de mise à niveau des transactions sur la chaîne.
Aperçu des interactions pour soumettre des transactions dans Sui
Initialement, les transactions des utilisateurs sont soumises via RPC frontal et transmises aux services principaux après une vérification de base. Le service backend Sui est chargé de valider davantage les charges utiles des transactions entrantes. Après avoir vérifié avec succès la signature de l'utilisateur, la transaction est convertie en un certificat de transaction (contenant les informations de transaction et la signature de Sui).
Ces certificats de transaction sont un élément fondamental du fonctionnement du réseau Sui et peuvent être diffusés entre différents nœuds de vérification du réseau. Pour les transactions de création/mise à niveau de contrat, avant qu'elles ne puissent être téléchargées dans la chaîne, le nœud de vérification appellera le vérificateur Sui pour vérifier et vérifier la validité de la structure/sémantique du contrat de ces certificats. C'est lors de cette étape critique de vérification que la vulnérabilité "boucle infinie" peut être déclenchée et exploitée.
Lorsque la vulnérabilité a été déclenchée, le processus de vérification a été interrompu indéfiniment, entravant ainsi la capacité du système à traiter de nouvelles transactions et provoquant l'arrêt complet du réseau. Pour ajouter l'insulte à l'injure, la situation a persisté après le redémarrage des nœuds, ce qui signifiait que les mesures d'atténuation traditionnelles étaient loin d'être adéquates. Une fois la vulnérabilité déclenchée, il y aura des "dommages continus" et laissera un impact durable sur l'ensemble du réseau Sui.
La solution de Sui
Après les commentaires de CertiK, Sui a confirmé la vulnérabilité en temps opportun et a publié un correctif pour corriger la faille critique. Le correctif assure la cohérence entre les changements d'état et les drapeaux après changement, en supprimant l'effet critique de l'attaque "roue de hamster".
Pour supprimer l'incohérence susmentionnée, le correctif de Sui inclut un petit mais critique ajustement de la fonction AbstractState::join. Ce correctif supprime la logique consistant à déterminer le résultat de la fusion d'états avant d'exécuter AbstractValue::join. Au lieu de cela, exécutez d'abord la fonction AbstractValue::join pour la fusion d'états et définissez la fusion en comparant le résultat final de la mise à jour avec la valeur d'état d'origine (ancienne _value) Un indicateur pour les modifications.
De cette manière, le résultat de la fusion d'états sera cohérent avec le résultat de la mise à jour réelle, et une boucle infinie ne se produira pas pendant le processus d'analyse.
En plus de corriger cette vulnérabilité spécifique, Sui a également déployé des atténuations pour réduire l'impact des futures vulnérabilités du validateur. Selon la réponse de Sui dans le rapport de bogue, l'atténuation implique une fonctionnalité appelée Denylist.
"Cependant, les validateurs ont un fichier de configuration de nœud qui leur permet de rejeter temporairement certaines classes de transactions. Cette configuration peut être utilisée pour désactiver temporairement le traitement des versions et des mises à niveau de packages. En raison de ce bogue, exécute Sui avant de signer une version ou une mise à niveau de package tx validateurs, tandis que la liste de refus empêchera le validateur de s'exécuter et supprimera le tx malveillant, le refus temporaire de ces types de tx est une atténuation efficace à 100 % (bien que cela perturbe temporairement le service pour les personnes essayant de publier ou de mettre à niveau le code).
Soit dit en passant, nous avons ce fichier de configuration de liste de refus TX depuis un certain temps déjà, mais nous avons également ajouté un mécanisme similaire pour les certificats en tant qu'atténuation de suivi de votre vulnérabilité de "boucle de validation" signalée précédemment. Avec ce mécanisme, nous aurons plus de flexibilité contre cette attaque : nous utiliserons la configuration de la liste de refus de certificats pour que les validateurs oublient les mauvais certificats (casser la boucle infinie), et la configuration de la liste de refus TX pour interdire la publication/les mises à niveau, empêchant ainsi la création de nouvelles transactions d'attaques malveillantes. Merci de nous avoir fait penser à ça !
Un validateur a un nombre limité de "ticks" (différents du gaz) pour la vérification du bytecode avant de signer une transaction, et si tout le bytecode émis dans une transaction ne peut pas être vérifié dans ce nombre de ticks, le validateur refusera de signer la transaction, empêchant l'empêcher de s'exécuter sur le réseau. Auparavant, la mesure ne s'appliquait qu'à un ensemble sélectionné de passes de validation complexes. Pour lutter contre cela, nous étendons le comptage à chaque validateur pour garantir une contrainte sur le travail qu'un validateur effectue pendant le processus de validation de chaque tick. Nous avons également corrigé un bogue potentiel de boucle infinie dans le validateur de fuite d'ID. "
--Explication des développeurs Sui sur les corrections de bugs
Dans l'ensemble, Denylist permet aux validateurs d'éviter temporairement d'exploiter les vulnérabilités des validateurs et de prévenir efficacement les dommages potentiels causés par certaines transactions malveillantes en désactivant le processus de publication ou de mise à niveau. Lorsque les mesures d'atténuation de Denylist entrent en vigueur, les nœuds s'assurent qu'ils peuvent continuer à fonctionner en sacrifiant leurs propres fonctions de contrat de publication/mise à jour.
Résumer
Dans cet article, nous partageons les détails techniques de l'attaque "Hamster Wheel" découverte par l'équipe CertiK Skyfall, expliquant comment ce nouveau type d'attaque exploite des vulnérabilités clés pour provoquer un arrêt complet du réseau Sui. En outre, nous avons également étudié attentivement la réponse rapide de Sui pour résoudre ce problème critique, et partagé le correctif de vulnérabilité et les méthodes d'atténuation ultérieures pour des vulnérabilités similaires.