Logo Zéphyrnet

Analyse des incidents Nomad Bridge

Date :

Tl;dr : Construire un meilleur écosystème crypto signifie construire un avenir meilleur et plus équitable pour nous tous. C'est pourquoi nous investissons dans la communauté au sens large pour nous assurer que toute personne souhaitant participer à la crypto-économie puisse le faire de manière sécurisée. Dans cet article de blog, nous partageons des leçons sur la nature de la vulnérabilité, la méthodologie d'exploitation, ainsi que l'analyse en chaîne du comportement des attaquants lors de l'incident Nomad Bridge.

Bien que le compromis du pont Nomad n'affecte pas directement Coinbase, nous croyons fermement que les attaques contre toute entreprise de cryptographie sont mauvaises pour l'industrie dans son ensemble et espérons que les informations contenues dans le blog aideront à renforcer et à informer des projets similaires sur les menaces et les techniques utilisées par les acteurs malveillants.

Par : Peter Kacherginsky, Threat Intelligence et Heidi Wilder, Enquêtes spéciales

Le 1er août 2022 Pont nomade a subi le quatrième plus grand piratage DeFi avec plus de 186 millions de dollars volés en quelques heures seulement. Comme nous l'avons décrit dans notre récent blog, du compromis Ronin Bridge de 540 millions de dollars en mars au piratage du pont Wormhole de 250 millions de dollars en février 2022, ce n'est pas une coïncidence si les ponts DeFi constituent certains des incidents les plus coûteux de notre industrie.

Ce qui rend le compromis Nomad Bridge unique, c'est la simplicité de l'exploit et le grand nombre d'individus qui en profitent pour vider tous les actifs stockés pièce par pièce.

Nomade est un protocole de pont prenant en charge Ethereum, Moonbeam et d'autres chaînes. Le protocole de pontage de Nomad est construit à l'aide de composants en chaîne et hors chaîne. Les contrats intelligents en chaîne sont utilisés pour collecter et distribuer des fonds pontés tandis que les agents hors chaîne relaient et vérifient les messages entre différentes chaînes de blocs. Chaque blockchain déploie un contrat Replica qui valide et stocke les messages dans une arborescence Merkle. Les messages peuvent être validés en fournissant une preuve avec le prouverEtProcess() appel ou pour les messages déjà vérifiés, ils peuvent être simplement soumis avec le traiter() appel. Les messages vérifiés sont transmis à un gestionnaire de pont (par exemple, un routeur ERC20) qui peut distribuer des actifs pontés.

Le 21 avril 2022 Nomade déployé une réplique contrat proxy pour gérer le traitement et la validation des réclamations des utilisateurs des actifs pontés. Ce proxy permettrait à Nomad de modifier facilement la logique d'implémentation tout en conservant le stockage lors des mises à niveau. Dans le cadre du déploiement du proxy, Nomad a défini les paramètres de contrat initiaux définis dans l'extrait ci-dessous :

