Added support for reverse and rotation bruteforce rules to rulegen
This commit is contained in:
parent
4ed47777b5
commit
893f96c719
2
LICENSE
2
LICENSE
@ -11,7 +11,7 @@ are permitted provided that the following conditions are met:
|
|||||||
list of conditions and the following disclaimer in the documentation and/or
|
list of conditions and the following disclaimer in the documentation and/or
|
||||||
other materials provided with the distribution.
|
other materials provided with the distribution.
|
||||||
|
|
||||||
Neither the name of the {organization} nor the names of its
|
Neither the name of the 'The Sprawl' nor the names of its
|
||||||
contributors may be used to endorse or promote products derived from
|
contributors may be used to endorse or promote products derived from
|
||||||
this software without specific prior written permission.
|
this software without specific prior written permission.
|
||||||
|
|
||||||
|
22
policygen.py
22
policygen.py
@ -6,27 +6,9 @@
|
|||||||
# VERSION 0.0.2
|
# VERSION 0.0.2
|
||||||
#
|
#
|
||||||
# Copyright (C) 2013 Peter Kacherginsky
|
# Copyright (C) 2013 Peter Kacherginsky
|
||||||
# All rights reserved.
|
# All rights reserved.#
|
||||||
#
|
#
|
||||||
# Redistribution and use in source and binary forms, with or without
|
# Please see the attached LICENSE file for additiona licensing information.
|
||||||
# modification, are permitted provided that the following conditions are met:
|
|
||||||
#
|
|
||||||
# 1. Redistributions of source code must retain the above copyright notice, this
|
|
||||||
# list of conditions and the following disclaimer.
|
|
||||||
# 2. Redistributions in binary form must reproduce the above copyright notice,
|
|
||||||
# this list of conditions and the following disclaimer in the documentation
|
|
||||||
# and/or other materials provided with the distribution.
|
|
||||||
#
|
|
||||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
||||||
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
||||||
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
||||||
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
|
||||||
# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
||||||
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
||||||
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
|
||||||
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
||||||
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
|
|
||||||
import sys, string, random
|
import sys, string, random
|
||||||
import datetime
|
import datetime
|
||||||
|
565
rulegen.py
565
rulegen.py
@ -5,33 +5,12 @@
|
|||||||
#
|
#
|
||||||
# This tool is part of PACK (Password Analysis and Cracking Kit)
|
# This tool is part of PACK (Password Analysis and Cracking Kit)
|
||||||
#
|
#
|
||||||
# VERSION 0.0.2
|
# VERSION 0.0.3
|
||||||
#
|
#
|
||||||
# Copyright (C) 2013 Peter Kacherginsky
|
# Copyright (C) 2013 Peter Kacherginsky
|
||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
# Redistribution and use in source and binary forms, with or without
|
# Please see the attached LICENSE file for additiona licensing information.
|
||||||
# modification, are permitted provided that the following conditions are met:
|
|
||||||
#
|
|
||||||
# 1. Redistributions of source code must retain the above copyright notice, this
|
|
||||||
# list of conditions and the following disclaimer.
|
|
||||||
# 2. Redistributions in binary form must reproduce the above copyright notice,
|
|
||||||
# this list of conditions and the following disclaimer in the documentation
|
|
||||||
# and/or other materials provided with the distribution.
|
|
||||||
#
|
|
||||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
||||||
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
||||||
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
||||||
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
|
||||||
# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
||||||
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
||||||
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
|
||||||
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
||||||
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
#
|
|
||||||
# CHANGELOG:
|
|
||||||
# [*] Fixed greedy substitution issue (thanks smarteam)
|
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
import re
|
import re
|
||||||
@ -41,7 +20,11 @@ import enchant
|
|||||||
|
|
||||||
from optparse import OptionParser, OptionGroup
|
from optparse import OptionParser, OptionGroup
|
||||||
|
|
||||||
VERSION = "0.0.1"
|
from collections import Counter
|
||||||
|
|
||||||
|
from multiprocessing import Queue, Process
|
||||||
|
|
||||||
|
VERSION = "0.0.3"
|
||||||
|
|
||||||
# Testing rules with hashcat --stdout
|
# Testing rules with hashcat --stdout
|
||||||
HASHCAT_PATH = "hashcat/"
|
HASHCAT_PATH = "hashcat/"
|
||||||
@ -52,6 +35,12 @@ class RuleGen:
|
|||||||
# Initialize Rule Generator class
|
# Initialize Rule Generator class
|
||||||
def __init__(self,language="en",providers="aspell,myspell",basename='analysis'):
|
def __init__(self,language="en",providers="aspell,myspell",basename='analysis'):
|
||||||
|
|
||||||
|
#######################################################################
|
||||||
|
# Multiprocessing
|
||||||
|
self.password_queue = multiprocessing.Queue()
|
||||||
|
self.rule_queue = multiprocessing.Queue()
|
||||||
|
self.word_queue = multiprocessing.Queue()
|
||||||
|
|
||||||
self.enchant_broker = enchant.Broker()
|
self.enchant_broker = enchant.Broker()
|
||||||
self.enchant_broker.set_ordering("*",providers)
|
self.enchant_broker.set_ordering("*",providers)
|
||||||
|
|
||||||
@ -73,19 +62,16 @@ class RuleGen:
|
|||||||
self.max_rules = 10
|
self.max_rules = 10
|
||||||
self.more_rules = False
|
self.more_rules = False
|
||||||
self.simple_rules = False
|
self.simple_rules = False
|
||||||
|
self.brute_rules = False
|
||||||
|
|
||||||
# Debugging options
|
# Debugging options
|
||||||
self.verbose = False
|
self.verbose = False
|
||||||
self.debug = False
|
self.debug = False
|
||||||
self.word = None
|
self.word = None # Custom word to use.
|
||||||
self.quiet = False
|
self.quiet = False
|
||||||
|
|
||||||
########################################################################
|
########################################################################
|
||||||
# Word and Rule Statistics
|
# Word and Rule Statistics
|
||||||
self.word_stats = dict()
|
|
||||||
self.rule_stats = dict()
|
|
||||||
self.password_stats = dict()
|
|
||||||
|
|
||||||
self.numeric_stats_total = 0
|
self.numeric_stats_total = 0
|
||||||
self.special_stats_total = 0
|
self.special_stats_total = 0
|
||||||
self.foreign_stats_total = 0
|
self.foreign_stats_total = 0
|
||||||
@ -176,6 +162,14 @@ class RuleGen:
|
|||||||
self.leet["$"] = "s"
|
self.leet["$"] = "s"
|
||||||
self.leet["+"] = "t"
|
self.leet["+"] = "t"
|
||||||
|
|
||||||
|
########################################################################
|
||||||
|
# Preanalysis rules to bruteforce for each word
|
||||||
|
self.preanalysis_rules = []
|
||||||
|
self.preanalysis_rules.append(([],self.hashcat_rule[':'])) # Blank rule
|
||||||
|
self.preanalysis_rules.append((['r'],self.hashcat_rule['r'])) # Reverse rule
|
||||||
|
#self.preanalysis_rules.append((['{'],self.hashcat_rule['}'])) # Rotate left
|
||||||
|
#self.preanalysis_rules.append((['}'],self.hashcat_rule['{'])) # Rotate right
|
||||||
|
|
||||||
############################################################################
|
############################################################################
|
||||||
# Calculate Levenshtein edit path matrix
|
# Calculate Levenshtein edit path matrix
|
||||||
def levenshtein(self,word,password):
|
def levenshtein(self,word,password):
|
||||||
@ -205,27 +199,34 @@ class RuleGen:
|
|||||||
|
|
||||||
return matrix
|
return matrix
|
||||||
|
|
||||||
############################################################################
|
|
||||||
# Print word X password matrix
|
|
||||||
def levenshtein_print(self,matrix,word,password):
|
def levenshtein_print(self,matrix,word,password):
|
||||||
|
""" 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):
|
||||||
# Reverse Levenshtein Path Algorithm by Peter Kacherginsky
|
""" Generates levenshtein rules. Returns a list of lists of levenshtein rules. """
|
||||||
# Generates a list of edit operations necessary to transform a source word
|
|
||||||
# into a password. Edit operations are recorded in the form:
|
# 1) Generate Levenshtein matrix
|
||||||
# (operation, password_offset, word_offset)
|
matrix = self.levenshtein(word, password)
|
||||||
# Where an operation can be either insertion, deletion or replacement.
|
|
||||||
def levenshtein_reverse_path(self,matrix,word,password):
|
# 2) Trace reverse paths through the matrix.
|
||||||
paths = self.levenshtein_reverse_recursive(matrix,len(matrix)-1,len(matrix[0])-1,0)
|
paths = self.levenshtein_reverse_recursive(matrix,len(matrix)-1,len(matrix[0])-1,0)
|
||||||
|
|
||||||
|
# 3) Return a collection of reverse paths.
|
||||||
return [path for path in paths if len(path) <= matrix[-1][-1]]
|
return [path for path in paths if len(path) <= matrix[-1][-1]]
|
||||||
|
|
||||||
# Calculate reverse Levenshtein paths (recursive, depth first, short-circuited)
|
|
||||||
def levenshtein_reverse_recursive(self,matrix,i,j,path_len):
|
def levenshtein_reverse_recursive(self,matrix,i,j,path_len):
|
||||||
|
""" Calculate reverse Levenshtein paths.
|
||||||
|
Recursive, Depth First, Short-circuited algorithm by Peter Kacherginsky
|
||||||
|
Generates a list of edit operations necessary to transform a source word
|
||||||
|
into a password. Edit operations are recorded in the form:
|
||||||
|
(operation, password_offset, word_offset)
|
||||||
|
Where an operation can be either insertion, deletion or replacement.
|
||||||
|
"""
|
||||||
|
|
||||||
if i == 0 and j == 0 or path_len > matrix[-1][-1]:
|
if i == 0 and j == 0 or path_len > matrix[-1][-1]:
|
||||||
return [[]]
|
return [[]]
|
||||||
@ -260,91 +261,129 @@ class RuleGen:
|
|||||||
|
|
||||||
return paths
|
return paths
|
||||||
|
|
||||||
############################################################################
|
|
||||||
def load_custom_wordlist(self,wordlist_file):
|
def load_custom_wordlist(self,wordlist_file):
|
||||||
self.enchant = enchant.request_pwl_dict(wordlist_file)
|
self.enchant = enchant.request_pwl_dict(wordlist_file)
|
||||||
|
|
||||||
############################################################################
|
def generate_words(self,password):
|
||||||
# Generate source words
|
""" Generate source word candidates."""
|
||||||
def generate_words_collection(self,password):
|
|
||||||
|
|
||||||
if self.debug: print "[*] Generating source words for %s" % password
|
if self.debug: print "[*] Generating source words for %s" % password
|
||||||
|
|
||||||
words = []
|
words = list()
|
||||||
if not self.simple_words: suggestions = self.generate_advanced_words(password)
|
words_collection = list()
|
||||||
else: suggestions = self.generate_simple_words(password)
|
|
||||||
|
|
||||||
best_found_distance = sys.maxint
|
# Let's collect best edit distance as soon as possible to prevent
|
||||||
|
# less efficient pre_rules like reversal and rotation from slowing
|
||||||
|
# us down with garbage
|
||||||
|
best_found_distance = 9999
|
||||||
|
|
||||||
unique_suggestions = []
|
#######################################################################
|
||||||
for word in suggestions:
|
# Generate words for each preanalysis rule
|
||||||
word = word.replace(' ','')
|
if not self.brute_rules:
|
||||||
word = word.replace('-','')
|
self.preanalysis_rules = self.preanalysis_rules[:1]
|
||||||
if not word in unique_suggestions:
|
|
||||||
unique_suggestions.append(word)
|
|
||||||
|
|
||||||
# NOTE: Enchant already returned a list sorted by edit distance, so
|
for pre_rule, pre_rule_lambda in self.preanalysis_rules:
|
||||||
# we simply need to get the best edit distance of the first word
|
|
||||||
# and compare the rest with it
|
|
||||||
for word in unique_suggestions:
|
|
||||||
|
|
||||||
matrix = self.levenshtein(word,password)
|
pre_password = pre_rule_lambda(password)
|
||||||
edit_distance = matrix[-1][-1]
|
|
||||||
|
|
||||||
# Record best edit distance and skip anything exceeding it
|
# Generate word suggestions
|
||||||
|
if self.word: suggestions = [self.word]
|
||||||
|
elif self.simple_words: suggestions = self.generate_simple_words(pre_password)
|
||||||
|
else: suggestions = self.generate_advanced_words(pre_password)
|
||||||
|
|
||||||
|
# HACK: Perform some additional expansion on multi-word suggestions
|
||||||
|
# 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('-','')
|
||||||
|
if not suggestion in suggestions:
|
||||||
|
suggestions.append(suggestion)
|
||||||
|
|
||||||
|
if len(suggestions) != len(set(suggestions)):
|
||||||
|
print sorted(suggestions)
|
||||||
|
print sorted(set(suggestions))
|
||||||
|
|
||||||
|
|
||||||
|
for suggestion in suggestions:
|
||||||
|
|
||||||
|
distance = enchant.utils.levenshtein(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)
|
||||||
|
|
||||||
|
#######################################################################
|
||||||
|
# Perform Optimization
|
||||||
|
for word in sorted(words, key=lambda word: word["distance"], reverse=False):
|
||||||
|
|
||||||
|
# Optimize for best distance
|
||||||
if not self.more_words:
|
if not self.more_words:
|
||||||
if edit_distance < best_found_distance:
|
if word["distance"] < best_found_distance:
|
||||||
best_found_distance = edit_distance
|
best_found_distance = word["distance"]
|
||||||
elif edit_distance > best_found_distance:
|
|
||||||
if self.verbose: print "[-] %s => {best distance exceeded: %d (%d)} => %s" % (word,edit_distance,best_found_distance,password)
|
|
||||||
break
|
|
||||||
|
|
||||||
if edit_distance <= self.max_word_dist:
|
elif word["distance"] > best_found_distance:
|
||||||
if self.debug: print "[+] %s => {edit distance: %d (%d)} = > %s" % (word,edit_distance,best_found_distance,password)
|
if self.verbose:
|
||||||
words.append((word,matrix,password))
|
print "[-] %s => {edit distance suboptimal: %d (%d)} => %s" % \
|
||||||
|
(word["suggestion"], word["distance"], best_found_distance, word["password"])
|
||||||
|
break
|
||||||
|
|
||||||
if not word in self.word_stats: self.word_stats[word] = 1
|
# Filter words with too big edit distance
|
||||||
else: self.word_stats[word] += 1
|
if word["distance"] <= self.max_word_dist:
|
||||||
|
if self.debug:
|
||||||
|
print "[+] %s => {edit distance: %d (%d)} = > %s" % \
|
||||||
|
(word["suggestion"], word["distance"],best_found_distance, word["password"])
|
||||||
|
|
||||||
|
words_collection.append(word)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
if self.verbose: print "[-] %s => {max distance exceeded: %d (%d)} => %s" % (word,edit_distance,self.max_word_dist,password)
|
if self.verbose:
|
||||||
|
print "[-] %s => {max distance exceeded: %d (%d)} => %s" % \
|
||||||
|
(word["suggestion"], word["distance"], self.max_word_dist, word["password"])
|
||||||
|
|
||||||
return words
|
if self.max_words:
|
||||||
|
words_collection = words_collection[:self.max_words]
|
||||||
|
|
||||||
|
return words_collection
|
||||||
|
|
||||||
############################################################################
|
|
||||||
# Generate simple words
|
|
||||||
def generate_simple_words(self,password):
|
def generate_simple_words(self,password):
|
||||||
if self.word:
|
""" Generate simple words. A simple spellcheck."""
|
||||||
return [self.word]
|
|
||||||
else:
|
return self.enchant.suggest(password)
|
||||||
return self.enchant.suggest(password)[:self.max_words]
|
|
||||||
|
|
||||||
############################################################################
|
|
||||||
# Generate advanced words
|
|
||||||
def generate_advanced_words(self,password):
|
def generate_advanced_words(self,password):
|
||||||
if self.word:
|
""" Generate advanced words.
|
||||||
return [self.word]
|
Perform some additional non-destructive cleaning to help spell-checkers:
|
||||||
else:
|
1) Remove non-alpha prefixes and appendixes.
|
||||||
# Remove non-alpha prefix and appendix
|
2) Perform common pattern matches (e.g. email).
|
||||||
insertion_matches = self.password_pattern["insertion"].match(password)
|
3) Replace non-alpha character substitutions (1337 5p34k)
|
||||||
if insertion_matches:
|
"""
|
||||||
password = insertion_matches.group('password')
|
|
||||||
|
|
||||||
# Email split
|
# Remove non-alpha prefix and/or appendix
|
||||||
email_matches = self.password_pattern["email"].match(password)
|
insertion_matches = self.password_pattern["insertion"].match(password)
|
||||||
if email_matches:
|
if insertion_matches:
|
||||||
password = email_matches.group('password')
|
password = insertion_matches.group('password')
|
||||||
|
|
||||||
# Replace common special character replacements (1337 5p34k)
|
# Pattern matches
|
||||||
preanalysis_password = ''
|
email_matches = self.password_pattern["email"].match(password)
|
||||||
for c in password:
|
if email_matches:
|
||||||
if c in self.leet: preanalysis_password += self.leet[c]
|
password = email_matches.group('password')
|
||||||
else: preanalysis_password += c
|
|
||||||
password = preanalysis_password
|
|
||||||
|
|
||||||
if self.debug: "[*] Preanalysis Password: %s" % password
|
# Replace common special character replacements (1337 5p34k)
|
||||||
|
preanalysis_password = ''
|
||||||
|
for c in password:
|
||||||
|
if c in self.leet: preanalysis_password += self.leet[c]
|
||||||
|
else: preanalysis_password += c
|
||||||
|
password = preanalysis_password
|
||||||
|
|
||||||
return self.enchant.suggest(password)[:self.max_words]
|
if self.debug: "[*] Preanalysis Password: %s" % password
|
||||||
|
|
||||||
|
return self.enchant.suggest(password)
|
||||||
|
|
||||||
############################################################################
|
############################################################################
|
||||||
# Hashcat specific offset definition 0-9,A-Z
|
# Hashcat specific offset definition 0-9,A-Z
|
||||||
@ -356,56 +395,58 @@ class RuleGen:
|
|||||||
if N.isdigit(): return int(N)
|
if N.isdigit(): return int(N)
|
||||||
else: return ord(N)-65+10
|
else: return ord(N)-65+10
|
||||||
|
|
||||||
############################################################################
|
def generate_hashcat_rules(self, suggestion, password):
|
||||||
# Generate hashcat rules
|
""" Generate hashcat rules. Returns a length sorted list of lists of hashcat rules."""
|
||||||
def generate_hashcat_rules_collection(self, lev_rules_collection):
|
|
||||||
|
|
||||||
|
# 2) Generate Levenshtein Rules
|
||||||
|
lev_rules = self.generate_levenshtein_rules(suggestion, password)
|
||||||
|
|
||||||
|
# 3) Generate Hashcat Rules
|
||||||
|
hashcat_rules = []
|
||||||
hashcat_rules_collection = []
|
hashcat_rules_collection = []
|
||||||
|
|
||||||
min_hashcat_rules_length = sys.maxint
|
#######################################################################
|
||||||
for (word,rules,password) in lev_rules_collection:
|
# Generate hashcat rule for each levenshtein rule
|
||||||
|
for lev_rule in lev_rules:
|
||||||
|
|
||||||
if self.simple_rules:
|
if self.simple_rules:
|
||||||
hashcat_rules = self.generate_simple_hashcat_rules(word,rules,password)
|
hashcat_rule = self.generate_simple_hashcat_rules(suggestion, lev_rule, password)
|
||||||
else:
|
else:
|
||||||
hashcat_rules = self.generate_advanced_hashcat_rules(word,rules,password)
|
hashcat_rule = self.generate_advanced_hashcat_rules(suggestion, lev_rule, password)
|
||||||
|
|
||||||
if not hashcat_rules == None:
|
if hashcat_rule == None:
|
||||||
|
print "[!] Processing FAILED: %s => ;( => %s" % (suggestion,password)
|
||||||
hashcat_rules_length = len(hashcat_rules)
|
|
||||||
|
|
||||||
if hashcat_rules_length <= self.max_rule_len:
|
|
||||||
hashcat_rules_collection.append((word,hashcat_rules,password))
|
|
||||||
|
|
||||||
# Determine minimal hashcat rules length
|
|
||||||
if hashcat_rules_length < min_hashcat_rules_length:
|
|
||||||
min_hashcat_rules_length = hashcat_rules_length
|
|
||||||
else:
|
|
||||||
if self.verbose: print "[!] %s => {max rule length exceeded: %d (%d)} => %s" % (word,hashcat_rules_length,self.max_rule_len,password)
|
|
||||||
else:
|
|
||||||
print "[!] Processing FAILED: %s => ;( => %s" % (word,password)
|
|
||||||
print " Sorry about that, please report this failure to"
|
print " Sorry about that, please report this failure to"
|
||||||
print " the developer: iphelix [at] thesprawl.org"
|
print " the developer: iphelix [at] thesprawl.org"
|
||||||
|
|
||||||
|
else:
|
||||||
|
hashcat_rules.append(hashcat_rule)
|
||||||
|
|
||||||
# Remove suboptimal rules
|
best_found_rule_length = 9999
|
||||||
if not self.more_rules:
|
|
||||||
min_hashcat_rules_collection = []
|
|
||||||
for (word,hashcat_rules,password) in hashcat_rules_collection:
|
|
||||||
|
|
||||||
hashcat_rules_length = len(hashcat_rules)
|
#######################################################################
|
||||||
if hashcat_rules_length == min_hashcat_rules_length:
|
# Perform Optimization
|
||||||
min_hashcat_rules_collection.append((word,hashcat_rules,password))
|
for hashcat_rule in sorted(hashcat_rules, key=lambda hashcat_rule: len(hashcat_rule)):
|
||||||
else:
|
|
||||||
if self.verbose: print "[!] %s => {rule length suboptimal: %d (%d)} => %s" % (word,hashcat_rules_length,min_hashcat_rules_length,password)
|
|
||||||
|
|
||||||
hashcat_rules_collection = min_hashcat_rules_collection
|
rule_length = len(hashcat_rule)
|
||||||
|
|
||||||
|
if not self.more_rules:
|
||||||
|
if rule_length < best_found_rule_length:
|
||||||
|
best_found_rule_length = rule_length
|
||||||
|
|
||||||
|
elif rule_length > best_found_rule_length:
|
||||||
|
if self.verbose:
|
||||||
|
print "[-] %s => {best rule length exceeded: %d (%d)} => %s" % \
|
||||||
|
(suggestion, rule_length, best_found_rule_length, password)
|
||||||
|
break
|
||||||
|
|
||||||
|
if rule_length <= self.max_rule_len:
|
||||||
|
hashcat_rules_collection.append(hashcat_rule)
|
||||||
|
|
||||||
return hashcat_rules_collection
|
return hashcat_rules_collection
|
||||||
|
|
||||||
############################################################################
|
|
||||||
# Generate basic hashcat rules using only basic insert,delete,replace rules
|
|
||||||
def generate_simple_hashcat_rules(self,word,rules,password):
|
def generate_simple_hashcat_rules(self,word,rules,password):
|
||||||
|
""" 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)
|
if self.debug: print "[*] Simple Processing %s => %s" % (word,password)
|
||||||
@ -439,10 +480,8 @@ class RuleGen:
|
|||||||
if self.debug: print "[!] Simple Processing FAILED: %s => %s => %s (%s)" % (word," ".join(hashcat_rules),password,word_rules)
|
if self.debug: print "[!] Simple Processing FAILED: %s => %s => %s (%s)" % (word," ".join(hashcat_rules),password,word_rules)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
############################################################################
|
|
||||||
# Generate advanced hashcat rules using full range of available rules
|
|
||||||
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. """
|
||||||
hashcat_rules = []
|
hashcat_rules = []
|
||||||
|
|
||||||
if self.debug: print "[*] Advanced Processing %s => %s" % (word,password)
|
if self.debug: print "[*] Advanced Processing %s => %s" % (word,password)
|
||||||
@ -472,7 +511,7 @@ class RuleGen:
|
|||||||
# Detecting global replacement such as sXY, l, u, C, c is a non
|
# Detecting global replacement such as sXY, l, u, C, c is a non
|
||||||
# trivial problem because different characters may be added or
|
# trivial problem because different characters may be added or
|
||||||
# removed from the word by other rules. A reliable way to solve
|
# removed from the word by other rules. A reliable way to solve
|
||||||
# this problem is to apply all of the rules the the source word
|
# this problem is to apply all of the rules the source word
|
||||||
# and keep track of its state at any given time. At the same
|
# and keep track of its state at any given time. At the same
|
||||||
# time, global replacement rules can be tested by completing
|
# time, global replacement rules can be tested by completing
|
||||||
# the rest of the rules using a simplified engine.
|
# the rest of the rules using a simplified engine.
|
||||||
@ -665,115 +704,146 @@ class RuleGen:
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
############################################################################
|
############################################################################
|
||||||
def print_hashcat_rules(self,hashcat_rules_collection):
|
def print_hashcat_rules(self, words, password):
|
||||||
|
|
||||||
for word,rules,password in hashcat_rules_collection:
|
# sorted(self.masks.keys(), key=lambda mask: self.masks[mask][sorting_mode], reverse=True):
|
||||||
|
|
||||||
|
best_found_rule_length = 9999
|
||||||
|
|
||||||
|
# Sorted list based on rule length
|
||||||
|
for word in sorted(words, key=lambda word: len(word["hashcat_rules"][0])):
|
||||||
|
|
||||||
|
for hashcat_rule in word["hashcat_rules"]:
|
||||||
|
|
||||||
|
rule_length = len(hashcat_rule)
|
||||||
|
|
||||||
|
if not self.more_rules:
|
||||||
|
if rule_length < best_found_rule_length:
|
||||||
|
best_found_rule_length = rule_length
|
||||||
|
|
||||||
|
elif rule_length > best_found_rule_length:
|
||||||
|
if self.verbose:
|
||||||
|
print "[-] %s => {best rule length exceeded: %d (%d)} => %s" % \
|
||||||
|
(word["suggestion"], rule_length, best_found_rule_length, password)
|
||||||
|
break
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
if self.hashcat:
|
||||||
|
self.verify_hashcat_rules(word["suggestion"], hashcat_rule + word["pre_rule"], password)
|
||||||
|
|
||||||
|
# TODO: Collect statistics later
|
||||||
|
# if hashcat_rule_str in self.rule_stats: self.rule_stats[hashcat_rule_str] += 1
|
||||||
|
# else: self.rule_stats[hashcat_rule_str] = 1
|
||||||
|
|
||||||
|
self.output_rules_f.write("%s\n" % hashcat_rule_str)
|
||||||
|
self.output_words_f.write("%s\n" % word["suggestion"])
|
||||||
|
|
||||||
|
############################################################################
|
||||||
|
def verify_hashcat_rules(self,word, rules, password):
|
||||||
|
import subprocess
|
||||||
|
|
||||||
|
f = open("%s/test.rule" % HASHCAT_PATH,'w')
|
||||||
|
f.write(" ".join(rules))
|
||||||
|
f.close()
|
||||||
|
|
||||||
|
f = open("%s/test.word" % HASHCAT_PATH,'w')
|
||||||
|
f.write(word)
|
||||||
|
f.close()
|
||||||
|
|
||||||
|
p = subprocess.Popen(["%s/hashcat-cli64.bin" % HASHCAT_PATH,"-r","%s/test.rule" % HASHCAT_PATH,"--stdout","%s/test.word" % HASHCAT_PATH], stdout=subprocess.PIPE)
|
||||||
|
out, err = p.communicate()
|
||||||
|
out = out.strip()
|
||||||
|
|
||||||
|
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)
|
if self.verbose: print "[+] %s => %s => %s" % (word, hashcat_rules_str, password)
|
||||||
|
|
||||||
if not hashcat_rules_str in self.rule_stats: self.rule_stats[hashcat_rules_str] = 1
|
else:
|
||||||
else: self.rule_stats[hashcat_rules_str] += 1
|
print "[!] Hashcat Verification FAILED: %s => %s => %s (%s)" % (word," ".join(rules or [':']),password,out)
|
||||||
|
|
||||||
self.output_rules_f.write("%s\n" % hashcat_rules_str)
|
def check_reversible_password(self, password):
|
||||||
self.output_words_f.write("%s\n" % word)
|
""" Check whether the password is likely to be reversed successfuly. """
|
||||||
|
|
||||||
############################################################################
|
|
||||||
def verify_hashcat_rules(self,hashcat_rules_collection):
|
|
||||||
|
|
||||||
for word,rules,password in hashcat_rules_collection:
|
|
||||||
|
|
||||||
f = open("%s/test.rule" % HASHCAT_PATH,'w')
|
|
||||||
f.write(" ".join(rules))
|
|
||||||
f.close()
|
|
||||||
|
|
||||||
f = open("%s/test.word" % HASHCAT_PATH,'w')
|
|
||||||
f.write(word)
|
|
||||||
f.close()
|
|
||||||
|
|
||||||
p = subprocess.Popen(["%s/hashcat-cli64.bin" % HASHCAT_PATH,"-r","%s/test.rule" % HASHCAT_PATH,"--stdout","%s/test.word" % HASHCAT_PATH], stdout=subprocess.PIPE)
|
|
||||||
out, err = p.communicate()
|
|
||||||
out = out.strip()
|
|
||||||
|
|
||||||
if out == password:
|
|
||||||
hashcat_rules_str = " ".join(rules or [':'])
|
|
||||||
if self.verbose: print "[+] %s => %s => %s" % (word, hashcat_rules_str, password)
|
|
||||||
|
|
||||||
if not hashcat_rules_str in self.rule_stats: self.rule_stats[hashcat_rules_str] = 1
|
|
||||||
else: self.rule_stats[hashcat_rules_str] += 1
|
|
||||||
|
|
||||||
self.output_rules_f.write("%s\n" % hashcat_rules_str)
|
|
||||||
self.output_words_f.write("%s\n" % word)
|
|
||||||
else:
|
|
||||||
print "[!] Hashcat Verification FAILED: %s => %s => %s (%s)" % (word," ".join(rules or [':']),password,out)
|
|
||||||
|
|
||||||
############################################################################
|
|
||||||
# Analyze a single password
|
|
||||||
def analyze_password(self,password):
|
|
||||||
if self.verbose: print "[*] Analyzing password: %s" % password
|
|
||||||
if self.verbose: start_time = time.clock()
|
|
||||||
|
|
||||||
# Skip all numeric passwords
|
# Skip all numeric passwords
|
||||||
if password.isdigit():
|
if password.isdigit():
|
||||||
if self.verbose: print "[!] %s => {skipping numeric} => %s" % (password,password)
|
if self.verbose and not self.quiet: print "[!] %s => {skipping numeric} => %s" % (password,password)
|
||||||
self.numeric_stats_total += 1
|
self.numeric_stats_total += 1
|
||||||
|
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:
|
||||||
print "[!] %s => {skipping alpha less than 25%%} => %s" % (password,password)
|
if self.verbose and not self.quiet:print "[!] %s => {skipping alpha less than 25%%} => %s" % (password,password)
|
||||||
self.special_stats_total += 1
|
self.special_stats_total += 1
|
||||||
|
return False
|
||||||
|
|
||||||
# Only check english ascii passwords for now, add more languages in the next version
|
# 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 [c for c in password if ord(c) < 32 or ord(c) > 126]:
|
||||||
if self.verbose: print "[!] %s => {skipping non ascii english} => %s" % (password,password)
|
if self.verbose and not self.quiet: print "[!] %s => {skipping non ascii english} => %s" % (password,password)
|
||||||
self.foreign_stats_total += 1
|
self.foreign_stats_total += 1
|
||||||
|
return False
|
||||||
|
|
||||||
# Analyze the password
|
|
||||||
else:
|
else:
|
||||||
|
return True
|
||||||
|
|
||||||
if not password in self.password_stats: self.password_stats[password] = 1
|
def analyze_password(self,password):
|
||||||
else: self.password_stats[password] += 1
|
""" Analyze a single password. """
|
||||||
|
|
||||||
# Short-cut words already in the dictionary
|
if self.verbose: print "[*] Analyzing password: %s" % password
|
||||||
if self.enchant.check(password):
|
if self.verbose: start_time = time.clock()
|
||||||
|
|
||||||
|
# Only process passwords likely to be dictionary based.
|
||||||
|
if self.check_reversible_password(password):
|
||||||
|
|
||||||
|
# TODO: Collect statistics later
|
||||||
|
# if password in self.password_stats: self.password_stats[password] += 1
|
||||||
|
# else: self.password_stats[password] = 1
|
||||||
|
|
||||||
|
words = []
|
||||||
|
|
||||||
|
# Short-cut words in the dictionary
|
||||||
|
if self.enchant.check(password) and not self.word:
|
||||||
|
|
||||||
# Record password as a source word for stats
|
# Record password as a source word for stats
|
||||||
if not password in self.word_stats: self.word_stats[password] = 1
|
# TODO: Collect statistics later
|
||||||
else: self.word_stats[password] += 1
|
# if password in self.word_stats: self.word_stats[password] += 1
|
||||||
|
# else: self.word_stats[password] = 1
|
||||||
|
|
||||||
hashcat_rules_collection = [(password,[],password)]
|
word = dict()
|
||||||
|
word["password"] = password
|
||||||
|
word["suggestion"] = password
|
||||||
|
word["hashcat_rules"] = [[],]
|
||||||
|
word["pre_rule"] = []
|
||||||
|
word["best_rule_length"] = 9999
|
||||||
|
|
||||||
|
words.append(word)
|
||||||
|
|
||||||
# Generate rules for words not in the dictionary
|
# Generate rules for words not in the dictionary
|
||||||
else:
|
else:
|
||||||
|
|
||||||
# Generate source words list
|
# Generate source words list
|
||||||
words_collection = self.generate_words_collection(password)
|
words = self.generate_words(password)
|
||||||
|
|
||||||
# Generate levenshtein rules collection for each source word
|
# Generate levenshtein reverse paths for each suggestion
|
||||||
lev_rules_collection = []
|
for word in words:
|
||||||
for word,matrix,password in words_collection:
|
|
||||||
|
|
||||||
# Generate multiple paths to get from word to password
|
# Generate a collection of hashcat_rules lists
|
||||||
lev_rules = self.levenshtein_reverse_path(matrix,word,password)
|
word["hashcat_rules"] = self.generate_hashcat_rules(word["suggestion"],word["password"])
|
||||||
for lev_rule in lev_rules:
|
|
||||||
lev_rules_collection.append((word,lev_rule,password))
|
|
||||||
|
|
||||||
# Generate hashcat rules collection
|
self.print_hashcat_rules(words, password)
|
||||||
hashcat_rules_collection = self.generate_hashcat_rules_collection(lev_rules_collection)
|
|
||||||
|
|
||||||
# Print complete for each source word for the original password
|
|
||||||
if self.hashcat:
|
|
||||||
self.verify_hashcat_rules(hashcat_rules_collection)
|
|
||||||
else:
|
|
||||||
self.print_hashcat_rules(hashcat_rules_collection)
|
|
||||||
|
|
||||||
if self.verbose: print "[*] Finished analysis in %.2f seconds" % (time.clock()-start_time)
|
if self.verbose: print "[*] Finished analysis in %.2f seconds" % (time.clock()-start_time)
|
||||||
|
|
||||||
############################################################################
|
|
||||||
# Analyze passwords file
|
# Analyze passwords file
|
||||||
def analyze_passwords_file(self,passwords_file):
|
def analyze_passwords_file(self,passwords_file):
|
||||||
print "[*] Analyzing passwords file: %s:" % passwords_file
|
""" Analyze provided passwords file. """
|
||||||
|
|
||||||
|
print "[*] Analyzing passwords file: %s:" % passwords_file
|
||||||
f = open(passwords_file,'r')
|
f = open(passwords_file,'r')
|
||||||
|
|
||||||
password_count = 0
|
password_count = 0
|
||||||
@ -783,53 +853,60 @@ class RuleGen:
|
|||||||
password = password.strip()
|
password = password.strip()
|
||||||
if len(password) > 0:
|
if len(password) > 0:
|
||||||
|
|
||||||
if password_count != 0 and password_count % 1000 == 0:
|
# Provide analysis time feedback to the user
|
||||||
|
if password_count != 0 and password_count % 10000 == 0:
|
||||||
current_analysis_time = time.clock() - analysis_start
|
current_analysis_time = time.clock() - analysis_start
|
||||||
if not self.quiet: print "[*] Processed %d passwords in %.2f seconds at the rate of %.2f p/sec" % (password_count, current_analysis_time, float(password_count)/current_analysis_time )
|
if not self.quiet: print "[*] Processed %d passwords in %.2f seconds at the rate of %.2f p/sec" % (password_count, current_analysis_time, password_count/current_analysis_time )
|
||||||
|
|
||||||
password_count += 1
|
password_count += 1
|
||||||
self.analyze_password(password)
|
self.analyze_password(password)
|
||||||
except (KeyboardInterrupt, SystemExit):
|
|
||||||
print "\n[*] Rulegen was interrupted."
|
|
||||||
|
|
||||||
|
except (KeyboardInterrupt, SystemExit):
|
||||||
|
print "\n[!] Rulegen was interrupted."
|
||||||
|
f.close()
|
||||||
|
|
||||||
analysis_time = time.clock() - analysis_start
|
analysis_time = time.clock() - 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 )
|
print "[*] Finished processing %d passwords in %.2f seconds at the rate of %.2f p/sec" % (password_count, analysis_time, float(password_count)/analysis_time )
|
||||||
|
|
||||||
password_stats_total = sum(self.password_stats.values())
|
|
||||||
print "[*] Analyzed %d passwords (%0.2f%%)" % (password_stats_total,float(password_stats_total)*100.0/float(password_count))
|
|
||||||
print "[-] Skipped %d all numeric passwords (%0.2f%%)" % (self.numeric_stats_total, float(self.numeric_stats_total)*100.0/float(password_stats_total))
|
|
||||||
print "[-] Skipped %d passwords with less than 25%% alpha characters (%0.2f%%)" % (self.special_stats_total, float(self.special_stats_total)*100.0/float(password_stats_total))
|
|
||||||
print "[-] Skipped %d passwords with non ascii characters (%0.2f%%)" % (self.foreign_stats_total, float(self.foreign_stats_total)*100.0/float(password_stats_total))
|
|
||||||
|
|
||||||
print "\n[*] Top 10 word statistics"
|
print "[*] Generating statistics for [%s] rules and words." % self.basename
|
||||||
top100_f = open("%s-top100.word" % self.basename, 'w')
|
print "[-] Skipped %d all numeric passwords (%0.2f%%)" % \
|
||||||
word_stats_total = sum(self.word_stats.values())
|
(self.numeric_stats_total, float(self.numeric_stats_total)*100.0/float(password_count))
|
||||||
for i,(word,count) in enumerate(sorted(self.word_stats.iteritems(), key=operator.itemgetter(1), reverse=True)[:100]):
|
print "[-] Skipped %d passwords with less than 25%% alpha characters (%0.2f%%)" % \
|
||||||
if i < 10: print "[+] %s - %d (%0.2f%%)" % (word, count, float(count)*100/float(word_stats_total))
|
(self.special_stats_total, float(self.special_stats_total)*100.0/float(password_count))
|
||||||
top100_f.write("%s\n" % word)
|
print "[-] Skipped %d passwords with non ascii characters (%0.2f%%)" % \
|
||||||
top100_f.close()
|
(self.foreign_stats_total, float(self.foreign_stats_total)*100.0/float(password_count))
|
||||||
print "[*] Saving Top 100 words in %s-top100.word" % self.basename
|
|
||||||
|
rules_file = open("%s.rule" % self.basename,'r')
|
||||||
|
rules_sorted_file = open("%s-sorted.rule" % self.basename, 'w')
|
||||||
|
rules_counter = Counter(rules_file)
|
||||||
|
rule_counter_total = sum(rules_counter.values())
|
||||||
|
|
||||||
print "\n[*] Top 10 rule statistics"
|
print "\n[*] Top 10 rule statistics"
|
||||||
top100_f = open("%s-top100.rule" % self.basename, 'w')
|
rules_i = 0
|
||||||
rule_stats_total = sum(self.rule_stats.values())
|
for (rule, count) in rules_counter.most_common():
|
||||||
for i,(rule,count) in enumerate(sorted(self.rule_stats.iteritems(), key=operator.itemgetter(1), reverse=True)[:100]):
|
rules_sorted_file.write(rule)
|
||||||
if i < 10: print "[+] %s - %d (%0.2f%%)" % (rule, count, float(count)*100/float(rule_stats_total))
|
if rules_i < 10: print "[+] %s - %d (%0.2f%%)" % (rule.rstrip('\r\n'), count, count*100/rule_counter_total)
|
||||||
top100_f.write("%s\n" % rule)
|
rules_i += 1
|
||||||
top100_f.close()
|
|
||||||
print "[*] Saving Top 100 rules in %s-top100.rule" % self.basename
|
|
||||||
|
|
||||||
print "\n[*] Top 10 password statistics"
|
rules_file.close()
|
||||||
top100_f = open("%s-top100.password" % self.basename, 'w')
|
rules_sorted_file.close()
|
||||||
password_stats_total = sum(self.password_stats.values())
|
|
||||||
for i,(password,count) in enumerate(sorted(self.password_stats.iteritems(), key=operator.itemgetter(1), reverse=True)[:100]):
|
|
||||||
if i < 10: print "[+] %s - %d (%0.2f%%)" % (password, count, float(count)*100/float(password_stats_total))
|
|
||||||
top100_f.write("%s\n" % password)
|
|
||||||
top100_f.close()
|
|
||||||
print "[*] Saving Top 100 passwords in %s-top100.password" % self.basename
|
|
||||||
|
|
||||||
f.close()
|
|
||||||
|
words_file = open("%s.word" % self.basename,'r')
|
||||||
|
words_sorted_file = open("%s-sorted.word" % self.basename,'w')
|
||||||
|
words_counter = Counter(words_file)
|
||||||
|
word_counter_total = sum(rules_counter.values())
|
||||||
|
|
||||||
|
print "\n[*] Top 10 word statistics"
|
||||||
|
words_i = 0
|
||||||
|
for (word, count) in words_counter.most_common():
|
||||||
|
words_sorted_file.write(word)
|
||||||
|
if words_i < 10: print "[+] %s - %d (%0.2f%%)" % (word.rstrip('\r\n'), count, count*100/word_counter_total)
|
||||||
|
words_i += 1
|
||||||
|
|
||||||
|
words_file.close()
|
||||||
|
words_sorted_file.close()
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|
||||||
@ -861,7 +938,8 @@ if __name__ == "__main__":
|
|||||||
ruletune.add_option("--maxrulelen", help="Maximum number of operations in a single rule", type="int", default=10, metavar="10")
|
ruletune.add_option("--maxrulelen", help="Maximum number of operations in a single rule", type="int", default=10, metavar="10")
|
||||||
ruletune.add_option("--maxrules", help="Maximum number of rules to consider", type="int", default=5, metavar="5")
|
ruletune.add_option("--maxrules", help="Maximum number of rules to consider", type="int", default=5, metavar="5")
|
||||||
ruletune.add_option("--morerules", help="Generate suboptimal rules", action="store_true", default=False)
|
ruletune.add_option("--morerules", help="Generate suboptimal rules", action="store_true", default=False)
|
||||||
ruletune.add_option("--simplerules", help="Generate simple rules insert,delete,hashcat",action="store_true", default=False)
|
ruletune.add_option("--simplerules", help="Generate simple rules insert,delete,replace",action="store_true", default=False)
|
||||||
|
ruletune.add_option("--bruterules", help="Bruteforce reversal and rotation rules (slow)",action="store_true", default=False)
|
||||||
parser.add_option_group(ruletune)
|
parser.add_option_group(ruletune)
|
||||||
|
|
||||||
spelltune = OptionGroup(parser, "Fine tune spell checker engine:")
|
spelltune = OptionGroup(parser, "Fine tune spell checker engine:")
|
||||||
@ -899,6 +977,8 @@ if __name__ == "__main__":
|
|||||||
rulegen.max_rules=options.maxrules
|
rulegen.max_rules=options.maxrules
|
||||||
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
|
||||||
|
if rulegen.brute_rules: print "[!] Bruteforcing reversal and rotation rules. (slower)"
|
||||||
|
|
||||||
# Debugging options
|
# Debugging options
|
||||||
rulegen.word = options.word
|
rulegen.word = options.word
|
||||||
@ -920,4 +1000,5 @@ if __name__ == "__main__":
|
|||||||
|
|
||||||
# Analyze a single password or several passwords in a file
|
# Analyze a single password or several passwords in a file
|
||||||
if options.password: rulegen.analyze_password(args[0])
|
if options.password: rulegen.analyze_password(args[0])
|
||||||
else: rulegen.analyze_passwords_file(args[0])
|
else:
|
||||||
|
rulegen.analyze_passwords_file(args[0])
|
23
statsgen.py
23
statsgen.py
@ -8,25 +8,7 @@
|
|||||||
# Copyright (C) 2013 Peter Kacherginsky
|
# Copyright (C) 2013 Peter Kacherginsky
|
||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
# Redistribution and use in source and binary forms, with or without
|
# Please see the attached LICENSE file for additiona licensing information.
|
||||||
# modification, are permitted provided that the following conditions are met:
|
|
||||||
#
|
|
||||||
# 1. Redistributions of source code must retain the above copyright notice, this
|
|
||||||
# list of conditions and the following disclaimer.
|
|
||||||
# 2. Redistributions in binary form must reproduce the above copyright notice,
|
|
||||||
# this list of conditions and the following disclaimer in the documentation
|
|
||||||
# and/or other materials provided with the distribution.
|
|
||||||
#
|
|
||||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
||||||
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
||||||
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
||||||
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
|
||||||
# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
||||||
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
||||||
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
|
||||||
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
||||||
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
import re, operator, string
|
import re, operator, string
|
||||||
@ -110,6 +92,7 @@ class StatsGen:
|
|||||||
return "".join(mask)
|
return "".join(mask)
|
||||||
|
|
||||||
def generate_stats(self, filename):
|
def generate_stats(self, filename):
|
||||||
|
""" Generate password statistics. """
|
||||||
|
|
||||||
f = open(filename,'r')
|
f = open(filename,'r')
|
||||||
|
|
||||||
@ -152,6 +135,8 @@ class StatsGen:
|
|||||||
f.close()
|
f.close()
|
||||||
|
|
||||||
def print_stats(self):
|
def print_stats(self):
|
||||||
|
""" Print password statistics. """
|
||||||
|
|
||||||
print "[+] Analyzing %d%% (%d/%d) of passwords" % (self.filter_counter*100/self.total_counter, self.filter_counter, self.total_counter)
|
print "[+] Analyzing %d%% (%d/%d) of passwords" % (self.filter_counter*100/self.total_counter, self.filter_counter, self.total_counter)
|
||||||
print " NOTE: Statistics below is relative to the number of analyzed passwords, not total number of passwords"
|
print " NOTE: Statistics below is relative to the number of analyzed passwords, not total number of passwords"
|
||||||
print "\n[*] Line Count Statistics..."
|
print "\n[*] Line Count Statistics..."
|
||||||
|
Loading…
x
Reference in New Issue
Block a user