Brute Forcing Salted Password

Since my last post showed how to check a salted password, I figured this time we can look over some example code for brute forcing a salted password. Of course this is just proof of concept and should not be used on any password you do not have access to.

The code I tossed together is using a bit of some examples I found online, and is no where near optimized for speed, but as I will show it will work.

First lets check out the code:

#!/usr/bin/env python
import sys
from md5 import md5
from crypt import crypt

def nextChar():
    '''create a generator for spitting out our chars'''
    chars = range(97,124) # Lower case range
    chars = map(chr, chars)
    for char in chars:
        yield char

def passRecurse(width, position, baseString):
    '''Loop over all characters using width'''
    for c in nextChar():
        if (position < width - 1):
            thisPassword = baseString + c
            passRecurse(width, position + 1, baseString + c)
        thisPassword = baseString + c

        # link in to external function here
        newhash = SHA512Salt(thisPassword, salt)
        if newhash == hash:
            print 'Password hash matached: %s' % thisPassword
            sys.exit()

def SHA512Salt(password, salt):
    output = crypt(password, '$6$%s$' % salt)
    return output.split('$')[-1]

# Set up some variables and start the Brute
width = 3
salt = sys.argv[1]
hash = sys.argv[2]
passRecurse(width, 0, '')

I was not able to find or think of a better way to create passwords using a character map other than calling the function within the function (eww….), if you have any examples or suggestion please let me know.

This time I set the password for the dummy user to bat (save some time and CPU cycles I suppose):

# cat /etc/shadow| grep dummy
dummy:$6$cIYtIxDs$XzsdCG1TD3hO3qpEtUardUbVPQRsEOgq.1yqWl8iFp0DCF4ZSbppuj63sXHwzEj7Faz5MRoVf2LxtD9kUwpG5.:15313:0:99999:7:::

Using the Python script above works as so (and ran in 3.690s):

$ ./brute.py cIYtIxDs "XzsdCG1TD3hO3qpEtUardUbVPQRsEOgq.1yqWl8iFp0DCF4ZSbppuj63sXHwzEj7Faz5MRoVf2LxtD9kUwpG5."
Password hash matached: bat

UNIX SHA-512 Passwords & Python

Well today I spent a bit of time looking up how /etc/shadow created their shadow passwords.

I found a good source for the method at http://www.akkadia.org/drepper/SHA-crypt.txt , and if you take some time to review it you will notice the steps are a bit involved

Lucky for us Python has a Crypt module that works nicely.

First off I started by creating a dummy user with the password of test on one of my Linux computers:

# cat /etc/shadow | grep dummy
dummy:$6$JJ1sbaL/$2P4ZcZ10VjIa9f5.8N/GJukYCg.RJVBTP2h30v2mnh0Q5izqc2rtKZ6qtI0Ewiyji
      0hX9zIVfftOVuJTIsBSKQ/:15310:0:99999:7:::

And like that we have a password we can attempt to validate.

Next I will need to define our salt in Python. A good article explaining the format of /etc/shadow can be found on cyberciti.biz. Like the article says the salt is found between the $ after the username and before the password hash. For comparison I will also extract the hash:

>>> salt = 'JJ1sbaL/'
>>> hash = '2P4ZcZ10VjIa9f5.8N/GJukYCg.RJVBTP2h30v2mnh0Q5izqc2rtKZ6qtI0Ewiyj0hX9zIVfftOVuJTIsBSKQ/'

Now that we have our salt we can create a hash of our password using that salt and verify it matches the hash shown in /etc/shadow:

>>> import crypt
>>> output = crypt.crypt('test', '$6$%s$' % salt)
>>> print output
$6$JJ1sbaL/$2P4ZcZ10VjIa9f5.8N/GJukYCg.RJVBTP2h30v2mnh0Q5izqc2rtKZ6qtI0Ewiyj0hX9zIVfftOVuJTIsBSKQ/

And lastly lets compare the new hash with the one found in /etc/shadow:

>>> newhash = output.split('$')[-1]
>>> print newhash
'2P4ZcZ10VjIa9f5.8N/GJukYCg.RJVBTP2h30v2mnh0Q5izqc2rtKZ6qtI0Ewiyj0hX9zIVfftOVuJTIsBSKQ/'
>>>
>>> newhash == hash
True

Wordlist with all Possible ASCII Elements

After a bit of thinking I believe I have a better way to create all possible ASCII pass phrases, rather than create a recursive function that calls itself as I did in the previous post.

This method takes a slightly different approach, but I believe it to return the same results.

First off let’s create a list of all lower case ASCII character numbers:

>>> chars = range(97, 123)
>>> chars
[97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112,
113, 114, 115, 116, 117, 118, 119, 120, 121, 122]
>>> map(chr, chars)
['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p',
'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z']

Next we can set our max word width and current word width variables:

import itertools
maxwidth = 3
currentwidth = 1

At this point we are ready to run with a while loop:

while int(currentwidth) <= int(maxwidth):
    for i in itertools.product(chars, repeat=currentwidth):
        print ''.join(['%c' % c for c in list(i)])
    currentwidth += 1

The loop above will run until currentwidth is greater than maxwidth (makes sense right), at that point we use itertools’s product method to create all possible orderings for our characters.

Then using list comprehension I print out the %c which returns the alphabetical character.

And lastly we increase the currentwidth by 1

What itertools.product does is creates all possible combinations (including repeated chars):

>>> [i for i in itertools.product(chars, repeat=2)]
[(97, 97), (97, 98), (97, 99), (97, 100), (97, 101), (97, 102), (97,
 103), (97, 104), (97, 105), (97, 106), (97, 107), (97, 108), (97, 109),
 (97, 110), (97, 111), (97, 112), (97, 113), (97, 114), (97, 115), (97,
 116), (97, 117), (97, 118), (97, 119), (97, 120), (97, 121), (97, 122),
 (98, 97), (98, 98), (98, 99), (98, 100), (98, 101), (98, 102), (98,
103), (98, 104), (98, 105), (98, 106), (98, 107), (98, 108), (98, 109),
 (98, 110), (98, 111), (98, 112), (98, 113), (98, 114), (98, 115), (98,
 116), (98, 117), (98, 118), (98, 119), (98, 120), (98, 121), (98, 122),
 (99, 97), (99, 98), (99, 99), (99, 100), (99, 101), (99, 102), (99,
......
.....
...

Of course you probably want to see those tuples of characters as letters right:

>>> for i in itertools.product(chars, repeat=2):
...   ''.join(['%c' % c for c in i])
...
'aa'
'ab'
'ac'
'ad'
'ae'
'af'
'ag'
'ah'
'ai'
'aj'
'ak'
'al'
'am'
'an'
'ao'
'ap'
'aq'
'ar'
'as'
'at'
'au'
'av'
'aw'
'ax'
..
..
..