Identifying hashes

Nearly every web application you use that stores a password of yours, should store your credentials in some form of hashed format for added security. A good hashing system in place for user passwords can be very useful in case your database is ever stolen, as this will extend the time taken for a hacker to crack them.

For this reason, we have numerous different hashing methods, some of which are reused throughout different applications, such as MD5 and SHA hashes, but some such as Des(UNIX) are less commonly found. Because of this, it is a good idea to be able to match a hash value to the hashing function it belongs to. We cannot base this purely on hash length as many hashing functions share the same length, so to aid us with this we are going to use regular expressions (Regex). This allows us to define the length, the characters used, and whether any numerical values are present.

Getting ready

For this script, we will only be using the re module.

How to do it…

As previously mentioned, we are going to be basing the script around Regex values and using those to map input hashes to the stored hash values. This will allow us to very quickly pick out potential matches for the hashes:

import re
def hashcheck (hashtype, regexstr, data):
    try:
        valid_hash = re.finditer(regexstr, data)
        result = [match.group(0) for match in valid_hash]
        if result: 
            return "This hash matches the format of: " + hashtype
    except: pass
string_to_check = raw_input('Please enter the hash you wish to check: ')
hashes = (
("Blowfish(Eggdrop)", r"^+[a-zA-Z0-9/.]{12}$"),
("Blowfish(OpenBSD)", r"^$2a$[0-9]{0,2}?$[a-zA-Z0- 9/.]{53}$"),
("Blowfish crypt", r"^$2[axy]{0,1}$[a-zA-Z0-9./]{8}$[a-zA-Z0- 9./]{1,}$"),
("DES(Unix)", r"^.{0,2}[a-zA-Z0-9/.]{11}$"),
("MD5(Unix)", r"^$1$.{0,8}$[a-zA-Z0-9/.]{22}$"),
("MD5(APR)", r"^$apr1$.{0,8}$[a-zA-Z0-9/.]{22}$"),
("MD5(MyBB)", r"^[a-fA-F0-9]{32}:[a-z0-9]{8}$"),
("MD5(ZipMonster)", r"^[a-fA-F0-9]{32}$"),
("MD5 crypt", r"^$1$[a-zA-Z0-9./]{8}$[a-zA-Z0-9./]{1,}$"),
("MD5 apache crypt", r"^$apr1$[a-zA-Z0-9./]{8}$[a-zA-Z0- 9./]{1,}$"),
("MD5(Joomla)", r"^[a-fA-F0-9]{32}:[a-zA-Z0-9]{16,32}$"),
("MD5(Wordpress)", r"^$P$[a-zA-Z0-9/.]{31}$"),
("MD5(phpBB3)", r"^$H$[a-zA-Z0-9/.]{31}$"),
("MD5(Cisco PIX)", r"^[a-zA-Z0-9/.]{16}$"),
("MD5(osCommerce)", r"^[a-fA-F0-9]{32}:[a-zA-Z0-9]{2}$"),
("MD5(Palshop)", r"^[a-fA-F0-9]{51}$"),
("MD5(IP.Board)", r"^[a-fA-F0-9]{32}:.{5}$"),
("MD5(Chap)", r"^[a-fA-F0-9]{32}:[0-9]{32}:[a-fA-F0-9]{2}$"),
("Juniper Netscreen/SSG (ScreenOS)", r"^[a-zA-Z0-9]{30}:[a-zA-Z0- 9]{4,}$"),
("Fortigate (FortiOS)", r"^[a-fA-F0-9]{47}$"),
("Minecraft(Authme)", r"^$sha$[a-zA-Z0-9]{0,16}$[a-fA-F0- 9]{64}$"),
("Lotus Domino", r"^(?[a-zA-Z0-9+/]{20})?$"),
("Lineage II C4", r"^0x[a-fA-F0-9]{32}$"),
("CRC-96(ZIP)", r"^[a-fA-F0-9]{24}$"),
("NT crypt", r"^$3$[a-zA-Z0-9./]{8}$[a-zA-Z0-9./]{1,}$"),
("Skein-1024", r"^[a-fA-F0-9]{256}$"),
("RIPEMD-320", r"^[A-Fa-f0-9]{80}$"),
("EPi hash", r"^0x[A-F0-9]{60}$"),
("EPiServer 6.x < v4", r"^$episerver$*0*[a-zA-Z0-9]{22}==*[a- zA-Z0-9+]{27}$"),
("EPiServer 6.x >= v4", r"^$episerver$*1*[a-zA-Z0- 9]{22}==*[a-zA-Z0-9]{43}$"),
("Cisco IOS SHA256", r"^[a-zA-Z0-9]{43}$"),
("SHA-1(Django)", r"^sha1$.{0,32}$[a-fA-F0-9]{40}$"),
("SHA-1 crypt", r"^$4$[a-zA-Z0-9./]{8}$[a-zA-Z0-9./]{1,}$"),
("SHA-1(Hex)", r"^[a-fA-F0-9]{40}$"),
("SHA-1(LDAP) Base64", r"^{SHA}[a-zA-Z0-9+/]{27}=$"),
("SHA-1(LDAP) Base64 + salt", r"^{SSHA}[a-zA-Z0- 9+/]{28,}[=]{0,3}$"),
("SHA-512(Drupal)", r"^$S$[a-zA-Z0-9/.]{52}$"),
("SHA-512 crypt", r"^$6$[a-zA-Z0-9./]{8}$[a-zA-Z0-9./]{1,}$"),
("SHA-256(Django)", r"^sha256$.{0,32}$[a-fA-F0-9]{64}$"),
("SHA-256 crypt", r"^$5$[a-zA-Z0-9./]{8}$[a-zA-Z0-9./]{1,}$"),
("SHA-384(Django)", r"^sha384$.{0,32}$[a-fA-F0-9]{96}$"),
("SHA-256(Unix)", r"^$5$.{0,22}$[a-zA-Z0-9/.]{43,69}$"),
("SHA-512(Unix)", r"^$6$.{0,22}$[a-zA-Z0-9/.]{86}$"),
("SHA-384", r"^[a-fA-F0-9]{96}$"),
("SHA-512", r"^[a-fA-F0-9]{128}$"),
("SSHA-1", r"^({SSHA})?[a-zA-Z0-9+/]{32,38}?(==)?$"),
("SSHA-1(Base64)", r"^{SSHA}[a-zA-Z0-9]{32,38}?(==)?$"),
("SSHA-512(Base64)", r"^{SSHA512}[a-zA-Z0-9+]{96}$"),
("Oracle 11g", r"^S:[A-Z0-9]{60}$"),
("SMF >= v1.1", r"^[a-fA-F0-9]{40}:[0-9]{8}&"),
("MySQL 5.x", r"^*[a-f0-9]{40}$"),
("MySQL 3.x", r"^[a-fA-F0-9]{16}$"),
("OSX v10.7", r"^[a-fA-F0-9]{136}$"),
("OSX v10.8", r"^$ml$[a-fA-F0-9$]{199}$"),
("SAM(LM_Hash:NT_Hash)", r"^[a-fA-F0-9]{32}:[a-fA-F0-9]{32}$"),
("MSSQL(2000)", r"^0x0100[a-f0-9]{0,8}?[a-f0-9]{80}$"),
("MSSQL(2005)", r"^0x0100[a-f0-9]{0,8}?[a-f0-9]{40}$"),
("MSSQL(2012)", r"^0x02[a-f0-9]{0,10}?[a-f0-9]{128}$"),
("TIGER-160(HMAC)", r"^[a-f0-9]{40}$"),
("SHA-256", r"^[a-fA-F0-9]{64}$"),
("SHA-1(Oracle)", r"^[a-fA-F0-9]{48}$"),
("SHA-224", r"^[a-fA-F0-9]{56}$"),
("Adler32", r"^[a-f0-9]{8}$"),
("CRC-16-CCITT", r"^[a-fA-F0-9]{4}$"),
("NTLM)", r"^[0-9A-Fa-f]{32}$"),
)
counter = 0
for h in hashes:
    text = hashcheck(h[0], h[1], string_to_check)
    if text is not None:
        counter += 1
        print text
