pack/policygen.py

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()