Une erreur de quelques octets: analyse de l'attaque contre Liquid Network

Une erreur de quelques octets: analyse de l'attaque contre Liquid Network

Une vulnérabilité dans le code de Liquid Network a permis de créer environ 3,998.5 L-BTC en exploitant une faille du mécanisme de mise en cache des vérifications cryptographiques.

9 sept. 2026

Le 6 septembre 2026, le réseau Liquid Network a subi une attaque exploitant une vulnérabilité dans la base de code d'Elements, utilisée pour vérifier les transactions confidentielles.

Le problème venait de la construction ambiguë de la clé de cache lors de la vérification du rangeproof, la preuve cryptographique qui confirme qu'un montant caché de transaction est admissible. De ce fait, deux jeux de données de vérification différents pouvaient produire la même entrée de cache.

L'attaquant a d'abord préparé des transactions spéciales et rempli le cache avec elles, puis a envoyé une transaction malveillante augmentant le volume d'actifs émis: elle a réutilisé un résultat de vérification déjà enregistré et a permis de créer des L-BTC non autorisés.

L'attaquant a ensuite échangé ces actifs au sein du réseau Liquid contre de vrais bitcoins, via le mécanisme de retrait des fonds vers le réseau principal Bitcoin.

Contexte

Les bitcoins bloqués dans un portefeuille Bitcoin dédié, contrôlé par la fédération Liquid, garantissent les L-BTC émis sur Liquid. Selon le système d'ancrage prévu, un L-BTC doit correspondre à un BTC placé sous le contrôle de la fédération.

Au lieu de publier le montant, une transaction peut contenir un engagement cryptographique de Pedersen. L'actif lui-même peut être représenté par un engagement similaire. Une équation mathématique spécifique confirme que les valeurs entrantes et sortantes de la transaction sont équilibrées, tandis que le rangeproof démontre que chaque montant caché se situe dans la plage non négative autorisée.

En particulier, la vérification du rangeproof fait partie du mécanisme censé garantir qu'un montant caché est réellement correct et ne peut pas servir à créer des actifs au-delà de la quantité autorisée. Or, la vérification effective de cette validité dépend de plusieurs éléments à la fois: le rangeproof, l'engagement sur la valeur, l'engagement sur l'actif et le scriptPubKey.

Le script est transmis à secp256k1_rangeproof_verify comme donnée supplémentaire incluse dans la vérification cryptographique. Une preuve valide pour une combinaison (C, X, S) ne devient pas automatiquement valide pour une autre combinaison.

Transactions clés

Date/heureTXIDDescription
2026-09-06 13:52:10271147100a94f6337b6c3db39b30c92d5b97ed91597307b6f721f73a15187ec5Transaction préparatoire
2026-09-06 13:53:10f24a4b179b5cc7e88b25a763911f7cbdf2bf45d1d1b5ab611e94461cef0a183fLa transaction qui a créé un volume supplémentaire de L-BTC au sein de Liquid
2026-09-06 13:54:103875a6d6ed4af708e6fd90d1c5504dc014e52c7093a566987252006d6cf1146bLa transaction crée de nouvelles sorties confidentielles Liquid contrôlées par l'attaquant ou par le portefeuille suivant
2026-09-06 14:00:10c6ea588ac26f5838b6acbb2a444a33b325bbfeb39bf16dfe27b47215ffd72267La transaction crée de nouvelles sorties confidentielles Liquid contrôlées par l'attaquant ou par le portefeuille suivant
2026-09-06 14:01:1046f117c990580501a5156937a8c8affda551a38b3c0b99d29eb8469ee6beb3d2Retrait de Liquid d'un montant de 2.65138358 L-BTC
2026-09-06 14:06:10ce4caece413cd9d444ce7ed9f54e5b328b3da5e4af301aff59a3571f76e988f2Retrait de Liquid d'un montant de 3,996.01834922 L-BTC
2026-09-06 14:28:5685d2ca15bea33a592e73ed40c6a5da887feecf1e77f58ec7f580e008416450433,996.01834922 BTC envoyés à l'adresse bc1ql4mfu6aundtkksxklfajs2h3t9nzcd6gyqjlte
2026-09-07 18:09:25a6d697a25266ce3c78774fd1d75f896b7af522ada209b0f6228ea497bc49a46d3,400 BTC renvoyés à l'adresse bc1qdlld6antmv4xug242ed83q7k4rqw50cwfns38szx4qu2f4jwaxxsuhwxxr

