Cleaned up source code spacing and added mask output to maskgen.py

This commit is contained in:
iphelix 2013-07-17 19:46:52 -07:00
parent 7cfa4ab6a2
commit 4f84adc320
3 changed files with 361 additions and 340 deletions

View File

@ -29,6 +29,7 @@
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
import csv, string import csv, string
import datetime
from operator import itemgetter from operator import itemgetter
from optparse import OptionParser from optparse import OptionParser
@ -45,194 +46,214 @@ allmasks = dict()
## Calculate complexity of a single mask ## Calculate complexity of a single mask
################################################################## ##################################################################
def complexity(mask): def complexity(mask):
count = 1 count = 1
for char in mask[1:].split("?"): for char in mask[1:].split("?"):
if char == "l": count *= 26
elif char == "u": count *= 26
elif char == "d": count *= 10
elif char == "s": count *= 33
else: "[!] Error, unknown mask ?%s" % char
return count # Loweralpha
if char == "l":
count *= 26
# Upperalpha
elif char == "u":
count *= 26
# Numeric
elif char == "d":
count *= 10
# Special
elif char == "s":
count *= 33
# Unknown mask
else:
print "[!] Error, unknown mask ?%s in a mask %s" % (char,mask)
return count
################################################################### ###################################################################
## Calculate complexity of a complex mask ## Calculate complexity of a complex mask
################################################################### ###################################################################
def maskcomplexity(mask): def maskcomplexity(mask):
complexity = 1 combined_complexity = 1
for submask in mask.split(" "): for submask in mask.split():
permutations = 0 combined_complexity *= complexity(submask)
for char in submask[1:].split("?"):
if char == "l": permutations += 26
elif char == "u": permutations += 26
elif char == "d": permutations += 10
elif char == "s": permutations += 33
else: "[!] Error, unknown mask ?%s" % char
if permutations: complexity *= permutations
return complexity return combined_complexity
################################################################### ###################################################################
## Check if complex mask matches a sample mask ## Check if complex mask matches a sample mask
################################################################### ###################################################################
def matchmask(checkmask,mask): def matchmask(checkmask,mask):
length = len(mask)/2 length = len(mask)/2
checklength = len(checkmask.split(" ")) checklength = len(checkmask.split(" "))
if length == checklength: if length == checklength:
masklist = mask[1:].split("?") masklist = mask[1:].split("?")
for i, submask in enumerate(checkmask.split(" ")): for i, submask in enumerate(checkmask.split(" ")):
for char in submask[1:].split("?"): for char in submask[1:].split("?"):
if char == masklist[i]: if char == masklist[i]:
break break
else: else:
return False return False
else: else:
return True return True
else: else:
return False return False
################################################################### ###################################################################
## Combine masks ## Combine masks
################################################################### ###################################################################
def genmask(mask): def genmask(mask):
global mastermasks global mastermasks
length = len(mask)/2 length = len(mask)/2
try: if not length in mastermasks:
lengthmask = mastermasks[length] mastermasks[length] = dict()
except:
mastermasks[length] = dict()
lengthmask = mastermasks[length]
for i,v in enumerate(mask[1:].split("?")): for i,v in enumerate(mask[1:].split("?")):
try:
positionmask = lengthmask[i]
except:
lengthmask[i] = set()
positionmask = lengthmask[i]
positionmask.add("?%s" % v) if not i in mastermasks[length]:
mastermasks[length][i] = set()
mastermasks[length][i].add("?%s" % v)
################################################################### ###################################################################
## Store all masks in based on length and count ## Store all masks in based on length and count
################################################################### ###################################################################
def storemask(mask,occurrence): def storemask(mask,occurrence):
global allmasks global allmasks
length = len(mask)/2 length = len(mask)/2
#print "Storing mask %s" % mask if not length in allmasks:
try: allmasks[length] = dict()
lengthmask = allmasks[length]
except:
allmasks[length] = dict()
lengthmask = allmasks[length]
lengthmask[mask] = int(occurrence) allmasks[length][mask] = int(occurrence)
def main(): def main():
# Constants # Constants
total_occurrence = 0 total_occurrence = 0
sample_occurrence = 0 sample_occurrence = 0
sample_time = 0 sample_time = 0
# TODO: I want to actually see statistical analysis of masks not just based on size but also frequency and time # TODO: I want to actually see statistical analysis of masks not just based on size but also frequency and time
# per length and per count # per length and per count
header = " _ \n" header = " _ \n"
header += " MaskGen 0.0.2 | |\n" header += " MaskGen 0.0.2 | |\n"
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"
parser = OptionParser("%prog [options] masksfile.csv", version="%prog "+VERSION) parser = OptionParser("%prog [options] masksfile.csv", version="%prog "+VERSION)
parser.add_option("--minlength", dest="minlength",help="Minimum password length", type="int", metavar="8") parser.add_option("--minlength", dest="minlength",help="Minimum password length", type="int", metavar="8")
parser.add_option("--maxlength", dest="maxlength",help="Maximum password length", type="int", metavar="8") parser.add_option("--maxlength", dest="maxlength",help="Maximum password length", type="int", metavar="8")
parser.add_option("--mintime", dest="mintime",help="Minimum time to crack", type="int", metavar="") parser.add_option("--mintime", dest="mintime",help="Minimum time to crack", type="int", metavar="")
parser.add_option("--maxtime", dest="maxtime",help="Maximum time to crack", type="int", metavar="") parser.add_option("--maxtime", dest="maxtime",help="Maximum time to crack", type="int", metavar="")
parser.add_option("--complexity", dest="complexity",help="maximum password complexity", type="int", metavar="") parser.add_option("--complexity", dest="complexity",help="maximum password complexity", type="int", metavar="")
parser.add_option("--occurrence", dest="occurrence",help="minimum times mask was used", type="int", metavar="") parser.add_option("--occurrence", dest="occurrence",help="minimum times mask was used", type="int", metavar="")
parser.add_option("--checkmask", dest="checkmask",help="check mask coverage", metavar="?u?l ?l ?l ?l ?l ?d") parser.add_option("--checkmask", dest="checkmask",help="check mask coverage", metavar="?u?l ?l ?l ?l ?l ?d")
parser.add_option("--showmasks", dest="showmasks",help="Show matching masks", action="store_true", default=False) parser.add_option("--showmasks", dest="showmasks",help="Show matching masks", action="store_true", default=False)
parser.add_option("--pps", dest="pps",help="Passwords per Second", type="int", default=pps, metavar="1000000000") parser.add_option("--pps", dest="pps",help="Passwords per Second", type="int", default=pps, metavar="1000000000")
parser.add_option("-q", "--quiet", action="store_true", dest="quiet", default=False, help="Don't show headers.") parser.add_option("-o", "--masksoutput", dest="masks_output",help="Save masks to a file", metavar="masks.hcmask")
(options, args) = parser.parse_args()
# Print program header parser.add_option("-q", "--quiet", action="store_true", dest="quiet", default=False, help="Don't show headers.")
if not options.quiet: (options, args) = parser.parse_args()
print header
if len(args) != 1: # Print program header
parser.error("no masks file specified") if not options.quiet:
exit(1) print header
print "[*] Analysing masks: %s" % args[0] if len(args) != 1:
maskReader = csv.reader(open(args[0],'r'), delimiter=',', quotechar='"') parser.error("no masks file specified")
#headerline = maskReader.next() exit(1)
# Check the coverage of a particular mask for a given set print "[*] Analysing masks: %s" % args[0]
if options.checkmask: maskReader = csv.reader(open(args[0],'r'), delimiter=',', quotechar='"')
length = len(options.checkmask.split(" ")) #headerline = maskReader.next()
# Prepare master mask list for analysis # Check the coverage of a particular mask for a given set
mastermasks[length] = dict() if options.checkmask:
lengthmask = mastermasks[length] length = len(options.checkmask.split(" "))
for i, submask in enumerate(options.checkmask.split(" ")):
lengthmask[i] = set()
positionmask = lengthmask[i]
for char in submask[1:].split("?"):
positionmask.add("?%s" % char)
for (mask,occurrence) in maskReader: # Prepare master mask list for analysis
total_occurrence += int(occurrence) mastermasks[length] = dict()
if matchmask(options.checkmask,mask):
sample_occurrence += int(occurrence)
storemask(mask,occurrence)
# Generate masks from a given set for i, submask in enumerate(options.checkmask.split(" ")):
else:
for (mask,occurrence) in maskReader:
total_occurrence += int(occurrence)
if (not options.occurrence or int(occurrence) >= options.occurrence) and \ mastermasks[length][i] = set()
(not options.maxlength or len(mask)/2 <= options.maxlength) and \ for char in submask[1:].split("?"):
(not options.minlength or len(mask)/2 >= options.minlength) and \ mastermasks[length][i].add("?%s" % char)
(not options.complexity or complexity(mask) <= options.complexity) and \
(not options.maxtime or complexity(mask)/options.pps <= options.maxtime) and \
(not options.mintime or complexity(mask)/options.pps >= options.mintime):
genmask(mask) for (mask,occurrence) in maskReader:
storemask(mask,occurrence) total_occurrence += int(occurrence)
sample_occurrence += int(occurrence) if matchmask(options.checkmask,mask):
sample_occurrence += int(occurrence)
storemask(mask,occurrence)
#################################################################################### # Generate masks from a given set
## Analysis else:
#################################################################################### for (mask,occurrence) in maskReader:
for length,lengthmask in sorted(mastermasks.iteritems()): total_occurrence += int(occurrence)
maskstring = ""
for position,maskset in lengthmask.iteritems(): maskstring += "%s " % string.join(maskset,"")
mask_time = maskcomplexity(maskstring)/options.pps if (not options.occurrence or int(occurrence) >= options.occurrence) and \
sample_time += mask_time (not options.maxlength or len(mask)/2 <= options.maxlength) and \
(not options.minlength or len(mask)/2 >= options.minlength) and \
(not options.complexity or complexity(mask) <= options.complexity) and \
(not options.maxtime or complexity(mask)/options.pps <= options.maxtime) and \
(not options.mintime or complexity(mask)/options.pps >= options.mintime):
length_occurrence = 0 genmask(mask)
storemask(mask,occurrence)
sample_occurrence += int(occurrence)
for mask, occurrence in allmasks[length].iteritems(): ####################################################################################
length_occurrence += int(occurrence) ## Analysis
print "[*] [%d] [%d/%d] [%.02f] [%dd|%dh|%dm|%ds] %s" % (length, length_occurrence, total_occurrence, length_occurrence*100/total_occurrence, mask_time/60/60/24, mask_time/60/60, mask_time/60, mask_time,maskstring) ####################################################################################
if options.showmasks: if options.masks_output:
for mask,mask_occurrence in sorted(allmasks[length].iteritems(),key=itemgetter(1),reverse=True): f = open(options.masks_output,'w+')
mask_time = complexity(mask)/options.pps
print " [%d] [%d/%d] [%.02f] [%.02f] [%dd|%dh|%dm|%ds] %s" % (length, mask_occurrence, length_occurrence, mask_occurrence*100/length_occurrence, mask_occurrence*100/total_occurrence,mask_time/60/60/24, mask_time/60/60, mask_time/60, mask_time,mask)
print "[*] Coverage is %%%d (%d/%d)" % (sample_occurrence*100/total_occurrence,sample_occurrence,total_occurrence) for length,lengthmask in sorted(mastermasks.iteritems()):
print "[*] Total time [%dd|%dh|%dm|%ds]" % (sample_time/60/60/24,sample_time/60/60,sample_time/60,sample_time) maskstring = ""
for position,maskset in lengthmask.iteritems():
maskstring += "%s " % string.join(maskset,"")
length_occurrence = 0
combined_mask_time = 0
# Calculate occurrence and time complexity of component masks
# for each password length.
for mask, occurrence in allmasks[length].iteritems():
length_occurrence += int(occurrence)
combined_mask_time += complexity(mask)/options.pps
sample_time += combined_mask_time
time_string = "Don't bother."
if not combined_mask_time > 3784320000:
time_string = str(datetime.timedelta(seconds=combined_mask_time))
print "[*] [%d] [%d/%d] [%.02f] [%s]" % (length, length_occurrence, total_occurrence, length_occurrence*100/total_occurrence, time_string)
if options.showmasks:
for mask,mask_occurrence in sorted(allmasks[length].iteritems(),key=itemgetter(1),reverse=True):
mask_time = complexity(mask)/options.pps
print " [%d] [%d/%d] [%.02f] [%.02f] [%dd|%dh|%dm|%ds] %s" % (length, mask_occurrence, length_occurrence, mask_occurrence*100/length_occurrence, mask_occurrence*100/total_occurrence,mask_time/60/60/24, mask_time/60/60, mask_time/60, mask_time,mask)
if options.masks_output:
for mask,mask_occurrence in sorted(allmasks[length].iteritems(),key=itemgetter(1),reverse=True):
f.write("%s\n" % mask)
print "[*] Coverage is %%%d (%d/%d)" % (sample_occurrence*100/total_occurrence,sample_occurrence,total_occurrence)
print "[*] Total time [%dd|%dh|%dm|%ds]" % (sample_time/60/60/24,sample_time/60/60,sample_time/60,sample_time)
if __name__ == "__main__": if __name__ == "__main__":
main() main()

