Python Web Penetration Testing Cookbook - Sample Chapter
Python Web Penetration Testing Cookbook - Sample Chapter
ee
Sa
m
pl
Andrew Mabbitt is a penetration tester living in London, UK. He spends his time beating
down networks, mentoring, and helping newbies break into the industry. In his free time, he
loves to travel, break things, and master the art of sarcasm.
Preface
Welcome to our book on Python and web application testing. Penetration testing is a massive
field and the realms of Python are even bigger. We hope that our little book can help you
make these enormous fields a little more manageable. If you're a Python guru, you can look
for ideas to apply your craft to penetration testing, or if you are a newbie Pythonist with some
penetration testing chops, then you're in luck, this book is also for you.
Preface
Chapter 8, Payloads and Shells, covers a small set of proof of concept C2 channels,
basic post-exploitation scripts, and on server enumeration tools.
Chapter 9, Reporting, covers scripts that focus to make the reporting of vulnerabilities easier
and a less painful process.
Encryption and
Encoding
In this chapter, we will cover the following topics:
Identifying hashes
135
Introduction
In this chapter, we will be covering encryption and encoding in the world of Python.
Encryption and encoding are two very important aspects of web applications, so doing
them using Python!
We will be digging into the world of MD5s and SHA hashes, knocking on the door of Base64
and ROT13, and taking a look at some of the most popular hashing and ciphers out there.
We will also be turning back time and looking at some very old methods and ways to make
and break them.
Getting ready
For this script, we will only need the hashlib module.
How to do it
Generating an MD5 hash within Python is extremely simple, due to the nature of the module
we can import. We need to define the module to import and then decide which string we want
to hash. We should hard code this into the script, but this means the script would have to be
modified each time a new string has to be hashed.
Instead, we use the raw_input feature in Python to ask the user for a string:
import hashlib
message = raw_input("Enter the string you would like to hash: ")
md5 = hashlib.md5(message.encode())
print (md5.hexdigest())
How it works
The hashlib module does the bulk of the work for us behind the scenes. Hashlib is a
giant library that enables users to hash MD5, SHA1, SHA256, and SHA512, among others
extremely quickly and easily. This is the reasoning for using this module.
136
Chapter 7
We first import the module using the standard method:
import hashlib
We then need the string that we wish to MD5 encode. As mentioned earlier, this could be
hard-coded into the script but it's not extremely practical. The way around this is to ask for
the input from the user by using the raw_input feature. This can be achieved by:
message = raw_input("Enter what you wish to ask the user here: ")
Once we have the input, we can continue to encode the string using hashlib's built-in
functions. For this, we simply call the .encode() function after defining the string we are
going to be using:
md5 = hashlib.md5(message.encode())
Finally, we can print the output of the string that uses the .hexdigest() function. If we do
not use hexdigest, the hex representation of each byte will be printed.
Here is an example of the script in full swing:
Enter the string you would like to hash: pythonrules
048c0fc556088fabc53b76519bfb636e
Getting ready
Once again for these scripts, we will only be requiring the hashlib module.
How to do it
Generating SHA hashes within Python is also extremely simple by using the imported module.
With simple tweaks, we can change whether we would like to generate an SHA1, SHA128, or
SHA256 hash.
137
How it works
The hashlib module once again does the bulk of the work for us here. We can utilize the
features within the module.
We start by importing the module by using:
import hashlib
We then need to prompt for the string to encode using SHA. We ask the user for input
rather than using hard-coding, so that the script can be used over and over again.
This can be done with:
message = raw_input("Enter the string you would like to hash: )
Once we have the string, we can start the encoding process. The next part depends on the
SHA encoding that you would like to use:
sha = hashlib.sha*(message)
138
Chapter 7
We need to replace * with either 1, 128, or 256. Once we have the message SHA-encoded,
we need to use the hexdigest() function once again so the output becomes readable.
We do this with:
sha*=sha.hexdigest()
Once the output has become readable, we simply need to print the hash output:
print sha*
Getting ready
For the following script, we will only require the hashlib module.
How to do it
We are going to tie everything previously done together to form one big script. This will output
three versions of SHA hashes and also an MD5 hash, so the user can choose which one they
would like to use:
import hashlib
message = raw_input("Enter the string you would like to hash: ")
md5 = hashlib.md5(message)
md5 = md5.hexdigest()
sha1 = hashlib.sha1(message)
sha1 = sha1.hexdigest()
sha256 = hashlib.sha256(message)
sha256 = sha256.hexdigest()
139
How it works
Once again, after importing the correct module into this script, we need to receive the user
input that we wish to turn into an encoded string:
import hashlib
message = raw_input('Please enter the string you would like to
hash: ')
From here, we can start sending the string through all of the different encoding methods and
ensuring they are passed through hexdigest() so the output becomes readable:
md5 = hashlib.md5(message)
md5 = md5.hexdigest()
sha1 = hashlib.sha1(message)
sha1 = sha1.hexdigest()
sha256 = hashlib.sha256(message)
sha256 = sha256.hexdigest()
sha512 = hashlib.sha512(message)
sha512 = sha512.hexdigest()
Once we have created all of the encoded strings, it is simply a matter of printing each of these
to the user:
print
print
print
print
print
140
Chapter 7
Here is an example of the script in action:
Enter the string you would like to hash: test
MD5 Hash = 098f6bcd4621d373cade4e832627b4f6
SHA1 Hash= a94a8fe5ccb19ba61c4c0873d391e987982fbbd3
SHA256 Hash=
9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08
SHA512 Hash=
ee26b0dd4af7e749aa1a8ee3c10ae9923f618980772e473f8819a5d4940e0
db27ac185f8a0e1d5f84f88bc887fd67b143732c304cc5fa9ad8e6f57f50028a8ff
End of list.
Getting ready
For this script, we will need the hashlib library and the uuid library.
How to do it
For this real-world example, we will be implementing an SHA256 encoding scheme and
generating a salt to make it even more secure by defeating precomputed hash tables.
We will then run it through password-checking to ensure the password was typed correctly:
#!/usr/bin/python
import uuid
import hashlib
# Let's do the hashing. We create a salt and append it to the
password once hashes.
def hash(password):
salt = uuid.uuid4().hex
return hashlib.sha512(salt.encode() +
password.encode()).hexdigest() + ':' + salt
# Let's confirm that worked as intended.
141
How it works
To begin the script, we need to import the correct libraries:
import uuid
import hashlib
We then need to define the function that will hash the password. We start by creating a
salt, using the uuid library. Once the salt has been generated, we use hashlib.sha256
to string together the salt encode and the password encode and make it readable by using
hexdigest and finally appending the salt to the end of it:
def hash(password):
salt = uuid.uuid4().hex
return hashlib.sha512(salt.encode() +
password.encode()).hexdigest() + ':' + salt
Next, we move onto the check password function. This is what is going to confirm our original
password is the same as the second one to ensure there were no mistakes. This is done by
using the same method as before:
def check(hashed, p2):
password, salt = hashed.split(':')
return password == hashlib.sha512(salt.encode() +
p2.encode()).hexdigest()
142
Chapter 7
Once we have created the blocks of code that we need, we can then start asking the user
for the required input. We start off by asking for the original password and using the hash_
password function to create the hash. This then gets printed out to the user. After the first
password has been done, we ask for the password again to ensure there has been no spelling
mistakes. The check_password function then hashes the password again and compares the
original to the new one. If they match, the user is informed that the password is correct; if not,
the user is informed that the passwords do not match:
password = raw_input('Please enter a password: ')
hashed = hash(password)
print('The string to store in the db is: ' + hashed)
re = raw_input('Please re-enter your password: ')
if check(hashed, re):
print('Password Match')
else:
print('Password Mismatch')
The preceding result is an example of a user enter the same password twice. Here is an
example of the user failing to enter the same password:
Please enter a password: password1
The string to store in the db is:
418bba0beeaef52ce523dafa9b19baa449562cf034ebd1e4fea8c007dd49cb
1004e10b837f13d59b13236c54668e44c9d0d8dbd03e32cd8afad6eff04541
ed07:1d9cd2d9de5c46068b5c2d657ae45849
Please re-enter your password: password
Password Mismatch
143
Getting ready
For this script, we will be using the bcrypt module within Python. This can be installed by
using either pip or easy_install, albeit you will want to ensure version 0.4 is installed and
not version 1.1.1, as version 1.1.1 removes some functionality from the Bcrypt module.
How to do it
Generating Bcrypt hashes within Python is similar to generating other hashes such as
SHA and MD5, but also slightly different. Like the other hashes, we can either prompt the
user for a password or hard-code it into the script. The hashing in Bcrypt is more complex
due to the use of randomly generated salts, which get appended to the original hash. This
increases the complexity of the hash and therefore increases the security of the password
stored within the hash function.
This script also has a checking module at the end, which relates to a real-world example.
It requests the user to re-enter the password they want to hash and ensures that it matches
the original input. Password confirmation is a very common practice among many developers
and in the modern age, nearly every registration form uses this:
import bcrypt
# Let's first enter a password
new = raw_input('Please enter a password: ')
# We'll encrypt the password with bcrypt with the default salt
value of 12
hashed = bcrypt.hashpw(new, bcrypt.gensalt())
# We'll print the hash we just generated
print('The string about to be stored is: ' + hashed)
# Confirm we entered the correct password
plaintext = raw_input('Please re-enter the password to check: ')
# Check if both passwords match
if bcrypt.hashpw(plaintext, hashed) == hashed:
print 'It\'s a match!'
else:
print 'Please try again.'
144
Chapter 7
How it works
We start the script off by importing the required module. In this case, we only need the
bcrypt module:
import bcrypt
We can then request the input from the user by using the standard raw_input method:
new = raw_input('Please enter a password: ')
After we have the input, we can get down to the nitty gritty hashing methods. To begin with,
we use the bcrypt.hashpw function to hash the input. We then give it the value of the
inputted password and then also randomly generate a salt, using bcrypt.gensalt().
This can be achieved by using:
hashed = bcrypt.hashpw(new, bcrypt.gensalt())
We then print the hashed value out to the user, so they can see the hash that has
been generated:
print ('The string about to be stored is: ' + hashed)
Now, we start the password confirmation. We have to prompt the user for the password
again so that we can confirm that they entered it correctly:
plaintext = raw_input('Please re-enter the password to check: ')
Once we have the password, we check whether both passwords match by using
the == feature within Python:
If bcrypt.hashpw(plaintext, hashed) == hashed:
print "It\'s a match"
else:
print "Please try again".
145
Getting ready
For this script, we will only need the hashlib module.
How to do it
To start cracking the MD5 hashes, we need to load a file containing a list of words that
will be encrypted in MD5. This will allow us to loop through the hashes and check whether
we have a match:
import hashlib
target = raw_input("Please enter your hash here: ")
dictionary = raw_input("Please enter the file name of your
dictionary: ")
def main():
with open(dictionary) as fileobj:
for line in fileobj:
line = line.strip()
if hashlib.md5(line).hexdigest() == target:
print "Hash was successfully cracked %s: The value
is %s" % (target, line)
return ""
print "Failed to crack the file."
if __name__ == "__main__":
main()
146
Chapter 7
How it works
We first start by loading the module into Python as normal:
import hashlib
We need user input for both the hash we would like to crack and also the name of the
dictionary we are going to load to crack against:
target = raw_input("Please enter your hash here: ")
dictionary = raw_input("Please enter the file name of your
dictionary: ")
Once we have the hash we would like to crack and the dictionary, we can continue with the
encoding. We need to open the dictionary file and encode each string, one by one. We can
then check to see whether any of the hashes match the original one we are aiming to crack. If
there is a match, our script will then inform us and give us the value:
def main():
with open(dictionary) as fileobj:
for line in fileobj:
line = line.strip()
if hashlib.md5(line).hexdigest() == target:
print "Hash was successfully cracked %s: The value
is %s" % (target, line)
return ""
print "Failed to crack the file."
147
Getting ready
Thankfully for the Base64 encoding, we do not require any external modules.
How to do it
To generate the Base64 encoded string, we can use default Python features to help us
achieve it:
#!/usr/bin/python
msg = raw_input('Please enter the string to encode: ')
print "Your B64 encoded string is: " + msg.encode('base64')
How it works
Encoding a string in Base64 within Python is very simple and can be done in a two-line
script. To begin we need to have the string fed to us as a user input so we have something
to work with:
msg = raw_input('Please enter the string to encode: ')
Once we have the string, we can do the encoding as we print out the result, using
msg.encode('base64'):
print "Your B64 encoded string is: " + msg.encode('base64')
148
Chapter 7
Getting ready
For this script, we will need quite specific modules. We will be needing the maketrans
feature, and the lowercase and uppercase features from the string module.
How to do it
To use the ROT13 encoding method, we need to replicate what the ROT13 cipher actually
does. The 13 indicates that each letter will be moved 13 places along the alphabet scale,
which makes the encoding very easy to reverse:
from string import maketrans, lowercase, uppercase
def rot13(message):
lower = maketrans(lowercase, lowercase[13:] + lowercase[:13])
upper = maketrans(uppercase, uppercase[13:] + uppercase[:13])
return message.translate(lower).translate(upper)
message = raw_input('Enter :')
print rot13(message)
How it works
This is the first of our scripts that doesn't simply require the hashlib module; instead it
requires specific features from a string. We can import these using the following:
from string import maketrans, lowercase, uppercase
Next, we can create a block of code to do the encoding for us. We use the maketrans
feature of Python to tell the interpreter to move the letters 13 places across and to keep
uppercase within the uppercase and lower within the lower. We then request that it returns
the value to us:
def rot13(message):
lower = maketrans(lowercase, lowercase[13:] + lowercase[:13])
upper = maketrans(uppercase, uppercase[13:] + uppercase[:13])
return message.translate(lower).translate(upper)
149
Once we have the user input, we can then print out the value of our string being passed
through our rot13 block of code:
print rot13(message)
Getting ready
For this script, there is no requirement for any external libraries.
150
Chapter 7
How to do it
To solve this problem, we run our string against values in our periodic dictionary and
transformed the discovered values into their ascii form. This in returned the output of
our final answer:
string =
"TaPoGeTaBiGePoHfTmGeYbAtPtHoPoTaAuPtGeAuYbGeBiHoTaTmPtHoTmGePoA
uGeErTaBiHoAuRnTmPbGePoHfTmGeTmRaTaBiPoTmPtHoTmGeAuYbGeTbGeLuTmP
tTmPbTbOsGePbTmTaLuPtGeAuYbGeAuPbErTmPbGeTaPtGePtTbPoAtPbTmGeTbP
tErGePoAuGeYbTaPtErGePoHfTmGeHoTbAtBiTmBiGeLuAuRnTmPbPtTaPtLuGeP
oHfTaBiGeAuPbErTmPbPdGeTbPtErGePoHfTaBiGePbTmYbTmPbBiGeTaPtGeTmT
lAtTbOsGeIrTmTbBiAtPbTmGePoAuGePoHfTmGePbTmOsTbPoTaAuPtBiGeAuYbG
eIrTbPtGeRhGeBiAuHoTaTbOsGeTbPtErGeHgAuOsTaPoTaHoTbOsGeRhGeTbPtE
rGePoAuGePoHfTmGeTmPtPoTaPbTmGeAtPtTaRnTmPbBiTmGeTbBiGeTbGeFrHfA
uOsTmPd"
n=2
list = []
answer = []
[list.append(string[i:i+n]) for i in range(0, len(string), n)]
print set(list)
periodic ={"Pb": 82, "Tl": 81, "Tb": 65, "Ta": 73, "Po": 84, "Ge":
32, "Bi": 83, "Hf": 72, "Tm": 69, "Yb": 70, "At": 85, "Pt": 78,
"Ho": 67, "Au": 79, "Er": 68, "Rn": 86, "Ra": 88, "Lu": 71,
"Os": 76, "Tl": 81, "Pd": 46, "Rh": 45, "Fr": 87, "Hg": 80,
"Ir": 77}
for value in list:
if value in periodic:
answer.append(chr(periodic[value]))
lastanswer = ''.join(answer)
print lastanswer
151
How it works
To start this script off, we first defined the key string within the script. The n variable was then
defined as 2 for later use and two empty lists were created list and answer:
string = --snipped-n=2
list = []
answer = []
We then started to create the list, which ran through the string and pulled out the sets of two
letters and appended them to the list value, which was then printed:
[list.append(string[i:i+n]) for i in range(0, len(string), n)]
print set(list)
Each of the two letters corresponded to a value in the periodic table, which relates to a
number. Those numbers when transformed into ascii related to a character. Once this was
discovered, we needed to map the elements to their periodic number and store that:
periodic ={"Pb": 82, "Tl": 81, "Tb": 65, "Ta": 73, "Po": 84, "Ge":
32, "Bi": 83, "Hf": 72, "Tm": 69, "Yb": 70, "At": 85, "Pt": 78,
"Ho": 67, "Au": 79, "Er": 68, "Rn": 86, "Ra": 88, "Lu": 71,
"Os": 76, "Tl": 81, "Pd": 46, "Rh": 45, "Fr": 87, "Hg": 80,
"Ir": 77}
We are then able to create a loop that will go through the list of elements that we previously
created and named as list, and map them to the value in the periodic set of data that we
created. As this is running, we can have it append the findings into our answer string while
transforming the ascii number to the relevant letter:
for value in list:
if value in periodic:
answer.append(chr(periodic[value]))
Chapter 7
Getting ready
For this, we will only need the string module.
How to do it
Since the Atbash cipher works by using the opposite value of a character in the alphabet,
we can create a maketrans feature to substitute characters:
import string
input = raw_input("Please enter the value you would like to Atbash
Cipher: ")
transform = string.maketrans(
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz",
"ZYXWVUTSRQPONMLKJIHGFEDCBAzyxwvutsrqponmlkjihgfedcba")
final = string.translate(input, transform)
print final
How it works
After importing the correct module, we request the input from the user for the value they
would like encipher into the Atbash cipher:
import string
input = raw_input("Please enter the value you would like to Atbash
Ciper: ")
Next, we create the maketrans feature to be used. We do this by listing the first set of
characters that we would like to be substituted and then listing another set of characters
that we will use to replace the previous ones:
transform = string.maketrans(
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz",
"ZYXWVUTSRQPONMLKJIHGFEDCBAzyxwvutsrqponmlkjihgfedcba")
Finally, we just need to give a value to the transformation, apply it, and print the value out to
get the end result:
final = string.translate(input, transform)
print final
153
Getting ready
Put a list of XORed phrases in a file. Place that file in the same folder as your script
(or don't; it just makes it marginally easier if you do).
How to do it
The script should look something like this:
import sys
import string
f = open("ciphers.txt", "r")
MSGS = f.readlines()
def strxor(a, b):
if len(a) > len(b):
return "".join([chr(ord(x) ^ ord(y)) for (x, y) in
zip(a[:len(b)], b)])
else:
154
Chapter 7
return "".join([chr(ord(x) ^ ord(y)) for (x, y) in zip(a,
b[:len(a)])])
def encrypt(key, msg):
c = strxor(key, msg)
return c
for msg in MSGS:
for value in string.ascii_letters:
for value2 in string.ascii_letters:
for value3 in string.ascii_letters:
key = value+value2+value3
answer = encrypt(msg, key)
print answer[3:]
How it works
This script is pretty straightforward. We open a file with the XORed values in them and
split it by lines:
f = open("ciphers.txt", "r")
MSGS = f.readlines()
We shamelessly use the industry standard XOR python. Basically, this function equates two
strings to the same length and XOR them together:
def strxor(a, b):
if len(a) > len(b):
return "".join([chr(ord(x) ^ ord(y)) for (x, y) in
zip(a[:len(b)], b)])
else:
return "".join([chr(ord(x) ^ ord(y)) for (x, y) in zip(a,
b[:len(a)])])
def encrypt(key, msg):
c = strxor(key, msg)
return c
155
We then encrypt the line with the generated key and print it out. We can pipe this a file with
ease, as we've shown throughout the book already:
answer = encrypt(msg, key)
print answer[3:]
X n +1 = ( aX n + c ) mod m
Here, X is the current value, a is a fixed multiplier, c is a fixed increment, and m is a fixed
modulus. If any data is leaked, such as the multiplier, modulus, and increment in this
example, it is possible to calculate the seed and thus the next values.
Getting ready
The situation here is where an application is generating random 2-digit numbers and
returning them to you. You have the multiplier, modulus, and increment. This may seem
strange, but this has happened in live tests.
How to do it
Here is the code:
C = ""
A = ""
M = ""
156
Chapter 7
print "Starting attempt to brute"
for i in range(1, 99999999):
a = str((A * int(str(i)+'00') + C) % 2**M)
if a[-2:] == "47":
b = str((A * int(a) + C) % 2**M)
if b[-2:] == "46":
c = str((A * int(b) + C) % 2**M)
if c[-2:] == "57":
d = str((A * int(c) + C) % 2**M)
if d[-2:] == "56":
e = str((A * int(d) + C) % 2**M)
if e[-2:] == "07":
f = str((A * int(e) + C) % 2**M)
if f[-2:] == "38":
g = str((A * int(f) + C) % 2**M)
if g[-2:] == "81":
h = str((A * int(g) + C) % 2**M)
if h[-2:] == "32":
j = str((A * int(h) + C) %
2**M)
if j[-2:] == "19":
k = str((A * int(j) + C) %
2**M)
if k[-2:] == "70":
l = str((A * int(k) +
C) % 2**M)
if l[-2:] == "53":
print "potential
number found: "+l
print "next 9 values are:"
for i in range(1, 10):
l = str((A * int(l) + C) % 2**M)
print l[-2:]
How it works
We set our three values, the increment, the multiplier, and the modulo as C, A, and M
respectively:
C = ""
A = ""
M = ""
157
We then perform our first LCG transformation and generate possible values with the first
value taken from the web page marked highlighted in the following example:
a = str((A * int(str(i)+'00') + C) % 2**M)
We take the second value generated by the web page and check the outcome of this
transform against that:
if a[-2:] == "47":
If it works, we then perform the next transform with the numbers that matched the
first transform:
b = str((A * int(a) + C) % 2**M)
We repeat this process 10 times here, but it can be reproduced as many times as
necessary until we find an output that has matched all the numbers so far. We print an
alert with that number:
print "potential number found: "+l
We then repeat the process 10 more times, with that number as the seed to generate the
next 10 values to allow us to predict the new values.
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.
158
Chapter 7
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-Z09\/\.]{53}$"),
("Blowfish crypt", r"^\$2[axy]{0,1}\$[a-zA-Z0-9./]{8}\$[a-zA-Z09./]{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-Z09./]{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}$"),
159
160
Chapter 7
("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
161
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-Z09\/\.]{53}$"),
("Blowfish crypt", r"^\$2[axy]{0,1}\$[a-zA-Z0-9./]{8}\$[a-zA-Z09./]{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-Z09./]{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}$"),
)
162
Chapter 7
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!"
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
163
www.PacktPub.com
Stay Connected: