Milk Sad : autopsie d’un vol de bitcoins et mauvaise cryptographie

Une équipe de chercheurs, réunie sous le nom de code Milk Sad, a récemment découvert une vulnérabilité affectant l’un des outils open source pour Bitcoin les plus vieux en existence. Cette librairie, créée en 2011, et régulièrement mise à jour, est utilisée pour générer des phrases mnémoniques. Autrement dit, de nombreux portefeuilles logiciels s’appuient sur cet outil pour générer les clefs privées de leurs utilisateurs.

Un problème de cryptographie, et plus précisément d’entropie, c’est-à-dire concernant la génération de nombres aléatoires, a pu être exploité par des pirates afin de subtiliser les précieux bitcoins de plusieurs utilisateurs.

Dans cet article technique, nous étudierons cette faille et la méthode utilisée par les pirates. Nous allons le voir, les utilisateurs concernés avaient pourtant très bien fait les choses en matière de sécurité, lors de la création de leur portefeuille.

Cet article est largement inspiré et écrit grâce aux travaux conjoints d’une équipe de chercheurs réunie sous le nom de Milk Sad – le papier original se trouve ici : https://milksad.info/.

Un vol de bitcoins inexplicable

L’équipe de chercheurs qui nous relate cette affaire a dû enquêter plusieurs semaines. Tout commence le 23 juillet 2023. Ce jour-là, un utilisateur du réseau Bitcoin eut la très désagréable surprise de s’apercevoir que ses bitcoins avaient disparu de son wallet. Il s’est très vite rendu compte qu’il s’agissait d’un vol, et non pas d’un bug. En effet, une transaction sortante fut effectuée le 12 juillet vers une adresse inconnue. Il n’avait pourtant pas utilisé son hardware wallet durant une longue période.

Il était de plus extrêmement bien sécurisé. En utilisateur averti, il l’avait créé de la façon suivante :

  • Premièrement, génération de la phrase mnémonique (BIP39, 24 mots) sur un ordinateur portable isolé d’Internet et de tout réseau ;
  • Le PC tournait sous Linux, et les clefs privées furent générées à l’aide d’un logiciel que Bob avait compilé lui-même ;
  • Ensuite, Bob avait entré la phrase mnémonique sur des hardwares wallets Ledger et Trezor ;
  • Enfin, les deux hardwares wallets étaient physiquement sécurisés et comportaient un PIN.

Toutes les conditions étaient donc réunies pour que ses bitcoins soient parfaitement sécurisés.

L’enquête commence

Tout d’abord, Bob a contacté plusieurs de ses amis qui avaient également utilisé la même méthode pour stocker leurs bitcoins. Il fut avéré que l’une d’entre elles, Alice, avait été également spoliée. En effet, en consultant son adresse, une transaction sortante (l’intégralité du solde) fut effectuée le même jour, à la minute près.

Il devenait clair que Bob et Alice avaient été victimes d’une sorte de hack. Et malheureusement, ils s’aperçurent ensuite que toutes leurs cryptomonnaies, également sécurisées via les mêmes hardwares wallets, avaient également disparu (des ethers).

S’il s’agissait d’une forme d’hameçonnage visant à convaincre les victimes d’autoriser les portefeuilles à effectuer un transfert, les conséquences eurent été limitées. Il en aurait été de même si le pirate avait eu accès aux clés privées individuelles d’un ou plusieurs sous-comptes. Il s’agissait donc d’un hack élaboré, où la clé privée maître de leurs wallets avait été exposée.

Les doutes s’installent

Bob et Alice ont vite compris qu’un vol de ce type, effectué simultanément, était hautement improbable. En outre, tous deux avaient pris des précautions drastiques pour stocker leurs clés privées. Un peu plus tard, ils apprirent qu’ils n’étaient pas les seules victimes. En observant les transactions sur la blockchain, il apparut que plusieurs centaines de wallets avaient été drainés.

La panique commença à s’installer : l’attaquant aurait-il repéré une faille au sein des hardwares wallets ? Les primitives cryptographiques correspondant à la génération des clés privées seraient-elles en cause ? Quelqu’un aurait-il utilisé un ordinateur quantique ?

