Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
201 changes: 97 additions & 104 deletions rulegen.py
Original file line number Diff line number Diff line change
Expand Up @@ -204,22 +204,23 @@ def __init__(self, language="en", providers="aspell,myspell", basename='analysis

########################################################################
# Common numeric and special character substitutions (1337 5p34k)
self.leet = dict()
self.leet["1"] = "i"
self.leet["2"] = "z"
self.leet["3"] = "e"
self.leet["4"] = "a"
self.leet["5"] = "s"
self.leet["6"] = "b"
self.leet["7"] = "t"
self.leet["8"] = "b"
self.leet["9"] = "g"
self.leet["0"] = "o"
self.leet["!"] = "i"
self.leet["|"] = "i"
self.leet["@"] = "a"
self.leet["$"] = "s"
self.leet["+"] = "t"
self.leet = {
'1': 'i',
'2': 'z',
'3': 'e',
'4': 'a',
'5': 's',
'6': 'b',
'7': 't',
'8': 'b',
'9': 'g',
'0': 'o',
'!': 'i',
'|': 'i',
'@': 'a',
'$': 's',
'+': 't'
}

########################################################################
# Preanalysis rules to bruteforce for each word
Expand Down Expand Up @@ -391,8 +392,8 @@ def generate_words(self, password):
# TODO: May be I should split these two and see if I can generate
# rules for each of the suggestions
for suggestion in suggestions[:self.max_words]:
suggestion = suggestion.replace(' ', '')
suggestion = suggestion.replace('-', '')
suggestion = suggestion.replace(' ', '').replace('-', '')

if suggestion not in suggestions:
suggestions.append(suggestion)

Expand All @@ -403,14 +404,13 @@ def generate_words(self, password):
for suggestion in suggestions:
distance = self.levenshtein_distance(suggestion, pre_password)

word = dict()
word["suggestion"] = suggestion
word["distance"] = distance
word["password"] = pre_password
word["pre_rule"] = pre_rule
word["best_rule_length"] = 9999

words.append(word)
words.append({
'suggestion': suggestion,
'distance': distance,
'password': pre_password,
'pre_rule': pre_rule,
'best_rule_length': 9999
})

#######################################################################
# Perform Optimization
Expand Down Expand Up @@ -600,8 +600,8 @@ def generate_advanced_hashcat_rules(self, word, rules, password):
word_rules = word

# Generate case statistics
password_lower = len([c for c in password if c.islower()])
password_upper = len([c for c in password if c.isupper()])
password_lower = sum(c.islower() for c in password)
password_upper = sum(c.isupper() for c in password)

for i, (op, p, w) in enumerate(rules):

Expand Down Expand Up @@ -842,15 +842,15 @@ def check_reversible_password(self, password):

# Skip passwords with less than 25% of alpha character
# TODO: Make random word detection more reliable based on word entropy.
elif len([c for c in password if c.isalpha()]) < len(password) // 4:
elif sum(c.isalpha() for c in password) < len(password) // 4:
if self.verbose and not self.quiet:
print("[!] %s => {skipping alpha less than 25%%} => %s" % (password, password))
self.special_stats_total += 1
return False

# Only check english ascii passwords for now
# TODO: Add support for more languages.
elif [c for c in password if ord(c) < 32 or ord(c) > 126]:
elif any(ord(c) < 32 or ord(c) > 126 for c in password):
if self.verbose and not self.quiet:
print("[!] %s => {skipping non ascii english} => %s" % (password, password))
self.foreign_stats_total += 1
Expand Down Expand Up @@ -898,11 +898,9 @@ def print_hashcat_rules(self, words, password, rules_queue, words_queue):

# Sorted list based on rule length
for word in sorted(words, key=lambda w: len(w["hashcat_rules"][0])):

words_queue.put(word["suggestion"])

for hashcat_rule in word["hashcat_rules"]:

rule_length = len(hashcat_rule)

if not self.more_rules:
Expand All @@ -917,6 +915,7 @@ def print_hashcat_rules(self, words, password, rules_queue, words_queue):

if rule_length <= self.max_rule_len:
hashcat_rule_str = " ".join(hashcat_rule + word["pre_rule"] or [':'])

if self.verbose:
print("[+] %s => %s => %s" % (word["suggestion"], hashcat_rule_str, password))

Expand Down Expand Up @@ -945,51 +944,49 @@ def rule_worker(self, rules_queue, output_rules_filename):
""" Worker to store generated rules. """
print("[*] Saving rules to %s" % output_rules_filename)

f = open(output_rules_filename, 'w')
if self.debug:
print("[*] Rule worker started.")
try:
while True:
rule = rules_queue.get()
with open(output_rules_filename, 'w') as f:
if self.debug:
print("[*] Rule worker started.")
try:
while True:
rule = rules_queue.get()

# Interrupted by a Death Pill
if rule is None:
break
# Interrupted by a Death Pill
if rule is None:
break

f.write("%s\n" % rule)
f.flush()
f.write("%s\n" % rule)
f.flush()

except (KeyboardInterrupt, SystemExit):
if self.debug:
print("[*] Rule worker terminated.")
except (KeyboardInterrupt, SystemExit):
if self.debug:
print("[*] Rule worker terminated.")

f.close()
if self.debug:
print("[*] Rule worker stopped.")

def word_worker(self, words_queue, output_words_filename):
""" Worker to store generated rules. """
print("[*] Saving words to %s" % output_words_filename)

f = open(output_words_filename, 'w')
if self.debug:
print("[*] Word worker started.")
try:
while True:
word = words_queue.get()
with open(output_words_filename, 'w') as f:
if self.debug:
print("[*] Word worker started.")
try:
while True:
word = words_queue.get()

# Interrupted by a Death Pill
if word is None:
break
# Interrupted by a Death Pill
if word is None:
break

f.write("%s\n" % word)
f.flush()
f.write("%s\n" % word)
f.flush()

except (KeyboardInterrupt, SystemExit):
if self.debug:
print("[*] Word worker terminated.")
except (KeyboardInterrupt, SystemExit):
if self.debug:
print("[*] Word worker terminated.")

f.close()
if self.debug:
print("[*] Word worker stopped.")

Expand All @@ -1013,47 +1010,45 @@ def analyze_passwords_file(self, passwords_file):
multiprocessing.Process(target=self.word_worker, args=(words_queue, "%s.word" % self.basename)).start()

# Continue with the main thread

f = open(passwords_file, 'r', encoding="latin-1", errors='ignore')

password_count = 0
analysis_start = time.time()
segment_start = analysis_start
try:
for password in f:
password = password.rstrip('\r\n')
if len(password) > 0:

# Provide analysis time feedback to the user
if not self.quiet and password_count != 0 and password_count % 5000 == 0:
segment_time = time.time() - segment_start
print("[*] Processed %d passwords in %.2f seconds at the rate of %.2f p/sec" %
(password_count, segment_start - analysis_start, 5000 / segment_time))
segment_start = time.time()

password_count += 1

# Perform preliminary checks and add password to the queue
if self.check_reversible_password(password):
passwords_queue.put(password)

except (KeyboardInterrupt, SystemExit):
print("\n[!] Rulegen was interrupted.")

else:
# Signal workers to stop.
for i in range(self.threads):
passwords_queue.put(None)
with open(passwords_file, 'r', encoding="latin-1", errors='ignore') as f:
analysis_start = time.time()
segment_start = analysis_start

try:
for password in f:
password = password.rstrip('\r\n')

if len(password) > 0:
# Provide analysis time feedback to the user
if not self.quiet and password_count != 0 and password_count % 5000 == 0:
segment_time = time.time() - segment_start
print("[*] Processed %d passwords in %.2f seconds at the rate of %.2f p/sec" %
(password_count, segment_start - analysis_start, 5000 / segment_time))
segment_start = time.time()

password_count += 1

# Perform preliminary checks and add password to the queue
if self.check_reversible_password(password):
passwords_queue.put(password)

except (KeyboardInterrupt, SystemExit):
print("\n[!] Rulegen was interrupted.")

# Wait for all of the queued passwords to finish.
while not passwords_queue.empty():
time.sleep(1)
else:
# Signal workers to stop.
for i in range(self.threads):
passwords_queue.put(None)

# Signal writers to stop.
rules_queue.put(None)
words_queue.put(None)
# Wait for all of the queued passwords to finish.
while not passwords_queue.empty():
time.sleep(1)

f.close()
# Signal writers to stop.
rules_queue.put(None)
words_queue.put(None)

analysis_time = time.time() - analysis_start
print("[*] Finished processing %d passwords in %.2f seconds at the rate of %.2f p/sec" %
Expand Down Expand Up @@ -1104,13 +1099,11 @@ def analyze_passwords_file(self, passwords_file):
############################################################################
def verify_hashcat_rules(self, word, rules, password):

f = open("%s/test.rule" % HASHCAT_PATH, 'w')
f.write(" ".join(rules))
f.close()
with open("%s/test.rule" % HASHCAT_PATH, 'w') as f:
f.write(" ".join(rules))

f = open("%s/test.word" % HASHCAT_PATH, 'w')
f.write(word)
f.close()
with open("%s/test.word" % HASHCAT_PATH, 'w') as f:
f.write(word)

p = subprocess.Popen(["%s/hashcat-cli64.bin" % HASHCAT_PATH, "-r", "%s/test.rule" % HASHCAT_PATH, "--stdout",
"%s/test.word" % HASHCAT_PATH], stdout=subprocess.PIPE)
Expand Down