Updated Policy Generator with better logic and a new --noncompliant flag to test
for passwords not following defined password policy.
This commit is contained in:
parent
05013d199b
commit
a55be30bc5
@ -96,7 +96,8 @@ class MaskGen:
|
|||||||
self.masks[mask]['time'] = mask_complexity/self.pps
|
self.masks[mask]['time'] = mask_complexity/self.pps
|
||||||
self.masks[mask]['optindex'] = 1 - mask_complexity/mask_occurrence
|
self.masks[mask]['optindex'] = 1 - mask_complexity/mask_occurrence
|
||||||
|
|
||||||
def print_masks(self,sorting_mode):
|
def generate_masks(self,sorting_mode):
|
||||||
|
""" Generate optimal password masks sorted by occurrence, complexity or optindex """
|
||||||
sample_count = 0
|
sample_count = 0
|
||||||
sample_time = 0
|
sample_time = 0
|
||||||
sample_occurrence = 0
|
sample_occurrence = 0
|
||||||
@ -263,7 +264,7 @@ if __name__ == "__main__":
|
|||||||
sorting_mode = "optindex"
|
sorting_mode = "optindex"
|
||||||
|
|
||||||
print "[*] Sorting masks by their [%s]." % sorting_mode
|
print "[*] Sorting masks by their [%s]." % sorting_mode
|
||||||
maskgen.print_masks(sorting_mode)
|
maskgen.generate_masks(sorting_mode)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
217
policygen.py
217
policygen.py
@ -28,11 +28,12 @@
|
|||||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
import string, random
|
import sys, string, random
|
||||||
|
import datetime
|
||||||
from optparse import OptionParser, OptionGroup
|
from optparse import OptionParser, OptionGroup
|
||||||
import itertools
|
import itertools
|
||||||
|
|
||||||
VERSION = "0.0.1"
|
VERSION = "0.0.2"
|
||||||
|
|
||||||
# PPS (Passwords per Second) Cracking Speed
|
# PPS (Passwords per Second) Cracking Speed
|
||||||
pps = 1000000000
|
pps = 1000000000
|
||||||
@ -41,58 +42,124 @@ pps = 1000000000
|
|||||||
sample_time = 0
|
sample_time = 0
|
||||||
total_time = 0
|
total_time = 0
|
||||||
|
|
||||||
##################################################################
|
class PolicyGen:
|
||||||
# Calculate complexity of a single mask
|
def __init__(self):
|
||||||
##################################################################
|
self.output_file = None
|
||||||
def complexity(mask):
|
|
||||||
|
self.minlength = 8
|
||||||
|
self.maxlength = 8
|
||||||
|
self.mindigit = None
|
||||||
|
self.minlower = None
|
||||||
|
self.minupper = None
|
||||||
|
self.minspecial = None
|
||||||
|
self.maxdigit = None
|
||||||
|
self.maxlower = None
|
||||||
|
self.maxupper = None
|
||||||
|
self.maxspecial = None
|
||||||
|
|
||||||
|
|
||||||
|
# PPS (Passwords per Second) Cracking Speed
|
||||||
|
self.pps = 1000000000
|
||||||
|
self.showmasks = False
|
||||||
|
|
||||||
|
def getcomplexity(self, mask):
|
||||||
|
""" Return mask complexity. """
|
||||||
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: print "[!] Error, unknown mask ?%s in a mask %s" % (char,mask)
|
||||||
|
|
||||||
return count
|
return count
|
||||||
|
|
||||||
###################################################################
|
def generate_masks(self, noncompliant):
|
||||||
# Check whether a sample password mask matches defined policy
|
""" Generate all possible password masks matching the policy """
|
||||||
###################################################################
|
|
||||||
def filtermask(maskstring,options):
|
|
||||||
global total_time, sample_time
|
|
||||||
|
|
||||||
# define character counters
|
total_count = 0
|
||||||
lowercount = uppercount = digitcount = specialcount = 0
|
sample_count = 0
|
||||||
|
|
||||||
# calculate password complexity and cracking time
|
# NOTE: It is better to collect total complexity
|
||||||
mask_time = complexity(maskstring)/options.pps
|
# in order not to lose precision when dividing by pps
|
||||||
total_time += mask_time
|
total_complexity = 0
|
||||||
|
sample_complexity = 0
|
||||||
|
|
||||||
for char in maskstring[1:].split("?"):
|
# TODO: Randomize or even statistically arrange matching masks
|
||||||
|
for length in xrange(self.minlength, self.maxlength+1):
|
||||||
|
print "[*] Generating %d character password masks." % length
|
||||||
|
total_length_count = 0
|
||||||
|
sample_length_count = 0
|
||||||
|
|
||||||
|
total_length_complexity = 0
|
||||||
|
sample_length_complexity = 0
|
||||||
|
|
||||||
|
for masklist in itertools.product(['?d','?l','?u','?s'], repeat=length):
|
||||||
|
|
||||||
|
mask = ''.join(masklist)
|
||||||
|
|
||||||
|
lowercount = 0
|
||||||
|
uppercount = 0
|
||||||
|
digitcount = 0
|
||||||
|
specialcount = 0
|
||||||
|
|
||||||
|
mask_complexity = self.getcomplexity(mask)
|
||||||
|
|
||||||
|
total_length_count += 1
|
||||||
|
total_length_complexity += mask_complexity
|
||||||
|
|
||||||
|
# Count charachter types in a mask
|
||||||
|
for char in mask[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 \
|
# NOTE: Perform exact opposite (XOR) operation if noncompliant
|
||||||
uppercount >= options.minupper and uppercount <= options.maxupper and \
|
# flag was set when calling the function.
|
||||||
digitcount >= options.mindigits and digitcount <= options.maxdigits and \
|
if ((not self.minlower or lowercount >= self.minlower) and \
|
||||||
specialcount >= options.minspecial and specialcount <= options.maxspecial:
|
(not self.maxlower or lowercount <= self.maxlower) and \
|
||||||
sample_time += mask_time
|
(not self.minupper or uppercount >= self.minupper) and \
|
||||||
if options.verbose:
|
(not self.maxupper or uppercount <= self.maxupper) and \
|
||||||
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)
|
(not self.mindigit or digitcount >= self.mindigit) and \
|
||||||
return True
|
(not self.maxdigit or digitcount <= self.maxdigit) and \
|
||||||
else:
|
(not self.minspecial or specialcount >= self.minspecial) and \
|
||||||
return False
|
(not self.maxspecial or specialcount <= self.maxspecial)) ^ noncompliant :
|
||||||
|
|
||||||
|
sample_length_count += 1
|
||||||
|
sample_length_complexity += mask_complexity
|
||||||
|
|
||||||
|
if self.showmasks:
|
||||||
|
mask_time = mask_complexity/self.pps
|
||||||
|
time_human = ">1 year" if mask_time > 60*60*24*365 else str(datetime.timedelta(seconds=mask_time))
|
||||||
|
print "[{:>2}] {:<30} [l:{:>2} u:{:>2} d:{:>2} s:{:>2}] [{:>8}] ".format(length, mask, lowercount,uppercount,digitcount,specialcount, time_human)
|
||||||
|
|
||||||
|
if self.output_file:
|
||||||
|
self.output_file.write("%s\n" % mask)
|
||||||
|
|
||||||
|
|
||||||
def main():
|
|
||||||
# define mask counters
|
total_count += total_length_count
|
||||||
total_count = sample_count = 0
|
sample_count += sample_length_count
|
||||||
|
|
||||||
|
total_complexity += total_length_complexity
|
||||||
|
sample_complexity += sample_length_complexity
|
||||||
|
|
||||||
|
|
||||||
|
total_time = total_complexity/self.pps
|
||||||
|
total_time_human = ">1 year" if total_time > 60*60*24*365 else str(datetime.timedelta(seconds=total_time))
|
||||||
|
print "[*] Total Masks: %d Time: %s" % (total_count, total_time_human)
|
||||||
|
|
||||||
|
sample_time = sample_complexity/self.pps
|
||||||
|
sample_time_human = ">1 year" if sample_time > 60*60*24*365 else str(datetime.timedelta(seconds=sample_time))
|
||||||
|
print "[*] Policy Masks: %d Time: %s" % (sample_count, sample_time_human)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
|
||||||
header = " _ \n"
|
header = " _ \n"
|
||||||
header += " PolicyGen 0.0.1 | |\n"
|
header += " PolicyGen %s | |\n" % VERSION
|
||||||
header += " _ __ __ _ ___| | _\n"
|
header += " _ __ __ _ ___| | _\n"
|
||||||
header += " | '_ \ / _` |/ __| |/ /\n"
|
header += " | '_ \ / _` |/ __| |/ /\n"
|
||||||
header += " | |_) | (_| | (__| < \n"
|
header += " | |_) | (_| | (__| < \n"
|
||||||
@ -103,56 +170,60 @@ def main():
|
|||||||
|
|
||||||
# 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("-o", "--outputmasks", dest="output_masks",help="Save masks to a file", metavar="masks.hcmask")
|
||||||
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", metavar="1000000000")
|
||||||
parser.add_option("--pps", dest="pps", help="Passwords per Second", type="int", default=pps, metavar="1000000000")
|
parser.add_option("--showmasks", dest="showmasks", help="Show matching masks", action="store_true", default=False)
|
||||||
parser.add_option("-v", "--verbose", action="store_true", dest="verbose")
|
parser.add_option("--noncompliant", dest="noncompliant", help="Generate masks for noncompliant passwords", action="store_true", default=False)
|
||||||
|
|
||||||
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("--minlength", dest="minlength", type="int", metavar="8", default=8, help="Minimum password length")
|
||||||
group.add_option("--minlower", dest="minlower", help="Minimum number of lower-case characters", default=0, type="int", metavar="1")
|
group.add_option("--maxlength", dest="maxlength", type="int", metavar="8", default=8, help="Maximum password length")
|
||||||
group.add_option("--minupper", dest="minupper", help="Minimum number of upper-case characters", default=0, type="int", metavar="1")
|
group.add_option("--mindigit", dest="mindigit", type="int", metavar="1", help="Minimum number of digits")
|
||||||
group.add_option("--minspecial", dest="minspecial", help="Minimum number of special characters", default=0, type="int", metavar="1")
|
group.add_option("--minlower", dest="minlower", type="int", metavar="1", help="Minimum number of lower-case characters")
|
||||||
group.add_option("--maxdigits", dest="maxdigits", help="Maximum number of digits", default=9999, type="int", metavar="3")
|
group.add_option("--minupper", dest="minupper", type="int", metavar="1", help="Minimum number of upper-case characters")
|
||||||
group.add_option("--maxlower", dest="maxlower", help="Maximum number of lower-case characters", default=9999, type="int", metavar="3")
|
group.add_option("--minspecial",dest="minspecial",type="int", metavar="1", help="Minimum number of special characters")
|
||||||
group.add_option("--maxupper", dest="maxupper", help="Maximum number of upper-case characters", default=9999, type="int", metavar="3")
|
group.add_option("--maxdigit", dest="maxdigit", type="int", metavar="3", help="Maximum number of digits")
|
||||||
group.add_option("--maxspecial", dest="maxspecial", help="Maximum number of special characters", default=9999, type="int", metavar="3")
|
group.add_option("--maxlower", dest="maxlower", type="int", metavar="3", help="Maximum number of lower-case characters")
|
||||||
parser.add_option("-q", "--quiet", action="store_true", dest="quiet", default=False, help="Don't show headers.")
|
group.add_option("--maxupper", dest="maxupper", type="int", metavar="3", help="Maximum number of upper-case characters")
|
||||||
|
group.add_option("--maxspecial",dest="maxspecial",type="int", metavar="3", help="Maximum number of special characters")
|
||||||
parser.add_option_group(group)
|
parser.add_option_group(group)
|
||||||
|
|
||||||
(options, args) = parser.parse_args()
|
parser.add_option("-q", "--quiet", action="store_true", dest="quiet", default=False, help="Don't show headers.")
|
||||||
|
|
||||||
# cleanup maximum occurence options
|
(options, args) = parser.parse_args()
|
||||||
if options.maxlower > options.length: options.maxlower = options.length
|
|
||||||
if options.maxdigits > options.length: options.maxdigits = options.length
|
|
||||||
if options.mindigits > options.length: options.mindigits = options.length
|
|
||||||
if options.maxupper > options.length: options.maxupper = 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
|
policygen = PolicyGen()
|
||||||
|
|
||||||
|
# Settings
|
||||||
|
if options.output_masks:
|
||||||
|
print "[*] Saving generated masks to [%s]" % options.output_masks
|
||||||
|
policygen.output_file = open(options.output_masks, 'w')
|
||||||
|
|
||||||
|
# Password policy
|
||||||
|
if options.minlength: policygen.minlength = options.minlength
|
||||||
|
if options.maxlength: policygen.maxlength = options.maxlength
|
||||||
|
if options.mindigit: policygen.mindigit = options.mindigit
|
||||||
|
if options.minlower: policygen.minlower = options.minlower
|
||||||
|
if options.minupper: policygen.minupper = options.minupper
|
||||||
|
if options.minspecial: policygen.minspecial = options.minspecial
|
||||||
|
if options.maxdigit: policygen.maxdigits = options.maxdigit
|
||||||
|
if options.maxlower: policygen.maxlower = options.maxlower
|
||||||
|
if options.maxupper: policygen.maxupper = options.maxupper
|
||||||
|
if options.maxspecial: policygen.maxspecial = options.maxspecial
|
||||||
|
|
||||||
|
# Misc
|
||||||
|
if options.pps: policygen.pps = options.pps
|
||||||
|
if options.showmasks: policygen.showmasks = options.showmasks
|
||||||
|
|
||||||
|
# Print current password policy
|
||||||
print "[*] Password policy:"
|
print "[*] Password policy:"
|
||||||
print "[+] Password length: %d" % options.length
|
print " Pass Lengths: min:%d max:%d" % (options.minlength,options.maxlength)
|
||||||
print "[+] Minimum strength: lower: %d, upper: %d, digits: %d, special: %d" % (options.minlower, options.minupper, options.mindigits, options.minspecial)
|
print " Min strength: l:%s u:%s d:%s s:%s" % (options.minlower, options.minupper, options.mindigit, options.minspecial)
|
||||||
print "[+] Maximum strength: lower: %d, upper: %d, digits: %d, special: %d" % (options.maxlower, options.maxupper, options.maxdigits, options.maxspecial)
|
print " Max strength: l:%s u:%s d:%s s:%s" % (options.maxlower, options.maxupper, options.maxdigit, options.maxspecial)
|
||||||
|
|
||||||
if options.output: f = open(options.output, 'w')
|
print "[*] Generating [%s] masks." % ("compliant" if not options.noncompliant else "non-compliant")
|
||||||
|
policygen.generate_masks(options.noncompliant)
|
||||||
# generate all possible password masks and compare them to policy
|
|
||||||
# TODO: Randomize or even statistically arrange matching masks
|
|
||||||
for password in itertools.product(['?l','?u','?d','?s'],repeat=options.length):
|
|
||||||
if filtermask(''.join(password), options):
|
|
||||||
if options.output: f.write("%s\n" % ''.join(password))
|
|
||||||
sample_count +=1
|
|
||||||
total_count += 1
|
|
||||||
|
|
||||||
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 "[*] 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__":
|
|
||||||
main()
|
|
Loading…
x
Reference in New Issue
Block a user