From 08915e0fee7dc5a7c74d8e27d21904ab79a97ef7 Mon Sep 17 00:00:00 2001 From: xeals Date: Fri, 21 Mar 2025 09:57:21 +1100 Subject: [PATCH] Write out analysis files with passwords, as well as source password file We need it to write out the source passwords as a reference as some are removed while processing if they don't fit the model that rulegen is good at (such as all-numerical passwords). --- rulegen.py | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/rulegen.py b/rulegen.py index 970f600..bd8e764 100755 --- a/rulegen.py +++ b/rulegen.py @@ -746,7 +746,7 @@ class RuleGen: else: return True - def analyze_password(self,password, rules_queue=multiprocessing.Queue(), words_queue=multiprocessing.Queue()): + def analyze_password(self,password, index=0, rules_queue=multiprocessing.Queue(), words_queue=multiprocessing.Queue()): """ Analyze a single password. """ LOGGER.verbose("[*] Analyzing password: %s" % password) @@ -762,6 +762,7 @@ class RuleGen: word["hashcat_rules"] = [[],] word["pre_rule"] = [] word["best_rule_length"] = 9999 + word["index"] = index words.append(word) @@ -776,6 +777,7 @@ class RuleGen: # Generate a collection of hashcat_rules lists word["hashcat_rules"] = self.generate_hashcat_rules(word["suggestion"],word["password"]) + word["index"] = index self.print_hashcat_rules(words, password, rules_queue, words_queue) @@ -786,7 +788,7 @@ class RuleGen: # Sorted list based on rule length for word in sorted(words, key=lambda word: len(word["hashcat_rules"][0])): - words_queue.put(word["suggestion"]) + words_queue.put((word["index"], word["suggestion"])) for hashcat_rule in word["hashcat_rules"]: @@ -806,7 +808,7 @@ class RuleGen: hashcat_rule_str = " ".join(hashcat_rule + word["pre_rule"] or [':']) LOGGER.verbose("[+] %s => %s => %s" % (word["suggestion"], hashcat_rule_str, password)) - rules_queue.put(hashcat_rule_str) + rules_queue.put((word["index"], hashcat_rule_str)) def password_worker(self,i, passwords_queue, rules_queue, words_queue): @@ -818,7 +820,8 @@ class RuleGen: # Interrupted by a Death Pill if password == None: break - self.analyze_password(password, rules_queue, words_queue) + index, password = password + self.analyze_password(password, index, rules_queue, words_queue) except (KeyboardInterrupt, SystemExit): LOGGER.debug("[*] Password analysis worker [%d] terminated." % i) @@ -837,7 +840,7 @@ class RuleGen: # Interrupted by a Death Pill if rule == None: break - f.write("%s\n" % rule) + f.write("%d\t%s\n" % rule) f.flush() except (KeyboardInterrupt, SystemExit): @@ -859,7 +862,7 @@ class RuleGen: # Interrupted by a Death Pill if word == None: break - f.write("%s\n" % word) + f.write("%d\t%s\n" % word) f.flush() except (KeyboardInterrupt, SystemExit): @@ -890,6 +893,10 @@ class RuleGen: f = open(passwords_file,'r', encoding=encoding) + output_pwd_filename = "%s.password" % self.basename + LOGGER.info("[*] Saving analysed passwords to to %s" % output_pwd_filename) + o = open(output_pwd_filename, "w") + password_count = 0 analysis_start = time.time() segment_start = analysis_start @@ -909,7 +916,9 @@ class RuleGen: # Perform preliminary checks and add password to the queue if self.check_reversible_password(password): - passwords_queue.put(password) + passwords_queue.put((password_count, password)) + o.write("%d\t%s\n" % (password_count, password)) + o.flush() except (KeyboardInterrupt, SystemExit): LOGGER.warning("\n[!] Rulegen was interrupted.") @@ -927,11 +936,14 @@ class RuleGen: rules_queue.put(None) words_queue.put(None) + o.close() f.close() analysis_time = time.time() - analysis_start LOGGER.info("[*] Finished processing %d passwords in %.2f seconds at the rate of %.2f p/sec" % (password_count, analysis_time, float(password_count)/analysis_time )) + return + LOGGER.info("[*] Generating statistics for [%s] rules and words." % self.basename) LOGGER.info("[-] Skipped %d all numeric passwords (%0.2f%%)" % \ (self.numeric_stats_total, float(self.numeric_stats_total)*100.0/float(password_count)))