Quelle est la faille et la méthode de ce vol de bitcoins ? Milk Sad mène l'enquête
Bitcoin

Les premiers indices

En planchant sur le problème, Bob et Alice comprirent que la configuration utilisée pour générer leurs wallets respectifs était la même. Un PC Linux complètement isolé d’Internet. L’outil utilisé pour générer les phrases mnémoniques était le même : Libbitcoin Explorer, version 3.x.

Il s’agit d’un outil open source très connu. Son fichier binaire (bx) permet de générer un wallet hors-ligne via les commandes suivantes :

# generate 256 bits of entropy, turn it into BIP39 mnemonics
bx seed -b 256 | bx mnemonic-new
<output of secret BIP39 mnemonic words>

Il s’agit de la commande Linux permettant de générer une phrase mnémonique BIP29 de 24 mots. C’est donc la clé de voûte de n’importe quel wallet, en termes de sécurité.

Cette commande pourrait alors être à l’origine du problème. Par exemple, le générateur de nombre aléatoire des ordinateurs Linux pourrait ne pas avoir l’entropie nécessaire…

C’est ainsi qu’Alice et Bob demandèrent l’aide d’experts en sécurité de l’information pour tenter d’y voir plus clair.

>> Euro > Crypto au meilleur prix en 1 clic ! (lien commercial) <<

Revue du code source

L’équipe commença à passer en revue le code source de la commande bx seed, contenue dans le fichier binaire.

Cette commande fait appel à une première fonction – new_seed(size_t bit_length) – dans libbitcoin-explorer src/utility.cpp. Cette dernière fait elle-même appel à une autre fonction – pseudo_random_fill(data_chunk& out) – au sein de la librairie libbitcoin-system.

console_result seed::invoke(std::ostream& output, std::ostream& error)
{
    const auto bit_length = get_bit_length_option();

    // These are soft requirements for security and rationality.
    // We use bit vs. byte length input as the more familiar convention.
    if (bit_length < minimum_seed_size * byte_bits ||
        bit_length % byte_bits != 0)
    {
        error << BX_SEED_BIT_LENGTH_UNSUPPORTED << std::endl;
        return console_result::failure;
    }

    const auto seed = new_seed(bit_length);
    ...
}
data_chunk new_seed(size_t bit_length)
{
    size_t fill_seed_size = bit_length / byte_bits;
    data_chunk seed(fill_seed_size);
    pseudo_random_fill(seed);
    return seed;
}

Le terme « pseudo-random » peut semer le doute. Cependant, un générateur de nombres pseudo-aléatoire (PRNG) n’est pas un danger en soi, si tant est qu’il soit un CSPRNG, c’est-à-dire un Générateur de nombres pseudo-aléatoire cryptographiquement sécurisé.

Mersenne Twister

Les chercheurs ont alors suivi les appels successifs de la fonction.

pseudo_random::fill(data_chunk& out) -> pseudo_random::next() -> pseudo_random::next(uint8_t begin, uint8_t end) -> std::mt19937& pseudo_random::get_twister()

Ce sont ces entrées, mt19937 et twister, qui mirent la puce à l’oreille de nos experts. En effet, le PRNG utilisé est Mersenne Twister. Il ne s’agit pas d’un CSPRNG ! Il ne devrait donc jamais être utilisé pour générer des informations sensibles, tels des secrets cryptographiques.

De plus, l’état interne de Mersenne Twister peut-être inversé par un attaquant qui connaît quelques centaines de sorties. En conséquence, cela met en danger les sorties (inconnues de l’attaquant) provenant du même flux.

Cependant, si le générateur de nombres pseudo aléatoire est réinitialisé lors de chaque création de wallet, comment cette faille pourrait-elle être exploitée pour commettre un vol à distance ?

Horloge système

Les chercheurs continuèrent d’explorer le code du fichier pseudo_random.cpp et n’eurent pas à aller bien loin pour découvrir le réel problème.

// Use the clock for seeding.
const auto get_clock_seed = []() NOEXCEPT
{
    const auto now = high_resolution_clock::now();
    return static_cast<uint32_t>(now.time_since_epoch().count());
};

// This is thread safe because the instance is thread static.
if (twister.get() == nullptr)
{
    // Seed with high resolution clock.
    twister.reset(new std::mt19937(get_clock_seed()));
}

