Comment j'ai retrouvé mon mot de passe KeePass !

Posté le Fri 21 December 2018 dans Réflexions
Temps de lecture estimé : 9 minute(s).

Contexte

J'utilise Dropbox. C'est facile, c'est pratique, et ça marche bien. Je suis cependant prudent de nature, et je ne veux pas stocker mes données personnelles en clair sur les serveurs de Dropbox aux États-Unis, car je ne ne leur fais tout simplement pas confiance. Une fuite de données est bien vite arrivée. En fait, une fuite de données est déjà arrivée...

J'utilise donc un outil qui s'appelle BoxCryptor pour chiffrer mes données avant de les stocker dans le cloud. Pour chiffrer mes données, il faut un mot de passe, que nous appellerons M1. J'en ai généré un bien long avec plein de caractères spéciaux, et pour ne pas avoir besoin de m'en rappeler, je l'ai stocké dans un fichier KeePass. Parce que KeePass c'est facile, c'est pratique, et ça marche bien.

Ce fichier KeePass est lui-même protégé par un mot de passe, que nous appellerons M2. Même s'il a une sécurité qui me semble suffisante, M2 est bien moins complexe que M1, car je veux pouvoir m'en rappeler. Il s'agit donc d'un mot de passe qu'on pourrait qualifier de standard, contenant une chaîne de caractères avec des majuscules et des minuscules, des chiffres et des caractères spéciaux.

Bon, jusque là tout va pour le mieux dans le meilleur des mondes...

Panique

Il y a quelques jours, j'ai eu besoin de reconfigurer mon accès à BoxCryptor. J'ai donc souhaité récupérer le mot de passe M1 qui se trouve dans mon fichier KeePass. Pour cela il faut ouvrir le fichier KeePass... et donc se souvenir du mot de passe M2. Impossible...

Impossible. Impossible ? Impossible !!

Là, j'étais dans le merde. On ne peut pas vraiment dire autrement. Autant je fais attention à ne rien laisser de sensible dans mon téléphone, car on me l'a déjà volé, autant tous mes documents importants sont stockés dans BoxCryptor. Mes projets. Mes présentations. Mes impôts. Mes CVs. Mes justificatifs. Mes courriers. Mes sauvegardes de ce blog. Mes souvenirs aussi. Mince alors... comment je vais faire ?

J'ai essayé de retrouver ce p... de mot de passe pendant plusieurs jours. Il n'y avait pas moyen. J'ai alors hésité entre tomber dans la déprime et pleurer, sombrer dans la colère et jeter mon ordi par la fenêtre, ou laisser tomber, déménager et recommencer une nouvelle vie quelque-part en Amérique du sud.

Geek attitude

Après 3 jours de panique, d'énervement et d'essais infructueux, j'ai décidé d'envisager une autre solution. J'ai enfilé mon chapeau blanc, et je me suis attaqué au craquage de mon propre fichier KeePass. Et en fait ce fut beaucoup plus facile que je ne le pensais...

Alors non, je préfère le dire tout de suite, je n'ai pas réussi à craquer le chiffrement AES-256. Sinon je serais déjà quelque part dans les îles, les pieds dans l'eau et le compte en banque bien rempli... Mais quand on essaie de craquer ses propres fichiers, on a un gros avantage : on sait à peu près à quoi devrait ressembler le mot de passe qu'on cherche...

C'était mon mot de passe après tout, même si je l'avais oublié. Je savais donc à peu près de quels caractères il était constitué, même si je ne me rappelais plus trop dans quel ordre je les avais mis, ni s'ils étaient en majuscules ou en minuscules. Je savais les différentes suites de chiffres qu'il pouvait contenir, même si je ne savais plus si elles étaient au début, au milieu ou à la fin. Je savais les caractères spéciaux que j'étais le plus susceptible d'avoir utilisés. Bref, d'après pas mal de pistes tout de même...

Analyse

