159 lines
7.6 KiB
Python
Executable File
159 lines
7.6 KiB
Python
Executable File
#!/usr/bin/python
|
|
# PolicyGen - Analyze and Generate password masks according to a password policy
|
|
#
|
|
# This tool is part of PACK (Password Analysis and Cracking Kit)
|
|
#
|
|
# VERSION 0.0.2
|
|
#
|
|
# Copyright (C) 2013 Peter Kacherginsky
|
|
# All rights reserved.
|
|
#
|
|
# Redistribution and use in source and binary forms, with or without
|
|
# 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 string, random
|
|
from optparse import OptionParser, OptionGroup
|
|
import itertools
|
|
|
|
VERSION = "0.0.1"
|
|
|
|
# PPS (Passwords per Second) Cracking Speed
|
|
pps = 1000000000
|
|
|
|
# Global Variables
|
|
sample_time = 0
|
|
total_time = 0
|
|
|
|
##################################################################
|
|
# Calculate complexity of a single mask
|
|
##################################################################
|
|
def complexity(mask):
|
|
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
|
|
else: "[!] Error, unknown mask ?%s" % char
|
|
|
|
return count
|
|
|
|
###################################################################
|
|
# Check whether a sample password mask matches defined policy
|
|
###################################################################
|
|
def filtermask(maskstring,options):
|
|
global total_time, sample_time
|
|
|
|
# define character counters
|
|
lowercount = uppercount = digitcount = specialcount = 0
|
|
|
|
# calculate password complexity and cracking time
|
|
mask_time = complexity(maskstring)/options.pps
|
|
total_time += mask_time
|
|
|
|
for char in maskstring[1:].split("?"):
|
|
if char == "l": lowercount += 1
|
|
elif char == "u": uppercount += 1
|
|
elif char == "d": digitcount += 1
|
|
elif char == "s": specialcount += 1
|
|
|
|
# Filter according to password policy
|
|
if lowercount >= options.minlower and lowercount <= options.maxlower and \
|
|
uppercount >= options.minupper and uppercount <= options.maxupper and \
|
|
digitcount >= options.mindigits and digitcount <= options.maxdigits and \
|
|
specialcount >= options.minspecial and specialcount <= options.maxspecial:
|
|
sample_time += mask_time
|
|
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)
|
|
return True
|
|
else:
|
|
return False
|
|
|
|
|
|
def main():
|
|
# define mask counters
|
|
total_count = sample_count = 0
|
|
|
|
header = " _ \n"
|
|
header += " PolicyGen 0.0.1 | |\n"
|
|
header += " _ __ __ _ ___| | _\n"
|
|
header += " | '_ \ / _` |/ __| |/ /\n"
|
|
header += " | |_) | (_| | (__| < \n"
|
|
header += " | .__/ \__,_|\___|_|\_\\\n"
|
|
header += " | | \n"
|
|
header += " |_| iphelix@thesprawl.org\n"
|
|
header += "\n"
|
|
|
|
# parse command line arguments
|
|
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", "--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("-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.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("--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("--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("--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")
|
|
parser.add_option("-q", "--quiet", action="store_true", dest="quiet", default=False, help="Don't show headers.")
|
|
parser.add_option_group(group)
|
|
|
|
(options, args) = parser.parse_args()
|
|
|
|
# cleanup maximum occurence options
|
|
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
|
|
if not options.quiet:
|
|
print header
|
|
|
|
# print current password policy
|
|
print "[*] Password policy:"
|
|
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 "[+] 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')
|
|
|
|
# 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()
|