Updated documentation.

Implemented policy detection in statsgen.
Statsgen is now free of regex to speed things up.
This commit is contained in:
iphelix 2013-08-08 09:27:40 -07:00
parent 32e92d10d3
commit 7ef9748553
4 changed files with 641 additions and 632 deletions

1065
README

File diff suppressed because it is too large Load Diff

View File

@ -3,7 +3,7 @@
#
# This tool is part of PACK (Password Analysis and Cracking Kit)
#
# VERSION 0.0.2
# VERSION 0.0.3
#
# Copyright (C) 2013 Peter Kacherginsky
# All rights reserved.

View File

@ -811,7 +811,7 @@ class RuleGen:
def password_worker(self,i, passwords_queue, rules_queue, words_queue):
print "[*] Password analysis worker [%d] started." % i
if self.debug: print "[*] Password analysis worker [%d] started." % i
try:
while True:
password = passwords_queue.get()
@ -821,15 +821,16 @@ class RuleGen:
self.analyze_password(password, rules_queue, words_queue)
except (KeyboardInterrupt, SystemExit):
print "[*] Password analysis worker [%d] terminated." % i
if self.debug: print "[*] Password analysis worker [%d] terminated." % i
print "[*] Password analysis worker [%d] stopped." % i
if self.debug: print "[*] Password analysis worker [%d] stopped." % i
def rule_worker(self, rules_queue, output_rules_filename):
""" Worker to store generated rules. """
print "[*] Saving rules to %s" % output_rules_filename
f = open(output_rules_filename, 'w')
print "[*] Rule worker started."
if self.debug: print "[*] Rule worker started."
try:
while True:
rule = rules_queue.get()
@ -841,16 +842,17 @@ class RuleGen:
f.flush()
except (KeyboardInterrupt, SystemExit):
print "[*] Rule worker terminated."
if self.debug: print "[*] Rule worker terminated."
f.close()
print "[*] Rule worker stopped."
if self.debug: print "[*] Rule worker stopped."
def word_worker(self, words_queue, output_words_filename):
""" Worker to store generated rules. """
print "[*] Saving words to %s" % output_words_filename
f = open(output_words_filename, 'w')
print "[*] Word worker started."
if self.debug: print "[*] Word worker started."
try:
while True:
word = words_queue.get()
@ -862,16 +864,17 @@ class RuleGen:
f.flush()
except (KeyboardInterrupt, SystemExit):
print "[*] Word worker terminated."
if self.debug: print "[*] Word worker terminated."
f.close()
print "[*] Word worker stopped."
if self.debug: print "[*] Word worker stopped."
# Analyze passwords file
def analyze_passwords_file(self,passwords_file):
""" Analyze provided passwords file. """
print "[*] Analyzing passwords file: %s:" % passwords_file
print "[*] Press Ctrl-C to end execution and generate statistical analysis."
# Setup queues
passwords_queue = multiprocessing.Queue(multiprocessing.cpu_count() * 100)
@ -948,7 +951,7 @@ class RuleGen:
rules_counter = Counter(rules_file)
rule_counter_total = sum(rules_counter.values())
print "\n[*] Top 10 rule statistics"
print "\n[*] Top 10 rules"
rules_i = 0
for (rule, count) in rules_counter.most_common():
rules_sorted_file.write(rule)
@ -964,7 +967,7 @@ class RuleGen:
words_counter = Counter(words_file)
word_counter_total = sum(rules_counter.values())
print "\n[*] Top 10 word statistics"
print "\n[*] Top 10 words"
words_i = 0
for (word, count) in words_counter.most_common():
words_sorted_file.write(word)
@ -1082,10 +1085,7 @@ if __name__ == "__main__":
print "[*] Using Enchant '%s' module. For best results please install" % rulegen.enchant.provider.name
print " '%s' module language dictionaries." % rulegen.enchant.provider.name
if not options.quiet:
print "[*] Saving rules to %s.rule" % options.basename
print "[*] Saving words to %s.word" % options.basename
print "[*] Press Ctrl-C to end execution and generate statistical analysis."
# Analyze a single password or several passwords in a file
if options.password:

View File