View File

@ -45,114 +45,114 @@ total_time = 0
# Calculate complexity of a single mask # Calculate complexity of a single mask
################################################################## ##################################################################
def complexity(mask): def complexity(mask):
count = 1 count = 1
for char in mask[1:].split("?"): for char in mask[1:].split("?"):
if char == "l": count *= 26 if char == "l": count *= 26
elif char == "u": count *= 26 elif char == "u": count *= 26
elif char == "d": count *= 10 elif char == "d": count *= 10
elif char == "s": count *= 33 elif char == "s": count *= 33
else: "[!] Error, unknown mask ?%s" % char else: "[!] Error, unknown mask ?%s" % char
return count return count
################################################################### ###################################################################
# Check whether a sample password mask matches defined policy # Check whether a sample password mask matches defined policy
################################################################### ###################################################################
def filtermask(maskstring,options): def filtermask(maskstring,options):
global total_time, sample_time global total_time, sample_time
# define character counters # define character counters
lowercount = uppercount = digitcount = specialcount = 0 lowercount = uppercount = digitcount = specialcount = 0
# calculate password complexity and cracking time # calculate password complexity and cracking time
mask_time = complexity(maskstring)/options.pps mask_time = complexity(maskstring)/options.pps
total_time += mask_time total_time += mask_time
for char in maskstring[1:].split("?"): for char in maskstring[1:].split("?"):
if char == "l": lowercount += 1 if char == "l": lowercount += 1
elif char == "u": uppercount += 1 elif char == "u": uppercount += 1
elif char == "d": digitcount += 1 elif char == "d": digitcount += 1
elif char == "s": specialcount += 1 elif char == "s": specialcount += 1
# Filter according to password policy # Filter according to password policy
if lowercount >= options.minlower and lowercount <= options.maxlower and \ if lowercount >= options.minlower and lowercount <= options.maxlower and \
uppercount >= options.minupper and uppercount <= options.maxupper and \ uppercount >= options.minupper and uppercount <= options.maxupper and \
digitcount >= options.mindigits and digitcount <= options.maxdigits and \ digitcount >= options.mindigits and digitcount <= options.maxdigits and \
specialcount >= options.minspecial and specialcount <= options.maxspecial: specialcount >= options.minspecial and specialcount <= options.maxspecial:
sample_time += mask_time sample_time += mask_time
if options.verbose: if options.verbose:
print "[*] [%dd|%dh|%dm|%ds] %s [l:%d u:%d d:%d s:%d]" % (mask_time/60/60/24, mask_time/60/60, mask_time/60, mask_time,maskstring,lowercount,uppercount,digitcount,specialcount) print "[*] [%dd|%dh|%dm|%ds] %s [l:%d u:%d d:%d s:%d]" % (mask_time/60/60/24, mask_time/60/60, mask_time/60, mask_time,maskstring,lowercount,uppercount,digitcount,specialcount)
return True return True
else: else:
return False return False
def main(): def main():
# define mask counters # define mask counters
total_count = sample_count = 0 total_count = sample_count = 0
header = " _ \n" header = " _ \n"
header += " PolicyGen 0.0.1 | |\n" header += " PolicyGen 0.0.1 | |\n"
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"
# parse command line arguments # parse command line arguments
parser = OptionParser("%prog [options]\n\nType --help for more options", version="%prog "+VERSION) parser = OptionParser("%prog [options]\n\nType --help for more options", version="%prog "+VERSION)
parser.add_option("--length", dest="length", help="Password length", type="int", default=8, metavar="8") parser.add_option("--length", dest="length", help="Password length", type="int", default=8, metavar="8")
parser.add_option("-o", "--output", dest="output",help="Save masks to a file", metavar="masks.txt") parser.add_option("-o", "--output", dest="output",help="Save masks to a file", metavar="masks.txt")
parser.add_option("--pps", dest="pps", help="Passwords per Second", type="int", default=pps, metavar="1000000000") parser.add_option("--pps", dest="pps", help="Passwords per Second", type="int", default=pps, metavar="1000000000")
parser.add_option("-v", "--verbose", action="store_true", dest="verbose") parser.add_option("-v", "--verbose", action="store_true", dest="verbose")
group = OptionGroup(parser, "Password Policy", "Define the minimum (or maximum) password strength policy that you would like to test") group = OptionGroup(parser, "Password Policy", "Define the minimum (or maximum) password strength policy that you would like to test")
group.add_option("--mindigits", dest="mindigits", help="Minimum number of digits", default=0, type="int", metavar="1") group.add_option("--mindigits", dest="mindigits", help="Minimum number of digits", default=0, type="int", metavar="1")
group.add_option("--minlower", dest="minlower", help="Minimum number of lower-case characters", default=0, type="int", metavar="1") group.add_option("--minlower", dest="minlower", help="Minimum number of lower-case characters", default=0, type="int", metavar="1")
group.add_option("--minupper", dest="minupper", help="Minimum number of upper-case characters", default=0, type="int", metavar="1") group.add_option("--minupper", dest="minupper", help="Minimum number of upper-case characters", default=0, type="int", metavar="1")
group.add_option("--minspecial", dest="minspecial", help="Minimum number of special characters", default=0, type="int", metavar="1") group.add_option("--minspecial", dest="minspecial", help="Minimum number of special characters", default=0, type="int", metavar="1")
group.add_option("--maxdigits", dest="maxdigits", help="Maximum number of digits", default=9999, type="int", metavar="3") group.add_option("--maxdigits", dest="maxdigits", help="Maximum number of digits", default=9999, type="int", metavar="3")
group.add_option("--maxlower", dest="maxlower", help="Maximum number of lower-case characters", default=9999, type="int", metavar="3") group.add_option("--maxlower", dest="maxlower", help="Maximum number of lower-case characters", default=9999, type="int", metavar="3")
group.add_option("--maxupper", dest="maxupper", help="Maximum number of upper-case characters", default=9999, type="int", metavar="3") group.add_option("--maxupper", dest="maxupper", help="Maximum number of upper-case characters", default=9999, type="int", metavar="3")
group.add_option("--maxspecial", dest="maxspecial", help="Maximum number of special characters", default=9999, type="int", metavar="3") group.add_option("--maxspecial", dest="maxspecial", help="Maximum number of special characters", default=9999, type="int", metavar="3")
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_group(group) parser.add_option_group(group)
(options, args) = parser.parse_args() (options, args) = parser.parse_args()
# cleanup maximum occurence options # cleanup maximum occurence options
if options.maxlower > options.length: options.maxlower = options.length if options.maxlower > options.length: options.maxlower = options.length
if options.maxdigits > options.length: options.maxdigits = options.length if options.maxdigits > options.length: options.maxdigits = options.length
if options.mindigits > options.length: options.mindigits = options.length if options.mindigits > options.length: options.mindigits = options.length
if options.maxupper > options.length: options.maxupper = options.length if options.maxupper > options.length: options.maxupper = options.length
if options.maxspecial > options.length: options.maxspecial = options.length if options.maxspecial > options.length: options.maxspecial = options.length
# Print program header # Print program header
if not options.quiet: if not options.quiet:
print header print header
# print current password policy # print current password policy
print "[*] Password policy:" print "[*] Password policy:"
print "[+] Password length: %d" % options.length print "[+] Password length: %d" % options.length
print "[+] Minimum strength: lower: %d, upper: %d, digits: %d, special: %d" % (options.minlower, options.minupper, options.mindigits, options.minspecial) print "[+] Minimum strength: lower: %d, upper: %d, digits: %d, special: %d" % (options.minlower, options.minupper, options.mindigits, options.minspecial)
print "[+] Maximum strength: lower: %d, upper: %d, digits: %d, special: %d" % (options.maxlower, options.maxupper, options.maxdigits, options.maxspecial) print "[+] Maximum strength: lower: %d, upper: %d, digits: %d, special: %d" % (options.maxlower, options.maxupper, options.maxdigits, options.maxspecial)
if options.output: f = open(options.output, 'w') if options.output: f = open(options.output, 'w')
# generate all possible password masks and compare them to policy # generate all possible password masks and compare them to policy
# TODO: Randomize or even statistically arrange matching masks # TODO: Randomize or even statistically arrange matching masks
for password in itertools.product(['?l','?u','?d','?s'],repeat=options.length): for password in itertools.product(['?l','?u','?d','?s'],repeat=options.length):
if filtermask(''.join(password), options): if filtermask(''.join(password), options):
if options.output: f.write("%s\n" % ''.join(password)) if options.output: f.write("%s\n" % ''.join(password))
sample_count +=1 sample_count +=1
total_count += 1 total_count += 1
if options.output: f.close() if options.output: f.close()
print "[*] Total Masks: %d Runtime: [%dd|%dh|%dm|%ds]" % (total_count, total_time/60/60/24, total_time/60/60, total_time/60, total_time) print "[*] Total Masks: %d Runtime: [%dd|%dh|%dm|%ds]" % (total_count, total_time/60/60/24, total_time/60/60, total_time/60, total_time)
print "[*] Policy Masks: %d Runtime: [%dd|%dh|%dm|%ds]" % (sample_count, sample_time/60/60/24, sample_time/60/60, sample_time/60, sample_time) print "[*] Policy Masks: %d Runtime: [%dd|%dh|%dm|%ds]" % (sample_count, sample_time/60/60/24, sample_time/60/60, sample_time/60, sample_time)
if __name__ == "__main__": if __name__ == "__main__":
main() main()

