Hacker un fichier 7z protégé par un mot de passe en Python

hacker 7zip python

Hacker un fichier 7z protégé par un mot de passe en Python

Hacker un fichier compressé au format 7z, protégé par un mot de passe, en Python est en théorie faisable. Mais en pratique, vous allez voir que pour une personne lambda, c’est rarement faisable…

hack 7z python

Hacker un fichier 7z en Python par force brute: le principe

Introduction pour hacker un fichier 7z en Python

A priori, le fichier 7z que l’on doit hacker est protégé par un mot de passe dont on ignore tout : longueur, alphabet utilisé (c’est-à-dire caractères qui apparaissent dans le mot de passe). Bref, c’est la merde !

En effet, il faut se baser sur un alphabet le plus large possible!

En Python, on va donc se baser sur l’alphabet suivant:

alphabet = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*()-_+=~`[]{}:;\"'<>,.?/\\"

Les lettres majuscules et minuscules de l’alphabet latin, les chiffres et les caractères spéciaux disponibles au clavier (on va supposer que c’est suffisant), soit 93 caractères… Rien que ça !

C’est quoi la force brute ?

La force brute consiste à essayer un à un tous les “mots” possibles que l’on peut former à partir de notre alphabet… Et il y en a un bon paquet!

May the brute force be with you !

Obi-wan Kenobi

Déjà, il faut tester tous les mots de 1 lettre, soit 93 possibilités.

Ensuite, il faut tester tous les mots de 2 lettres que l’on peut former sur un alphabet de 93 lettres, soit 93² = 8649 possibilités. Il s’agit ici de produits cartésiens (on dit aussi une 2-liste sur un ensemble de 93 éléments).

Nombre de produits cartésiens: interlude mathématiques

Quand on dispose d’un alphabet de n lettres et que l’on souhaite savoir combien de mots de p lettres on peut faire sur cet alphabet, on utilise la formule:$$n^p.$$

Hacker un fichier 7z en Python par force brute: la pratique

Dans la pratique, on ne sait pas combien de lettres comporte le mot de passe. Il faut donc tous les tester.

Notre programme doit donc partir d’une longueur de 2 (on suppose que le mot de passe n’est pas composé uniquement d’un caractère) et tester toutes les 2-listes. Si le mot de passe n’a pas été trouvé, on passe à toutes les 3-listes. On continue ainsi jusqu’à trouver le mot de passe.

Un programme

On va se servir du module itertools et de sa fonction product. On va aussi se servir du module py7zr qui permet, entre autre, de décompresser un fichier 7z.

from py7zr import SevenZipFile
from itertools import product

alphabet = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*()-_+=~`[]{}:;\"'<>,.?/\\"

longueur = 2
find = False
    
while find == False:
   liste = [ ''.join(i) for i in list(product(alphabet, repeat=longueur)) ]
    for pwd in liste:
        try:
            archive = SevenZipFile(r'monfichier.7z', mode='r', password = pwd)
            archive.extractall()
            archive.close()
            find = True
        except:
            pass
    
    longueur += 1