Vous l’aurez compris, la seed de l’algorithme du PRNG est basée sur 32 bits du temps système. Effrayant, d’un point de vue de la sûreté cryptographique… Les chercheurs, n’en croyant pas leurs yeux, vérifièrent si bx utilisait bien ceci pour générer les clés privées.

Pour ce faire, ils se livrèrent à une expérience sur les variables d’environnement :

  • Utilisation de bx version 3.2 avec une librairie libfaketime ;
  • Exécutions séparées sous des horloges système strictement identiques.
$ wget https://github.com/libbitcoin/libbitcoin-explorer/releases/download/v3.2.0/bx-linux-x64-qrcode -O ~/.local/bin/bx
$ chmod +x ~/.local/bin/bx
$ sudo apt install libfaketime
$ export LD_PRELOAD=/usr/lib/x86_64-linux-gnu/faketime/libfaketime.so.1 FAKETIME_FMT=%s FAKETIME=0

$ bx seed -b 256 | bx mnemonic-new
milk sad wage cup reward umbrella raven visa give list decorate bulb gold raise twenty fly manual stand float super gentle climb fold park

$ bx seed -b 256 | bx mnemonic-new
milk sad wage cup reward umbrella raven visa give list decorate bulb gold raise twenty fly manual stand float super gentle climb fold park

Ils s’aperçurent alors avec horreur que les phrases mnémoniques des wallets étaient exactement les mêmes avec des horloges identiques !

Il s’agit bien de la première preuve que générateur de nombres n’est ni aléatoire ni pseudo-aléatoire.

Autres découvertes

En fouillant un peu plus, l’équipe fut assurée que le code utilisait le standard MT19937 du PRNG Mersenne Twister. Contrairement au standard MT19937-64, il emploie une seed de 32 bits en entrée. Cela signifie que le PRNG présente un plafond de 2^32 positions de départ, et ce, qu’il soit basé sur /dev/random ou sur l’horloge système.

Ainsi, en exécutant bx seed -b 256 pour obtenir 256 bits d’entropie, le résultat correspond à 32 bits d’une horloge de haute précision, qui sont passés dans un « twister », puis étendus à 256 bits, sans ajouter de nouvelles informations.

S’il s’agissait d’entropie réelle, le nombre de variations grandirait exponentiellement avec la taille de la seed. La différence entre le résultat attendu (256 bits) et le résultat réel (32 bits) est donc astronomique.

Dans ce cas, un attaquant peut recalculer la seed originelle par cassage brut après un maximum de 4,29 milliards d’essais. Cela ne prend que quelques jours de calculs sur un PC de gaming standard.

C’est donc une faille catastrophique. L’équipe se retrouva alors le 21 juillet en présence d’informations extrêmement sensibles. Avant de procéder à la divulgation totale de leurs travaux, seul le FBI fut alerté, tout d’abord pour éviter aux victimes de payer des taxes quant à leurs fonds volés. De plus, les victimes étant américaines, cela pourrait permettre de limiter le mouvement des fonds vers des plateformes de change le cas échéant.

>> Euro > Crypto au meilleur prix en 1 clic ! (lien commercial) <<

Impact de la découverte

L’équipe décida d’utiliser le nom de code « Milk Sad« . Il s’agit des deux premiers mots de la phrase mnémonique générée par bx pour le temps 0.

Affaire Cake Wallet

La découverte rappela rapidement à l’équipe une faille du même type au sein de Cake Wallet. En effet, en mai 2021, les développeurs de Cake Wallet annoncèrent une vulnérabilité découlant aussi d’une faible entropie. Les utilisateurs furent incités à changer leurs phrases mnémoniques.

Cake Wallet - Vulnérabilité

La faille provenait également de l’algorithme utilisé pour générer les seeds, appelé Dart.

Uint8List randomBytes(int length, {bool secure = false}) {
  assert(length > 0);
  final random = secure ? Random.secure() : Random();
  final ret = Uint8List(length);
  for (var i = 0; i < length; i++) {
    ret[i] = random.nextInt(256);
  }
  return ret;
}

La fonction Random() peut en effet revenir à 0 ou au temps système.

