Skip to content
Permalink
Browse files
Tests for Bcrypt and Salted versions added
  • Loading branch information
aa9863 committed Oct 18, 2021
1 parent 90b6418 commit 0fefd4ccf61197171dcf9699611f3301901d0dac
Show file tree
Hide file tree
Showing 2 changed files with 284 additions and 0 deletions.
@@ -0,0 +1,134 @@
"""
Test of the code with Bcrypt hashes
Almost exactly the same as the test_salted cases.
But as Bcrypt deals with salts automagically, we dont really need to worry about that.
"""

import unittest
import time
import hashlib

import bcrypt

TARGETS = [b'$2b$12$CQdfZxhjQH83jY.RZNfGPegUWSL05J5Amekp5pmjXkAgvtuBynZzy',
b'$2b$12$4YiyGckKJXQGKMkGwOqM5.JKTjJIMlwwdfHYHtLhfc38lFU9iZicK',
b'$2b$12$8vMax8thp0D7G2yns8gY7unUmdZiiJKJrjhW7pRtoo.X4YDObRVCK',
b'$2b$12$JE6BgAfbDdLgWNnMuaqyeuvhmXECqsnZPMXqO3Rv.pxrzT8Zn4wqy',
b'$2b$12$YINaek/4qU8/k2egmjZxn.MYU7lVehNi8p94iEDFvM7MaS3BUlZKK']

CORRECT_WORDS = [b"coffee",b"azerty",b"spitfire", b"f00tball", b"1qazxsw23edc"]

CORRECT_MATCHES = dict(zip(TARGETS, CORRECT_WORDS))




def crackHash_Salt(wordlist, target):
"""
This time we have a list of salted hashes.
It means we can no longer take the approach we used before, of
cracking all hashes and stashing in a dictionary.
Instead we extract the salt, append it to each of the items in the wordlist
Then check if we get a match
"""


#Go through the wordlist and get the hash for each item.
for plaintext in wordlist:
plaintext = plaintext.strip().encode() #Damn Bytestrings

if bcrypt.checkpw(plaintext, target):
return plaintext


def crackList_Salt(wordlist, targets):
"""
Use the strategy above to crack a list of passowords that have been salted
As we cant store we just iterate through the list, and collect the details
"""

matches = {}

for item in targets:
cracked = crackHash_Salt(wordlist, item)
matches[item] = cracked

return matches







class TestCases(unittest.TestCase):
@classmethod
def setUpClass(cls):
"""
A bit of magic to keep the stats.
Called the first time the class is run
"""

cls.statsDict = {}

@classmethod
def tearDownClass(cls):
"""
And a bit more magic to print the stats.
"""

print("\n\n{0} STATS (Bcrypt Salted) {0}".format("-"*20))

print("Crack Single Salted : {0}".format(cls.statsDict["singleSalt"]))
print("Crack List of Salted: {0}".format(cls.statsDict["saltList"]))

def setUp(self):
"""
Load the wordlist each time we run a test case
Here we open the wordlist file, then store it as an array
This lets us reuse the list multiple times
"""

with open("10-million-password-list-top-10000.txt") as fd:
#Store as an array
self.wordlist = fd.readlines()

#SO I am going to cheat here and use a very abbreviated wordlist (Otherwis it takes forever)
self.wordlist = ["foo","bar","coffee" ,"azerty","spitfire", "f00tball", "1qazxsw23edc"]

def testSingle_ProperSalt(self):
"""
Check how long it takes to crack a single salted hash
"""

t1 = time.time()
out = crackHash_Salt(self.wordlist, TARGETS[0])
t2 = time.time()
out = out.decode() #Remember Bytes
self.assertEqual(out, "coffee") #check we were successful (And remember Bytes here)
self.statsDict["singleSalt"] = t2-t1


def testList_ProperSalt(self):
"""
And Crack a list of salted passwords
"""
t1 = time.time()
out = crackList_Salt(self.wordlist, TARGETS)
t2 = time.time()
self.assertEqual(out, CORRECT_MATCHES) #check we were successful
self.statsDict["saltList"] = t2-t1




