Hack my cipher

J’offre 43.16 DUĞ1 à qui découvre mon mnemonic membre ĞDev.

[post édité suite à cette discution]

Il est là: https://cloud.axiom-team.fr/s/Bkb9zGKTtQpz3y8/download/encrypted_mnemonic.tar.gz

En indice:

@Spencer wanna play again ? :slight_smile:

2 Likes

Pourquoi utiliser CBC plutôt que AES-GCM ? Il faut vraiment savoir ce qu’on fait quand on utilise les modes opératoires à la main plutôt qu’un AEAD standardisé.

J’essaie d’ajouter ton format à g1force.

2 Likes

Tu as raison, j’ai donc modifié pour AES-GCM .

Voici donc la nouvelle version du code utilisé: lib/services/wallet_service.dart · fc3d8a7892cd097562747d540629fdd8c9589111 · clients / Ğazelle · GitLab

Et voici le même mnemonic chiffré avec AES-GCM: https://cloud.axiom-team.fr/s/sxiF2LoR8ep6sFG/download/encrypted_mnemonic.tar.gz

Je rappel que c’est mon mnemonic avec dérivation membre GDev, et que je suis smith et dans le comité technique.
L’enjeux est donc de taille.

3 Likes

Pourrais-tu donner les valeurs des champs iv et ciphertext ? Je n’ai pas trouvé de bibliothèque Rust appropriée ni de spécification de ce format de fichier… (il y en a pour le format de fichier Hive de Microsoft, la base de données Hive d’Apache, encore d’autres Hive et Isar mais pas ceux de Dart)

Edit: et des valeurs avec un mnemonic (pour un autre mnemonic bien sûr), pour qu’on puisse tester qu’on a bien les bons paramètres de crypto, ce serait pratique.

3 Likes

J’aimerais bien jouer à ça, mais pas sur mes heures de boulot pendant lesquelles je pense que c’est hors sujet et pas en dehors de mes heures de boulot pendant lesquelles je décolle de l’écran.

2 Likes
Mnemonic de test : "abandon amount liar amount expire adjust cage candy arch gather drum buyer"
code pin : 1234
IV: [127, 126, 63, 250, 202, 36, 9, 248, 210, 125, 100, 13, 49, 167, 186, 139]
Ciphertext: rpq8vMHott/1RNighX+yezpRuiO0Wpnf0jZ/VtrskEEgr5JQq1ylpYuSixBmyL2Mrx0CbcThAJye1po+26xLhzPQGlycM5m6Sft/LxhOCADS43DElmt/lwh1

Ce mnemonic n’est chiffré qu’avec un code pin à 4 chiffres, donc ça devrait être super simple à brute forcer :slight_smile:
Et tous le code de chiffrement/déchiffrement est open source.
Tu devrais pouvoir prendre le contrôle de mon compte membre gdev facilement.

Cela dit si tu y arrives ainsi, je m’éclate une couille.

2 Likes

Tu utilises AES128 avec une clé de 256 bits, ce qui n’est pas standard donc ça dépend de la cuisine interne de la lib Dart (troncature, hash…?). Je pense qu’utiliser AES256 avec une clé de 256 (ou AES128 avec une clé de 128) faciliterait les autres implémentations et te permettrait de changer de bibliothèque à l’avenir si nécessaire.

En tout cas pour l’instant je n’arrive pas à reproduire le chiffrement, même en regardant le code de la lib Dart.

Non je génère bien une clé de 256 bits avec Argon:

Future<Argon2BytesGenerator> _createKeyDerivator(
    String password, Uint8List salt) async {
  final argon2 = Argon2BytesGenerator();
  argon2.init(
    Argon2Parameters(
      Argon2Parameters.ARGON2_id,
      salt,
      secret: utf8.encode(password),
      desiredKeyLength: 32,
      iterations: 3,
      memoryPowerOf2: 16,
      lanes: 4,
    ),
  );
  return argon2;
}

desiredKeyLength: 32,

J’aurais pu être beaucoup sévère sur les paramètres de Argon avec par exemple:

  iterations: 10,
  memoryPowerOf2: 18,
  lanes: 8

Mais je veux que cet algo soit efficace sur smartphone, donc je ne veux pas trop pousser.

Mon implémentation est tout à fait standard.


En tout cas, temps que personne n’aura le contrôle sur mon compte membre gdev ici, je proposerai des clients v2s avec chiffrement par code PIN à 4 chiffres entré par l’utilisateur.