Random::Random() {
  uint64_t seed = FLAG_random_seed;
  if (seed == 0) {
    Dart_EntropySource callback = Dart::entropy_source_callback();
    if (callback != nullptr) {
      if (!callback(reinterpret_cast<uint8_t*>(&seed), sizeof(seed))) {
        // Callback failed. Reset the seed to 0.
        seed = 0;
      }
    }
  }
  if (seed == 0) {
    // We did not get a seed so far. As a fallback we do use the current time.
    seed = OS::GetCurrentTimeMicros();
  }
  Initialize(seed);
}

Affaire Trust Wallet

Nous avions informé nos lecteurs le 24 avril 2023 d’une vulnérabilité majeure chez Trust Wallet, qui causa environ 170 000 dollars de pertes chez ses utilisateurs. Ledger Donjon avait alors produit une analyse complète de la faille : elle est étonnamment proche de ce que l’équipe Milk Sad a pu observer avec le fichier bx.

Les premières attaques remontent à décembre 2022 ; cependant, il aura fallu attendre plusieurs mois pour que les fonds disparaissent des wallets générés via bx.

La faille présente dans la version concernée de Trust Wallet et celle de bx partagent les mêmes fondamentaux. Il s’agit de la mauvaise entropie de Mersenne Twister MT19937. La seule différence est que bx utilise l’horloge système pour utiliser MT19937, mais cela n’a pas de conséquence quant aux attaques par force brute à distance. La faille réside principalement dans les 32 bits servant d’espace à la génération des clés.

Les détails des vulnérabilités

Dans le cas de Trust Wallet, les phrases mnémoniques sont d’une longueur de 12 mots. Elles nécessitent ainsi 128 bits d’entropie. Dans le cas de bx, plus flexible, les sorties générées peuvent être de 128, 192 ou 256 bits.

Le PRNG n’est cependant pas utilisé de la même façon. Même si Trust Wallet et bx utilisent tous deux l’algorithme MT19937 Mersenne Twister, les sorties sont traitées différemment.

Avec Trust Wallet, le procédé est le suivant :

  • En sortie, 32 bits issus de MT19937 ;
  • Trust Wallet prend les derniers 8 bits pour remplir le tableau d’entropie ;
  • Les 24 bits restants sont éliminés via & 0x000000ff bitwise-and dans wasm/src/Random.cpp.
// Copyright © 2017-2022 Trust Wallet.
//

[...]

void random_buffer(uint8_t* buf, size_t len) {
    std::mt19937 rng(std::random_device{}());
    std::generate_n(buf, len, [&rng]() -> uint8_t { return rng() & 0x000000ff; });
    return;
}

Avec bx, le code en C++ utilisé dans libbitcoin-system pour la fonction pseudo_random::fill() effectue la même troncature, mais à l’inverse, c’est-à-dire en prenant les 8 bits les plus significatifs issus du PRNG. Ainsi, les sorties générées par bx seed sont dans un espace totalement différent des sorties générées avec le code de Trust Wallet. Les phrases mnémoniques BIP39 résultantes sont donc aussi différentes.

L’équipe de Milk Sad a pu en avoir confirmation grâce à ce code fournit par Ledger Donjon à titre d’exemple :

[...]
RNG seed: 0x8ec170a8
Mnemonic:
sorry slush already pass garden decade grid drip machine cradle call put
[...]

En recalculant la seed issue du RNG (0x8ec170a8) avec le code de bx pour une phrase de 12 mots, le résultat est alors le suivant :

local chef load churn future essence type leave program weird ancient owner

On voit tout de suite que le procédé est différent. L’équipe Milk Sad not la phrase prémonitoire écrite par Ledger Donjon :

During our investigations, we also noticed that a few addresses were vulnerable while they had been generated a long time before the Trust Wallet release. That probably means this vulnerability exists in some other wallet implementations which is concerning…

Ledger Donjon

Ledger Donjon n’a probablement pas poussé ses recherches en incluant les wallets générés par les versions 3.x de bx. Cependant, il y a probablement d’autres wallets affectés, utilisant MT19937 pour générer des phrases mnémoniques de 12 mots. L’équipe de Milk Sad ne s’est pas penchée sur le sujet.

Le plan de divulgation de Milk Sad

