Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
9c5bef9
Applied 2to3 to each python file
Hydraze Jun 18, 2016
67ae930
deleted unused imports
Hydraze Jun 18, 2016
7b148df
fix statsgen.py's file opening mode and prints of masks
Hydraze Jun 18, 2016
39620d4
fix divisions in maskgen
Hydraze Jun 19, 2016
1f8243c
fix divisions in policygen
Hydraze Jun 19, 2016
3c93ef6
fix small spelling typo
Hydraze Jun 19, 2016
1d73c2a
first pass of cleanup: modified None comparisons to use the is keyword
Hydraze Jun 19, 2016
1c94727
Add an exception handler in case the provider dict does not exist
Hydraze Jun 21, 2016
602342d
Fix file opening to mimic old behaviour
Hydraze Jun 21, 2016
6fc2ad8
Use a python3 integer division instead of the floating one
Hydraze Jun 21, 2016
a208d84
PEP8 compliance for statsgen.py
Hydraze Jun 21, 2016
996fdf8
make analyze_password a static method
Hydraze Jun 21, 2016
8f7ccfc
remove an useless call to len()
Hydraze Jun 21, 2016
8abf9a2
cleanup of maskgen.py (PEP8 compliance)
Hydraze Jun 21, 2016
20e2136
fix a typo in loadmasks where the opened file was arg[0] instead of t…
Hydraze Jun 21, 2016
901ac05
add a static decorator to getcomplexity
Hydraze Jun 21, 2016
17f27f1
improved PEP8 compliance for policygen.py and code cleanup
Hydraze Jun 21, 2016
0b22917
add the staticmethod decorator for getcomplexity method
Hydraze Jun 21, 2016
15e77ef
clean up and PEP8 compliance for rulegen.py
Hydraze Jun 22, 2016
7492eb6
make some methods static in rulegen
Hydraze Jun 22, 2016
e3ed4a8
small style in statsgen.py
Hydraze Jun 22, 2016
5d3ace9
Delete an useless list() added by 2to3
Hydraze Aug 8, 2016
ef1c210
policygen.py now handles maxdigit directive correctly.
Hydraze Aug 8, 2016
92f073a
Fix bug when threads != cpu_count
matlink Mar 17, 2017
3134998
Merge pull request #1 from matlink/patch-2
Hydraze Mar 18, 2017
ba885da
shabang python3
Aug 17, 2017
9f63259
fix python3 env
matlink Aug 17, 2017
78c931f
Fixed 'x' and 'O' rules
Chick3nman Feb 5, 2018
5cd5f1b
Merge pull request #2 from matlink/master
Hydraze Feb 15, 2018
fd779b2
Merge pull request #3 from Chick3nman/master
Hydraze Nov 28, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
211 changes: 123 additions & 88 deletions maskgen.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#!/usr/bin/python
#!/usr/bin/env python3
# MaskGen - Generate Password Masks
#
# This tool is part of PACK (Password Analysis and Cracking Kit)
Expand All @@ -10,14 +10,13 @@
#
# Please see the attached LICENSE file for additional licensing information.

import sys
import csv
import datetime
from operator import itemgetter
from optparse import OptionParser, OptionGroup

VERSION = "0.0.3"


class MaskGen:
def __init__(self):
# Masks collections with meta data
Expand All @@ -26,10 +25,10 @@ def __init__(self):
self.target_time = None
self.output_file = None

self.minlength = None
self.maxlength = None
self.mintime = None
self.maxtime = None
self.minlength = None
self.maxlength = None
self.mintime = None
self.maxtime = None
self.mincomplexity = None
self.maxcomplexity = None
self.minoccurrence = None
Expand All @@ -42,52 +41,60 @@ def __init__(self):
# Counter for total masks coverage
self.total_occurrence = 0

