À des enregistrements de notarisation parfaitement légitimes ont été ajoutés des enregistrements malveillants dupliqués contenant une racine d'état (state root) substituée. Ces enregistrements ont été signés par les notaires de Verus, puis transmis à Ethereum, où ils ont remplacé la véritable state root de confiance. Ayant pris le contrôle de cette valeur, l'attaquant a pu envoyer via le pont une fausse preuve d'importation qui a permis de retirer bien plus de fonds que ce qui avait réellement été envoyé, alors même que l'exportation initiale ne s'élevait qu'à 0.01 VRSC.
Qu'est-ce qu'une notarisation
Sur le réseau Verus, une notarisation est un point de contrôle signé entre deux blockchains. C'est elle qui définit l'état du réseau par rapport auquel sont ensuite vérifiées les preuves de transferts et d'opérations d'exportation.
Une telle confirmation contient :
- l'identifiant de la blockchain ou de la monnaie ;
- la hauteur de la notarisation ;
- une ou plusieurs racines de preuve (proofRoots), comprenant :
- l'identifiant du réseau ;
- la hauteur du bloc ;
- la racine d'état (state/proof root) ;
- le hash du bloc ;
- la puissance de calcul accumulée ;
- des informations sur la monnaie et l'état du convertisseur ;
- une référence à la notarisation précédente ;
- le hash de la notarisation cross-chain précédente ;
- des informations sur le nœud et l'initiateur.
Les racines de preuve sont constituées à partir des données publiques des blockchains Verus et Ethereum. Tout utilisateur peut créer et transmettre un candidat à la notarisation, mais le réseau ne l'acceptera que si les exigences de consensus sont respectées et si l'UTXO correct de la chaîne de notarisations précédente est utilisé.
Les signatures des notaires (CNotarySignature) font partie de la structure CNotaryEvidence et sont publiées de manière ouverte. C'est grâce à cela que l'attaquant a pu obtenir ces signatures et les réutiliser.
Chronologie de l'attaque (UTC)
Les attaquants se sont soigneusement préparés au piratage et ont agi pendant deux jours.
Comment l'attaque s'est déroulée : quatre étapes
L'ensemble de l'algorithme du piratage du protocole peut être divisé en quatre étapes clés.
1. Substitution de la notarisation sur le réseau Verus
La première transaction a utilisé la sortie confirmée précédente de la chaîne de notarisations (4f4f…:1) et en a créé une nouvelle (0c247…:0). Bien que l'interface de l'explorateur de blocs n'affichât que deux enregistrements légitimes, des enregistrements supplémentaires comportant de fausses state roots ont été insérés de manière dissimulée à l'intérieur des données sérialisées.
La cause résidait dans les particularités du traitement des données. Verus lisait d'abord la liste des racines sous forme de tableau (array), puis la transférait dans une structure Map. Grâce à cela, l'attaquant a pu créer deux transactions consécutives, chacune contenant en outre plusieurs enregistrements malveillants cachés avec des state roots substituées.
2. Substitution de la racine d'état sur Ethereum
Ensuite, l'attaquant a attendu que le logiciel des notaires vérifie les premiers enregistrements corrects. Puis, 11 nœuds notariaux ont signé la notarisation dans son intégralité, y compris les enregistrements dupliqués cachés contenant des données malveillantes, que le système Verus ignorait en réalité.
Les signatures obtenues ont été incluses dans la structure CNotaryEvidence. L'attaquant a ensuite récupéré ces signatures via l'interface RPC de Verus, puis a construit un appel à la fonction setLatestData() sur Ethereum.
Du côté d'Ethereum, l'attaquant n'a joué qu'un rôle de relais : il a copié les données en octets de la notarisation ainsi que les signatures légitimes des notaires et les a transmises au contrat intelligent.
Lors de l'exécution de la fonction deserializeNotarization(), le contrat Ethereum traitait le tableau proofRoots de manière séquentielle. Ce faisant, chaque enregistrement portant l'identifiant du système Verus écrasait la valeur de stateRoot:
- e premier enregistrement - la véritable state root ;
- le deuxième - des données Ethereum ;
- le troisième - une state root malveillante ;
- le quatrième - de nouveau malveillante ;
- le cinquième - également malveillante.
En conséquence, le dernier enregistrement a entièrement remplacé la véritable racine d'état de confiance. C'est précisément cette fausse state root qui a ensuite été utilisée pour vérifier la fausse preuve de transfert via le pont.
3. Préparation de l'exportation via Verus
L'attaquant a ensuite créé une demande de transfert cross-chain tout à fait ordinaire. Dans la transaction 5b5043febfd7f7c089e976d11d0f87a904881266bb96dd118b3d69b0e02aab52, une nouvelle adresse a initié une exportation de seulement 0.01 VRSC, en payant des frais d'environ 12 unités via Bridge.vETH.
Après cela, la demande a été traitée par le convertisseur, et LuckPool a constitué une exportation par lots dans une transaction distincte. C'est précisément cette demande parfaitement légitime qui est ensuite devenue la base de la construction de la fausse preuve.
En particulier, les paramètres suivants ont été utilisés:
- hashtransfers : 440787f9114dbb41b7d3d6f6c48980d51221f62ef0a33613d1d305b98ae85053
- sourceheightstart : 4162638
- sourceheightend : 4162847
4. Fausse importation et retrait de fonds via Ethereum
Lors de l'étape finale, l'attaquant a appelé la fonction submitImports(). Dans la structure CCE, composée de 14 champs, seuls quelques paramètres clés ont été modifiés. Le champ hashtransfers a été entièrement falsifié de manière à correspondre aux transactions de retrait de fonds.
En outre :
- la valeur numInputs a été changée en 8 ;
- firstInput - en 0 ;
- les champs totalAmounts, totalFees et totalBurned étaient totalement absents.
Une partie de la preuve est restée originale. En particulier, les valeurs réelles suivantes ont été utilisées :
- l'identifiant de la transaction (TxID) ;
- le premier nœud de l'arbre de preuves (First node) ;
- les paramètres généraux du format des preuves, les versions et les identifiants des systèmes.
Tous les autres éléments ont été générés artificiellement. Étant donné que la state root malveillante avait déjà été enregistrée par Ethereum comme étant de confiance, la preuve finale a passé la vérification avec succès, malgré l'absence du volume de fonds correspondant dans le transfert initial.
En quoi consistait la vulnérabilité : Verus et Ethereum lisaient les données différemment
Verus percevait les enregistrements sérialisés comme une structure dans laquelle les éléments dupliqués étaient en pratique ignorés après la conversion du tableau en Map.
Ethereum, au contraire, traitait tous les enregistrements de manière séquentielle, si bien que chaque enregistrement suivant pouvait écraser la valeur de la state root.
En conséquence, les notaires de Verus ont signé des données qu'ils considéraient comme parfaitement correctes, mais Ethereum a interprété ces mêmes octets différemment et a finalement enregistré comme racine d'état de confiance une valeur entièrement contrôlée par l'attaquant.
Une fois qu'Ethereum a reconnu cette fausse state root comme fiable, toute preuve d'importation construite à partir d'elle était considérée comme légitime par le système.
Un facteur supplémentaire a été une erreur logique dans le contrat du pont Ethereum. Le contrat ne vérifiait pas si le montant indiqué dans la demande d'importation correspondait au volume d'actifs réellement exporté du réseau Verus.
Il suffisait de construire un faux paramètre hashtransfers correspondant aux transactions de retrait de fonds pour passer avec succès la vérification de la structure CCE.
Mouvement des fonds dérobés : 2,778 ETH via Tornado Cash

Après avoir exploité avec succès la vulnérabilité, l'attaquant a échangé les actifs dérobés contre 2,778.8662 ETH via Relay, puis a transféré les fonds obtenus vers le service d'anonymisation Tornado Cash.