La procédure de divulgation entre les deux affaires est sensiblement différente. En effet, dans le cas de Trust Wallet, il s’agit d’une entité unique, commerciale, bien établie et financée. L’équipe de développement était en contact direct avec les victimes, et pouvait donc les informer individuellement d’un problème avec leur portefeuille. De plus, elles n’étaient vulnérables que pour une période temporelle limitée, et la faille fut découverte rapidement.

En conséquence, Trust Wallet a décidé d’inciter financièrement ses utilisateurs à déplacer leurs fonds en lieu sûr. L’équipe a également remboursé les utilisateurs lésés et offert un bug bounty de 100 000 dollars à Ledger Donjon.

Dans le cas de Milk Sad, tout est plus délicat. La librairie Libbitcoin est open source, développée dans un but non-commercial, et les codeurs n’ont pas de canaux de communication directs avec leurs utilisateurs. Les portefeuilles affectés ont été créés sur plusieurs années, les clés privées concernées ont probablement été importées et exportées de nombreuses fois, tout comme les phrases mnémoniques BIP39. Cela rendait la tâche beaucoup plus difficile pour les chercheurs en cybersécurité.

La présence des écrits de Ledger Donjon et de Trust Wallet avaient probablement mis la puce à l’oreille de plusieurs pirates. Ces derniers devaient déjà être en train de scanner activement la blockchain pour trouver des portefeuilles bx à exploiter. Toutes les clés privées générées avec bx seed étaient compromises.

L’équipe Milk Sad a donc jugé qu’une divulgation rapide était nécessaire. En effet, le temps joue dans ce cas en faveur des attaquants. Ainsi, au lieu du standard de 90 jours, l’équipe s’est fixé un objectif de 7 jours pour la divulgation.

Bilan des vols on-chain

L’équipe s’est focalisée sur la blockchain de Bitcoin, par manque de temps. Toutefois, toutes cryptomonnaies conservées sur un wallet généré via bx seed sont potentiellement concernées. Le bilan fourni par l’équipe est donc malheureusement une estimation basse de la quantité de fonds ayant pu être dérobés.

Le vol du 23 juillet 2023

Il est attribué à un attaquant unique et bien préparé. Au total, le butin représente 29,65 BTC pour une valeur de 850 000 dollars au moment des faits.

Milk Sad - Vols du 23 juillet 2023

Autres mouvements de fonds suspects

L’équipe a également identifié des mouvements de fonds suspects qu’elle attribue à des pirates exploitant la même faille.

Ces mouvements débutent le 3 mai 2023 et consistent en de multiples balayages de wallets pour de petites sommes, jusqu’à 0,33 BTC. Ils se sont poursuivis jusqu’au 15 juillet. Les fonds ont ensuite été transférés vers d’autres adresses. Il peut cependant s’agir d’une activité légitime, étant donné qu’il n’y a eu aucun rapport de vol de la part des propriétaires des adresses concernées. Cela pourrait être un utilisateur souhaitant réunir ses fonds en provenance de multiples wallets vers un nouveau.

Adresses potentielles de l’attaquant :

La liste des cryptomonnaies dérobées via cette faille est variée. Elle est confirmée par les victimes :

  • Bitcoin (BTC)
  • Ethereum (ETH)
  • Ripple (XRP)
  • Dogecoin (DOGE)
  • Solana (SOL)
  • Litecoin (LTC)
  • Bitcoin Cash (BCH)
  • Zcash (ZEC)

Il y a probablement de nombreux autres types de cryptomonnaies concernées à cause de la flexibilité de bx seed. Le coût de l’attaque est celui des investigations (identifier des wallets à cibler) et des frais de transaction pour effectuer le vol.

L’équipe de chercheurs évalue la borne basse des fonds dérobés via cette méthode à 900 000 dollars.

Motivations et limites de l’équipe Milk Sad

L’équipe de chercheurs a tenu à effectuer ces investigations pour deux principales raisons :

  • Évaluer les dommages causés par la vulnérabilité en termes financiers ;
  • Prouver que cette faille est bien réelle et constitue l’unique explication à la disparition des fonds des victimes.

