Skip to content
Permalink
0fefd4ccf6
Switch branches/tags

Name already in use

A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
Go to file
 
 
Cannot retrieve contributors at this time
150 lines (105 sloc) 4.01 KB
"""
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