[Python] Passwort cracker for Zip-archives




Do you have a zip-file with a password and you can't remember it?

Then let's write a little tool, that tries to recover it.



Disclaimer:
"Cracking passwords and getting access to files of others without their permission is criminal! This blog post is only for illustrative purposes, to get an insight of how an hacker would probably do it, so you know how to protect yourself against it. This blog post is not meant to seduce you in any way to use it for criminal purposes! So i don't take any responsibility if you use it for criminal purposes, nor do i take any responsibility for the consequences which arise from it. Also, the authors of the linked pages are responsible for their sites and their content, so i don't take any responsibility for that."




So, that being said, let's begin.


The dictionary attack

There are several ways to crack a password, however we will discuss only one of them, the dictionary attack.


How does it work?

It's quite simple: We have a password list - the dictionary, with hundreds of thousands of common used passwords and we will try every single one of them to see, if it is the right one.
We don't have to write those lists on our own, there are several websites we can download them, one of them is: https://wiki.skullsecurity.org/Passwords.
If you don't want to download one from there, i have a small list, you can download from here: passwords.txt

The more passwords a password list contains, the more likely you will succeed. So it is essential to spend some time on them to prepare good lists.



Accessing the Zip-File


At first, let's write a function that accesses the zip-file and tries to open it:


1
2
3
4
5
6
7
def extract(zip, pwd):
    try:
        zip.extractall(pwd=str.encode(pwd))
        print("Success! Possible Password: " + pwd)
        return True
    except:
        return False


What does it do?

Let's look at it line by line:

def extract(zip, pwd):
"zip_file"  is our zip-file
"password" is the password with what we try to open the zip-file

zip.extractall(pwd=str.encode(pwd))
Here we call the method "extractall" on the given zip-file and passing the given password "pwd".
Because we get the password as string from the password list, but the function "extractall" expects a password in bytes format, we have to convert it to bytes via "str.encode()"

If the method "extractall" fails to open the zip-file due to a wrong password (what will be the case most of the time), the program would quit immediately.

To prevent that, we will wrap a try-except block around it:

1
2
3
4
try:
    zip.extractall(pwd=str.encode(pwd))
except:
    pass

The "try - except" statement tries to execute the try-block, if it fails, it will execute the except-block instead.

Now we are safe and our program won't quit as soon as it tries to open a zip-archive with a wrong password.

Does the try-block succeed however, we know, we have found the right password:
print("Success! Password: " + password)
So we can print it on the screen



Reading the password list



Now we need a function that reads the password file and returns it as a list:


1
2
3
4
5
6
7
def open_password_file(password_file_path):
    with open(password_file_path, "r") as password_file:
        passwords = []
        for line in password_file:
            password = line.strip("\n")
            passwords.append(password)
    return passwords


Let's have a look:

def open_password_file(password_file_path):
Here we pass the path as argument "password_file_path" to the function.


passwords = []
Now we create an empty list that will hold all the passwords.

with open(password_file_path, "r") as password_file:
Here we open the password file in read mode and give it the alias "password_file"


for line in password_file:
In a loop we go through each line of the password file...

password = line.strip("\n")
... and remove the line break. Also we store the result in the variable "password"

passwords.append(password)
Here we append the password to the list "passwords"


return passwords
After we have read all passwords from the file, we return the list.


Now we have a list that contains all passwords from the file.




Creating threads

In order to check multiple passwords at the same time, we will create a thread for each password that needs to be checked.

For that, we need to import the module that makes it a lot easier to deal with threads.


1
from multiprocessing.pool import ThreadPool



To simplify it even further, we will use our class from the post: [Python] Easy multithreading.


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
class MultiThread():
    __thread_pool = None

    @classmethod
    def begin(cls, max_threads):
        MultiThread.__thread_pool = ThreadPool(max_threads)

    @classmethod
    def end(cls):
        MultiThread.__thread_pool.close()
        MultiThread.__thread_pool.join()

    def __init__(self, target=None, args:tuple=()):
        self.__target = target
        self.__args = args

    def run(self):
        try:
            result = MultiThread.__thread_pool.apply_async(self.__target, args=self.__args)
            return result.get()
        except:
            pass





The dictionary attack


Now comes the fun part: writing a function, that performs the dictionary attack:


1
2
3
4
5
6
7
8
9
def dictionary_attack(zip_path, password_file_path):
    zipFile = zipfile.ZipFile(zip_path)
    passwords = open_password_file(password_file_path)
    MultiThread.begin(300)
    for pwd in passwords:
        t = MultiThread(target=extract, args=(zipFile, pwd))
        success = t.run()
        if success: return
    MultiThread.end()


Let's go through each line:

def dictionary_attack(zip_path, password_file_path):
Here we pass the path to our zip-file, and the path to our password file.

zipFile = zipfile.ZipFile(zip_path)
Here we accessing our zip-file and storing it's handle in "zipFile".


passwords = open_password_file(password_file_path)
Here we call our "open_password_file"-function to create a list of passwords and storing them in "passwords".


MultiThread.begin(300)
Here we initialize multithreading and limit the maximum of simultaneously running threaths to 300.

for pwd in passwords:
Now we go through each password of our list...

t = MultiThread(target=extract, args=(zipFile, pwd))
... and create a thread for each password. "Target" is the function the thread will execute and "args" are the arguments of that function.

success = t.run()
Here we start our thread and store the return value of the called target function in the variable "success".

if success: return
If the password has been found, we want the loop to stop.

MultiThread.end()
The end of the multithreading part. So here we wait for all the threads to finish their execution.




The function call


And last but not least, we call our function "dictionary_attack":

1
dictionary_attack("test.zip", "passwords.txt")

"test.zip" is the Zip-file we want to get the password for and "password.txt" is the file that contains all the passwords.



Here is the complete code:


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
import zipfile
from multiprocessing.pool import ThreadPool


class MultiThread():
    __thread_pool = None

    @classmethod
    def begin(cls, max_threads):
        MultiThread.__thread_pool = ThreadPool(max_threads)

    @classmethod
    def end(cls):
        MultiThread.__thread_pool.close()
        MultiThread.__thread_pool.join()

    def __init__(self, target=None, args:tuple=()):
        self.__target = target
        self.__args = args

    def run(self):
        try:
            result = MultiThread.__thread_pool.apply_async(self.__target, args=self.__args)
            return result.get()
        except:
            pass


def dictionary_attack(zip_path, password_file_path):
    zipFile = zipfile.ZipFile(zip_path)
    passwords = open_password_file(password_file_path)
    MultiThread.begin(400)
    for pwd in passwords:
        t = MultiThread(target=extract, args=(zipFile, pwd))
        success = t.run()
        if success: return
    MultiThread.end()


def extract(zip, pwd):
    try:
        zip.extractall(pwd=str.encode(pwd))
        print("Success! Possible Password: " + pwd)
        return True
    except:
        return False

def open_password_file(password_file_path):
    with open(password_file_path, "r") as password_file:
        passwords = []
        for line in password_file:
            password = line.strip("\n")
            passwords.append(password)
    return passwords


dictionary_attack("test.zip", "passwords.txt")






How to: Safe passwords

Knowing, that those password files are available for everyone on the Internet, we should think about our passwords. Especially when we know, how they are made.

The words in a password list are not randomly chosen, they are all real passwords and they are already being hacked. So those are list of hacked passwords.



So how do we protect ourselves against those attacks?

There are a few simple rules for creating safe passwords:

  1. choose a passwords with a length of at least 12 characters
  2. don't create passwords with words that are related to you. Like the name of your children, your day of birth etc. .
  3. Don't have only lower case letters, have a mix of upper case and lower case letters
  4. Have at least one digit in your password
  5. Have at least one special character like "+", "-", "&" etc. in your password




How safe is my password?


You can check it easily on howsecureismypassword.net.
It will show you how long it will probably take to bruteforce your password.
However, when you choose a very silly password like "Password123", it shows you that it would take 41 years to hack it. However, probably every password list out there contains the password "Password123". So it would be hacked within seconds.





Comments

Post a Comment

Popular posts from this blog

[Python] Machine Learning Intro #1 - Hello World

WebGL 2 Guide - #1 Einführung