L’équipe avait également l’espoir qu’en menant ses recherches, et dans le cas où des sommes substantielles pouvaient être à risque, elle pourrait en informer les propriétaires afin de les prévenir. Ce n’aurait pas été une tâche facile : elle disposait de plusieurs options :

  • Relayer un message d’alerte via une plateforme de change centralisée que le propriétaire aurait utilisé pour effectuer des dépôts directs ;
  • Trouver des adresses dont le propriétaire est publiquement identifié ;
  • Utiliser d’autres canaux de communication potentiels.

Fort heureusement, l’équipe n’a pas identifié de fonds à risque, en se fixant un plancher de 5000 dollars. L’analyse concernait tous les wallets générés avec les versions 3.x de bx. Elle n’est bien entendu pas complète : des wallets avec des chemins de dérivation un peu différents pourraient être concernés. De même, certains portefeuilles utilisant des phrases BIP39 additionnelles pourraient rendre l’identification plus difficile.

La team Milk Sad tient également à formuler trois points très importants :

  • Les deux victimes originelles ont pu récupérer une petite portion de leurs fonds, qui n’avaient pas encore été volés, en utilisant leur configuration pré-vol.
  • L’équipe n’a pas déplacé de fonds depuis les wallets des victimes inconnues, et ce pour toutes les cryptomonnaies concernées.
  • L’équipe n’a aucun rapport de près ou de loin avec l’attaquant.

L’identité du pirate est donc à ce jour inconnue.

Prévenir un mal inguérissable ?

L’équipe tient à insister sur un point important : bien que ses membres soient tous bien intentionnés, déplacer des fonds ne leur appartenant pas afin de prévenir un vol est un cauchemar du point de vue légal. Et ce, même dans des circonstances « idéales » : une victime unique, une preuve claire de la propriété des fonds, des identités bien établies de chaque côté, une juridiction unique, et des moyens de communication privée bien établis. Une telle décision comporte des risques.

Ainsi, dans sa situation, et avec des circonstances bien moins favorables, l’équipe Milk Sad opta pour le statu quo. À titre d’illustration, elle donne le scénario suivant aux chevaliers blancs potentiels. Étant donné qu’un attaquant peut recalculer les clés privées du propriétaire d’un wallet affecté, cela annule la validité de toute signature effectuée avec ces dernières. Dans le cas où l’on décide de déplacer les fonds vers un wallet que l’on contrôle, comment alors s’assurer qu’une personne prétendant être le propriétaire du portefeuille original est la bonne ? C’est particulièrement vrai lorsqu’il n’y a aucune entité centralisée (telle une plateforme de change) pour appuyer ses dires et fournir des preuves circonstancielles. De plus, ce problème n’inclut pas la mise en conformité avec les lois anti-blanchiment (AML) ou fiscales.

L’équipe Milk Sad s’est donc contenté de fournir des explications et des outils permettant de vérifier si son wallet est concerné : lookup service.

Implémentation de l’outil de recherche

L’équipe s’est focalisée sur les formats d’adresses et chemins de dérivation aux standards BIP44, BIP49 et BIP84. Il s’agit de trouver les portefeuilles Bitcoin générés par bx seed | bx mnemonic-new via les versions 3.x de bx, avec les longueurs de phrases mnémoniques et configurations BIP39 classiques.

Cela couvre donc :

  • 128 bits = 12 mots, bx seed -b 128
  • 192 bits = 18 mots, bx seed -b 192 (par défaut)
  • 256 bits = 24 mots, bx seed -b 256

Elle a utilisé une liste publique d’adresses historiquement observées par le réseau Bitcoin. Puis elle a utilisé un filtre de Bloom, avec un taux de faux positifs très bas sur l’ensemble de données. Cela lui a permis d’éliminer rapidement de nombreux portefeuilles candidats inutilisés, et évité d’effectuer des recherches coûteuses via un nœud complet Bitcoin.

Seuls les portefeuilles où la première adresse a été utilisée furent pris en considération. De même, seuls les portefeuilles utilisant les chemins de dérivation standard BIP44, BIP49, et BIP84 sont pris en compte.

Les résultats

Au total, elle a découvert 2600 portefeuilles distincts et actifs basés sur la mauvaise entropie de bx seed.