Alors réfléchissons... Mon mot de passe devait avoir la structure suivante :

Note : Ceci est un mot de passe inventé. Toute ressemblance avec un mot de passe existant ou ayant existé ne serait que pure coïncidence...

Voyons...

  • Je connaissais la structure de mon mot de passe.
  • Je savais quels caractères spéciaux j'avais pu utiliser.
  • Je savais quels mots j'avais pu utiliser.
  • Je savais quels chiffres j'avais pu utiliser.

Hum... Et bien oui, avec l'aide de mon pote John, je pouvais le craquer ! Pour cela, j'allais devoir :

  • Générer un dictionnaire de mots de passe
  • Lancer une attaque brute-force

Allez, c'est parti !

Génération d'un dictionnaire

J'ai donc entamé l'écriture d'un petit code python prenant tout ceci en compte. Celui-ci m'a permis de générer un fichier contenant tous les mots de passe possibles. Pour être plus clair, ce fichier est sensé contenir tous mes mots de passe au milieu de milliers d'autres qui leur ressemblent beaucoup. Mon dieu, il ne faut JAMAIS que quelqu'un tombe là-dessus...

Pourquoi du python ? En fait il y a quelques temps j'avais déjà fait un code qui générait des fichiers de mots de passe en C#. Mais pour changer j'avais envie de faire ça en python. Alors je répondrais : et pourquoi pas en python ? :-)

Bref, voici le code qui m'a permis de générer ce fichier. Je l'ai un peu raccourci, mais l'idée y est :

import itertools

specialchars = '&"#]|\)-_'

def gen(l, n):
    yield from itertools.product(*([l] * n))

def writeline(file, line):
    file.write(line + '\n')

def addsuffixes(p):
    for i in range(4):
        for suffix in gen(specialchars, i):
            passwd = p + ''.join(suffix)
            writeline(file, passwd)

if __name__ == '__main__':
    file = open('password.list', 'w')
    try:
        for i in range(3):
            for txt in ['password', 'DrowssaP']:
                for num in ['123', '456']:
                    for prefix in gen(specialchars, i):
                        passwd = ''.join(prefix) + txt + num
                        writeline(file, passwd)
                        addsuffixes(passwd)

    finally:
        file.close()

23 lignes de code. C'est beau le python quand même... Exécutons ce script :

python3 generate.py

Ce code permet d'obtenir un fichier contenant 298 844 mots de passe. Le contenu ressemble à ceci :

password456__]
password456__|
password456__\
password456__)
password456__-
password456___
DrowssaP123
DrowssaP123
DrowssaP123&
DrowssaP123#
DrowssaP123]
DrowssaP123|
DrowssaP123\
DrowssaP123)
DrowssaP123-
DrowssaP123_

Attaque brute-force

Une attaque brute-force (ou attaque au dictionnaire) est une attaque qui consiste à tester une à une toutes les possibilités. Dans mon cas, cela revient à prendre un à un tous les mots de passe du fichier généré, et à les essayer.

Il y a quelques mois, j'avais justement un peu joué avec un outil qui s'appelle John The Ripper. C'est un programme qui permet entre autres de faire du craquage de mots de passe en brute force. C'était exacement ce qu'il me fallait !

Étape 1 : installer les bons outils

Je ne vais pas m'attarder là-dessus, ce n'est pas le but. A titre d'exeple, voici les commandes que j'ai utilisées pour installer les dépendances, cloner le repository GitHub et compiler john :

sudo apt-get -y install build-essential libssl-dev git zlib1g-dev
git clone git://github.com/magnumripper/JohnTheRipper
cd JohnTheRipper/src
./configure
make -s clean && make -sj4

Étape 2 : extraire le hash du mot de passe

Afin de pouvoir faire une attaque brute-force, il nous faut le hash du mot de passe. Celui-ci peut facilement être extrait du fichier keepass avec la commande keepass2john. Si le fichier KeePass à craquer se nomme Database.kdbx, il suffit alors d'executer la commande suivante :

