Vulnérabilité Frontend de Loopring expliquée
0x568B
June 12th, 2022

Initialement publié en anglais par StarkWare le 7 Mai, 2020

Photo par iMattSmart sur Unsplash
Photo par iMattSmart sur Unsplash

TL;DR

Nous avons trouvé une vulnérabilité critique dans le frontend du wallet Loopring, qui permet à quiconque de prendre le contrôle de chaque compte créé en l’utilisant. La vulnérabilité survient parce que le frontend du wallet Loopring utilise un entier 32 bits pour obtenir la clé privée de chaque utilisateur. Cela permettrait à un attaquant de reproduire toutes ces clés privées. Nous avons démontré la vulnérabilité en récupérant les clés privées de plus d’une douzaine de comptes. Ce problème a été communiqué à Loopring, qui agit rapidement pour y remédier.

L’histoire de deux clés

Dans Loopring, l’utilisateur dispose de deux types de clés:

  • Une clé Ethereum
  • La clé du compte (loopring)

La clé de compte est nécessaire pour ses propriétés SNARK-friendlyess, car il est beaucoup (beaucoup) plus facile de prouver une signature signée par la clé de compte que par la clé Ethereum. La clé de compte, étant compatible SNARK, diffère de la clé Ethereum par plusieurs paramètres (par ex. la courbe elliptique). Pour cette raison, il n’est pas facilement supporté par d’autres portefeuilles.

Lorsqu’un utilisateur rejoint le système, il a très probablement une clé Ethereum, mais pas encore une clé de compte. Donc, même si l’utilisateur choisit MetaMask comme son portefeuille lors de la connexion à Loopring, il ne servira que sa tx Ethereum (pour les interactions avec le contrat, y compris les dépôts et les retraits). Mais pour passer des commandes, l’utilisateur devra utiliser sa clé de compte.

Loopring a choisi d’exécuter toutes les opérations liées au compte dans le navigateur. Par conséquent, dans le cadre du processus d’inscription, l’utilisateur génère sa clé de compte, et une correspondance entre les deux types de clés est enregistrée dans le contrat. A partir de ce moment, chaque fois que l’utilisateur doit soumettre des commandes, elle utilisera sa clé de compte et non sa clé Ethereum.

Le processus de dérivation de la clé de compte

Pour générer une paire de clés de compte (public, privé¹), l’utilisateur doit saisir un mot de passe. Ce mot de passe, ainsi que l’adresse Ethereum de l’utilisateur, est utilisé pour obtenir sa clé de compte :

wallet.js
wallet.js

Cela en soi est une mauvaise pratique: puisque les adresses Ethereum, ainsi que les clés publiques du compte, sont lisibles à partir du contrat, une recherche complète des mots de passe est possible. Et comme l’histoire nous l’a appris à maintes reprises (voir les ‘brain wallets’²), la plupart des utilisateurs ne sont pas doués pour choisir un mot de passe fort.

Ça ne s’arrête pas là. Allons inspecter plus loin :

Exchange.js
Exchange.js
EdDSA.js
EdDSA.js

L’adresse Ethereum et le mot de passe sont utilisés pour générer la seed. Ensuite, la seed est utilisée pour dériver randomNumber. Ce numéro aléatoire est ensuite utilisé pour obtenir l’entropie qui donnera à l’utilisateur la clé privée désirée.

Mais comment est dérivé randomNumber de la seed ?

hashCode

Jetons un coup d’oeil au hashcode :

bitstream.js
bitstream.js

Donc, dans notre cas, le hashCode va passer au-dessus de la seed char by char, et va calculer h à partir de celle-ci.

Mais Math.imul fait une multiplication³ d’entiers 32 bits ! Cela signifie que le résultat est un entier 32 bits. L’autre opération que nous voyons ici (l’addition) est | 0. Mais les opérations binaires en JavaScript donnent des entiers 32 bits⁴ (tous les opérandes sont transformés en entiers 32 bits).

Résultat: Le hashCode retourne un entier 32 bits ! Cela signifie que peu importe ce que la seed est, randomNumber sera un entier de 32 bits.

Toute l’entropie pour générer des clés privées, tout l’espace de touches, est 32 bits.

Implications

Quel que soit le mot de passe de l’utilisateur, la clé privée de son compte sera dérivée d’un espace de clé de 32 bits. Nous pouvons calculer toutes les clés de compte possibles dans le système (à condition qu’elles aient été créées en utilisant leur interface).

Par conséquent, nous pouvons trouver les clés privées de tous les utilisateurs enregistrés via l’API. En fait, à court terme, nous avons réussi à reproduire les clés privées de plus d’une dizaine de comptes, pour démontrer cette vulnérabilité.

En contrôlant ces clés, les attaquants peuvent soumettre des commandes au nom des utilisateurs. Cela peut leur permettre de voler de l’argent. Une façon de le faire est de soumettre suffisamment de commandes à une paire illiquide avec un prix qui est loin du prix actuel. Par exemple, si les attaquants veulent voler des DAI sur les comptes, ils peuvent envoyer de nombreuses commandes proposant “d'acheter 1 ETH pour 10 000 DAI”. Dans le même temps, les attaquants passeront des commandes pour “vendre 1 ETH pour 10 000 DAI”. Si la paire n’est pas assez liquide, ces commandes seront exécutées (une fois le carnet d’ordres épuisé) et le compte de l’attaquant gagnera ces DAI pour une petite quantité d’ETH.

Conclusions

Actuellement, les portefeuilles Ethereum sécurisés sont livrés avec une variété limitée de normes cryptographiques, et ne sont pas assez souples pour toute sorte de personnalisation. Un système qui ne peut pas utiliser ces normes doit avoir la capacité de construire un portefeuille alternatif à partir de rien. Cela inclut la réalisation d’opérations liées à la sécurité dans le navigateur (ce qui est déconseillé); l’utilisation de mots de passe pour générer des clés (ce qui est considéré comme une mauvaise pratique); et l’introduction de bugs critiques dans le processus, comme le bug de hashcode décrit ci-dessus (ce qui est très mauvais).

Espérons que dans un avenir proche les wallets Ethereum deviendront plus souples et soutiendront un ensemble plus large de primitives cryptographiques. Ceci, une fois de plus, déconnectera le processus de création de portefeuilles sécurisés de celui de construction dApps, empêchant ainsi de tels bugs à l’avenir.

Remerciements : Merci à Louis Guthmann (@GuthL), chef de produit et chercheur chez StarkWare, pour son aide dans l’exploitation de la vulnérabilité.

~Avihu Levy (@avihu28), responsable produit, StarkWare

— —

¹ Le code utilise le mot “secret” au lieu du mot communément utilisé “privé”.

² https://bitcoin.stackexchange.com/questions/8449/how-safe-is-a-brain-wallet

³ https://stackoverflow.com/questions/21052816/why-would-i-use-math-imul

https://developer.mozilla.org/en-US/docs/web/javascript/reference/operators#Bitwise_OR

Traduction faite par @Theyozz

Arweave TX
PJB0m7eiymJ5XVDkaVDWgz-TkOR7tS8nmWChQDIJFTo
Ethereum Address
0x568B12eBBE85521D2cd8a2C9B7a8EF3f48aa2d66
Content Digest
GEf7QCil3hu_S1yR0D49BAmuhTeh45sntNm2sf7bGoE