def getcomplexity(self, mask):
@staticmethod
def getcomplexity(mask):
""" Return mask complexity. """
count = 1
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
elif char == "a": count *= 95
else: print "[!] Error, unknown mask ?%s in a mask %s" % (char,mask)
if char == "l":
count *= 26
elif char == "u":
count *= 26
elif char == "d":
count *= 10
elif char == "s":
count *= 33
elif char == "a":
count *= 95
else:
print("[!] Error, unknown mask ?%s in a mask %s" % (char, mask))

return count

def loadmasks(self, filename):
""" Load masks and apply filters. """
maskReader = csv.reader(open(args[0],'r'), delimiter=',', quotechar='"')
mask_reader = csv.reader(open(filename, 'r'), delimiter=',', quotechar='"')

for (mask,occurrence) in maskReader:
for (mask, occurrence) in mask_reader:

if mask == "": continue
if not mask:
continue

mask_occurrence = int(occurrence)
mask_length = len(mask)/2
mask_length = len(mask) // 2
mask_complexity = self.getcomplexity(mask)
mask_time = mask_complexity/self.pps
mask_time = mask_complexity // self.pps

self.total_occurrence += mask_occurrence

# Apply filters based on occurrence, length, complexity and time
if (self.minoccurrence == None or mask_occurrence >= self.minoccurrence) and \
(self.maxoccurrence == None or mask_occurrence <= self.maxoccurrence) and \
(self.mincomplexity == None or mask_complexity <= self.mincomplexity) and \
(self.maxcomplexity == None or mask_complexity <= self.maxcomplexity) and \
(self.mintime == None or mask_time <= self.mintime) and \
(self.maxtime == None or mask_time <= self.maxtime) and \
(self.maxlength == None or mask_length <= self.maxlength) and \
(self.minlength == None or mask_length >= self.minlength):
if (self.minoccurrence is None or mask_occurrence >= self.minoccurrence) and \
(self.maxoccurrence is None or mask_occurrence <= self.maxoccurrence) and \
(self.mincomplexity is None or mask_complexity <= self.mincomplexity) and \
(self.maxcomplexity is None or mask_complexity <= self.maxcomplexity) and \
(self.mintime is None or mask_time <= self.mintime) and \
(self.maxtime is None or mask_time <= self.maxtime) and \
(self.maxlength is None or mask_length <= self.maxlength) and \
(self.minlength is None or mask_length >= self.minlength):