Adresses clés

AdresseFonction
ex1q7kgx4ptje7px48tn0nsmc6se5pngdp3smpqa2wAdresse de l'attaquant sur le réseau Liquid
bc1qgslsydz56d0ed6827hdemfmk5w2f6ldyc6wt7pBénéficiaire du versement de la fédération de 3,996.01834922 BTC
bc1ql4mfu6aundtkksxklfajs2h3t9nzcd6gyqjlteBénéficiaire du versement de la fédération de 2.65138358 BTC
bc1ql4mfu6aundtkksxklfajs2h3t9nzcd6gyqjlteAdresse de l'attaquant servant à consolider les fonds et à transmettre des messages dans la blockchain
bc1qdlld6antmv4xug242ed83q7k4rqw50cwfns38szx4qu2f4jwaxxsuhwxxrPortefeuille Bitcoin de la fédération Liquid utilisé pour l'ancrage et les retraits

1. La falsification du cache de preuves

Pour l'analyse, les données brutes de sérialisation d'Elements ont été récupérées et décomposées, et pas seulement les champs affichés par un explorateur de blockchain. Les sorties :0 des deux transactions préparatoires coïncident entièrement pour l'actif, l'engagement sur la valeur, le nonce, le script, la surjection proof et le rangeproof.

En utilisant des décalages commençant à zéro et une borne supérieure exclue de la plage, l'output_0 de la transaction 271147100a94f6337b6c3db39b30c92d5b97ed91597307b6f721f73a15187ec5 a été décodé à partir des 8,882 octets de la transaction brute, ce qui a donné les données suivantes:

a. C0 = 09d6c6150e95e576d288dcc890bf48518119c6f227ea1be6f9a932792dbee683f5

b. S0 = 6a|43|086f5d67160fc4b477954fb09ef321e5b589d7a07740a1a6df494ed2335b1d01d8|0a0a488 de4899d0ae757f6cf8368663184d164106111ed9eaecf510e35282ddc6d|6a.

i. 6a signifie OP_RETURN. ii. 43 indique qu'il faut ensuite placer 67 octets.

Ces 67 octets ont délibérément la structure suivante: un C1 de 33 octets, un X de 33 octets et un 6a d'un octet. Le script est donc S0 = 6a43 || C1 || X || 6a, où X = 0a0a488de4899d0ae757f6cf8368663184d164106111ed9eaecf510e35282ddc6d est le résultat de secp256k1_generator_generate(L-BTC asset ID).

2. Le remplissage du cache dans le mempool actif

Lors de la connexion d'un bloc, les règles de cache sont telles qu'une entrée existante est consommée, mais qu'en l'absence d'entrée, aucune nouvelle n'est enregistrée. La connexion du bloc 4,050,335 aurait donc dû supprimer l'entrée de cache créée au moment de la première acceptation des deux transactions préparatoires visibles.

Par conséquent, pour que la transaction créant des L-BTC supplémentaires puisse passer la vérification après la connexion du bloc 4,050,335 et avant le contrôle de la sortie incorrecte, chaque nœud acceptant la transaction avait besoin d'une nouvelle transaction valide portant la même séquence (P0, C0, X, S0).

L'identifiant de cette transaction de remplissage et ses octets d'origine ne sont pas encore connus. Pour les retrouver, il faudrait peut-être le fichier debug.log de l'un des functionaries, le mempool.dat en vigueur à ce moment-là, un enregistrement des échanges de données entre nœuds ou une copie sauvegardée de l'état du mempool.