@ -3,18 +3,19 @@
#
# This tool is part of PACK (Password Analysis and Cracking Kit)
#
# VERSION 0.0.2
# VERSION 0.0.3
#
# Copyright (C) 2013 Peter Kacherginsky
# All rights reserved.
#
# Please see the attached LICENSE file for additiona licensing information.
# Please see the attached LICENSE file for additional licensing information.
import sys
import re, operator, string
from optparse import OptionParser, OptionGroup
import time
VERSION = "0.0.2"
VERSION = "0.0.3"
class StatsGen:
def __init__(self):
@ -25,37 +26,8 @@ class StatsGen:
self.maxlength = None
self.simplemasks = None
self.charsets = None
# Constants
self.chars_regex = list()
self.chars_regex.append(('numeric',re.compile('^[0-9]+$')))
self.chars_regex.append(('loweralpha',re.compile('^[a-z]+$')))
self.chars_regex.append(('upperalpha',re.compile('^[A-Z]+$')))
self.chars_regex.append(('mixedalpha',re.compile('^[a-zA-Z]+$')))
self.chars_regex.append(('loweralphanum',re.compile('^[a-z0-9]+$')))
self.chars_regex.append(('upperalphanum',re.compile('^[A-Z0-9]+$')))
self.chars_regex.append(('mixedalphanum',re.compile('^[a-zA-Z0-9]+$')))
self.chars_regex.append(('special',re.compile('^[^a-zA-Z0-9]+$')))
self.chars_regex.append(('loweralphaspecial',re.compile('^[^A-Z0-9]+$')))
self.chars_regex.append(('upperalphaspecial',re.compile('^[^a-z0-9]+$')))
self.chars_regex.append(('mixedalphaspecial',re.compile('^[^0-9]+$')))
self.chars_regex.append(('loweralphaspecialnum',re.compile('^[^A-Z]+$')))
self.chars_regex.append(('upperalphaspecialnum',re.compile('^[^a-z]+$')))
self.chars_regex.append(('mixedalphaspecialnum',re.compile('.*')))
self.masks_regex = list()
self.masks_regex.append(('alldigit',re.compile('^\d+$', re.IGNORECASE)))
self.masks_regex.append(('allstring',re.compile('^[a-z]+$', re.IGNORECASE)))
self.masks_regex.append(('stringdigit',re.compile('^[a-z]+\d+$', re.IGNORECASE)))
self.masks_regex.append(('digitstring',re.compile('^\d+[a-z]+$', re.IGNORECASE)))
self.masks_regex.append(('digitstringdigit',re.compile('^\d+[a-z]+\d+$', re.IGNORECASE)))
self.masks_regex.append(('stringdigitstring',re.compile('^[a-z]+\d+[a-z]+$', re.IGNORECASE)))
self.masks_regex.append(('allspecial',re.compile('^[^a-z0-9]+$', re.IGNORECASE)))
self.masks_regex.append(('stringspecial',re.compile('^[a-z]+[^a-z0-9]+$', re.IGNORECASE)))
self.masks_regex.append(('specialstring',re.compile('^[^a-z0-9]+[a-z]+$', re.IGNORECASE)))
self.masks_regex.append(('stringspecialstring',re.compile('^[a-z]+[^a-z0-9]+[a-z]+$', re.IGNORECASE)))
self.masks_regex.append(('stringspecialdigit',re.compile('^[a-z]+[^a-z0-9]+\d+$', re.IGNORECASE)))
self.masks_regex.append(('specialstringspecial',re.compile('^[^a-z0-9]+[a-z]+[^a-z0-9]+$', re.IGNORECASE)))
self.quiet = False
self.debug = True
# Stats dictionaries
self.stats_length = dict()
@ -63,33 +35,88 @@ class StatsGen:
self.stats_advancedmasks = dict()
self.stats_charactersets = dict()
# Ignore stats with less than 1% coverage
self.hiderare = False
self.filter_counter = 0
self.total_counter = 0
def simplemasks_check(self, password):
for (name,regex) in self.masks_regex:
if regex.match(password):
return name
else:
return "othermask"
# Minimum password complexity counters
self.mindigit = None
self.minupper = None
self.minlower = None
self.minspecial = None
def characterset_check(self, password):
for (name,regex) in self.chars_regex:
if regex.match(password):
return name
else:
return "otherchar"
self.maxdigit = None
self.maxupper = None
self.maxlower = None
self.maxspecial = None
def advancedmask_check(self, password):
mask = list()
def analyze_password(self, password):
# Password length
pass_length = len(password)
# Character-set and policy counters
digit = 0
lower = 0
upper = 0
special = 0
simplemask = list()
advancedmask_string = ""
# Detect simple and advanced masks
for letter in password:
if letter in string.digits: mask.append("?d")
elif letter in string.lowercase: mask.append("?l")
elif letter in string.uppercase: mask.append("?u")
else: mask.append("?s")
return "".join(mask)
if letter in string.digits:
digit += 1
advancedmask_string += "?d"
if not simplemask or not simplemask[-1] == 'digit': simplemask.append('digit')
elif letter in string.lowercase:
lower += 1
advancedmask_string += "?l"
if not simplemask or not simplemask[-1] == 'string': simplemask.append('string')
elif letter in string.uppercase:
upper += 1
advancedmask_string += "?u"
if not simplemask or not simplemask[-1] == 'string': simplemask.append('string')
else:
special += 1
advancedmask_string += "?s"
if not simplemask or not simplemask[-1] == 'special': simplemask.append('special')
# String representation of masks
simplemask_string = ''.join(simplemask) if len(simplemask) <= 3 else 'othermask'
# Policy
policy = (digit,lower,upper,special)
# Determine character-set
if digit and not lower and not upper and not special: charset = 'numeric'
elif not digit and lower and not upper and not special: charset = 'loweralpha'
elif not digit and not lower and upper and not special: charset = 'upperalpha'
elif not digit and not lower and not upper and special: charset = 'special'
elif not digit and lower and upper and not special: charset = 'mixedalpha'
elif digit and lower and not upper and not special: charset = 'loweralphanum'
elif digit and not lower and upper and not special: charset = 'upperalphanum'
elif not digit and lower and not upper and special: charset = 'loweralphaspecial'
elif not digit and not lower and upper and special: charset = 'upperalphaspecial'
elif digit and not lower and not upper and special: charset = 'specialnum'
elif not digit and lower and upper and special: charset = 'mixedalphaspecial'
elif digit and not lower and upper and special: charset = 'upperalphaspecialnum'
elif digit and lower and not upper and special: charset = 'loweralphaspecialnum'
elif digit and lower and upper and not special: charset = 'mixedalphanum'
else: charset = 'all'
return (pass_length, charset, simplemask_string, advancedmask_string, policy)
def generate_stats(self, filename):
""" Generate password statistics. """
@ -98,12 +125,13 @@ class StatsGen:
for password in f:
password = password.rstrip('\r\n')
if len(password) == 0: continue
self.total_counter += 1
pass_length = len(password)
characterset = self.characterset_check(password)
simplemask = self.simplemasks_check(password)
advancedmask = self.advancedmask_check(password)
(pass_length,characterset,simplemask,advancedmask, policy) = self.analyze_password(password)
(digit,lower,upper,special) = policy
if (self.charsets == None or characterset in self.charsets) and \
(self.simplemasks == None or simplemask in self.simplemasks) and \
@ -112,6 +140,18 @@ class StatsGen:
self.filter_counter += 1
if self.mindigit == None or digit < self.mindigit: self.mindigit = digit
if self.maxdigit == None or digit > self.maxdigit: self.maxdigit = digit
if self.minupper == None or upper < self.minupper: self.minupper = upper
if self.maxupper == None or upper > self.maxupper: self.maxupper = upper
if self.minlower == None or lower < self.minlower: self.minlower = lower
if self.maxlower == None or lower > self.maxlower: self.maxlower = lower
if self.minspecial == None or special < self.minspecial: self.minspecial = special
if self.maxspecial == None or special > self.maxspecial: self.maxspecial = special
if pass_length in self.stats_length:
self.stats_length[pass_length] += 1
else:
@ -139,22 +179,28 @@ class StatsGen:
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 "\n[*] Line Count Statistics..."
print "\n[*] Length:"
for (length,count) in sorted(self.stats_length.iteritems(), key=operator.itemgetter(1), reverse=True):
if self.hiderare and not count*100/self.filter_counter > 0: continue
print "[+] %25d: %02d%% (%d)" % (length, count*100/self.filter_counter, count)
print "\n[*] Charset statistics..."
print "\n[*] Character-set:"
for (char,count) in sorted(self.stats_charactersets.iteritems(), key=operator.itemgetter(1), reverse=True):
if self.hiderare and not count*100/self.filter_counter > 0: continue
print "[+] %25s: %02d%% (%d)" % (char, count*100/self.filter_counter, count)
print "\n[*] Simple Mask statistics..."
print "\n[*] Password complexity:"
print "[+] digit: min(%s) max(%s)" % (self.mindigit, self.maxdigit)
print "[+] lower: min(%s) max(%s)" % (self.minlower, self.maxlower)
print "[+] upper: min(%s) max(%s)" % (self.minupper, self.maxupper)
print "[+] special: min(%s) max(%s)" % (self.minspecial, self.maxspecial)
print "\n[*] Simple Masks:"
for (simplemask,count) in sorted(self.stats_simplemasks.iteritems(), key=operator.itemgetter(1), reverse=True):
if self.hiderare and not count*100/self.filter_counter > 0: continue
print "[+] %25s: %02d%% (%d)" % (simplemask, count*100/self.filter_counter, count)
print "\n[*] Advanced Mask statistics..."
print "\n[*] Advanced Masks:"
for (advancedmask,count) in sorted(self.stats_advancedmasks.iteritems(), key=operator.itemgetter(1), reverse=True):
if count*100/self.filter_counter > 0:
print "[+] %25s: %02d%% (%d)" % (advancedmask, count*100/self.filter_counter, count)