JohnTheRipper/run/keepass2john Database.kdbx > kee.hash

Cela crée un fichier kee.hash contenant le hash qu'il va falloir craquer.

Étape 3 : lancer l'attaque

On passe en paramètres la liste de mots de passe ainsi que le fichier contenant le hash :

JohnTheRipper/run/john --wordlist=password.list kee.hash

Et maintenant, on attend tranquillement... en buvant un bière par exemple... ou en mangeant un morceau de chocolat... mais conseil d'ami : il vaut mieux éviter la bière au chocolat...

30 minutes plus tard...

Voici le résultat :

Using default input encoding: UTF-8
Loaded 1 password hash (KeePass [SHA256 AES 32/64 OpenSSL])
Cost 1 (iteration count) is 60000 for all loaded hashes
Cost 2 (version) is 2 for all loaded hashes
Cost 3 (algorithm [0=AES, 1=TwoFish, 2=ChaCha]) is 0 for all loaded hashes
Will run 2 OpenMP threads
Press 'q' or Ctrl-C to abort, almost any other key for status
0g 0:00:00:12 0.35% (ETA: 17:18:14) 0g/s 97.17p/s 97.17c/s 97.17C/s password456]"\..password456]#]
0g 0:00:06:02 11.31% (ETA: 17:14:15) 0g/s 98.43p/s 98.43c/s 98.43C/s &]password123]\"..&]password123]\_
0g 0:00:06:13 11.68% (ETA: 17:14:08) 0g/s 98.47p/s 98.47c/s 98.47C/s &|password123)_-..&|password123-&\
0g 0:00:06:22 11.97% (ETA: 17:14:04) 0g/s 98.45p/s 98.45c/s 98.45C/s &\password123-)|..&\password123--#
0g 0:00:10:36 20.35% (ETA: 17:12:59) 0g/s 98.14p/s 98.14c/s 98.14C/s |&password123&#]..|&password123&]"
0g 0:00:14:29 28.11% (ETA: 17:12:24) 0g/s 98.32p/s 98.32c/s 98.32C/s -"password123&_\..-"password123"&]
0g 0:00:26:45 53.30% (ETA: 17:11:05) 0g/s 99.74p/s 99.74c/s 99.74C/s ]-DrowssaP456\\..]-DrowssaP456)]
]-DrowssaP123#-_ (Database)
1g 0:00:29:05 DONE (2018-12-18 17:39) 0.000555g/s 107.6p/s 107.6c/s 107.6C/s ]-DrowssaP123#-|..]-DrowssaP123#_#
Use the "--show" option to display all of the cracked passwords reliably
Session completed

En 30 petites minutes, mon mot de passe a été retrouvé !

Conclusion

La conclusion est toute simple : je suis super content, j'ai retrouvé mon mot de passe, et par la même occasion mon accès à mes données chiffrées !

Sinon, pour mettre ce résultat un peu plus en perspective, il ne faut pas oublier que je n'ai pas craqué n'importe quel mot de passe. J'ai craqué mon mot de passe, dont je connaissais la structure. Avec les bons outils, ce n'était donc pas si difficile.

Craquer un mot de passe qui n'est pas le sien est une autre histoire. Ce n'est techniquement pas beaucoup plus complexe : on a une liste de mots de passe et on les essaie un par un. Cependant, le nombre de tentatives à faire est beaucoup, beaucoup plus important.

C'est pour cette raison qu'une attaque brute-force fonctionnera sur les mots de passe simples, qui restent malheureusement très utilisés aujourd'hui. Pour assurer un maximum de sécurité, chaque mot de passe devrait être unique (n'être utilisé que pour un seul compte), et devrait être suffisamment long.

Mais je ne vais pas parler maintenant de comment choisir un bon mot de passe. Je laisse ce sujet pour une prochaine fois...