2

I'm trying to create a brute force Python code for alphabetical and alphanumerical combinations and have it report the password and amount of time it took.

For numerical combinations I made this:

import datetime as dt

Password4 = 123456

def crack_password():
    start = dt.datetime.now()
    for n in range(1000000):
        password_guess = '{0:04d}'.format(n)
             if password_guess == str(Password4):
                end = dt.datetime.now()
                print("Password found: {} in {}".format(password_guess, end - start))
               break
    guesses = crack_password()

For alphanumerical combinations (does not work) I tried:

import random

letters = [str(i) for i in range('a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p')]
s = [''.join([a,b,c,d,e,f,g,h]) for a in letters for b in letters for c   in letters for d in letters for e in letters for f in letters for g in letters  for h in letters]
random.shuffle(s)
real_password = 'aaaaaaaa'
i = 0

for code in s:
if code == real_password:
    print()
        print('The password is: ', code)
        break
    else:
        i += 1
        print(i, ' failures', end='\r')

It should report either number of attempts or time it took.

user4157124
  • 2,809
  • 13
  • 27
  • 42
Alex H
  • 69
  • 1
  • 2
  • 7

2 Answers2

13

Here's a naiive brute force method that will guess numbers (string.digits) and lower case letters (string.ascii_lowercase). You can use itertools.product with repeat set to the current password length guessed. You can start at 1 character passwords (or whatever your lower bound is) then cap it at a maximum length too. Then just return when you find the match.

import itertools
import string

def guess_password(real):
    chars = string.ascii_lowercase + string.digits
    attempts = 0
    for password_length in range(1, 9):
        for guess in itertools.product(chars, repeat=password_length):
            attempts += 1
            guess = ''.join(guess)
            if guess == real:
                return 'password is {}. found in {} guesses.'.format(guess, attempts)
            # uncomment to display attempts, though will be slower
            #print(guess, attempts)

print(guess_password('abc'))

Output

a 1
b 2
c 3
d 4
...
aba 1369
abb 1370
password is abc. found in 1371 guesses.
Cory Kramer
  • 114,268
  • 16
  • 167
  • 218
  • @AlexH One can use `print(guess, attempts,end="\r")` to rewrite the same line, but why does it even matters to print the failed password avoid it and save some cpu cycles. – Vinod Srivastav Nov 24 '21 at 23:30
2

One possible option which would preserve almost exactly your current code is to convert to base 36 with the following "digits": 0-9a-z. This will give you every possible alpha-numeric combination for n characters if you search in range(36**n).

Using a simplified function from How to convert an integer in any base to a string?:

def baseN(num, b=36, numerals="0123456789abcdefghijklmnopqrstuvwxyz"):
    return ((num == 0) and numerals[0]) or (baseN(num // b, b, numerals).lstrip(numerals[0]) + numerals[num % b])

You can then loop through numbers as in your example:

>>> for i in range(10000, 10005):
...     print(baseN(i).zfill(5))
...
007ps
007pt
007pu
007pv
007pw

To get all 3-letter possibilities, you can loop as follows:

for i in range(36**3):
    possible = baseN(i).zfill(3)
Community
  • 1
  • 1
brianpck
  • 8,084
  • 1
  • 22
  • 33
  • I like this solution as it shows more explicitely how the counting with a different base works. One remark: your use of zfills assumes that the first character in the numerals is a zero character. To make it more generic, it is better to do baseN(i).rjust(3, numerals[0]) – Eelco van Vliet Mar 18 '20 at 11:06