Saturday, March 2, 2019

Understanding password cracking - the basics from a Linux perspective

This post is focused on teaching you the basics of password cracking, leveraging a Linux "/etc/shadow" file for our demo.

First let us take a look at a sample entry for from the /etc/shadow

root@securitnik:~# cat /etc/shadow | grep root
root:$6$uPdhX/Zf$Kp.rcb4AWwtx0EJq235tzthWXdIEoJnhZjOHbil3od1AyMf3t8Yi6dAPlhbHVG9SLx5VSIPrXTZB8ywpoOJgi.:17564:0:99999:7:::

Each entry above, separated by the ":" represents a different field. For us we will focus on the first two, with the values "root" and "$6$uPdhX/Zf$Kp.rcb4AWwtx0EJq235tzthWXdIEoJnhZjOHbil3od1AyMf3t8Yi6dAPlhbHVG9SLx5VSIPrXTZB8ywpoOJgi." The first entry represents the username which in this case is "root" and the second the password representation.

From the second field - the password representation - the "6" between the first two "$" represents the encryption algorithm being used. This is further confirmed by looking at our "/etc/login.defs" file with a focus on the "ENCRYPT_METHOD". In our file below, we see "ENCRYPT_METHOD SHA512". This tells us that the system is using SHA512.


root@securitynik:~# cat /etc/login.defs | grep "ENCRYPT_METHOD"
# This variable is deprecated. You should use ENCRYPT_METHOD.
ENCRYPT_METHOD SHA512
# Only used if ENCRYPT_METHOD is set to SHA256 or SHA512.

The second value we need to focus on is the salt, which is in this example "uPdhX/Zf" found between the second and third "$". In this case we are using an 8 byte salt. Consider the salt a random value that is used to make the password stronger. The bigger the salt, the stronger the password should be. For example, an 8 byte salt should make the password harder to crack than a 2 byte crack.

From above, we know the hashing algorithm is SHA512 and we have the salt. The only thing missing now is a string which when we put them all together, we can recompute the password representation. Specifically, this is what we need:
HASH ALGORITHM + SALT + STRING PASSWORD = PASSWORD REPRESENTATION

Here is what we have:
SHA512 (6) +  uPdhX/Zf + ???? = PASSWORD REPRESENTATION

Let's load up python3.7 and leverage it's "crypt" module to help us out here.

root@securitynik:~# python3.7
Python 3.7.2 (default, Jan  3 2019, 02:55:40)
[GCC 8.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import crypt
>>> crypt.crypt("Passw0rd", salt="$6$uPdhX/Zf$")
'$6$uPdhX/Zf$FtB3X5oeGRWXXg92TjXp/9yuy21iBYTa/YMKrCIUtV4rDBd9zK5tZ0mr0sefszzdz0NUbM670CYJw.g1DFkJu.'
>>> crypt.crypt("GuessMe", salt="$6$uPdhX/Zf$")
'$6$uPdhX/Zf$GhShpI0WBBLbDVRkGrOjcnmseqzlduCrXdX0prmlMIMtF/1XlgBKtl4ptpyxJDzvqCQCHeK5iNW1tvEETGmYz0'
>>> 


From above we tried the first password as "Passw0rd" and the second password "GuessMe". However, as you can see above, none of these combinations produced a value that matched our original from the "/etc/shadow" file which is "$6$uPdhX/Zf$Kp.rcb4AWwtx0EJq235tzthWXdIEoJnhZjOHbil3od1AyMf3t8Yi6dAPlhbHVG9SLx5VSIPrXTZB8ywpoOJgi."

At this point, we can continue trying this process entering each password manually until we find a match or we can simply automate this process with a dictionary. Let's choose the latter.

First we need a dictionary. You can find many dictionaries online but let's just create one of our own. To do, let's add 10 words to a file called "SecurityNik.lst".

root@securitynik:~# echo "Passw0rd" >> SecurityNik.lst
root@securitynik:~# echo "GuessMe" >> SecurityNik.lst
root@securitynik:~# echo "admin" >> SecurityNik.lst
root@securitynik:~# echo "root" >> SecurityNik.lst
root@securitynik:~# echo "1234567890" >> SecurityNik.lst
root@securitynik:~# echo "Password1" >> SecurityNik.lst
root@securitynik:~# echo "ftp" >> SecurityNik.lst
root@securitynik:~# echo "root" >> SecurityNik.lst
root@securitynik:~# echo "SecurityNik" >> SecurityNik.lst
root@securitynik:~# echo "toor" >> SecurityNik.lst

When we perform a "cat" on the file, we see:

root@securitynik:~# cat SecurityNik.lst
Passw0rd
GuessMe
admin
root
1234567890
Password1
ftp
root
SecurityNik
toor


Let's now code up our password cracker:


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
#!/usr/bin/env python3.7

# passwordCracker.py
# Author: Nik alleyne
# Author Blog: www.securitynik.com
# Date: 2019-02-15

import crypt
from subprocess import call
from sys import exit, argv

# This function handles usage information
def usage():
    print("password_file: A file consisting of passwords you would like to use.")
    print("./passwordCraker.py <password_file>")
    exit(0)


# This function checks to see if the username is found within the file
def passwordCracker(passwordFile):
    # Store the password we are trying to crack as a variable
    passwordToCrack = "$6$uPdhX/Zf$Kp.rcb4AWwtx0EJq235tzthWXdIEoJnhZjOHbil3od1AyMf3t8Yi6dAPlhbHVG9SLx5VSIPrXTZB8ywpoOJgi."
    print("[*] we will be looking for a match for password '{}'".format(passwordToCrack));
    print("[*] Starting password cracking ...")
    print("[*] Loading password file '{}' into memory ...".format(passwordFile))
    
    # Load the file entered on the command line into memory
    passwordFile = open(passwordFile, 'r')

    # Store salt as variable
    passwordSalt = "$6$uPdhX/Zf$"
    passwordFound = False;

    # Read the file line by line
    for line in passwordFile.readlines():
        passwordFound = crypt.crypt(line.strip(),salt=passwordSalt)
        if ( passwordFound == passwordToCrack.strip() ):
            print("[+] MATCH FOUND! Password is:{}".format(line).strip())
            print("[+] Password {} \n MATCHES \n Password {} ".format(passwordToCrack,passwordFound))
            exit(0)
        else:
            print("[-] Trying password:{}".format(line).strip())


if __name__ == '__main__':
    call("clear")
    print("passwordCrakcer.py")
    print("Author: Nik Alleyne")
    print("Author Blog: www.securitynik.com")

    # Check the number of arguments being entered on the command line
    if ( len(argv) != 2 ):
        usage()
        
    # calls the passwordCracker function
    passwordCracker(argv[1])
   

The above code, helps to give us a better understanding of how password cracking works.


Now while we went through this process manually and writing our own code, the reality is, there are tools which can make this process easier for you. More specifically the three that I can immediately recommend are:
1. John the Ripper
2. Hashcat
3. Cain and Abel

While the above tools are helpful, it is important that you understand how some of these tools works and thus I hope this post contributed to making that learning process easier.


References:
https://en.wikipedia.org/wiki/Salt_(cryptography)
https://docs.python.org/3/library/crypt.html
https://docs.python.org/3/tutorial/inputoutput.html#reading-and-writing-files
Python Cheat Sheet Updated for 2022 - from Libraries to Internet Modules (pcwdld.com)

No comments:

Post a Comment