self.masks[mask] = dict()
self.masks[mask]['length'] = mask_length
self.masks[mask]['occurrence'] = mask_occurrence
self.masks[mask]['complexity'] = 1 - mask_complexity
self.masks[mask]['time'] = mask_time
self.masks[mask]['optindex'] = 1 - mask_complexity/mask_occurrence
self.masks[mask]['optindex'] = 1 - (mask_complexity // mask_occurrence)

def generate_masks(self,sorting_mode):
def generate_masks(self, sorting_mode):
""" Generate optimal password masks sorted by occurrence, complexity or optindex """
sample_count = 0
sample_time = 0
Expand All @@ -97,12 +104,16 @@ def generate_masks(self,sorting_mode):
# Group by length 1,2,3,4,5,6,7,8,9,10....
# Group by occurrence 10%, 20%, 30%, 40%, 50%....

if self.showmasks: print "[L:] Mask: [ Occ: ] [ Time: ]"
for mask in sorted(self.masks.keys(), key=lambda mask: self.masks[mask][sorting_mode], reverse=True):
if self.showmasks:
print("[L:] Mask: [ Occ: ] [ Time: ]")

for mask in sorted(self.masks.keys(), key=lambda m: self.masks[m][sorting_mode], reverse=True):

if self.showmasks:
time_human = ">1 year" if self.masks[mask]['time'] > 60*60*24*365 else str(datetime.timedelta(seconds=self.masks[mask]['time']))
print "[{:>2}] {:<30} [{:<7}] [{:>8}] ".format(self.masks[mask]['length'], mask, self.masks[mask]['occurrence'], time_human)
time_human = ">1 year" if self.masks[mask]['time'] > 60*60*24*365 \
else str(datetime.timedelta(seconds=self.masks[mask]['time']))
print("[{:>2}] {:<30} [{:<7}] [{:>8}] ".format(self.masks[mask]['length'], mask,
self.masks[mask]['occurrence'], time_human))

if self.output_file:
self.output_file.write("%s\n" % mask)
Expand All @@ -112,14 +123,15 @@ def generate_masks(self,sorting_mode):
sample_count += 1

if self.target_time and sample_time > self.target_time:
print "[!] Target time exceeded."
print("[!] Target time exceeded.")
break

print "[*] Finished generating masks:"
print " Masks generated: %s" % sample_count
print " Masks coverage: %d%% (%d/%d)" % (sample_occurrence*100/self.total_occurrence,sample_occurrence,self.total_occurrence)
print("[*] Finished generating masks:")
print(" Masks generated: %s" % sample_count)
print(" Masks coverage: %d%% (%d/%d)" % (sample_occurrence * 100 // self.total_occurrence,
sample_occurrence, self.total_occurrence))
time_human = ">1 year" if sample_time > 60*60*24*365 else str(datetime.timedelta(seconds=sample_time))
print " Masks runtime: %s" % time_human
print(" Masks runtime: %s" % time_human)

def getmaskscoverage(self, checkmasks):

Expand All @@ -128,7 +140,9 @@ def getmaskscoverage(self, checkmasks):

total_complexity = 0

if self.showmasks: print "[L:] Mask: [ Occ: ] [ Time: ]"
if self.showmasks:
print("[L:] Mask: [ Occ: ] [ Time: ]")

for mask in checkmasks:
mask = mask.strip()
mask_complexity = self.getcomplexity(mask)
Expand All @@ -138,31 +152,34 @@ def getmaskscoverage(self, checkmasks):
if mask in self.masks:

if self.showmasks:
time_human = ">1 year" if self.masks[mask]['time'] > 60*60*24*365 else str(datetime.timedelta(seconds=self.masks[mask]['time']))
print "[{:>2}] {:<30} [{:<7}] [{:>8}] ".format(self.masks[mask]['length'], mask, self.masks[mask]['occurrence'], time_human)
time_human = ">1 year" if self.masks[mask]['time'] > 60*60*24*365 \
else str(datetime.timedelta(seconds=self.masks[mask]['time']))
print("[{:>2}] {:<30} [{:<7}] [{:>8}] ".format(self.masks[mask]['length'], mask,
self.masks[mask]['occurrence'], time_human))

if self.output_file:
self.output_file.write("%s\n" % mask)

sample_occurrence += self.masks[mask]['occurrence']
sample_count += 1

if self.target_time and total_complexity/self.pps > self.target_time:
print "[!] Target time exceeded."
if self.target_time and total_complexity / self.pps > self.target_time:
print("[!] Target time exceeded.")
break

# TODO: Something wrong here, complexity and time doesn't match with estimated from policygen
total_time = total_complexity/self.pps
total_time = total_complexity / self.pps
time_human = ">1 year" if total_time > 60*60*24*365 else str(datetime.timedelta(seconds=total_time))
print "[*] Finished matching masks:"
print " Masks matched: %s" % sample_count
print " Masks coverage: %d%% (%d/%d)" % (sample_occurrence*100/self.total_occurrence,sample_occurrence,self.total_occurrence)
print " Masks runtime: %s" % time_human
print("[*] Finished matching masks:")
print(" Masks matched: %s" % sample_count)
print(" Masks coverage: %d%% (%d/%d)" % (sample_occurrence * 100 / self.total_occurrence,
sample_occurrence, self.total_occurrence))
print(" Masks runtime: %s" % time_human)


if __name__ == "__main__":

header = " _ \n"
header = " _ \n"
header += " MaskGen %s | |\n" % VERSION
header += " _ __ __ _ ___| | _\n"
header += " | '_ \ / _` |/ __| |/ /\n"
Expand All @@ -174,73 +191,91 @@ def getmaskscoverage(self, checkmasks):

parser = OptionParser("%prog pass0.masks [pass1.masks ...] [options]", version="%prog "+VERSION)

parser.add_option("-t", "--targettime", dest="target_time", type="int", metavar="86400", help="Target time of all masks (seconds)")
parser.add_option("-o", "--outputmasks", dest="output_masks", metavar="masks.hcmask", help="Save masks to a file")
parser.add_option("-t", "--targettime", dest="target_time", type="int", metavar="86400",
help="Target time of all masks (seconds)")
parser.add_option("-o", "--outputmasks", dest="output_masks", metavar="masks.hcmask",
help="Save masks to a file")

filters = OptionGroup(parser, "Individual Mask Filter Options")
filters.add_option("--minlength", dest="minlength", type="int", metavar="8", help="Minimum password length")
filters.add_option("--maxlength", dest="maxlength", type="int", metavar="8", help="Maximum password length")
filters.add_option("--mintime", dest="mintime", type="int", metavar="3600", help="Minimum mask runtime (seconds)")
filters.add_option("--maxtime", dest="maxtime", type="int", metavar="3600", help="Maximum mask runtime (seconds)")
filters.add_option("--mincomplexity", dest="mincomplexity", type="int", metavar="1", help="Minimum complexity")
filters.add_option("--maxcomplexity", dest="maxcomplexity", type="int", metavar="100", help="Maximum complexity")
filters.add_option("--minoccurrence", dest="minoccurrence", type="int", metavar="1", help="Minimum occurrence")
filters.add_option("--maxoccurrence", dest="maxoccurrence", type="int", metavar="100", help="Maximum occurrence")
filters.add_option("--minlength", dest="minlength", type="int", metavar="8", help="Minimum password length")
filters.add_option("--maxlength", dest="maxlength", type="int", metavar="8", help="Maximum password length")
filters.add_option("--mintime", dest="mintime", type="int", metavar="3600", help="Minimum mask runtime (seconds)")
filters.add_option("--maxtime", dest="maxtime", type="int", metavar="3600", help="Maximum mask runtime (seconds)")
filters.add_option("--mincomplexity", dest="mincomplexity", type="int", metavar="1", help="Minimum complexity")
filters.add_option("--maxcomplexity", dest="maxcomplexity", type="int", metavar="100", help="Maximum complexity")
filters.add_option("--minoccurrence", dest="minoccurrence", type="int", metavar="1", help="Minimum occurrence")
filters.add_option("--maxoccurrence", dest="maxoccurrence", type="int", metavar="100", help="Maximum occurrence")
parser.add_option_group(filters)

sorting = OptionGroup(parser, "Mask Sorting Options")
sorting.add_option("--optindex", action="store_true", dest="optindex", help="sort by mask optindex (default)", default=False)
sorting.add_option("--occurrence", action="store_true", dest="occurrence", help="sort by mask occurrence", default=False)
sorting.add_option("--complexity", action="store_true", dest="complexity", help="sort by mask complexity", default=False)
sorting.add_option("--optindex", action="store_true", dest="optindex", help="sort by mask optindex (default)",
default=False)
sorting.add_option("--occurrence", action="store_true", dest="occurrence", help="sort by mask occurrence",
default=False)
sorting.add_option("--complexity", action="store_true", dest="complexity", help="sort by mask complexity",
default=False)
parser.add_option_group(sorting)

coverage = OptionGroup(parser, "Check mask coverage")
coverage.add_option("--checkmasks", dest="checkmasks", help="check mask coverage", metavar="?u?l?l?l?l?l?d,?l?l?l?l?l?d?d")
coverage.add_option("--checkmasksfile", dest="checkmasks_file", help="check mask coverage in a file", metavar="masks.hcmask")
coverage.add_option("--checkmasks", dest="checkmasks", help="check mask coverage",
metavar="?u?l?l?l?l?l?d,?l?l?l?l?l?d?d")
coverage.add_option("--checkmasksfile", dest="checkmasks_file", help="check mask coverage in a file",
metavar="masks.hcmask")
parser.add_option_group(coverage)

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)