3. La création de L-BTC supplémentaires au sein de Liquid

Le décodage de l'output_1 de la transaction d'inflation f24a4b179b5cc7e88b25a763911f7cbdf2bf45d1d1b5ab611e94461cef0a183f a donné les données suivantes:

a. C1 = 086f5d67160fc4b477954fb09ef321e5b589d7a07740a1a6df494ed2335b1d01d8.

Ce sont exactement les 33 premiers octets des données transmises par le script OP_RETURN de la sortie préparatoire.

Il faut noter que le rangeproof P1 est plus long que P0 de 68 octets, ces données supplémentaires se trouvant à la fin: 09d6c6150e95e576d288dcc890bf48518119c6f227ea1be6f9a932792dbee683f5

b. 0a0a488de4899d0ae757f6cf8368663184d164106111ed9eaecf510e35282ddc6d6a43, ce qui donne le C0 de la transaction préparatoire: 09d6c6150e95e576d288dcc890bf48518119c6f227ea1be6f9a932792dbee683f5

et X = 0a0a488de4899d0ae757f6cf8368663184d164106111ed9eaecf510e35282ddc6d est le résultat de secp256k1_generator_generate(L-BTC asset ID). L'intégration du générateur exact de L-BTC a permis aux octets du script de la transaction préparatoire de s'aligner sur le champ du générateur d'actif de la transaction attaquante.

c. Au total

Transaction préparatoire valide: P0 || C0 || X || (6a43 || C1 || X || 6a)

Transaction attaquante non valide: (P0 || C0 || X || 6a43) || C1 || X || 6a

Les deux se réduisent finalement au même flux de 4,301 octets: P0 || C0 || X || 6a43 || C1 || X || 6a

4. Le retrait des fonds

Les L-BTC peuvent être échangés de nouveau contre de vrais BTC placés sous le contrôle de la fédération Liquid. La transaction suivante, 3875a6d6…f1146b, a utilisé la sortie f24…:0 de la transaction liée à l'attaque. Deux autres transactions ont créé des sorties explicites pour retirer des fonds vers le réseau principal Bitcoin:

  • 46f117c9…eb3d2: 2.65138358 L-BTC
  • ce4caece…988f2: 3,996.01834922 L-BTC

La transaction Bitcoin 8db751a6…7b140, confirmée dans le bloc 965,783, a versé les deux montants exacts retirés à la suite de l'attaque.

En quoi consistait la vulnérabilité?

Elements met en cache les rangeproofs vérifiés avec succès afin de ne pas exécuter deux fois la même opération cryptographique coûteuse. Deux constructions importent ici.

Cette conception ne tenait pas compte du générateur d'actif ni du script, alors que ces deux paramètres influent sur la validité de la preuve. Le commit c26d719 a tenté de lier l'entrée de cache à l'ensemble du contexte de vérification.

Le correctif contenant l'implémentation du cache

Transaction préparatoire valide: (P0, C0, X, 6a43 || C1 || X || 6a)

Transaction attaquante non valide: (P0 || C0 || X || 6a43, C1, X, 6a)

Les sorties observées de la transaction préparatoire et de la transaction attaquante ne créent pas de collision avec l'ancienne clé P || C. Leurs données d'entrée pour l'ancienne clé diffèrent à la fois par la longueur et par le contenu.

La construction de l'attaque correspond en revanche exactement à la clé à quatre champs sans séparateurs introduite par ce correctif. Le passage réussi de la transaction indique donc une implémentation semblable à c26d719 combinée à un cache rempli à l'avance. Les versions exactes des binaires déployés sur les différents nœuds functionaries de Liquid n'ont pas été rendues publiques.

Problème de synchronisation: comment le protocole Verus a perdu $7.5M à cause des différences entre blockchains

Le mouvement des fonds

Adresse: bc1ql4mfu6aundtkksxklfajs2h3t9nzcd6gyqjlte.

#Piratage
Liquid Network piraté à cause d'un cache: qu'est-il arrivé à 4,000 BTC?