View File

@ -35,11 +35,11 @@ from optparse import OptionParser
VERSION = "0.0.2" VERSION = "0.0.2"
try: try:
import psyco import psyco
psyco.full() psyco.full()
print "[*] Using Psyco to accelerate parsing." print "[*] Using Psyco to accelerate parsing."
except ImportError: except ImportError:
print "[?] Psyco is not available. Install Psyco on 32-bit systems for faster parsing." print "[?] Psyco is not available. Install Psyco on 32-bit systems for faster parsing."
password_counter = 0 password_counter = 0
@ -75,124 +75,124 @@ masks_regex.append(('stringspecialdigit',re.compile('^[a-z]+[^a-z0-9]+\d+$', re.
masks_regex.append(('specialstringspecial',re.compile('^[^a-z0-9]+[a-z]+[^a-z0-9]+$', re.IGNORECASE))) masks_regex.append(('specialstringspecial',re.compile('^[^a-z0-9]+[a-z]+[^a-z0-9]+$', re.IGNORECASE)))
def length_check(password): def length_check(password):
return len(password) return len(password)
def masks_check(password): def masks_check(password):
for (name,regex) in masks_regex: for (name,regex) in masks_regex:
if regex.match(password): if regex.match(password):
return name return name
else: else:
return "othermask" return "othermask"
def chars_check(password): def chars_check(password):
for (name,regex) in chars_regex: for (name,regex) in chars_regex:
if regex.match(password): if regex.match(password):
return name return name
else: else:
return "otherchar" return "otherchar"
def advmask_check(password): def advmask_check(password):
advmask = list() advmask = list()
for letter in password: for letter in password:
if letter in string.digits: advmask.append("?d") if letter in string.digits: advmask.append("?d")
elif letter in string.lowercase: advmask.append("?l") elif letter in string.lowercase: advmask.append("?l")
elif letter in string.uppercase: advmask.append("?u") elif letter in string.uppercase: advmask.append("?u")
else: advmask.append("?s") else: advmask.append("?s")
return "".join(advmask) return "".join(advmask)
def main(): def main():
password_length = dict() password_length = dict()
masks = dict() masks = dict()
advmasks = dict() advmasks = dict()
chars = dict() chars = dict()
filter_counter = 0 filter_counter = 0
total_counter = 0 total_counter = 0
header = " _ \n" header = " _ \n"
header += " StatsGen 0.0.2 | |\n" header += " StatsGen 0.0.2 | |\n"
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"
parser = OptionParser("%prog [options] passwords.txt", version="%prog "+VERSION) parser = OptionParser("%prog [options] passwords.txt", version="%prog "+VERSION)
parser.add_option("-l", "--length", dest="length_filter",help="Password length filter.",metavar="8") parser.add_option("-l", "--length", dest="length_filter",help="Password length filter.",metavar="8")
parser.add_option("-c", "--charset", dest="char_filter", help="Password charset filter.", metavar="loweralpha") parser.add_option("-c", "--charset", dest="char_filter", help="Password charset filter.", metavar="loweralpha")
parser.add_option("-m", "--mask", dest="mask_filter",help="Password mask filter", metavar="stringdigit") parser.add_option("-m", "--mask", dest="mask_filter",help="Password mask filter", metavar="stringdigit")
parser.add_option("-o", "--maskoutput", dest="mask_output",help="Save masks to a file", metavar="masks.csv") parser.add_option("-o", "--masksoutput", dest="mask_output",help="Generate and save masks db to a file", metavar="masks.csv")
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.")
(options, args) = parser.parse_args() (options, args) = parser.parse_args()
# 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")
exit(1) exit(1)
print "[*] Analyzing passwords: %s" % args[0] print "[*] Analyzing passwords: %s" % args[0]
f = open(args[0],'r') f = open(args[0],'r')
for password in f: for password in f:
password = password.strip() password = password.strip()
total_counter += 1 total_counter += 1
pass_len = length_check(password) pass_len = length_check(password)
mask_set = masks_check(password) mask_set = masks_check(password)
char_set = chars_check(password) char_set = chars_check(password)
advmask = advmask_check(password) advmask = advmask_check(password)
if (not options.length_filter or str(pass_len) in options.length_filter.split(',')) and \ if (not options.length_filter or str(pass_len) in options.length_filter.split(',')) and \
(not options.char_filter or char_set in options.char_filter.split(',')) and \ (not options.char_filter or char_set in options.char_filter.split(',')) and \
(not options.mask_filter or mask_set in options.mask_filter.split(',')): (not options.mask_filter or mask_set in options.mask_filter.split(',')):
filter_counter += 1 filter_counter += 1
try: password_length[pass_len] += 1 try: password_length[pass_len] += 1
except: password_length[pass_len] = 1 except: password_length[pass_len] = 1
try: masks[mask_set] += 1 try: masks[mask_set] += 1
except: masks[mask_set] = 1 except: masks[mask_set] = 1
try: chars[char_set] += 1 try: chars[char_set] += 1
except: chars[char_set] = 1 except: chars[char_set] = 1
try: advmasks[advmask] += 1 try: advmasks[advmask] += 1
except: advmasks[advmask] = 1 except: advmasks[advmask] = 1
f.close() f.close()
print "[+] Analyzing %d%% (%d/%d) passwords" % (filter_counter*100/total_counter, filter_counter, total_counter) print "[+] Analyzing %d%% (%d/%d) passwords" % (filter_counter*100/total_counter, filter_counter, 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..."
for (length,count) in sorted(password_length.iteritems(), key=operator.itemgetter(1), reverse=True): for (length,count) in sorted(password_length.iteritems(), key=operator.itemgetter(1), reverse=True):
if count*100/filter_counter > 0: if count*100/filter_counter > 0:
print "[+] %25d: %02d%% (%d)" % (length, count*100/filter_counter, count) print "[+] %25d: %02d%% (%d)" % (length, count*100/filter_counter, count)
print "\n[*] Mask statistics..." print "\n[*] Mask statistics..."
for (mask,count) in sorted(masks.iteritems(), key=operator.itemgetter(1), reverse=True): for (mask,count) in sorted(masks.iteritems(), key=operator.itemgetter(1), reverse=True):
print "[+] %25s: %02d%% (%d)" % (mask, count*100/filter_counter, count) print "[+] %25s: %02d%% (%d)" % (mask, count*100/filter_counter, count)
print "\n[*] Charset statistics..." print "\n[*] Charset statistics..."
for (char,count) in sorted(chars.iteritems(), key=operator.itemgetter(1), reverse=True): for (char,count) in sorted(chars.iteritems(), key=operator.itemgetter(1), reverse=True):
print "[+] %25s: %02d%% (%d)" % (char, count*100/filter_counter, count) print "[+] %25s: %02d%% (%d)" % (char, count*100/filter_counter, count)
print "\n[*] Advanced Mask statistics..." print "\n[*] Advanced Mask statistics..."
for (advmask,count) in sorted(advmasks.iteritems(), key=operator.itemgetter(1), reverse=True): for (advmask,count) in sorted(advmasks.iteritems(), key=operator.itemgetter(1), reverse=True):
if count*100/filter_counter > 0: if count*100/filter_counter > 0:
print "[+] %25s: %02d%% (%d)" % (advmask, count*100/filter_counter, count) print "[+] %25s: %02d%% (%d)" % (advmask, count*100/filter_counter, count)
if options.mask_output: if options.mask_output:
print "\n[*] Saving Mask statistics to %s" % options.mask_output print "\n[*] Saving Mask statistics to %s" % options.mask_output
fmask = open(options.mask_output, "w") fmask = open(options.mask_output, "w")
for (advmask,count) in sorted(advmasks.iteritems(), key=operator.itemgetter(1), reverse=True): for (advmask,count) in sorted(advmasks.iteritems(), key=operator.itemgetter(1), reverse=True):
fmask.write("%s,%d\n" % (advmask,count)) fmask.write("%s,%d\n" % (advmask,count))
fmask.close() fmask.close()
if __name__ == "__main__": if __name__ == "__main__":
main() main()