# Make password/hash verification algorithm more efficient

I am trying to guess the paswords from the etc/shadow file(it has 43 user/passwords). And I have been given some hits about the passwords:

• Length is between 4 and 8 characters
• There can be only 2 numbers and only at the end
• Capital letters only in the beginning

So I started just with a small group composed by 4 character with 2 digits in it. But it takes so much time to process:

import crypt
import string
import itertools
import datetime

hashed = []
c = 0
cc = 0

for x in file: #Split the hash and the username
usr, hshd, wtf, iss, this, thing, here, doing, example = x.split(':')
hashed.append(hshd)
#GRUPO1 4 caracteres 2 numeros
letters = string.ascii_lowercase
digits = string.digits
grupo1=[]
group1=itertools.product(letters,repeat=2)
group1b=itertools.product(digits,repeat=2)
for x in itertools.product(group1,group1b):  #Join the possible iterations
string=''.join([''.join(k) for k in x])
grupo1.append(string)
print(len(grupo1))
for y in grupo1:#Get the one of the iterations and try it
prueba=y
for x in hashed: #Verify if that iteration is the password to any of the 43 users
rehashed = crypt.crypt(prueba, x)
if rehashed == x: #Password is found
print('La contraseña del usuario ' + username[c] + ' es ' + prueba)
cc = 1
c = c + 1
print('Lo sentimos "' + prueba + '" no es la contraseña de ningun usuario')


How can I improve the efficiency of this? I have a GTX 1070 if it helps for any kind of GPU processing.(I have no idea of this)

Just this small part of 4 characters is taking me for hours, it has still not finished.

• cant you hash prueba without the need of user hashes? Don´t understand the need of rehashed – juvian Nov 23 '18 at 17:39
• As long as I know, to verify if the password is correct I have to crypt the password with the hash, then if the hash generated in that is equal to the initial hash then it is correct. But I'm really new into this, maybe if you could put me an example of what you mean – 19mike95 Nov 23 '18 at 17:46
• 8 characters are already 26^6 * 10^2 possibilities, will take too long – juvian Nov 23 '18 at 18:02
• @juvian “_can’t you hash prueba without the need of the user hashes”? No. If I set my password as “S3cr3t”, the system adds a two character random salt, say “xy” before the hashing, and stores the hash with the salt prefix “xy”, such as “xy7A2cyZTu”. If another user also uses the password “S3cr3t”, but “3w” was used as the salt, their password might be hashed as “3wHj85dFh” ... completely different despite being the same password. Hashing the trial password with the stored hash is just passing the correct salt (first 2 characters) to use when hashing the trial password. – AJNeufeld Nov 24 '18 at 6:36

Starting with the code in my answer to your related question, we can factor out the source of the words being guessed and encapsulate it all in functions:

def get_users_to_crack(file_name):
users = {}
with open(file_name) as file:
for line in file:
return users

def crack(users, words):
cracked_users = {}
if not users:
return cracked_users, {}
return cracked_users, users


Now we just need a supplier of words. This can be either from a file, similar to your other question:

def dictionary_attack(file_name):
with open(file_name) as file:
for line in file:
word = line.rstrip('\n').capitalize()
if 4 <= len(word) <= 8:
yield word


Or you build it yourself, according to the scheme you supplied here:

from string import ascii_lowercase as a_z, ascii_uppercase as A_Z, digits
from itertools import product

def get_chars(length):
"""Produces the allowed chars according to the scheme given
[a-zA-Z][a-z]*(0-5)[a-z0-9]*2"""
assert length > 2
return [a_z + A_Z] + [a_z] * (length - 3) + [a_z + digits] * 2

def brute_force(min_length, max_length, get_chars):
for length in range(min_length, max_length + 1):
for word in product(*get_chars(length)):
yield "".join(word)


You can then combine them into one nice tool:

if __name__ == "__main__":