Dumping Linux Password Hashes

In my push to keep learning the Python programming language i thought a next good step is to make a simple script that grabs the password hashes on a Linux device and dump them to a file. The dump is formatted so that it is easy to read unlike the formatting used in the shadow file. I made this script purely for the challenge. It is not intended to be used for anything other than educational purposes.

So some background. Linux passwords are stored as a hash. A hash is a one way mathematical function which is used on the password to change it to something unintelligible. It is important to understand that this is a one way process. You are unable to convert some hashed data back to its intelligible form. One way of cracking hashed passwords is to use rainbow tables. These are tables populated with precomputed hashes and there paired plaintext password. They usually contain 1000’s of entries each with matching plaintext / hash value pairs.

Lets say for example you have a password hash: 436ad45345deed32. You want to find the password this hash represents using a rainbow table.

The rainbow table may look like the following:

          PLAINTEXT PASSWORD      HASH VALUE
        +----------------------+----------------------------+
        | password             |  43f54abeee342abe          |
        |                      |                            |
        | qwerty               |  AB5445afd56ad345          |
        |                      |                            |
        | dragon               |  4554bd44dd34cb32          |
        |                      |                            |
        | monkey               |  f632abcd345c34dc          |
        |                      |                            |
        | supersecurepass      |  436ad45345deed32          |
        |                      |                            |
        | letmein              |  4fd43344decad356          |
        |                      |                            |
        | baseball             |  ab4564fd4ed4556d          |
        |                      |                            |
        | mypass               |  c34ddef567ab345d          |
        |                      |                            |
        +----------------------+----------------------------+

You check your hash value with each one in the rainbow table until a match is found. When the match is found the matching plaintext password is looked up, you now have the password.

To combat this weakness an extra value is used when hashing the password. This value is called a salt. The salt is a random value which is added to the password before it is hashed.

Without Salt:

   Password        Hash Function           Hash Value
  +-----------+------------------------+-------------------+
  |           |                        |                   |
  |secpass123 | +------------------->  | ad4565bcd34dea    |
  |           |                        |                   |
  +-----------+------------------------+-------------------+

With Salt:

   Password       Salt           Concatenation         Hash Function     Hash Value
  +----------+-------------+-----------------------+------------------+----------------+
  |          |             |                       |                  |                |
  |secpass123| mysaltvalue | secpass123mysaltvalue | +------------->  | adb345252aed4f |
  |          |             |                       |                  |                |
  +----------+-------------+-----------------------+------------------+----------------+

By introducing this salt value before the hashing takes place it makes the rainbow tables much less efficient. To generate a rainbow table not only will you need a a list of passwords but also a salt value to go with each password. If the salt value is not known then the rainbow table wont work.

In UNIX systems these hashes are stored in a shadow file located in /etc/shadow. This is obviously locked out to anyone other than root by default. This means the script wont work unless run under root or sudo.

How it works

Running the script below reads this shadow file and outputs the results to both the terminal and a text file in the same location as where you saved the python script. The text file is called dump.txt.

the way the hash value is stored is each entry is delimited by a colon. For example the first part is the username, followed by a colon. The next part is the hash and salt, followed by a colon etc..
The script relies a lot on slices which allow you to slice a string into parts.

An example of an entry in the shadow file

test:$6$54dYBZqz$oDexE5blBiNer7V2qHXVgQvdhSzChH2kmQ2.op4nHLPxMldePB3CyEizwyhhLo3lwpTIFnzqN30K
uQOKEFuVe1
:15736:0:99999:7:::

To get the username i sliced from index 0 of the string to the index of the first colon.
After the first colon there is the characters $?$. the ? being a number which indicates the hash function which is used on the password. This value was easily obtained in the script as it is always two characters from the first colon.
Next is the salt value. This is directly after the hash type and ends with the $. A for loop is used in the script to gather index values for both colons and dollar symbols. This make the extraction of the values in the whole string much simpler.
Next is the password hash. Gathered by slicing the whole string from the index value of the 3rd dollar sign to the 2nd colon.
The last piece gathered in the script is the amount of time left till the password expires. This was gathered by slicing the string between the 4th and 5th colons.

shadowFile = open('/etc/shadow', 'r')
shadowFileList = shadowFile.readlines()
shadowFile.close()
dump = open('dump.txt', 'w')
for user in shadowFileList:
    if '$' in user:
        print 'The username is: ' + user[0:user.find(':')]
        dump.write('The username is: ' + user[0:user.find(':')] + '\n')
        hashtype = user[user.find(':') + 2]
        count = 0
        for letter in hashtype:
            if letter == '$':
                count = count + 1
        if hashtype == '1':
            hashtype = 'The hashing algorithm used is: MD5'
            dump.write('The hashing algorithm used is: MD5\n')
        elif hashtype == '2':
            hashtype = 'The hashing algorithm used is: BlowFish'
            dump.write('The hashing algorithm used is: BlowFish\n')
        elif hashtype == '5':
            hashtype = 'The hashing algorithm used is: SHA256'
            dump.write('The hashing algorithm used is: SHA256\n')
        elif hashtype == '6':
            hashtype = 'The hashing algorithm used is: SHA512'
            dump.write('The hashing algorithm used is: SHA512\n')
        else:
            hashtype = 'The hashing algorithm used is Unknown. It has a hash code value of:' + hashtype + '.'
            dump.write('The username is: ' + user[0:user.find(':')] + '\n')
        print hashtype
        delimitercolon = []
        delimiterdolla = []
        count = 0
        for char in user:
            if char == ':':
                delimitercolon.append(count)
            if char == '$':
                delimiterdolla.append(count)
            count = count + 1
        print 'The Hash is: ' + user[delimiterdolla[2] + 1:delimitercolon[1]]
        dump.write('The Hash is: ' + user[delimiterdolla[2] + 1:delimitercolon[1]] + '\n')
        print 'The Salt is: ' + user[delimiterdolla[1] + 1:delimiterdolla[2]]
        dump.write('The Salt is: ' + user[delimiterdolla[1] + 1:delimiterdolla[2]] + '\n')
        print 'The password is set to expire in ' + user[delimitercolon[3] + 1:delimitercolon[4]] + ' days.\n\n'
        dump.write('The password is set to expire in ' + user[delimitercolon[3] + 1:delimitercolon[4]] + ' days.\n\n\n')

The output looks like the following:

 The username is: test
 The hashing algorithm used is: SHA512
 The Hash is: oDexE5blBiNer7V2qHXVgQvdhSzChH2kmQ2.op4nHLPxMldePB3CyEizwyhhLo3lwpTIFnzqN30KuQOKEFuVe1
 The Salt is: 54dYBZqz
 The password is set to expire in 99999 days.

If you have any questions please let me know in the comments or by email james[at]jamesdotcom.com. Feel free to use the code, change it in anyway or whatever.

Leave a Reply

Your email address will not be published. Required fields are marked *