@@ -0,0 +1,150 @@
"""
Test Cases and Demo code for Salted Hashed
"""

import unittest
import time
import hashlib

TARGETS = ["rkKxLR$c35bf00b953186ec3be4916dd45deabc",
"MyEtoS$b0a56a1df2353c7629509a12a17f5a2d",
"kshhHk$9664b09bedf4ed95f1b7b024087cec12",
"FMVbrf$6751d1e9a8ee57b383836596869cd94a",
"wKBUOm$981132067d1a4ef9e943b8c300071a55"
]

CORRECT_WORDS = ["coffee","azerty","spitfire", "f00tball","1qazxsw23edc"]

CORRECT_MATCHES = dict(zip(TARGETS, CORRECT_WORDS))

# CORRECT_MATCHES = {'283140d63e0937fb652ff7066bbf5c2f': 'coffee',
# 'ba7c94b0431f30103c7eb5cdae180be6': 'azerty',
# 'ff0e0cefdceb54618f47767d17b95a12': 'spitfire',
# 'ef98a984f8ab1341039f9f3344d80298': 'f00tball',
# '25e2262b5d8c95f7ece0bc4f30f5213d': '1qazxsw23edc'}



def extractSalt(theHash):
"""
Break a Salted Hash into its compoenents.
We are using the convention of <salt>$<hash>
so can simply split on $
@return: Tuple of [salt, hash]
"""

return theHash.split("$")
#And modify the function with lookups we used before


def applySalt(plaintext, salt):
"""
Apply some Salt to the plaintext
"""
return "{0}{1}".format(plaintext, salt)

def crackHash_Salt(wordlist, target):
"""
This time we have a list of salted hashes.
It means we can no longer take the approach we used before, of
cracking all hashes and stashing in a dictionary.
Instead we extract the salt, append it to each of the items in the wordlist
Then check if we get a match
"""

#Fetch the Salt from the first item
theSalt, targetNoSalt = extractSalt(target)
#print("Extract Salt {0} and Hash {1}".format(theSalt, targetNoSalt))

#Go through the wordlist and get the hash for each item.
for plaintext in wordlist:
plaintext = plaintext.strip()
saltedText = applySalt(plaintext, theSalt)
theHash = hashlib.md5(saltedText.encode()).hexdigest()
#Store in the "Database"
if theHash == targetNoSalt:
return plaintext



def crackList_Salt(wordlist, targets):
"""
Use the strategy above to crack a list of passowords that have been salted
As we cant store we just iterate through the list, and collect the details
"""

matches = {}

for item in targets:
cracked = crackHash_Salt(wordlist, item)
matches[item] = cracked

return matches


class TestCases(unittest.TestCase):
@classmethod
def setUpClass(cls):
"""
A bit of magic to keep the stats.
Called the first time the class is run
"""

cls.statsDict = {}

@classmethod
def tearDownClass(cls):
"""
And a bit more magic to print the stats.
"""

print("\n\n{0} STATS (Salted) {0}".format("-"*20))

print("Crack Single Salted : {0}".format(cls.statsDict["singleSalt"]))
print("Crack List of Salted: {0}".format(cls.statsDict["saltList"]))

def setUp(self):
"""
Load the wordlist each time we run a test case
Here we open the wordlist file, then store it as an array
This lets us reuse the list multiple times
"""

with open("10-million-password-list-top-10000.txt") as fd:
#Store as an array
self.wordlist = fd.readlines()


def testSingle_ProperSalt(self):
"""
Check how long it takes to crack a single salted hash
"""

t1 = time.time()
out = crackHash_Salt(self.wordlist, TARGETS[0])
t2 = time.time()
self.assertEqual(out, "coffee") #check we were successful
self.statsDict["singleSalt"] = t2-t1


def testList_ProperSalt(self):
"""
And Crack a list of salted passwords
"""
t1 = time.time()
out = crackList_Salt(self.wordlist, TARGETS)
t2 = time.time()
self.assertEqual(out, CORRECT_MATCHES) #check we were successful
self.statsDict["saltList"] = t2-t1



0 comments on commit 0fefd4c

Please sign in to comment.