1
\$\begingroup\$

I have to do a Bruteforce script, so mine is working, but is also very very low. My teacher gave me a small Shadow file and from the shadow file I have to find the original password (MD5 used here). I know that my code is working but I'm looking for tips and tricks to improve it.

import hashlib
import itertools




#possible characters in user password
Alphabet = ("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890-_.;#@")
#minimum password value
CharLength = 6

#getting passwords and username from shadow file
with open("shadow_test", "r") as ins:
    array = []
    users = []
    passwd = []
    #getting everyline from shadow file into an array
    for line in ins:
        array.append(line)
    #saving only username and passwords
    for pw in array:
        str(pw)
        r= pw.split(":")
        users.append(r[0])
        passwd.append(r[1])
    # print(users)
    # print(passwd)

    list = []
    #removing passowrd with * or !
    for mdp in passwd:
        if mdp != '*' and mdp != '!':
            str(mdp)
            list.append(mdp)
            # trying to Bruteforce
            for _ in range(12):
                passwords = (''.join(word) for word in itertools.product(Alphabet, repeat=CharLength))

                #print(*passwords)
                for pswd in passwords:
                    hash_object = hashlib.md5(str.encode(pswd)).hexdigest()
                    # hash_object.update(*passwords.encode('utf-8'))
                    generatedpassword = '$1$' + hash_object
                    # print(generatedpassword)

                    for compare in list:
                        for user in users:
                            #print('on cherche le Mot de passe : ' + compare +' pour ' +user)
                            #print('mot de passe MD5 généré : ' +generatedpassword)
                            #print('mot de passe clair généré : ' +pswd)


                            if generatedpassword == compare:
                                print('Le Mot de passe pour' + user + ' est : ' + pswd)
\$\endgroup\$

1 Answer 1

2
\$\begingroup\$
  1. Use capital letters with underscores for constants https://pep8.org/#constants There is no need to wrap alphabet string into parentheses:
ALPHABET = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890-_.;#@"
LENGTH = 6
  1. Expression str(pw) does nothing so you can remove it. You probably meant pw = str(pw) but it is redundant since pw already is str

  2. You do not need array variable at all, just iterate over input file line by line. Also, unpacking is more readable:

    for line in ins:
        user, password, *rest = line.split(":")
        users.append(user)
        passwd.append(password)
  1. Since you ignore passwords "*" and "!" it is better to filter them out when reading from file.

  2. Since ins is not used after generating users and passwd lists you do not need to place rest of code inside with block. Actually, the first block may look like:

#getting passwords and username from shadow file
with open("shadow_test", "r") as ins:
    users = []                                                         
    passwd = []
    for line in ins:
        user, password, *rest = line.split(":")
        if password not in ('*', '!'):
            users.append(user)
            passwd.append(password)
  1. Why do you need for _ in range(12) loop? Just to make the program 12 times slower? Remove it.

  2. Do not use names of built-in funciton names for variables, like list. BTW, why do you need it at all? Just iterate over all possible words and look for their hash in passwd:

passwords = (''.join(word) for word in itertools.product(ALPHABET, repeat=LENGTH))
for pswd in passwords:
    # calculate hash of pswd and if it is equal to one in passwd print it
  1. When you compare generated hash with elements of passwd you do not need to iterate over all usernames and print them all! Actually it is better to make "password hash" -> "username" dictionary so you can check if generatedpassword is one of given password hashes faster (because checking that dict has a key generally is faster than checking each element of array):
with open("shadow_test", "r") as ins:
    data = dict()
    for line in ins:
        username, password, *rest = line.split(":")
        if password not in ('*', '!'):
            data[password] = username

password_hash = md5hash("qwerty")  # assume it is a function for calculating md5
if passwod_hash in data:
    print("qwerty is password of", data[password_hash])
  1. It is probably better to remove "$1$" from stored records rather than add it to each calculated hash:
if password not in ('*', '!'):
    assert password.startswith("$1$"), "Expect password records to begin with $1$"
    data[password[3:]] = username  # remove $1$ 

So the whole solution may look like:

import hashlib
import itertools


def md5hash(char_sequence):
    string = ''.join(char_sequence)
    hash_object = hashlib.md5(string.encode())
    return hash_object.hexdigest()


# possible characters in user password
ALPHABET = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890-_.;#@"
# password length
LENGTH = 6

# getting passwords and username from shadow file
with open("shadow_test", "r") as ins:
    data = dict()  # hash -> username mapping from shadow file
    for line in ins:
        username, password, *rest = line.split(":")
        if password not in ('*', '!'):
            data[password[3:]] = username  # remove $1$

# bruteforce
for word in itertools.product(ALPHABET, repeat=LENGTH):
    generated_hash = md5hash(word)
    if generated_hash in data:
        print('The password of user', data[generated_hash], 'is:', word)
```
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.