La majorité de ces wallets (2550) présentent un usage similaire : de petits dépôts effectués autour de la même date en 2018. Bien qu’elle n’en soit pas sûre, la team Milk Sad pense qu’il s’agit du résultat de l’utilisation automatique de bx, et ces portefeuilles pourraient appartenir au même propriétaire. Ils sont tous dans l’ensemble de sortie de 256 bits et les adresses sont de type BIP49 (avec le préfixe « 3« ) ce qui leur donne un signe distinctif.

En excluant cet ensemble, l’équipe a trouvé moins de 50 portefeuilles où l’utilisation ressemble fortement à l’activité d’un propriétaire humain. Cela reste incomplet.

Dans cet ensemble de portefeuilles, elle a pu découvrir et identifier les deux portefeuilles des victimes initiales, ce qui a conforté les chercheurs dans l’idée que leur explication pour le vol était correcte.

Bien qu’une grande partie de ces wallets ne comportaient pas de fonds avant 2023, et ne pouvaient donc pas être volés, elle les considère tout de même affectés :

  • N’importe quel dépôt court le risque immédiat d’être subtilisé via une méthode automatisée ;
  • L’accès aux clés privées sous-jacentes permet de reconstruire toutes les adresses dérivées et ce pour toutes les cryptomonnaies compatibles. Cela les relie donc à toutes les actions effectuées précédemment par le propriétaire.

Autres victimes

L’équipe a pu identifier d’autres victimes, comme l’utilisateur u/0n0t0le sur Reddit. Ce dernier a pu toutefois déplacer 1,05 BTC que les attaquants n’avaient pas trouvé.

Les phrases mnémoniques BIP39 ne fournissent pas d’indication claire quant au logiciel utilisé pour les générer. Comme ce format est conçu pour être importable et exportable sur d’autres logiciels, il est possible que les utilisateurs ne se rappellent tout simplement pas quand un portefeuille donné à été créé, ce qui rend la recherche plus difficile.

Chronologie de la divulgation

DateEvent
2022-11-17Ledger Donjon divulgue la vulnérabilité de Trust Wallet à Binance
2022-11-21Trust Wallet code un patch sur GitHub qui supprime l’utilisation de Mersenne Twister
2022-11-21+Trust Wallet notifie les utilisateurs affectés par la vulnérabilité
2022-12-?Exploitation on-chain des wallets Trust Wallet (selon leur équipe)
2023-03-?Deuxième exploitation on-chain (selon l’équipe)
2023-04-22Trust Wallet divulgue publiquement la faille
2023-04-25Ledger Donjon publie leur papier sur la vulnérabilité de Trust Wallet
2023-05-03Première exploitation potentielle des wallets bx 3.x
2023-07-12Principale exploitation des walletbx
2023-07-21Découverte de la vulnérabilité bx durant l’analyse de l’incident
2023-07-22Envoi de l’email initial pour établir la communication avec l’équipe Libbitcoin
2023-07-25Première réponse, indiquant qu’ils sont trop occupés pour prendre contact
2023-07-25Envoi du contexte sans le détails de la vulnérabilité à la team Libbitcoin, demande de suivi
2023-08-03Envoi des détails techniques de la vulnérabilité et du contexte de la divulgation à la Libbitcoin team
2023-08-03Réponse de la Libbitcoin team, indiquant qu’ils ne considèrent pas cela un bug
2023-08-04Demande de CVE pour la vulnérabilité bx seed dans les versions 3.x depuis MITRE
2023-08-05Réponse de la Libbitcoin team, détaillant pourquoi ils ne considèrent toujours pas cela comme un bug
2023-08-07MITRE assigne le CVE pour le problème de bx 3.x
2023-08-08Publication de l’article

La réponse des développeurs de Libbitcoin

L’équipe Libbitcoin a tout d’abord contesté la véracité de la divulgation. Puis elle a considéré que la commande bx seed n’avait jamais dû être utilisée par un utilisateur de bx, car son manque de sûreté serait suffisamment documenté.

L’équipe Milk Sad est en désaccord avec cette affirmation et fournit la chronologie et les ressources suivantes :