Et je garderai mes deux couilles aussi par la même occasion…

Le problème est d’utiliser AES128 avec une clé de 256 bits, alors qu’il est fait pour des clés de 256 bits. Pointy Castle accepte une clé trop longue mais ne spécifie pas clairement comment il la réduit.

Edit: sinon en extrapolant mes tests avec ces paramètres de Argon2, je devrais pouvoir tester 10 000 codes PIN en moins d’une heure sur mon ordi.

J’utilise bien AES256, je ne sais pas ce qui te fait penser le contraire.

Peut être la taille du tag d’authentification qui elle est de 128, (macSize), mais c’est une valeur recommandé pour GCM.

Recommendation for Block Cipher Modes of Operation: Galois/Counter Mode (GCM) and GMAC

• An authentication tag, or tag, for short, denoted T.

The bit length of the tag, denoted t, is a security parameter, as discussed in Appendix B. In general, t may be any one of the following five values: 128, 120, 112, 104, or 96. For certain applications, t may be 64 or 32, but the use of these two tag lengths constrains the length of the input data and the lifetime of the key

Source: https://nvlpubs.nist.gov/nistpubs/legacy/sp/nistspecialpublication800-38d.pdf


Tu devrais pouvoir dépouiller mon compte en quelques minutes, j’ai hâte de voir ça !

Ah oui pardon c’est bien le macSize 128 qui m’a enduit d’erreur.

Je n’arrive pas encore à reproduire le déchiffrement avec le PIN de test.

1 Like

Ok comme c’est important pour moi, j’ai fais des tests aussi de mon côté, que j’ai poussé dans ce dépot:

Il s’agit de 3 outils CLI, qui font strictement la même chose, en python, en dart, et en rust.

Cette commande prouve que les implémentations Dart et Python sont interopérable:

./test.sh python
Mnemonic original : abandon amount liar amount expire adjust cage candy arch gather drum buyer
Mnemonic décrypté : abandon amount liar amount expire adjust cage candy arch gather drum buyer
Le mnemonic décrypté correspond à l'original.

Cependant, l’implémentation Rust n’est pas compatible ni avec Dart, ni avec Python, comme le démontre cette commande:

./test.sh rust
Unhandled exception:
Instance of 'InvalidCipherTextException'
#0      BaseAEADBlockCipher.validateMac (package:pointycastle/src/impl/base_aead_block_cipher.dart:69:7)
#1      GCMBlockCipher.doFinal (package:pointycastle/block/modes/gcm.dart:208:5)
#2      _decrypt (file:///home/poka/Bureau/brute/crypt.dart:76:17)
<asynchronous suspension>
#3      main (file:///home/poka/Bureau/brute/crypt.dart:42:23)

Ce que j’ai changé depuis mon l’implémentation Dart de base:

  • Passage du lengh de iv de 16 à 12 bytes de manière à être compatible avec le Nonce dans l’implémentation Rust
    final iv = _createRandomBytes(12);

  • Pas de secret passé aux paramètres argon, celui-ci était le code pin lui même utilisé dans le process de chiffrement, ça ne servait à rien et compliquait la compatibilité:
    // secret: utf8.encode(password),

Ce que je ne comprends pas:

  • Comment l’implémentation Dart et Python peuvent-elles être compatible alors que je ne définit pas de secret aux paramètres argon côté Dart mais que j’en définit côté python ? Côté python ce secret est required. Hors les spec indique qu’il est totalement optionnel. Je le rajouterai en définité mais pas de cette manière qui était innutile.
    Sachant que si je définit un secret côté Dart à partir du pin, alors ça ne fonctionne plus côté python, alors que c’est précisément ce qu’il fait … Très étrange.

  • Bah pourquoi mon implémentation Rust n’est pas compatible avec python et dart…


Vous pouvez testez facilement vous même, il suffit d’installer dart, python3 et rust.

1 Like

Mon code de déchiffrement en Rust est là, utilisant la même crate aes-gcm : src/modes/gazelle.rs · master · tools / Ğ1force · GitLab

Cette crate supporte d’autres tailles d’IV, il faut lui passer en paramètre de type générique.

Je pense que j’utilise argon2 correctement (avec les bons arguments par rapport à la version Dart) puisque je reproduis le hash donné en exemple dans un readme de la lib Dart argon2.

