From a53a636478de4eee74bf07644b94d2d308423301 Mon Sep 17 00:00:00 2001 From: movatica Date: Sun, 22 Nov 2020 19:44:06 +0100 Subject: [PATCH 1/4] Refactor rulegen inline dictionary initialization --- rulegen.py | 33 +++++++++++++++++---------------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/rulegen.py b/rulegen.py index 29397f4..17d1864 100755 --- a/rulegen.py +++ b/rulegen.py @@ -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 From 0856f3088113322d27fb87badba2ed541196987a Mon Sep 17 00:00:00 2001 From: movatica Date: Sun, 22 Nov 2020 19:55:36 +0100 Subject: [PATCH 2/4] Refactor rulegen.py use generator expressions instead of list comprehensions where possible --- rulegen.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/rulegen.py b/rulegen.py index 17d1864..b93efab 100755 --- a/rulegen.py +++ b/rulegen.py @@ -601,8 +601,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): @@ -843,7 +843,7 @@ 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 @@ -851,7 +851,7 @@ def check_reversible_password(self, password): # 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 From 234fe179ae6557061e727889ab28c956c816478f Mon Sep 17 00:00:00 2001 From: movatica Date: Sun, 22 Nov 2020 20:02:58 +0100 Subject: [PATCH 3/4] use "with"-blocks for files --- rulegen.py | 141 +++++++++++++++++++++++++---------------------------- 1 file changed, 67 insertions(+), 74 deletions(-) diff --git a/rulegen.py b/rulegen.py index b93efab..9f49ed4 100755 --- a/rulegen.py +++ b/rulegen.py @@ -899,11 +899,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: @@ -918,6 +916,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)) @@ -946,25 +945,24 @@ 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.") @@ -972,25 +970,24 @@ 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.") @@ -1014,47 +1011,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" % @@ -1105,13 +1100,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) From 2368aff40d0ee1d09dd2baeef4b9bff1dbe9bcdb Mon Sep 17 00:00:00 2001 From: movatica Date: Sun, 22 Nov 2020 20:06:29 +0100 Subject: [PATCH 4/4] inline dictionary initialization --- rulegen.py | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/rulegen.py b/rulegen.py index 9f49ed4..94bf12a 100755 --- a/rulegen.py +++ b/rulegen.py @@ -392,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) @@ -404,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