if counter == 0:
    print "Your input hash did not match anything, sorry!"

How it works…

After we import the re module, which we are going to be using, we start to build our first block of code, which will be the heart of our script. We will try to use conventional naming throughout the script to make it more manageable further on. We pick the name hashcheck for this reason. We use the name hashtype to represent the names of the hashes that are upcoming in the Regex block of code, we use regexstr to represent the Regex, and we finally use data.

We create a string called valid_hash and give that the value of the iteration values after going through the data, which will only happen if we have a valid match. This can be seen further down where we give the value result the name of matching hash values that we detect using the Regex. We finally print the match if one, or more, is found and add our except statement to the end:

def hashcheck (hashtype, regexstr, data):
    try:
        valid_hash = re.finditer(regexstr, data)
        result = [match.group(0) for match in valid_hash]
        if result: 
            return "This hash matches the format of: " + hashtype
    except: pass

We then ask the user for their input, so we have something to match against the Regex. This is done as normal:

string_to_check = raw_input('Please enter the hash you wish to check: ')

Once this is done, we can move onto the nitty gritty Regex-fu. The reason we use Regex is so that we can differentiate between the different hashes, as they have different lengths and character sets. This is extremely helpful for MD5 hashes, as there are numerous different types of MD5 hashes, such as phpBB3 and MyBB forums.