Peut-être que la lib Rust aes-gcm fait quelque chose de plus ou de moins, peut-être qu’il faudrait essayer avec d’autres, par exemple un binding OpenSSL. (pour s’assurer que ce n’est pas la faute de la lib qui serait bizarre)

1 Like

J’ai enfin une version interopérable entre Rust ↔ Dart ↔ Python :slight_smile:

./test.sh rust
Mnemonic original : abandon amount liar amount expire adjust cage candy arch gather drum buyer
Mnemonic décrypté : abandon amount liar amount expire adjust cage candy arch gather drum buyer
Le mnemonic décrypté correspond à l'original.

./test.sh python
Mnemonic original : abandon amount liar amount expire adjust cage candy arch gather drum buyer
Mnemonic décrypté : abandon amount liar amount expire adjust cage candy arch gather drum buyer
Le mnemonic décrypté correspond à l'original.

par contre j’ai gardé un nonce de 12, car ça ne semble pas poser problème de sécurité à ce que je comprends. Et j’ai pas trouvé comment changé la taille de ce nonce accepté côté rust.

Je viens de pousser (plouf).

2 Likes

@tuxmain Voilà donc à nouveau le fichier .hive avec un nonce de 12, toujours mon compte membre gdev: https://cloud.axiom-team.fr/s/Bkb9zGKTtQpz3y8/download/encrypted_mnemonic.tar.gz

Pour vérifier le format, voici les nouvelles données de test:

Mnemonic de test : "abandon amount liar amount expire adjust cage candy arch gather drum buyer"
code pin : 1234
IV: [169, 224, 176, 114, 58, 240, 180, 61, 152, 171, 142, 95]
Ciphertext: YnFb9sH+Qx/HbKM9pBXunFacoJFOyI/PbzP4UxZUaPRfahXDTlD1Se1M1Nya5pgQaSC1sRnR6Vif6sGK2qebhovU6x8ZwxA0atk9OH9yyQQL72iO9w2ykHEh

Et en prime, le script Rust qui le test directement, certifié fonctionnel: decryp_cipher.rs · master · poka / crypt-argon-test · GitLab

Il suffit de changer le path dans le Cargo.toml puis cargo run pour le tester:

$ cargo run
   Compiling crypt v0.1.0 (/home/poka/Bureau/brute)
    Finished dev [unoptimized + debuginfo] target(s) in 0.26s
     Running `target/debug/crypt`
Decrypted mnemonic: abandon amount liar amount expire adjust cage candy arch gather drum buyer
Decrypted mnemonic matches the expected mnemonic.

Je met à jour le poste initial.


Du coup maintenant @tuxmain il n’y a plus d’excuse pour ne pas me dépouiller :slight_smile:
Mais je persiste et signe que tu n’y arrivera pas, malgré que ce soit un code à 4 chiffres et que tu as le code Rust pour déchiffrer le format.

Ensuite, une fois que tu aura compris que tu ne pourra pas le déchiffrer malgré tout ça, le nouveau format que je pense mettre en prod sera celui-ci: https://cloud.axiom-team.fr/s/tecXZQdLB98MbHy/download/encrypted_mnemonic_secure.tar.gz

Voir la fin de ce commit pour voir la petite différence.

Avec ça c’est encore plus claire je pense.
Mais étonne moi, je peux préparer une annonce gchange pour rechercher un casse noix.

Design Nutcracker GIF by Rochester Institute of Technology

Ça marche, j’obtiens le mnemonic avec 1234. Ça met 5 minutes avec une répartition aléatoire de la recherche.

Je vais essayer avec ton compte si j’arrive à parser le fichier.

1 Like

Wallace And Gromit Lol GIF by Aardman Animations

1 Like

Si tu me donnes les valeurs contenues dans le fichier je vais essayer le bruteforce.

Je ne vais pas essayer de faire la rétro-ingénierie de Hive (ils ont bien fait une crate Rust mais non documentée), ni installer le SDK de Google… je ne sais même pas si ce format de fichier est géré par Hive ou Isar.

J’ai l’impression de ne pas avoir saisi tout l’énoncé, ou bien il me manque des bouts …
Où se trouve la clé de chiffrement ? Si elle est en local, c’est foutu, il faut ton appareil pour pouvoir déchiffrer, peu importe la longueur de ton code.
Ou alors j’ai tout simplement rien compris au challenge :smiley:

Plutôt cette option ><

Il s’agit de mener une attaque par force brute pour retrouver le code à 4 chiffres qui a servi à chiffrer le sercret :wink: