Port rulegen.py to py3 and logging
This commit is contained in:
		
							
								
								
									
										210
									
								
								rulegen.py
									
									
									
									
									
								
							
							
						
						
									
										210
									
								
								rulegen.py
									
									
									
									
									
								
							| @@ -12,19 +12,23 @@ | |||||||
| # | # | ||||||
| # Please see the attached LICENSE file for additional licensing information. | # Please see the attached LICENSE file for additional licensing information. | ||||||
|  |  | ||||||
| import sys |  | ||||||
| import re |  | ||||||
| import time |  | ||||||
| import operator |  | ||||||
| import enchant |  | ||||||
|  |  | ||||||
| from optparse import OptionParser, OptionGroup |  | ||||||
|  |  | ||||||
| from collections import Counter | from collections import Counter | ||||||
|  | from optparse import OptionParser, OptionGroup | ||||||
| import subprocess | import enchant | ||||||
|  |  | ||||||
| import multiprocessing | import multiprocessing | ||||||
|  | import operator | ||||||
|  | import re | ||||||
|  | import subprocess | ||||||
|  | import sys | ||||||
|  | import time | ||||||
|  | import logging | ||||||
|  |  | ||||||
|  | logging.VERBOSE = 15 | ||||||
|  | logging.addLevelName(logging.VERBOSE, "VERBOSE") | ||||||
|  | logging.Logger.verbose = lambda self, msg, *args, **kwargs: \ | ||||||
|  |     self.log(logging.VERBOSE, msg, *args, **kwargs) | ||||||
|  |  | ||||||
|  | LOGGER = logging.getLogger("rulegen") | ||||||
|  |  | ||||||
| VERSION = "0.0.4" | VERSION = "0.0.4" | ||||||
|  |  | ||||||
| @@ -61,8 +65,6 @@ class RuleGen: | |||||||
|         self.brute_rules = False |         self.brute_rules = False | ||||||
|  |  | ||||||
|         # Debugging options |         # Debugging options | ||||||
|         self.verbose = False |  | ||||||
|         self.debug = False |  | ||||||
|         self.word = None # Custom word to use. |         self.word = None # Custom word to use. | ||||||
|         self.quiet = False |         self.quiet = False | ||||||
|  |  | ||||||
| @@ -75,10 +77,10 @@ class RuleGen: | |||||||
|         ######################################################################## |         ######################################################################## | ||||||
|         # Preanalysis Password Patterns |         # Preanalysis Password Patterns | ||||||
|         self.password_pattern = dict() |         self.password_pattern = dict() | ||||||
|         self.password_pattern["insertion"] = re.compile('^[^a-z]*(?P<password>.+?)[^a-z]*$', re.IGNORECASE) |         self.password_pattern["insertion"] = re.compile(r'^[^a-z]*(?P<password>.+?)[^a-z]*$', re.IGNORECASE) | ||||||
|         self.password_pattern["email"] = re.compile('^(?P<password>.+?)@[A-Z0-9.-]+\.[A-Z]{2,4}', re.IGNORECASE) |         self.password_pattern["email"] = re.compile(r'^(?P<password>.+?)@[A-Z0-9.-]+\.[A-Z]{2,4}', re.IGNORECASE) | ||||||
|         self.password_pattern["alldigits"] = re.compile('^(\d+)$', re.IGNORECASE) |         self.password_pattern["alldigits"] = re.compile(r'^(\d+)$', re.IGNORECASE) | ||||||
|         self.password_pattern["allspecial"]= re.compile('^([^a-z0-9]+)$', re.IGNORECASE) |         self.password_pattern["allspecial"]= re.compile(r'^([^a-z0-9]+)$', re.IGNORECASE) | ||||||
|  |  | ||||||
|         ######################################################################## |         ######################################################################## | ||||||
|         # Hashcat Rules Engine |         # Hashcat Rules Engine | ||||||
| @@ -172,9 +174,9 @@ class RuleGen: | |||||||
|         matrix = [] |         matrix = [] | ||||||
|  |  | ||||||
|         # Generate and populate the initial matrix |         # Generate and populate the initial matrix | ||||||
|         for i in xrange(len(password) + 1): |         for i in range(len(password) + 1): | ||||||
|             matrix.append([]) |             matrix.append([]) | ||||||
|             for j in xrange(len(word) + 1): |             for j in range(len(word) + 1): | ||||||
|                 if i == 0: |                 if i == 0: | ||||||
|                     matrix[i].append(j) |                     matrix[i].append(j) | ||||||
|                 elif j == 0: |                 elif j == 0: | ||||||
| @@ -183,8 +185,8 @@ class RuleGen: | |||||||
|                     matrix[i].append(0) |                     matrix[i].append(0) | ||||||
|  |  | ||||||
|         # Calculate edit distance for each substring |         # Calculate edit distance for each substring | ||||||
|         for i in xrange(1,len(password) + 1): |         for i in range(1,len(password) + 1): | ||||||
|             for j in xrange(1,len(word) + 1): |             for j in range(1,len(word) + 1): | ||||||
|                 if password[i-1] == word[j-1]: |                 if password[i-1] == word[j-1]: | ||||||
|                     matrix[i][j] = matrix[i-1][j-1] |                     matrix[i][j] = matrix[i-1][j-1] | ||||||
|                 else: |                 else: | ||||||
| @@ -205,7 +207,7 @@ class RuleGen: | |||||||
|         if not s1: |         if not s1: | ||||||
|             return len(s2) |             return len(s2) | ||||||
|       |       | ||||||
|         previous_row = xrange(len(s2) + 1) |         previous_row = range(len(s2) + 1) | ||||||
|         for i, c1 in enumerate(s1): |         for i, c1 in enumerate(s1): | ||||||
|             current_row = [i + 1] |             current_row = [i + 1] | ||||||
|             for j, c2 in enumerate(s2): |             for j, c2 in enumerate(s2): | ||||||
| @@ -219,11 +221,11 @@ class RuleGen: | |||||||
|  |  | ||||||
|     def levenshtein_print(self,matrix,word,password): |     def levenshtein_print(self,matrix,word,password): | ||||||
|         """ Print word X password matrix """ |         """ Print word X password matrix """ | ||||||
|         print "      %s" % "  ".join(list(word)) |         print("      %s" % "  ".join(list(word))) | ||||||
|         for i,row in enumerate(matrix): |         for i,row in enumerate(matrix): | ||||||
|             if i == 0: print " ", |             if i == 0: print(" "), | ||||||
|             else:      print password[i-1], |             else:      print(password[i-1]), | ||||||
|             print " ".join("%2d" % col for col in row) |             print(" ".join("%2d" % col for col in row)) | ||||||
|  |  | ||||||
|     def generate_levenshtein_rules(self, word, password): |     def generate_levenshtein_rules(self, word, password): | ||||||
|         """ Generates levenshtein rules. Returns a list of lists of levenshtein rules. """ |         """ Generates levenshtein rules. Returns a list of lists of levenshtein rules. """ | ||||||
| @@ -254,7 +256,7 @@ class RuleGen: | |||||||
|             cost = matrix[i][j] |             cost = matrix[i][j] | ||||||
|  |  | ||||||
|             # Calculate minimum cost of each operation |             # Calculate minimum cost of each operation | ||||||
|             cost_delete = cost_insert = cost_equal_or_replace = sys.maxint |             cost_delete = cost_insert = cost_equal_or_replace = sys.maxsize | ||||||
|             if i > 0: cost_insert = matrix[i-1][j] |             if i > 0: cost_insert = matrix[i-1][j] | ||||||
|             if j > 0: cost_delete = matrix[i][j-1] |             if j > 0: cost_delete = matrix[i][j-1] | ||||||
|             if i > 0 and j > 0: cost_equal_or_replace = matrix[i-1][j-1] |             if i > 0 and j > 0: cost_equal_or_replace = matrix[i-1][j-1] | ||||||
| @@ -285,7 +287,7 @@ class RuleGen: | |||||||
|     def generate_words(self,password): |     def generate_words(self,password): | ||||||
|         """ Generate source word candidates.""" |         """ Generate source word candidates.""" | ||||||
|  |  | ||||||
|         if self.debug: print "[*] Generating source words for %s" % password |         LOGGER.debug("[*] Generating source words for %s" % password) | ||||||
|  |  | ||||||
|         words = list() |         words = list() | ||||||
|         words_collection = list() |         words_collection = list() | ||||||
| @@ -319,8 +321,8 @@ class RuleGen: | |||||||
|                     suggestions.append(suggestion) |                     suggestions.append(suggestion) | ||||||
|  |  | ||||||
|             if len(suggestions) != len(set(suggestions)): |             if len(suggestions) != len(set(suggestions)): | ||||||
|                 print sorted(suggestions) |                 print(sorted(suggestions)) | ||||||
|                 print sorted(set(suggestions)) |                 print(sorted(set(suggestions))) | ||||||
|  |  | ||||||
|  |  | ||||||
|             for suggestion in suggestions: |             for suggestion in suggestions: | ||||||
| @@ -346,23 +348,20 @@ class RuleGen: | |||||||
|                     best_found_distance = word["distance"] |                     best_found_distance = word["distance"] | ||||||
|  |  | ||||||
|                 elif word["distance"] > best_found_distance: |                 elif word["distance"] > best_found_distance: | ||||||
|                     if self.verbose:  |                     LOGGER.verbose("[-] %s => {edit distance suboptimal: %d (%d)} => %s" % \ | ||||||
|                         print "[-] %s => {edit distance suboptimal: %d (%d)} => %s" % \ |                         (word["suggestion"], word["distance"], best_found_distance, word["password"])) | ||||||
|                         (word["suggestion"], word["distance"], best_found_distance, word["password"]) |  | ||||||
|                     break                        |                     break                        | ||||||
|  |  | ||||||
|             # Filter words with too big edit distance |             # Filter words with too big edit distance | ||||||
|             if word["distance"] <= self.max_word_dist: |             if word["distance"] <= self.max_word_dist: | ||||||
|                 if self.debug:  |                 LOGGER.debug("[+] %s => {edit distance: %d (%d)} = > %s" % \ | ||||||
|                     print "[+] %s => {edit distance: %d (%d)} = > %s" % \ |                 (word["suggestion"], word["distance"],best_found_distance, word["password"])) | ||||||
|                     (word["suggestion"], word["distance"],best_found_distance, word["password"]) |  | ||||||
|  |  | ||||||
|                 words_collection.append(word) |                 words_collection.append(word) | ||||||
|  |  | ||||||
|             else: |             else: | ||||||
|                 if self.verbose:  |                 LOGGER.verbose("[-] %s => {max distance exceeded: %d (%d)} => %s" % \ | ||||||
|                     print "[-] %s => {max distance exceeded: %d (%d)} => %s" % \ |                 (word["suggestion"], word["distance"], self.max_word_dist, word["password"])) | ||||||
|                     (word["suggestion"], word["distance"], self.max_word_dist, word["password"]) |  | ||||||
|  |  | ||||||
|         if self.max_words:  |         if self.max_words:  | ||||||
|             words_collection = words_collection[:self.max_words] |             words_collection = words_collection[:self.max_words] | ||||||
| @@ -399,7 +398,7 @@ class RuleGen: | |||||||
|             else: preanalysis_password += c |             else: preanalysis_password += c | ||||||
|         password = preanalysis_password |         password = preanalysis_password | ||||||
|  |  | ||||||
|         if self.debug: "[*] Preanalysis Password: %s" % password |         LOGGER.debug("[*] Preanalysis Password: %s" % password) | ||||||
|  |  | ||||||
|         return self.enchant.suggest(password) |         return self.enchant.suggest(password) | ||||||
|  |  | ||||||
| @@ -433,9 +432,9 @@ class RuleGen: | |||||||
|                 hashcat_rule = self.generate_advanced_hashcat_rules(suggestion, lev_rule, password) |                 hashcat_rule = self.generate_advanced_hashcat_rules(suggestion, lev_rule, password) | ||||||
|  |  | ||||||
|             if hashcat_rule == None: |             if hashcat_rule == None: | ||||||
|                 print "[!] Processing FAILED: %s => ;( => %s" % (suggestion,password) |                 LOGGER.warning("[!] Processing FAILED: %s => ;( => %s" % (suggestion,password)) | ||||||
|                 print "    Sorry about that, please report this failure to" |                 LOGGER.warning("    Sorry about that, please report this failure to") | ||||||
|                 print "    the developer: iphelix [at] thesprawl.org" |                 LOGGER.warning("    the developer: iphelix [at] thesprawl.org") | ||||||
|  |  | ||||||
|             else: |             else: | ||||||
|                 hashcat_rules.append(hashcat_rule) |                 hashcat_rules.append(hashcat_rule) | ||||||
| @@ -453,9 +452,8 @@ class RuleGen: | |||||||
|                     best_found_rule_length = rule_length |                     best_found_rule_length = rule_length | ||||||
|  |  | ||||||
|                 elif rule_length > best_found_rule_length: |                 elif rule_length > best_found_rule_length: | ||||||
|                     if self.verbose:  |                     LOGGER.verbose("[-] %s => {best rule length exceeded: %d (%d)} => %s" % \ | ||||||
|                         print "[-] %s => {best rule length exceeded: %d (%d)} => %s" % \ |                     (suggestion, rule_length, best_found_rule_length, password)) | ||||||
|                         (suggestion, rule_length, best_found_rule_length, password) |  | ||||||
|                     break |                     break | ||||||
|  |  | ||||||
|             if rule_length <= self.max_rule_len: |             if rule_length <= self.max_rule_len: | ||||||
| @@ -467,7 +465,7 @@ class RuleGen: | |||||||
|         """ Generate basic hashcat rules using only basic insert,delete,replace rules. """ |         """ Generate basic hashcat rules using only basic insert,delete,replace rules. """ | ||||||
|         hashcat_rules = [] |         hashcat_rules = [] | ||||||
|  |  | ||||||
|         if self.debug: print "[*] Simple Processing %s => %s" % (word,password) |         LOGGER.debug("[*] Simple Processing %s => %s" % (word,password)) | ||||||
|  |  | ||||||
|         # Dynamically apply rules to the source word |         # Dynamically apply rules to the source word | ||||||
|         # NOTE: Special case were word == password this would work as well. |         # NOTE: Special case were word == password this would work as well. | ||||||
| @@ -475,7 +473,7 @@ class RuleGen: | |||||||
|  |  | ||||||
|         for (op,p,w) in rules: |         for (op,p,w) in rules: | ||||||
|  |  | ||||||
|             if self.debug: print "\t[*] Simple Processing Started: %s - %s" % (word_rules, " ".join(hashcat_rules)) |             LOGGER.debug("\t[*] Simple Processing Started: %s - %s" % (word_rules, " ".join(hashcat_rules))) | ||||||
|  |  | ||||||
|             if op == 'insert': |             if op == 'insert': | ||||||
|                 hashcat_rules.append("i%s%s" % (self.int_to_hashcat(p),password[p])) |                 hashcat_rules.append("i%s%s" % (self.int_to_hashcat(p),password[p])) | ||||||
| @@ -489,20 +487,20 @@ class RuleGen: | |||||||
|                 hashcat_rules.append("o%s%s" % (self.int_to_hashcat(p),password[p])) |                 hashcat_rules.append("o%s%s" % (self.int_to_hashcat(p),password[p])) | ||||||
|                 word_rules = self.hashcat_rule['o'](word_rules,p,password[p]) |                 word_rules = self.hashcat_rule['o'](word_rules,p,password[p]) | ||||||
|  |  | ||||||
|         if self.debug: print "\t[*] Simple Processing Ended: %s => %s => %s" % (word_rules, " ".join(hashcat_rules),password) |         LOGGER.debug("\t[*] Simple Processing Ended: %s => %s => %s" % (word_rules, " ".join(hashcat_rules),password)) | ||||||
|  |  | ||||||
|         # Check if rules result in the correct password |         # Check if rules result in the correct password | ||||||
|         if word_rules == password: |         if word_rules == password: | ||||||
|             return hashcat_rules |             return hashcat_rules | ||||||
|         else: |         else: | ||||||
|             if self.debug: print "[!] Simple Processing FAILED: %s => %s => %s (%s)" % (word," ".join(hashcat_rules),password,word_rules) |             LOGGER.debug("[!] Simple Processing FAILED: %s => %s => %s (%s)" % (word," ".join(hashcat_rules),password,word_rules)) | ||||||
|             return None |             return None | ||||||
|  |  | ||||||
|     def generate_advanced_hashcat_rules(self,word,rules,password): |     def generate_advanced_hashcat_rules(self,word,rules,password): | ||||||
|         """ Generate advanced hashcat rules using full range of available rules. """ |         """ Generate advanced hashcat rules using full range of available rules. """ | ||||||
|         hashcat_rules = [] |         hashcat_rules = [] | ||||||
|  |  | ||||||
|         if self.debug: print "[*] Advanced Processing %s => %s" % (word,password) |         LOGGER.debug("[*] Advanced Processing %s => %s" % (word,password)) | ||||||
|  |  | ||||||
|         # Dynamically apply and store rules in word_rules variable. |         # Dynamically apply and store rules in word_rules variable. | ||||||
|         # NOTE: Special case where word == password this would work as well. |         # NOTE: Special case where word == password this would work as well. | ||||||
| @@ -514,7 +512,7 @@ class RuleGen: | |||||||
|  |  | ||||||
|         for i,(op,p,w) in enumerate(rules): |         for i,(op,p,w) in enumerate(rules): | ||||||
|  |  | ||||||
|             if self.debug: print "\t[*] Advanced Processing Started: %s - %s" % (word_rules, " ".join(hashcat_rules)) |             LOGGER.debug("\t[*] Advanced Processing Started: %s - %s" % (word_rules, " ".join(hashcat_rules))) | ||||||
|  |  | ||||||
|             if op == 'insert': |             if op == 'insert': | ||||||
|                 hashcat_rules.append("i%s%s" % (self.int_to_hashcat(p),password[p])) |                 hashcat_rules.append("i%s%s" % (self.int_to_hashcat(p),password[p])) | ||||||
| @@ -538,7 +536,7 @@ class RuleGen: | |||||||
|  |  | ||||||
|                 # This rule was made obsolete by a prior global replacement |                 # This rule was made obsolete by a prior global replacement | ||||||
|                 if word_rules[p] == password[p]: |                 if word_rules[p] == password[p]: | ||||||
|                     if self.debug: print "\t[*] Advanced Processing Obsolete Rule: %s - %s" % (word_rules, " ".join(hashcat_rules)) |                     LOGGER.debug("\t[*] Advanced Processing Obsolete Rule: %s - %s" % (word_rules, " ".join(hashcat_rules))) | ||||||
|  |  | ||||||
|                 # Swapping rules |                 # Swapping rules | ||||||
|                 elif p < len(password)-1 and p < len(word_rules)-1 and word_rules[p] == password[p+1] and word_rules[p+1] == password[p]: |                 elif p < len(password)-1 and p < len(word_rules)-1 and word_rules[p] == password[p+1] and word_rules[p+1] == password[p]: | ||||||
| @@ -649,7 +647,7 @@ class RuleGen: | |||||||
|                     hashcat_rules.append("o%s%s" % (self.int_to_hashcat(p),password[p])) |                     hashcat_rules.append("o%s%s" % (self.int_to_hashcat(p),password[p])) | ||||||
|                     word_rules = self.hashcat_rule['o'](word_rules,p,password[p])             |                     word_rules = self.hashcat_rule['o'](word_rules,p,password[p])             | ||||||
|  |  | ||||||
|         if self.debug: print "\t[*] Advanced Processing Ended: %s %s" % (word_rules, " ".join(hashcat_rules)) |         LOGGER.debug("\t[*] Advanced Processing Ended: %s %s" % (word_rules, " ".join(hashcat_rules))) | ||||||
|  |  | ||||||
|         ######################################################################## |         ######################################################################## | ||||||
|         # Prefix rules |         # Prefix rules | ||||||
| @@ -718,7 +716,7 @@ class RuleGen: | |||||||
|         if word_rules == password: |         if word_rules == password: | ||||||
|             return hashcat_rules |             return hashcat_rules | ||||||
|         else: |         else: | ||||||
|             if self.debug: print "[!] Advanced Processing FAILED: %s => %s => %s (%s)" % (word," ".join(hashcat_rules),password,word_rules) |             LOGGER.debug("[!] Advanced Processing FAILED: %s => %s => %s (%s)" % (word," ".join(hashcat_rules),password,word_rules)) | ||||||
|             return None |             return None | ||||||
|  |  | ||||||
|  |  | ||||||
| @@ -727,21 +725,21 @@ class RuleGen: | |||||||
|  |  | ||||||
|         # Skip all numeric passwords |         # Skip all numeric passwords | ||||||
|         if password.isdigit():  |         if password.isdigit():  | ||||||
|             if self.verbose and not self.quiet: print "[!] %s => {skipping numeric} => %s" % (password,password) |             if not self.quiet: LOGGER.verbose("[!] %s => {skipping numeric} => %s" % (password,password)) | ||||||
|             self.numeric_stats_total += 1 |             self.numeric_stats_total += 1 | ||||||
|             return False |             return False | ||||||
|  |  | ||||||
|         # Skip passwords with less than 25% of alpha character |         # Skip passwords with less than 25% of alpha character | ||||||
|         # TODO: Make random word detection more reliable based on word entropy. |         # 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 len([c for c in password if c.isalpha()]) < len(password)/4: | ||||||
|             if self.verbose and not self.quiet:print "[!] %s => {skipping alpha less than 25%%} => %s" % (password,password) |             if not self.quiet: LOGGER.verbose("[!] %s => {skipping alpha less than 25%%} => %s" % (password,password)) | ||||||
|             self.special_stats_total += 1 |             self.special_stats_total += 1 | ||||||
|             return False |             return False | ||||||
|  |  | ||||||
|         # Only check english ascii passwords for now |         # Only check english ascii passwords for now | ||||||
|         # TODO: Add support for more languages. |         # TODO: Add support for more languages. | ||||||
|         elif [c for c in password if ord(c) < 32 or ord(c) > 126]: |         elif [c for c in password if ord(c) < 32 or ord(c) > 126]: | ||||||
|             if self.verbose and not self.quiet: print "[!] %s => {skipping non ascii english} => %s" % (password,password) |             if not self.quiet: LOGGER.verbose("[!] %s => {skipping non ascii english} => %s" % (password,password)) | ||||||
|             self.foreign_stats_total += 1 |             self.foreign_stats_total += 1 | ||||||
|             return False |             return False | ||||||
|  |  | ||||||
| @@ -751,7 +749,7 @@ class RuleGen: | |||||||
|     def analyze_password(self,password, rules_queue=multiprocessing.Queue(), words_queue=multiprocessing.Queue()): |     def analyze_password(self,password, rules_queue=multiprocessing.Queue(), words_queue=multiprocessing.Queue()): | ||||||
|         """ Analyze a single password. """ |         """ Analyze a single password. """ | ||||||
|  |  | ||||||
|         if self.verbose: print "[*] Analyzing password: %s" % password |         LOGGER.verbose("[*] Analyzing password: %s" % password) | ||||||
|  |  | ||||||
|         words = [] |         words = [] | ||||||
|  |  | ||||||
| @@ -799,21 +797,20 @@ class RuleGen: | |||||||
|                         best_found_rule_length = rule_length |                         best_found_rule_length = rule_length | ||||||
|  |  | ||||||
|                     elif rule_length > best_found_rule_length: |                     elif rule_length > best_found_rule_length: | ||||||
|                         if self.verbose:  |                         LOGGER.verbose("[-] %s => {best rule length exceeded: %d (%d)} => %s" % \ | ||||||
|                             print "[-] %s => {best rule length exceeded: %d (%d)} => %s" % \ |                         (word["suggestion"], rule_length, best_found_rule_length, password)) | ||||||
|                             (word["suggestion"], rule_length, best_found_rule_length, password) |  | ||||||
|                         break |                         break | ||||||
|  |  | ||||||
|                 if rule_length <= self.max_rule_len: |                 if rule_length <= self.max_rule_len: | ||||||
|  |  | ||||||
|                     hashcat_rule_str = " ".join(hashcat_rule + word["pre_rule"] or [':']) |                     hashcat_rule_str = " ".join(hashcat_rule + word["pre_rule"] or [':']) | ||||||
|                     if self.verbose: print "[+] %s => %s => %s" % (word["suggestion"], hashcat_rule_str, password) |                     LOGGER.verbose("[+] %s => %s => %s" % (word["suggestion"], hashcat_rule_str, password)) | ||||||
|  |  | ||||||
|                     rules_queue.put(hashcat_rule_str) |                     rules_queue.put(hashcat_rule_str) | ||||||
|                      |                      | ||||||
|  |  | ||||||
|     def password_worker(self,i, passwords_queue, rules_queue, words_queue): |     def password_worker(self,i, passwords_queue, rules_queue, words_queue): | ||||||
|         if self.debug: print "[*] Password analysis worker [%d] started." % i |         LOGGER.debug("[*] Password analysis worker [%d] started." % i) | ||||||
|         try: |         try: | ||||||
|             while True: |             while True: | ||||||
|                 password = passwords_queue.get() |                 password = passwords_queue.get() | ||||||
| @@ -823,16 +820,16 @@ class RuleGen: | |||||||
|  |  | ||||||
|                 self.analyze_password(password, rules_queue, words_queue) |                 self.analyze_password(password, rules_queue, words_queue) | ||||||
|         except (KeyboardInterrupt, SystemExit): |         except (KeyboardInterrupt, SystemExit): | ||||||
|             if self.debug: print "[*] Password analysis worker [%d] terminated." % i |             LOGGER.debug("[*] Password analysis worker [%d] terminated." % i) | ||||||
|  |  | ||||||
|         if self.debug: print "[*] Password analysis worker [%d] stopped." % i |         LOGGER.debug("[*] Password analysis worker [%d] stopped." % i) | ||||||
|  |  | ||||||
|     def rule_worker(self, rules_queue, output_rules_filename): |     def rule_worker(self, rules_queue, output_rules_filename): | ||||||
|         """ Worker to store generated rules. """ |         """ Worker to store generated rules. """ | ||||||
|         print "[*] Saving rules to %s" % output_rules_filename |         LOGGER.info("[*] Saving rules to %s" % output_rules_filename) | ||||||
|  |  | ||||||
|         f = open(output_rules_filename, 'w') |         f = open(output_rules_filename, 'w') | ||||||
|         if self.debug: print "[*] Rule worker started." |         LOGGER.debug("[*] Rule worker started.") | ||||||
|         try: |         try: | ||||||
|             while True: |             while True: | ||||||
|                 rule = rules_queue.get() |                 rule = rules_queue.get() | ||||||
| @@ -844,17 +841,17 @@ class RuleGen: | |||||||
|                 f.flush() |                 f.flush() | ||||||
|  |  | ||||||
|         except (KeyboardInterrupt, SystemExit): |         except (KeyboardInterrupt, SystemExit): | ||||||
|             if self.debug: print "[*] Rule worker terminated." |             LOGGER.debug("[*] Rule worker terminated.") | ||||||
|  |  | ||||||
|         f.close() |         f.close() | ||||||
|         if self.debug: print "[*] Rule worker stopped." |         LOGGER.debug("[*] Rule worker stopped.") | ||||||
|  |  | ||||||
|     def word_worker(self, words_queue, output_words_filename): |     def word_worker(self, words_queue, output_words_filename): | ||||||
|         """ Worker to store generated rules. """ |         """ Worker to store generated rules. """ | ||||||
|         print "[*] Saving words to %s" % output_words_filename |         LOGGER.info("[*] Saving words to %s" % output_words_filename) | ||||||
|  |  | ||||||
|         f = open(output_words_filename, 'w') |         f = open(output_words_filename, 'w') | ||||||
|         if self.debug: print "[*] Word worker started." |         LOGGER.debug("[*] Word worker started.") | ||||||
|         try: |         try: | ||||||
|             while True: |             while True: | ||||||
|                 word = words_queue.get() |                 word = words_queue.get() | ||||||
| @@ -866,17 +863,17 @@ class RuleGen: | |||||||
|                 f.flush() |                 f.flush() | ||||||
|  |  | ||||||
|         except (KeyboardInterrupt, SystemExit): |         except (KeyboardInterrupt, SystemExit): | ||||||
|             if self.debug: print "[*] Word worker terminated." |             LOGGER.debug("[*] Word worker terminated.") | ||||||
|  |  | ||||||
|         f.close() |         f.close() | ||||||
|         if self.debug: print "[*] Word worker stopped." |         LOGGER.debug("[*] Word worker stopped.") | ||||||
|  |  | ||||||
|     # Analyze passwords file |     # Analyze passwords file | ||||||
|     def analyze_passwords_file(self,passwords_file): |     def analyze_passwords_file(self,passwords_file, encoding): | ||||||
|         """ Analyze provided passwords file. """ |         """ Analyze provided passwords file. """ | ||||||
|  |  | ||||||
|         print "[*] Analyzing passwords file: %s:" % passwords_file |         LOGGER.info("[*] Analyzing passwords file: %s:" % passwords_file) | ||||||
|         print "[*] Press Ctrl-C to end execution and generate statistical analysis." |         LOGGER.info("[*] Press Ctrl-C to end execution and generate statistical analysis.") | ||||||
|  |  | ||||||
|         # Setup queues |         # Setup queues | ||||||
|         passwords_queue = multiprocessing.Queue(self.threads) |         passwords_queue = multiprocessing.Queue(self.threads) | ||||||
| @@ -891,7 +888,7 @@ class RuleGen: | |||||||
|  |  | ||||||
|         # Continue with the main thread |         # Continue with the main thread | ||||||
|  |  | ||||||
|         f = open(passwords_file,'r') |         f = open(passwords_file,'r', encoding=encoding) | ||||||
|  |  | ||||||
|         password_count = 0 |         password_count = 0 | ||||||
|         analysis_start = time.time() |         analysis_start = time.time() | ||||||
| @@ -904,8 +901,8 @@ class RuleGen: | |||||||
|                     # Provide analysis time feedback to the user |                     # Provide analysis time feedback to the user | ||||||
|                     if not self.quiet and password_count != 0 and password_count % 5000 == 0: |                     if not self.quiet and password_count != 0 and password_count % 5000 == 0: | ||||||
|                         segment_time = time.time() - segment_start |                         segment_time = time.time() - segment_start | ||||||
|                         print "[*] Processed %d passwords in %.2f seconds at the rate of %.2f p/sec" % \ |                         LOGGER.info("[*] Processed %d passwords in %.2f seconds at the rate of %.2f p/sec" % \ | ||||||
|                             (password_count, segment_start - analysis_start, 5000/segment_time ) |                             (password_count, segment_start - analysis_start, 5000/segment_time )) | ||||||
|                         segment_start = time.time() |                         segment_start = time.time() | ||||||
|  |  | ||||||
|                     password_count += 1 |                     password_count += 1 | ||||||
| @@ -915,7 +912,7 @@ class RuleGen: | |||||||
|                         passwords_queue.put(password) |                         passwords_queue.put(password) | ||||||
|  |  | ||||||
|         except (KeyboardInterrupt, SystemExit): |         except (KeyboardInterrupt, SystemExit): | ||||||
|             print "\n[!] Rulegen was interrupted." |             LOGGER.warning("\n[!] Rulegen was interrupted.") | ||||||
|  |  | ||||||
|         else: |         else: | ||||||
|             # Signal workers to stop. |             # Signal workers to stop. | ||||||
| @@ -933,15 +930,15 @@ class RuleGen: | |||||||
|         f.close() |         f.close() | ||||||
|  |  | ||||||
|         analysis_time = time.time() - analysis_start |         analysis_time = time.time() - analysis_start | ||||||
|         print "[*] Finished processing %d passwords in %.2f seconds at the rate of %.2f p/sec" % (password_count, analysis_time, float(password_count)/analysis_time ) |         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 )) | ||||||
|  |  | ||||||
|         print "[*] Generating statistics for [%s] rules and words." % self.basename |         LOGGER.info("[*] Generating statistics for [%s] rules and words." % self.basename) | ||||||
|         print "[-] Skipped %d all numeric passwords (%0.2f%%)" % \ |         LOGGER.info("[-] Skipped %d all numeric passwords (%0.2f%%)" % \ | ||||||
|                     (self.numeric_stats_total, float(self.numeric_stats_total)*100.0/float(password_count)) |                     (self.numeric_stats_total, float(self.numeric_stats_total)*100.0/float(password_count))) | ||||||
|         print "[-] Skipped %d passwords with less than 25%% alpha characters (%0.2f%%)" % \ |         LOGGER.info("[-] Skipped %d passwords with less than 25%% alpha characters (%0.2f%%)" % \ | ||||||
|                     (self.special_stats_total, float(self.special_stats_total)*100.0/float(password_count)) |                     (self.special_stats_total, float(self.special_stats_total)*100.0/float(password_count))) | ||||||
|         print "[-] Skipped %d passwords with non ascii characters (%0.2f%%)" % \ |         LOGGER.info("[-] Skipped %d passwords with non ascii characters (%0.2f%%)" % \ | ||||||
|                     (self.foreign_stats_total, float(self.foreign_stats_total)*100.0/float(password_count)) |                     (self.foreign_stats_total, float(self.foreign_stats_total)*100.0/float(password_count))) | ||||||
|  |  | ||||||
|         # TODO: Counter breaks on large files. uniq -c | sort -rn is still the most  |         # TODO: Counter breaks on large files. uniq -c | sort -rn is still the most  | ||||||
|         #       optimal way. |         #       optimal way. | ||||||
| @@ -950,11 +947,11 @@ class RuleGen: | |||||||
|         rules_counter = Counter(rules_file) |         rules_counter = Counter(rules_file) | ||||||
|         rule_counter_total = sum(rules_counter.values()) |         rule_counter_total = sum(rules_counter.values()) | ||||||
|  |  | ||||||
|         print "\n[*] Top 10 rules" |         LOGGER.info("[*] Top 10 rules") | ||||||
|         rules_i = 0 |         rules_i = 0 | ||||||
|         for (rule, count) in rules_counter.most_common(): |         for (rule, count) in rules_counter.most_common(): | ||||||
|             rules_sorted_file.write(rule) |             rules_sorted_file.write(rule) | ||||||
|             if rules_i < 10: print "[+] %s - %d (%0.2f%%)" % (rule.rstrip('\r\n'), count, count*100/rule_counter_total) |             if rules_i < 10: LOGGER.info("[+] %s - %d (%0.2f%%)" % (rule.rstrip('\r\n'), count, count*100/rule_counter_total)) | ||||||
|             rules_i += 1 |             rules_i += 1 | ||||||
|  |  | ||||||
|         rules_file.close() |         rules_file.close() | ||||||
| @@ -966,11 +963,11 @@ class RuleGen: | |||||||
|         words_counter = Counter(words_file) |         words_counter = Counter(words_file) | ||||||
|         word_counter_total = sum(rules_counter.values()) |         word_counter_total = sum(rules_counter.values()) | ||||||
|  |  | ||||||
|         print "\n[*] Top 10 words" |         LOGGER.info("[*] Top 10 words") | ||||||
|         words_i = 0 |         words_i = 0 | ||||||
|         for (word, count) in words_counter.most_common(): |         for (word, count) in words_counter.most_common(): | ||||||
|             words_sorted_file.write(word) |             words_sorted_file.write(word) | ||||||
|             if words_i < 10: print "[+] %s - %d (%0.2f%%)" % (word.rstrip('\r\n'), count, count*100/word_counter_total) |             if words_i < 10: LOGGER.info("[+] %s - %d (%0.2f%%)" % (word.rstrip('\r\n'), count, count*100/word_counter_total)) | ||||||
|             words_i += 1 |             words_i += 1 | ||||||
|  |  | ||||||
|         words_file.close() |         words_file.close() | ||||||
| @@ -993,19 +990,19 @@ class RuleGen: | |||||||
|  |  | ||||||
|         if out == password: |         if out == password: | ||||||
|             hashcat_rules_str = " ".join(rules or [':']) |             hashcat_rules_str = " ".join(rules or [':']) | ||||||
|             if self.verbose: print "[+] %s => %s => %s" % (word, hashcat_rules_str, password) |             LOGGER.verbose("[+] %s => %s => %s" % (word, hashcat_rules_str, password)) | ||||||
|  |  | ||||||
|         else: |         else: | ||||||
|             print "[!] Hashcat Verification FAILED: %s => %s => %s (%s)" % (word," ".join(rules or [':']),password,out) |             LOGGER.error("[!] Hashcat Verification FAILED: %s => %s => %s (%s)" % (word," ".join(rules or [':']),password,out)) | ||||||
|  |  | ||||||
| if __name__ == "__main__": | if __name__ == "__main__": | ||||||
|  |  | ||||||
|     header  = "                       _ \n" |     header  = "                       _ \n" | ||||||
|     header += "     RuleGen %s    | |\n"  % VERSION |     header += "     RuleGen %s    | |\n"  % VERSION | ||||||
|     header += "      _ __   __ _  ___| | _\n" |     header += "      _ __   __ _  ___| | _\n" | ||||||
|     header += "     | '_ \ / _` |/ __| |/ /\n" |     header += "     | '_ \\ / _` |/ __| |/ /\n" | ||||||
|     header += "     | |_) | (_| | (__|   < \n" |     header += "     | |_) | (_| | (__|   < \n" | ||||||
|     header += "     | .__/ \__,_|\___|_|\_\\\n" |     header += "     | .__/ \\__,_|\\___|_|\\_\\\n" | ||||||
|     header += "     | |                    \n" |     header += "     | |                    \n" | ||||||
|     header += "     |_| iphelix@thesprawl.org\n" |     header += "     |_| iphelix@thesprawl.org\n" | ||||||
|     header += "\n" |     header += "\n" | ||||||
| @@ -1017,6 +1014,7 @@ if __name__ == "__main__": | |||||||
|     parser.add_option("-w","--wordlist", help="Use a custom wordlist for rule analysis.", metavar="wiki.dict") |     parser.add_option("-w","--wordlist", help="Use a custom wordlist for rule analysis.", metavar="wiki.dict") | ||||||
|     parser.add_option("-q", "--quiet", action="store_true", dest="quiet", default=False, help="Don't show headers.") |     parser.add_option("-q", "--quiet", action="store_true", dest="quiet", default=False, help="Don't show headers.") | ||||||
|     parser.add_option("--threads", type="int", default=multiprocessing.cpu_count(), help="Parallel threads to use for processing.") |     parser.add_option("--threads", type="int", default=multiprocessing.cpu_count(), help="Parallel threads to use for processing.") | ||||||
|  |     parser.add_option("-e", "--encoding", help="Input file encoding.", default="utf-8") | ||||||
|  |  | ||||||
|     wordtune = OptionGroup(parser, "Fine tune source word generation:") |     wordtune = OptionGroup(parser, "Fine tune source word generation:") | ||||||
|     wordtune.add_option("--maxworddist", help="Maximum word edit distance (Levenshtein)", type="int", default=10, metavar="10") |     wordtune.add_option("--maxworddist", help="Maximum word edit distance (Levenshtein)", type="int", default=10, metavar="10") | ||||||
| @@ -1037,7 +1035,7 @@ if __name__ == "__main__": | |||||||
|     spelltune.add_option("--providers", help="Comma-separated list of provider engines", default="aspell,myspell", metavar="aspell,myspell") |     spelltune.add_option("--providers", help="Comma-separated list of provider engines", default="aspell,myspell", metavar="aspell,myspell") | ||||||
|     parser.add_option_group(spelltune) |     parser.add_option_group(spelltune) | ||||||
|  |  | ||||||
|     debug = OptionGroup(parser, "Debuggin options:") |     debug = OptionGroup(parser, "Debugging options:") | ||||||
|     debug.add_option("-v","--verbose", help="Show verbose information.", action="store_true", default=False) |     debug.add_option("-v","--verbose", help="Show verbose information.", action="store_true", default=False) | ||||||
|     debug.add_option("-d","--debug", help="Debug rules.", action="store_true", default=False) |     debug.add_option("-d","--debug", help="Debug rules.", action="store_true", default=False) | ||||||
|     debug.add_option("--password", help="Process the last argument as a password not a file.", action="store_true", default=False) |     debug.add_option("--password", help="Process the last argument as a password not a file.", action="store_true", default=False) | ||||||
| @@ -1049,7 +1047,7 @@ if __name__ == "__main__": | |||||||
|  |  | ||||||
|     # Print program header |     # Print program header | ||||||
|     if not options.quiet: |     if not options.quiet: | ||||||
|         print header |         print (header) | ||||||
|  |  | ||||||
|     if len(args) < 1: |     if len(args) < 1: | ||||||
|         parser.error("no passwords file specified") |         parser.error("no passwords file specified") | ||||||
| @@ -1069,23 +1067,25 @@ if __name__ == "__main__": | |||||||
|     rulegen.more_rules=options.morerules |     rulegen.more_rules=options.morerules | ||||||
|     rulegen.simple_rules=options.simplerules |     rulegen.simple_rules=options.simplerules | ||||||
|     rulegen.brute_rules=options.bruterules |     rulegen.brute_rules=options.bruterules | ||||||
|     if rulegen.brute_rules: print "[!] Bruteforcing reversal and rotation rules. (slower)" |     if rulegen.brute_rules: LOGGER.info("[!] Bruteforcing reversal and rotation rules. (slower)") | ||||||
|  |  | ||||||
|     # Debugging options |     # Debugging options | ||||||
|     rulegen.word = options.word |     rulegen.word = options.word | ||||||
|     rulegen.verbose=options.verbose |  | ||||||
|     rulegen.debug = options.debug |  | ||||||
|     rulegen.hashcat = options.hashcat |     rulegen.hashcat = options.hashcat | ||||||
|     rulegen.quiet = options.quiet |     rulegen.quiet = options.quiet | ||||||
|  |  | ||||||
|  |     logging.basicConfig(level=logging.DEBUG if options.debug | ||||||
|  |                               else logging.VERBOSE if options.verbose | ||||||
|  |                               else logging.INFO) | ||||||
|  |  | ||||||
|     # Custom wordlist |     # Custom wordlist | ||||||
|     if not options.word: |     if not options.word: | ||||||
|         if options.wordlist: rulegen.load_custom_wordlist(options.wordlist) |         if options.wordlist: rulegen.load_custom_wordlist(options.wordlist) | ||||||
|         print "[*] Using Enchant '%s' module. For best results please install" % rulegen.enchant.provider.name |         LOGGER.info("[*] Using Enchant '%s' module. For best results please install" % rulegen.enchant.provider.name) | ||||||
|         print "    '%s' module language dictionaries." % rulegen.enchant.provider.name |         LOGGER.info("    '%s' module language dictionaries." % rulegen.enchant.provider.name) | ||||||
|  |  | ||||||
|     # Analyze a single password or several passwords in a file |     # Analyze a single password or several passwords in a file | ||||||
|     if options.password:  |     if options.password:  | ||||||
|         rulegen.analyze_password(args[0]) |         rulegen.analyze_password(args[0]) | ||||||
|     else: |     else: | ||||||
|         rulegen.analyze_passwords_file(args[0]) |         rulegen.analyze_passwords_file(args[0], options.encoding) | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user