misc = OptionGroup(parser, "Miscellaneous options")
misc.add_option("--pps", dest="pps",help="Passwords per Second", type="int", metavar="1000000000")
misc.add_option("--pps", dest="pps", help="Passwords per Second", type="int", metavar="1000000000")
misc.add_option("-q", "--quiet", action="store_true", dest="quiet", default=False, help="Don't show headers.")
parser.add_option_group(misc)

(options, args) = parser.parse_args()

# Print program header
if not options.quiet:
print header
print(header)

if len(args) < 1:
parser.error("no masks file specified! Please provide statsgen output.")
exit(1)

print "[*] Analyzing masks in [%s]" % args[0]
print("[*] Analyzing masks in [%s]" % args[0])

maskgen = MaskGen()

# Settings
if options.target_time: maskgen.target_time = options.target_time
if options.target_time:
maskgen.target_time = options.target_time
if options.output_masks:
print "[*] Saving generated masks to [%s]" % options.output_masks
print("[*] Saving generated masks to [%s]" % options.output_masks)
maskgen.output_file = open(options.output_masks, 'w')

# Filters
if options.minlength: maskgen.minlength = options.minlength
if options.maxlength: maskgen.maxlength = options.maxlength
if options.mintime: maskgen.mintime = options.mintime
if options.maxtime: maskgen.maxtime = options.maxtime
if options.mincomplexity: maskgen.mincomplexity = options.mincomplexity
if options.maxcomplexity: maskgen.maxcomplexity = options.maxcomplexity
if options.minoccurrence: maskgen.minoccurrence = options.minoccurrence
if options.maxoccurrence: maskgen.maxoccurrence = options.maxoccurrence
if options.minlength:
maskgen.minlength = options.minlength
if options.maxlength:
maskgen.maxlength = options.maxlength
if options.mintime:
maskgen.mintime = options.mintime
if options.maxtime:
maskgen.maxtime = options.maxtime
if options.mincomplexity:
maskgen.mincomplexity = options.mincomplexity
if options.maxcomplexity:
maskgen.maxcomplexity = options.maxcomplexity
if options.minoccurrence:
maskgen.minoccurrence = options.minoccurrence
if options.maxoccurrence:
maskgen.maxoccurrence = options.maxoccurrence