J’utilise ici un booléen find (ligne 7) qui est nativement False et qui va me servir pour indiquer si le mot de passe est trouvé par la suite (auquel cas, il passera à True.

Je créé donc une boucle conditionnelle while (ligne 9) sur ce booléen. Tant que le mot de passe n’est pas trouvé, j’exécute ce qui suit.

Je définis une variable liste qui est la liste de toutes les produits cartésiens possibles de taille longueur (qui s’incrémentera au fil des essais) sur notre alphabet.

Par défaut, le résultat est une liste de couple. Par exemple,

>>> list(product("ABCD", repeat=2))
[('A', 'A'), ('A', 'B'), ('A', 'C'), ('A', 'D'), ('B', 'A'), ('B', 'B'), ('B', 'C'), ('B', 'D'), ('C', 'A'), ('C', 'B'), ('C', 'C'), ('C', 'D'), ('D', 'A'), ('D', 'B'), ('D', 'C'), ('D', 'D')]

Ce n’est pas ce que l’on veut : il faut une liste de mots, donc je construis par compréhension ma liste en utilisant la méthode join, qui permet de transformer mes couples (‘x’,’y’) en mots ‘xy’.

>>> [''.join(i) for i in list(product('ABCD', repeat=2))]
['AA', 'AB', 'AC', 'AD', 'BA', 'BB', 'BC', 'BD', 'CA', 'CB', 'CC', 'CD', 'DA', 'DB', 'DC', 'DD']

Une fois cette liste de mots possibles construite, je la parcours (ligne 11) avec une boucle.

Dans cette boucle, il me faut gérer les exceptions. En effet, je compte utiliser la fonction de décompression mais si le mot de passe est faux, cela va engendrer une erreur. Il faut donc que je gère cette erreur. C’est la raison de la présence du try (ligne 12).

J’essaie (try) de décompresser (lignes 13, 14 et 15) mais si ça ne fonctionne pas, alors la clause après except (ligne 17) est exécutée. En l’occurrence, pass désigne une opération nulle, c’est-à-dire qu’il ne se passe rien.

Que faut le programme ?

Pas grand-chose à vrai dire ! En effet, il faut compter 0,5 seconde pour un test de décompression. Ainsi, si le mot de passe ne comporte que 2 caractères, il faut 8649 possibilités × 0,5 secondes, soit à peu près 4324 secondes (donc environ 1h12min) au pire des cas pour être assuré·e·s de trouver le mot de passe.

Mais nous ne sommes pas au pays des bisounours et les mots de passe font rarement 2 caractères… À l’aide de XCAS, et du code suivant:

s:=0;for (p:=2;p<15;p++) {s:=s+perm(93,p);}

on obtient un total de 3659791978821893009272558749 possibilités en testant des mots de passe de 2 à 14 caractères, soit \(1,2\times10^{20}\) années. Vous avez le temps ?

Une année compte 31536000 secondes. On peut donc effectuer 63072000 tests en une année, soit des tests pour un mot de passe de 2 à 3 lettres seulement!

Mais s’il n’y avait que ça ! Car nous sommes quand-même limités par la mémoire de l’ordinateur… La variable liste doit contenir toutes les permutations… et il y en a beaucoup ! Beaucoup trop même ! Pour un ordinateur classique, la mémoire est très vite saturée.

À titre d’exemple, la liste des permutations à 4 caractères est impossible à construire sur mon ordinateur Processeur AMD Ryzen 5 3500U with Radeon Vega Mobile Gfx, 2100 MHz, 4 cœur(s), 8 processeur(s) logique(s) et 8 Go de RAM.

Hacker un fichier 7z en Python: une autre méthode

Pour contrer ce problème de mémoire, on pourrait construire non pas une liste mais un fichier contenant toutes les permutations.

from itertools import product

alphabet = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*()-_+=~`[]{}:;\"'<>,.?/\\"
F = open('permutations.txt' , 'w', encoding = 'utf8')

for p in range(2,15):
    for c in product(alphabet,repeat=p):
        ligne = ''.join(c) + '\n'
        F.write( ligne )
    
F.close()

C’est nettement mieux que le script précédent, mais il faut tout de même s’armer de patience pour ne serait-ce que créer le fichier contenant tous les mots de passe possibles de 2 à 14 caractères… et je ne vous raconte même pas la taille du bousin !

À titre d’exemple, en 1h30, j’ai obtenu un fichier de 32 Go… et aucun éditeur ne peut l’ouvrir tellement il est volumineux !

Conclusion

Vous l’aurez compris, hacker un mot de passe par force brut est dans la pratique totalement stupide car très souvent irréalisable (pour monsieur et madame tout le monde).

Bien entendu, il existe des machines bien plus performantes que mon ordinateur personnel et ses 8 Go de RAM sur lesquelles on pourrait éventuellement obtenir des résultats plus probants. Mais en général, il faut oublier de tenter de hacker un mot de passe en Python.

Les logiciels que l’on peut trouver ci et là sont sans doute écrit en C, donc plus légèrement rapides, mais il ne faut pas s’attendre à une vitesse bien supérieure.

La force brute est donc l’une des pires façons de hacker un mot de passe.

Si la cryptographie vous intéresse, vous pouvez jeter un coup d’œil à cet article (stéganographie), ou encore à cet article (chiffrement de Hill).

Stéphane Pasquet
Stéphane Pasquet

Laissez votre message