fonction initialiser(uint32 _remoteDomain,adresse _updater,octets32 _commitRoot,uint256 _optimisticSeconds) initialiseur public {__NomadBase_initialize(_updater);// définit les variables de stockageentré = 1 ;remoteDomain = _remoteDomain ;commisRoot = _committedRoot ;confirmAt[_committedRoot] = 1 ;optimisticSeconds = _optimisticSeconds ;émettre SetOptimisticTimeout(_optimisticSeconds);}

Remarquez le surligné confirmer à affectation de carte qui définit une entrée initiale pour le _commitRoot à la valeur de 1. La variable _commitRoot est fourni comme paramètre d'initialisation par le déployeur de contrat de Nomad. Voyons ce qu'il a été défini lors de l'initialisation :

$ cast run 0x99662dacfb4b963479b159fc43c2b4d048562104fe154a4d0c2519ada72e50bf --quick --rpc-url $MAINNET_RPC_URLTraces:[261514] → nouveau UpgradeBeaconProxy@"0x5d94…aeba"├─ [2160] UpgradeBeacon::fallback() [appelstatique]│   └─ ← 0x0000000000000000000000007f58bb8311db968ab110889f2dfa04ab7e8e831b├─ [163890] Replica::initialize(1635148152, 0xb93d4dbb87b80f0869a5ce0839fb75acdbeb1b77, Assistance , 1800) [appel délégué]│ ├─ émettre la propriété transférée (propriétaire précédent : 0x0000000000000000000000000000000000000000, nouveau propriétaire : 0xa5bd5c661f373256c0ccfbc628fd52de74f9bb55)│ ├─ émettre un nouveau programme de mise à jour (ancien programme de mise à jour : 0x0000000000000000000000000000000000000000, nouveau programme de mise à jour : 0xb93d4dbb87b80f0869a5ce0839fb75acdbeb1b77)│ ├─ émettre SetOptimisticTimeout (délai : 1800)│ └─ ← ()└─ ← 439 octets de code

Fait intéressant, le paramètre d'initialisation _Racine engagée a été mis à 0. Par conséquent, le confirmer à map a maintenant une valeur de 1 pour une entrée 0 qui d'avril à ce jour :

$ appel de diffusion 0x5d94309e5a0090b165fa4181519701637b6daeba "confirmAt(bytes32)" 0x0 --rpc-url $MAINNET_RPC_URLAssistance 

Le 21 juin 2022, Nomad a effectué une série de mises à niveau de son infrastructure de pontage, y compris la mise en œuvre de Replica. L'une des modifications comprenait des mises à jour de la logique de vérification des messages dans le traiter() fonction:

fonction processus (octets mémoire _message) retours publics (bool _success) {// s'assure que le message était destiné à ce domaineoctets29 _m = _message.ref(0);require(_m.destination() == localDomain, "!destination");// s'assure que le message a été prouvébytes32 _messageHash = _m.keccak();require(acceptableRoot(messages[_messageHash]), "!proven");// vérifie la garde de réentréerequire(entered == 1, "!reentrant");entré = 0 ;// met à jour le statut du message comme traitémessages[_messageHash] = LEGACY_STATUS_PROCESSED ;// appel de la fonction handleIMessageRecipient(_m.recipientAddress()).handle(_m.origine(),_m.nonce(),_m.expéditeur(),_m.body().clone());// émet les résultats du processusémettre Process(_messageHash, vrai, "");// réinitialiser la garde de réentréeentré = 1 ;// renvoie vraireturn true;}

Le flux de vérification des messages inclut désormais un appel au racineacceptable() méthode qui, à son tour, fait référence confirmer à carte que nous avons mentionné ci-dessus:

function acceptableRoot(bytes32 _root) la vue publique renvoie (bool) {// il s'agit de la rétrocompatibilité pour les messages prouvés/traités// sous les versions précédentessi (_root == LEGACY_STATUS_PROVEN) renvoie vrai ;si (_root == LEGACY_STATUS_PROCESSED) renvoie faux ;uint256 _time = confirmAt[_root] ;si (_time == 0) {return false;}return block.timestamp >= _time ;}

La vulnérabilité apparaît dans un scénario où des messages frauduleux, absents du réseau de confiance messages[] carte, sont envoyés directement au traiter() méthode. Dans ce scénario messages[_messageHash] renvoie une valeur nulle par défaut pour les entrées inexistantes afin que le racineacceptable() méthode est appelée comme suit :

require(acceptableRoot(0), "!proven");

À son tour, le racineacceptable() méthode effectuera une recherche sur confirmer à[] map avec une valeur nulle comme suit :

uint256 _time = confirmAt[0] ;si (_time == 0) {return false;}return block.timestamp >= _time ;

Comme nous l'avons mentionné au début de cette section, confirmer à[] map a une entrée nulle définie résultant en racineacceptable() retour Vrai et autoriser les messages frauduleux.

L'exploit tire parti de la vulnérabilité ci-dessus en créant un message qui incite le pont Nomad à envoyer des jetons stockés sans autorisation appropriée. Ci-dessous un échantillon traiter() charge utile dans un transaction présenté par 0xb5c5…590e:

0x6265616d000000000000000000000000d3dfd3ede74e0dcebc1aa685e151332857efce2d000013d60065746800000000000000000000000088a69b4e698a4b090df6cf5bd7b2d47325ad30a3006574680000000000000000000000002260fac5e5542a773aa44fbcfedf7c193bc2c59903000000000000000000000000b5c55f76f90cc528b2609109ca14d8d84593590e00000000000000000000000000000000000000000000000000000002540be400e6e85ded018819209cfb948d074cb65de145734b5b0852e4a5db25cac2b8c39a

Le message de réplique a la structure suivante :

structure Message {uint32 _originDomain,octets32 _expéditeur,uint32 _nonce,uint32 _destinationDomain,octets32 _destinataire,octets mémoire _messageBody}

Le destinataire spécifique _Corps du message contient des données de transaction à traiter par le _destinataire. Les destinataires nomades acceptent plusieurs types de transactions et de messages, mais nous nous concentrerons sur le type de transfert :

structure BridgeMessage {domaine uint32 ;identifiant bytes32 ;type uint8 ;destinataire bytes32 ;montant uint256 ;hachage de détails uint256 ;}

Le décodage de la charge utile ci-dessus illustre comment 0xb5c55f76f90cc528b2609109ca14d8d84593590e a pu voler 100 WBTC en soumettant une charge utile spécialement conçue pour contourner les vérifications de message de Nomad.

Afin de mieux comprendre la cause profonde de l'exploit, nous développé un PoC pour le démontrer en drainant l'intégralité du solde du jeton sur le pont en quelques transactions seulement :

Lors de la rédaction d'un PoC, nous avons trouvé curieux que les attaquants choisissent d'extraire des fonds par petits incréments alors qu'ils auraient pu drainer la totalité du montant en une seule transaction. Cela est probablement dû au fait que les attaquants ne créent pas de messages de pont à partir de zéro, mais rejouent plutôt les transactions existantes avec des adresses de réception corrigées.

Plus de 186 millions de dollars en jetons ERC-20 ont été volés sur le pont Nomad entre le 1er août 2022 à 21h32 UTC et le 2 août 2022 à 05h49 UTC. Le volume le plus élevé de jetons volés était principalement USDC, suivi de WETH, WBTC et CQT. Dans la première heure de l'exploit, seuls WBTC et WETH ont été volés, suivis de plusieurs autres ERC-20.

La source: Tableau de bord des dunes

En analysant les données de la blockchain, nous voyons qu'il y avait différentes adresses superposées aux exploiteurs d'origine et utilisant des données d'entrée presque identiques avec des adresses de destinataires modifiées afin de siphonner le même jeton pour le même montant. Une fois que le contrat WBTC a été en grande partie épuisé, les attaquants ont ensuite épuisé le contrat WETH, et ainsi de suite.

Analyser plus en détail les premiers attaquants en bloc 15259101, nous constatons que les deux adresses initiales de l'attaquant ont utilisé un contrat d'assistance pour masquer l'exploit exact. Malheureusement, dans ce même bloc, plusieurs index vers le bas d'une autre adresse d'exploiteur semblent avoir eu du mal à interagir avec le contrat d'assistance et ont décidé de le contourner - et d'exposer publiquement les données d'entrée de l'exploit dans le processus. D'autres adresses dans le même et le dernier bloc ont ensuite emboîté le pas et ont utilisé des charges utiles presque identiques pour mener l'exploit.

Suite à l'exploitation initiale, et en raison de la facilité de déclenchement de l'exploit, des centaines d'imitateurs ont rejoint une exploitation massive d'un seul contrat. En analysant les charges utiles de divers futurs attaquants, nous avons constaté qu'il n'y avait pas seulement la réutilisation des mêmes jetons pontés et les mêmes montants, mais aussi que les fonds étaient constamment « pontés » depuis Moonbeam, tout comme l'exploit original.

L'attaque s'est déroulée en trois étapes : le test de vulnérabilité un jour avant l'attaque, l'exploit initial ciblant WBTC stocké sur le pont et l'étape de copie impliquant des centaines d'adresses uniques. Plongeons-nous dans chacun d'entre eux, y compris le retour partiel des actifs volés.

Tout au long du 31 juillet 2022, bitliq[.]eth a été trouvé pour déclencher la vulnérabilité en utilisant de petites quantités de WBTC et d'autres jetons. Par exemple, le 31 juillet 2022 11:19:39 AM + UTC, il a envoyé un transaction à la traiter() méthode sur la blockchain Ethereum avec la charge utile suivante :

0x617661780000000000000000000000005e5ea959686c73ed32c1bc71892f7f317d13a267000000390065746800000000000000000000000088a69b4e698a4b090df6cf5bd7b2d47325ad30a36176617800000000000000000000000050b7545627a5162f82a992c33b87adc75187b21803000000000000000000000000a8c83b1b30291a3a1a118058b5445cc83041cd9d000000000000000000000000000000000000000000000000000000000000f6088a36a47f8e81af64c44b079c42742190bbb402efb94e91c9515388af4c0669eb

La charge utile peut être décodée comme suit :

  • Chaîne d'origine : "avax"
  • Chaîne de destination : « eth »
  • Recipient: a8c83b1b30291a3a1a118058b5445cc83041cd9d (bitliq[.]eth)
  • Adresse du jeton : 0x50b7545627a5162F82A992c33b87aDc75187B218 (WBTC.e sur Avalanche)
  • Montant: 0.00062984 BTC

Cela correspond à 0.00062984 BTC transaction envoyé au pont sur la chaîne de l'Avalanche.

La charge utile a été envoyée à l'aide du traiter() méthode par opposition à la plus courante prouverEtProcess() et n'était pas présent dans la carte messages[] avant l'exécution dans le bloc 15249928 :

$ cast call 0x5d94309e5a0090b165fa4181519701637b6daeba "messages(bytes32)" "bc0f99a3ac1593c73dbbfe9e8dd29c749d8e1791cbe7f3e13d9ffd3ddea57284" --rpc-url $MAINNET_RPC_URL --block 15249928Assistance 

La transaction a réussi même sans fournir les preuves nécessaires en déclenchant la vulnérabilité dans le racineacceptable() méthode en lui fournissant une valeur de hachage racine 0x0 comme illustré dans le débogueur ci-dessous :

Source : débogueur tendrement

Messages non présents dans le messages[] le stockage peut être validé à l'aide du prouverEtProcess() méthode; cependant, puisque l'adresse appelée traiter() directement ils ont déclenché la vulnérabilité.

Chose intéressante, il semble que bitliq[.]eth testait également probablement le contrat de pont ERC-20 une heure avant l'exploit et a ponté plus de 0.01 WBTC vers Moonbeam. [Tx]

L'exploitation active a commencé le 1er août 2022 dans le même bloc 15259101 et a entraîné un vol combiné de 400 BTC.

Les quatre transactions utilisaient des charges utiles d'exploitation identiques à l'exception d'une adresse de destinataire telle que décrite dans le Vulnérabilité rubrique ci-dessus :

0x6265616d000000000000000000000000d3dfd3ede74e0dcebc1aa685e151332857efce2d000013d60065746800000000000000000000000088a69b4e698a4b090df6cf5bd7b2d47325ad30a3006574680000000000000000000000002260fac5e5542a773aa44fbcfedf7c193bc2c59903000000000000000000000000f57113d8f6ff35747737f026fe0b37d4d7f4277700000000000000000000000000000000000000000000000000000002540be400e6e85ded018819209cfb948d074cb65de145734b5b0852e4a5db25cac2b8c39a

Quelques observations sur ce qui précède :

  • Les trois premières adresses ont été financées par Tornado Cash et ont effectué des transactions actives entre elles, ce qui indique un seul groupe d'acteurs.
  • Contrairement aux deux premières transactions d'exploitation, 0xb5c5…590e ainsi que le bitliq[.]eth a envoyé la charge utile de l'exploit directement au contrat et sans l'utilisation de flashbots pour la cacher du mempool public.
  • bitliq[.]eth a rejoué une transaction d'exploit antérieure dans le même bloc 15259101 que 0xb5c5…590e indiquant soit une connaissance préalable de l'exploit, soit un apprentissage 0xb1fe…ae28 du mempool.
  • Les quatre transactions ont utilisé des charges utiles identiques, chacune volant 100 WBTC à la fois.

Au total, 88 % des adresses responsables des exploits ont été identifiées comme des imitateurs et, ensemble, elles ont volé environ 88 millions de dollars en jetons au pont.

La majorité des imitateurs ont utilisé une variante de l'exploit original en modifiant simplement les jetons ciblés, les montants et les adresses des destinataires. Nous pouvons classer les charges utiles uniques en les regroupant en fonction des contrats qu'elles appellent et de la méthode unique 4 octets invoquée, comme illustré ci-dessous :

D'après notre analyse, plus de 88 % des adresses uniques ont appelé le contrat vulnérable en utilisant directement le 928bc4b2 identifiant de la fonction qui correspond à la processus (octets) méthode utilisée dans l'exploit d'origine. Les autres effectuent le même appel en utilisant des contrats intermédiaires tels que 1cff79cd qui est le exécuter (adresse, octets) méthode, traitement par lot multiple traiter() transactions ensemble, et d'autres variations mineures.

Suite au compromis initial, les exploiteurs d'origine ont dû rivaliser avec des centaines d'imitateurs :

Alors que la majorité des jetons de valeur ont été réclamés par seulement deux des adresses des exploiteurs d'origine, des centaines d'autres ont pu revendiquer une partie des avoirs de bridge :

Vous trouverez ci-dessous un graphique montrant les jetons volés au fil du temps en USD. Il devient évident que les exploiteurs allaient jeton par jeton pendant qu'ils vidaient le pont.

À ce jour, 12% volés du contrat Nomad Bridge ont été restitués, y compris des retours partiels. La majorité des retours ont eu lieu dans les heures qui ont suivi la demande de Nomad Bridge d'envoyer des fonds à l'adresse de récupération le 3 août 2022. [Tweet, Tx]

Vous trouverez ci-dessous une ventilation des fonds retournés, qui incluent ETH et divers autres jetons, dont certains n'ont même jamais été sur le pont :

Les fonds continuent d'être renvoyés à l'adresse de récupération du pont, bien que plus lentement ces derniers jours que lorsque l'adresse a été initialement publiée :

La majorité des fonds retournés semblent être en USDC, suivis de DAI, CQT, WETH et WBTC. Ceci est notamment différent de la répartition des jetons exploités. La raison en est que les premiers exploiteurs d'origine ont principalement vidé le pont de WBTC et WETH. Contrairement aux exploiteurs à un stade ultérieur, ces exploiteurs ont déplacé des fonds sans intention de les restituer.

Fait intéressant, l'un des exploiteurs d'origine, bitliq[.]eth, n'a rendu que 100 ETH au contrat de pont, mais a commencé à encaisser le reste de ses bénéfices via renBTC et à le brûler en échange de BTC.

Catégoriser les « exploiteurs »

Lors de l'évaluation des exploiteurs de Nomad Bridge, les attaquants ont été classés dans les catégories suivantes :

  • Chapeaux noirs : Ceux qui ne retournent pas les fonds et continuent de les faire avancer.
  • Chapeaux blancs : ceux qui renvoient intégralement les fonds aux adresses de récupération
  • Veuillez noter que même si nous utilisons ici le terme chapeau blanc à des fins explicatives, la prise initiale des fonds n'a pas été autorisée et n'est pas une activité que nous approuverions.
  • Chapeaux gris : ceux qui renvoient partiellement les fonds aux adresses de récupération.
  • Inconnues inconnues : celles qui n'ont pas encore transféré de fonds.

Environ 24 % des fonds restent intacts. Nous soupçonnons qu'il s'agit soit d'attaquants qui attendent la chaleur, soit de dégénérés astucieux qui attendent une prime de Nomad. Cependant, le plus gros volume de fonds a avancé. Au 5 août, nous estimons qu'environ 64 % avaient progressé.

Pour rester à jour avec les dernières informations sur les fonds retournés, consultez ce tableau de bord.

Plonger dans les Blackhats

Parmi les fonds qui ont évolué, nous avons identifié plusieurs grands cercles d'adresses qui regroupent tous des fonds. En particulier, un groupe d'adresses semble avoir amassé plus de 62 millions de dollars en volume. Fait intéressant, une adresse au sein de ce cluster a été la première adresse à avoir mené l'exploit [hachage tx].

À ce jour, nous voyons principalement ces anneaux suivre l'un des modèles ci-dessous :

  • Activité du robot MEV
  • Mélangez-vous et tenez bon pour attendre la fin de la chaleur
  • Échange de fonds et éventuellement retour d'un montant partiel de fonds à l'adresse de récupération
  • Échanger des fonds et investir dans des projets DeFi ou encaisser dans divers CEX
  • Déplacer des fonds via Tornado Cash

Vous trouverez ci-dessous un exemple de la façon dont certaines adresses ont commencé à transférer des fonds via Tornado Cash, qui, depuis le 8 août 2022, est une entité sanctionnée.

Attention aux arnaques :

Plusieurs chapeaux blancs ont déjà reversé plus de 10% des fonds au contrat relais. Cependant, cela n'a pas été sans accrocs.

A l'origine, l'équipe Nomad postait sur les deux Twitter et sur la blockchain l'adresse Ethereum pour envoyer les fonds exploités à

Cependant, les escrocs ont intelligemment emboîté le pas et ont créé divers domaines ENS frauduleux pour se faire passer pour l'équipe Nomad et ont demandé que des fonds soient envoyés à des adresses personnalisées avec les mêmes caractères initiaux que l'adresse de récupération légitime.

Par exemple, ci-dessous est un message envoyé par l'un des escrocs. Notez l'adresse de récupération frauduleuse, le domaine ENS, ainsi que la prime de 10 %. Nomad a depuis proposé que les chapeaux blancs réclament 10% des bénéfices exploités. [Tx]

Alors que la plupart des contrats sont audités de manière approfondie par divers auditeurs de blockchain, les contrats peuvent encore contenir des vulnérabilités encore à découvrir. Bien que vous souhaitiez peut-être fournir des liquidités à un protocole particulier ou faire le pont entre les fonds, voici quelques conseils à garder à l'esprit :

  • Lorsque vous fournissez des liquidités, ne conservez pas tous vos fonds sur un seul protocole ou stockés dans le pont.
  • Assurez-vous d'examiner et de révoquer régulièrement toutes les approbations de contrat dont vous n'avez pas activement besoin.
  • Restez à jour avec les flux de renseignements de sécurité pour suivre les protocoles dans lesquels vous avez investi.

Coinbase s'engage à améliorer notre sécurité et celle de l'industrie au sens large, ainsi qu'à protéger nos utilisateurs. Nous pensons que de tels exploits peuvent être atténués et finalement évités. En plus de rendre les bases de code open source pour que le public puisse les consulter, nous recommandons des audits de protocole fréquents, la mise en œuvre de programmes de primes de bogues et une collaboration active avec des chercheurs en sécurité. Bien que cet exploit ait été une expérience d'apprentissage difficile, nous pensons que comprendre comment l'exploit s'est produit ne peut que contribuer à faire mûrir davantage notre jeune industrie.

spot_img

Dernières informations

spot_img