DateInformation
21/07/2013Ajout d’une commande newseed pour la génération d’entropie sur le prédécesseur de Libbitcoin Explorer
16/10/2014Première page de documentation sur le wiki de Libbitcoin Explorer pour bx seed
14/12/2014Première release de Libbitcoin Explorer (bx) version 2.0.0
21/12/2015Libbitcoin Explorer (bx) release 2.2.0
10/02/2017Libbitcoin Explorer (bx) release 2.3.0
19/01/2015Un membre de l’équipe Libbitcoin ajoute et met à jour les suggestions d’utilisation de bx seed dans “Mastering Bitcoin”
21/10/2016L’équipe Libbitcoin change la génération de seed vers Mersenne Twister via PR#559commit
08/03/2017Libbitcoin Explorer (bx) 3.0.0, inclut PR#559
29/08/2019Libbitcoin Explorer (bx) 3.6.0, dernière release officielle
02/05/2021La commande bx seed est renommée bx entropy sur GitHub, toujours basée sur Mersenne Twister, PR#62812

L’équipe Milk Sad n’a relevé qu’un seul avertissement au sujet de bx seed sur le wiki :

WARNING: Pseudorandom seeding can introduce cryptographic weakness into your keys. This command is provided as a convenience.

Documentation Libbitcoin Explorer

Elle insiste sur la faiblesse de l’expression « peut introduire » qui ne reflète pas la réalité de la faille.

Dans l’appendice de Mastering Bitcoin (A. Antonopoulos), la description de la commande écrite par les membres de Libbitcoin est la suivante :

Generate a random “seed” value using the seed command, which uses the operating system’s random number generator. Pass the seed to the ec-new command […] $ bx seed | bx ec-new > private_key[...]

De même, au chapitre 4 du même ouvrage, on trouve :

You can also use the Bitcoin Explorer command-line tool (see [appdx_bx]) to generate and display private keys with the commands seed, ec-new, and ec-to-wif: $ bx seed | bx ec-new | bx ec-to-wif[...]

Les auteurs de l’ouvrage en sont informé et feront les modifications nécessaires.

De plus, la documentation de Libbitcoin suivante inclut bx seed :

PageCommandeDernière modification
wiki landing pagebx seed | bx ec-new | bx ec-to-public | bx ec-to-address07/2018
bx mnemonic-new documentationbx seed -b 128 | bx mnemonic-new04/2017
bx hd-new documentationbx seed | bx hd-new04/2022
Random Numbersbx seed -b 25607/2018

Aucun de ces exemples ne comprend d’avertissement quant à l’insécurité des wallets générés ainsi.

Quelles leçons tirer de l’affaire Milk Sad ?

Grâce à l’excellent travail de l’équipe de chercheurs, on peut tirer 3 points essentiels :

  • Utiliser des passphrases BIP39 pour ses wallets, idéalement complexes, et basées sur l’entropie d’une source différente ;
  • Ne faire confiance qu’à des logiciels longuement et minutieusement audités lors de la création de ses wallets ;
  • Documenter précisément toutes les étapes de la génération de ses wallets pour le futur, cela pourrait s’avérer important.

L’équipe Milk Sad a pu découvrir une faille d’ampleur dans la librairie bx, ayant causé au moins 900 000 dollars de dégâts. Elle compte désormais effectuer des recherches sur le comportement des versions 2.x de bx. La page web dédiée sera mise à jour en fonction. Les chercheurs n’ont reçu aucune récompense pour leurs travaux et ne le souhaitent pas.

Ils se contentent d’appeler à utiliser les services commerciaux des différents membres :

  • Distrust, LLC
    • Infosec firm focusing on high risk clients
    • Pentesting, threat modeling, hands-on security engineering
    • Full stack security evaluations and advisory retainer contracts
    • Covers: Shane Engelman, Anton Livaja, Ryan Heywood, and Lance Vick
  • Christian Reitter
    • Freelance InfoSec Consultant
    • Pentesting, Code Audits, Security Research, Fuzzing
  • Heiko Schaefer
    • Focus on OpenPGP: OpenPGP CA, Sequoia PGP, OpenPGP on HSM devices (OpenPGP card, PKCS #11, PIV)

L’équipe Milk Sad

Morgan Phuc

Cofounder @ 8Decimals &amp; Partner @ Node Guardians - Making crypto great again - Journal du Coin / BitConseil