# Misc
if options.pps: maskgen.pps = options.pps
if options.showmasks: maskgen.showmasks = options.showmasks
if options.pps:
maskgen.pps = options.pps
if options.showmasks:
maskgen.showmasks = options.showmasks

print "[*] Using {:,d} keys/sec for calculations.".format(maskgen.pps)
print("[*] Using {:,d} keys/sec for calculations.".format(maskgen.pps))

# Load masks
for arg in args:
Expand All @@ -249,24 +284,24 @@ def getmaskscoverage(self, checkmasks):
# Matching masks from the command-line
if options.checkmasks:
checkmasks = [m.strip() for m in options.checkmasks.split(',')]
print "[*] Checking coverage of the these masks [%s]" % ", ".join(checkmasks)
print("[*] Checking coverage of the these masks [%s]" % ", ".join(checkmasks))
maskgen.getmaskscoverage(checkmasks)

# Matching masks from a file
elif options.checkmasks_file:
checkmasks_file = open(options.checkmasks_file, 'r')
print "[*] Checking coverage of masks in [%s]" % options.checkmasks_file
print("[*] Checking coverage of masks in [%s]" % options.checkmasks_file)
maskgen.getmaskscoverage(checkmasks_file)

# Printing masks in a file
else:
# Process masks according to specified sorting algorithm
if options.occurrence:
sorting_mode = "occurrence"
sort_mode = "occurrence"
elif options.complexity:
sorting_mode = "complexity"
sort_mode = "complexity"
else:
sorting_mode = "optindex"
sort_mode = "optindex"

print "[*] Sorting masks by their [%s]." % sorting_mode
maskgen.generate_masks(sorting_mode)
print("[*] Sorting masks by their [%s]." % sort_mode)
maskgen.generate_masks(sort_mode)
Loading