We name the set of Regexs something logical like hashes, and then define them:

hashes = (
("Blowfish(Eggdrop)", r"^+[a-zA-Z0-9/.]{12}$"),
("Blowfish(OpenBSD)", r"^$2a$[0-9]{0,2}?$[a-zA-Z0- 9/.]{53}$"),
("Blowfish crypt", r"^$2[axy]{0,1}$[a-zA-Z0-9./]{8}$[a-zA-Z0- 9./]{1,}$"),
("DES(Unix)", r"^.{0,2}[a-zA-Z0-9/.]{11}$"),
("MD5(Unix)", r"^$1$.{0,8}$[a-zA-Z0-9/.]{22}$"),
("MD5(APR)", r"^$apr1$.{0,8}$[a-zA-Z0-9/.]{22}$"),
("MD5(MyBB)", r"^[a-fA-F0-9]{32}:[a-z0-9]{8}$"),
("MD5(ZipMonster)", r"^[a-fA-F0-9]{32}$"),
("MD5 crypt", r"^$1$[a-zA-Z0-9./]{8}$[a-zA-Z0-9./]{1,}$"),
("MD5 apache crypt", r"^$apr1$[a-zA-Z0-9./]{8}$[a-zA-Z0- 9./]{1,}$"),
("MD5(Joomla)", r"^[a-fA-F0-9]{32}:[a-zA-Z0-9]{16,32}$"),
("MD5(Wordpress)", r"^$P$[a-zA-Z0-9/.]{31}$"),
("MD5(phpBB3)", r"^$H$[a-zA-Z0-9/.]{31}$"),
("MD5(Cisco PIX)", r"^[a-zA-Z0-9/.]{16}$"),
("MD5(osCommerce)", r"^[a-fA-F0-9]{32}:[a-zA-Z0-9]{2}$"),
("MD5(Palshop)", r"^[a-fA-F0-9]{51}$"),
("MD5(IP.Board)", r"^[a-fA-F0-9]{32}:.{5}$"),
("MD5(Chap)", r"^[a-fA-F0-9]{32}:[0-9]{32}:[a-fA-F0-9]{2}$"),
[...cut out...]
("NTLM)", r"^[0-9A-Fa-f]{32}$"),
)

We then need to find a way to return the data to the user in a manageable way, without letting them know each time a non-match is found. We do this by creating a counter. We set the value of this counter to 0 and continue. We then create a function named text, which will become the value of the name of the hash, should a match be found. An if statement is then used to prevent the unwanted messages we previously mentioned. We tell the script that if text is not none then a match has been found, so we raise the value of the counter and print the text. Using the counter idea means any non-matches found will not increase the counter and therefore will not be printed to the user:

counter = 0
for h in hashes:
    text = hashcheck(h[0], h[1], string_to_check)
    if text is not None:
        counter += 1
        print text

We finish the script off by letting the user know if there is no match, in the most polite way possible!

if counter == 0:
    print "Your input hash did not match anything, sorry!"

Here are some examples of the script in action:

Please enter the hash you wish to check: ok
No Matches

The preceding result finds no matches as there is no hashing system listed that outputs two character strings. The following is an example of a successful find:

Please enter the hash you wish to check: fd7a4c43ad7c20dbea0dc6dacc12ef6c36c2c382a0111c92f24244690eba65a2
This hash matches the format of: SHA-256
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.138.120.187