Skip to content

Commit 86318b0

Browse files
9_Typesof_Hash_Craker Added
1 parent 5d7c104 commit 86318b0

File tree

2 files changed

+244
-0
lines changed

2 files changed

+244
-0
lines changed
Lines changed: 244 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,244 @@
1+
#!/usr/bin/env python3
2+
3+
import hashlib
4+
import itertools
5+
import multiprocessing
6+
import os
7+
import string
8+
import threading
9+
import time
10+
11+
12+
class Cracker(object):
13+
ALPHA_LOWER = (string.ascii_lowercase,)
14+
ALPHA_UPPER = (string.ascii_uppercase,)
15+
ALPHA_MIXED = (string.ascii_lowercase, string.ascii_uppercase)
16+
PUNCTUATION = (string.punctuation,)
17+
NUMERIC = (''.join(map(str, range(0, 10))),)
18+
ALPHA_LOWER_NUMERIC = (string.ascii_lowercase, ''.join(map(str, range(0, 10))))
19+
ALPHA_UPPER_NUMERIC = (string.ascii_uppercase, ''.join(map(str, range(0, 10))))
20+
ALPHA_MIXED_NUMERIC = (string.ascii_lowercase, string.ascii_uppercase, ''.join(map(str, range(0, 10))))
21+
ALPHA_LOWER_PUNCTUATION = (string.ascii_lowercase, string.punctuation)
22+
ALPHA_UPPER_PUNCTUATION = (string.ascii_uppercase, string.punctuation)
23+
ALPHA_MIXED_PUNCTUATION = (string.ascii_lowercase, string.ascii_uppercase, string.punctuation)
24+
NUMERIC_PUNCTUATION = (''.join(map(str, range(0, 10))), string.punctuation)
25+
ALPHA_LOWER_NUMERIC_PUNCTUATION = (string.ascii_lowercase, ''.join(map(str, range(0, 10))), string.punctuation)
26+
ALPHA_UPPER_NUMERIC_PUNCTUATION = (string.ascii_uppercase, ''.join(map(str, range(0, 10))), string.punctuation)
27+
ALPHA_MIXED_NUMERIC_PUNCTUATION = (
28+
string.ascii_lowercase, string.ascii_uppercase, ''.join(map(str, range(0, 10))), string.punctuation
29+
)
30+
31+
def __init__(self, hash_type, hash, charset, progress_interval):
32+
"""
33+
Sets the hash type and actual hash to be used
34+
:param hash_type: What algorithm we want to use
35+
:param hash: The hash in base64 format
36+
:return:
37+
"""
38+
self.__charset = charset
39+
self.__curr_iter = 0
40+
self.__prev_iter = 0
41+
self.__curr_val = ""
42+
self.__progress_interval = progress_interval
43+
self.__hash_type = hash_type
44+
self.__hash = hash
45+
self.__hashers = {}
46+
47+
def __init_hasher(self):
48+
hashlib_type = self.__hash_type if self.__hash_type != "ntlm" else "md4"
49+
self.__hashers[self.__hash_type] = hashlib.new(hashlib_type)
50+
51+
def __encode_utf8(self, data):
52+
return data.encode("utf-8")
53+
54+
def __encode_utf16le(self, data):
55+
return data.encode("utf-16le")
56+
57+
@staticmethod
58+
def __search_space(charset, maxlength):
59+
"""
60+
Generates the search space for us to attack using a generator
61+
We could never pregenerate this as it would take too much time and require godly amounts of memory
62+
For example, generating a search space with a rough size of 52^8 would take over 50TB of RAM
63+
:param charset: The character set to generate a search space for
64+
:param maxlength: Maximum length the search space should be capped at
65+
:return:
66+
"""
67+
return (
68+
''.join(candidate) for candidate in
69+
itertools.chain.from_iterable(
70+
itertools.product(charset, repeat=i) for i in
71+
range(1, maxlength + 1)
72+
)
73+
)
74+
75+
def __attack(self, q, max_length):
76+
"""
77+
Tries all possible combinations in the search space to try and find a match.
78+
This is an extremely tight loop so we need to inline and reduce work as much as we can in here.
79+
:param q: Work queue
80+
:param max_length: Maximum length of the character set to attack
81+
:return:
82+
"""
83+
self.__init_hasher()
84+
self.start_reporting_progress()
85+
hash_fn = self.__encode_utf8 if self.__hash_type != "ntlm" else self.__encode_utf16le
86+
for value in self.__search_space(self.__charset, max_length):
87+
hasher = self.__hashers[self.__hash_type].copy()
88+
self.__curr_iter += 1
89+
self.__curr_val = value
90+
hasher.update(hash_fn(value))
91+
if self.__hash == hasher.hexdigest():
92+
q.put("FOUND")
93+
q.put("{}Match found! Password is {}{}".format(os.linesep, value, os.linesep))
94+
self.stop_reporting_progress()
95+
return
96+
97+
q.put("NOT FOUND")
98+
self.stop_reporting_progress()
99+
100+
@staticmethod
101+
def work(work_q, done_q, max_length):
102+
"""
103+
Take the data given to us from some process and kick off the work
104+
:param work_q: This is what will give us work from some other process
105+
:param done_q: Used to signal the parent from some other process when we are done
106+
:param max_length: Maximum length of the character set
107+
:return:
108+
"""
109+
obj = work_q.get()
110+
obj.__attack(done_q, max_length)
111+
112+
def start_reporting_progress(self):
113+
self.__progress_timer = threading.Timer(self.__progress_interval, self.start_reporting_progress)
114+
self.__progress_timer.start()
115+
print(
116+
f"Character set: {self.__charset}, iteration: {self.__curr_iter}, trying: {self.__curr_val}, hashes/sec: {self.__curr_iter - self.__prev_iter}",
117+
flush=True)
118+
self.__prev_iter = self.__curr_iter
119+
120+
def stop_reporting_progress(self):
121+
self.__progress_timer.cancel()
122+
print(f"Finished character set {self.__charset} after {self.__curr_iter} iterations", flush=True)
123+
124+
125+
if __name__ == "__main__":
126+
character_sets = {
127+
"01": Cracker.ALPHA_LOWER,
128+
"02": Cracker.ALPHA_UPPER,
129+
"03": Cracker.ALPHA_MIXED,
130+
"04": Cracker.NUMERIC,
131+
"05": Cracker.ALPHA_LOWER_NUMERIC,
132+
"06": Cracker.ALPHA_UPPER_NUMERIC,
133+
"07": Cracker.ALPHA_MIXED_NUMERIC,
134+
"08": Cracker.PUNCTUATION,
135+
"09": Cracker.ALPHA_LOWER_PUNCTUATION,
136+
"10": Cracker.ALPHA_UPPER_PUNCTUATION,
137+
"11": Cracker.ALPHA_MIXED_PUNCTUATION,
138+
"12": Cracker.NUMERIC_PUNCTUATION,
139+
"13": Cracker.ALPHA_LOWER_NUMERIC_PUNCTUATION,
140+
"14": Cracker.ALPHA_UPPER_NUMERIC_PUNCTUATION,
141+
"15": Cracker.ALPHA_MIXED_NUMERIC_PUNCTUATION
142+
}
143+
144+
hashes = {
145+
"01": "MD5",
146+
"02": "MD4",
147+
"03": "LM",
148+
"04": "NTLM",
149+
"05": "SHA1",
150+
"06": "SHA224",
151+
"07": "SHA256",
152+
"08": "SHA384",
153+
"09": "SHA512"
154+
}
155+
156+
prompt = "Specify the character set to use:{}{}".format(os.linesep, os.linesep)
157+
for key, value in sorted(character_sets.items()):
158+
prompt += "{}. {}{}".format(key, ''.join(value), os.linesep)
159+
160+
while True:
161+
try:
162+
charset = input(prompt).zfill(2)
163+
selected_charset = character_sets[charset]
164+
except KeyError:
165+
print("{}Please select a valid character set{}".format(os.linesep, os.linesep))
166+
continue
167+
else:
168+
break
169+
170+
prompt = "{}Specify the maximum possible length of the password: ".format(os.linesep)
171+
172+
while True:
173+
try:
174+
password_length = int(input(prompt))
175+
except ValueError:
176+
print("{}Password length must be an integer".format(os.linesep))
177+
continue
178+
else:
179+
break
180+
181+
prompt = "{}Specify the hash's type:{}".format(os.linesep, os.linesep)
182+
for key, value in sorted(hashes.items()):
183+
prompt += "{}. {}{}".format(key, value, os.linesep)
184+
185+
while True:
186+
try:
187+
hash_type = hashes[input(prompt).zfill(2)]
188+
except KeyError:
189+
print("{}Please select a supported hash type".format(os.linesep))
190+
continue
191+
else:
192+
break
193+
194+
prompt = "{}Specify the hash to be attacked: ".format(os.linesep)
195+
196+
while True:
197+
try:
198+
user_hash = input(prompt)
199+
except ValueError:
200+
print("{}Something is wrong with the format of the hash. Please enter a valid hash".format(os.linesep))
201+
continue
202+
else:
203+
break
204+
205+
print(f"Trying to crack hash {user_hash}", flush=True)
206+
processes = []
207+
work_queue = multiprocessing.Queue()
208+
done_queue = multiprocessing.Queue()
209+
progress_interval = 3
210+
cracker = Cracker(hash_type.lower(), user_hash.lower(), ''.join(selected_charset), progress_interval)
211+
start_time = time.time()
212+
p = multiprocessing.Process(target=Cracker.work,
213+
args=(work_queue, done_queue, password_length))
214+
processes.append(p)
215+
work_queue.put(cracker)
216+
p.start()
217+
218+
if len(selected_charset) > 1:
219+
for i in range(len(selected_charset)):
220+
progress_interval += .2
221+
cracker = Cracker(hash_type.lower(), user_hash.lower(), selected_charset[i], progress_interval)
222+
p = multiprocessing.Process(target=Cracker.work,
223+
args=(work_queue, done_queue, password_length))
224+
processes.append(p)
225+
work_queue.put(cracker)
226+
p.start()
227+
228+
failures = 0
229+
while True:
230+
data = done_queue.get()
231+
if data == "NOT FOUND":
232+
failures += 1
233+
elif data == "FOUND":
234+
print(done_queue.get())
235+
for p in processes:
236+
p.terminate()
237+
238+
break
239+
240+
if failures == len(processes):
241+
print("{}No matches found{}".format(os.linesep, os.linesep))
242+
break
243+
244+
print("Took {} seconds".format(time.time() - start_time))

9_Typesof_Hash_Craker/README.md

Whitespace-only changes.

0 commit comments

Comments
 (0)