mirror of
https://github.com/trustedsec/hate_crack.git
synced 2025-12-05 20:39:59 -08:00
258 lines
10 KiB
Python
Executable File
258 lines
10 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
# 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.
|
|
#
|
|
# Please see the attached LICENSE file for additional licensing information.
|
|
|
|
import sys
|
|
import string
|
|
import random
|
|
import datetime
|
|
from optparse import OptionParser, OptionGroup
|
|
import itertools
|
|
|
|
VERSION = "0.0.2"
|
|
|
|
|
|
class PolicyGen:
|
|
def __init__(self):
|
|
self.output_file = None
|
|
|
|
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
|
|
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)
|
|
|
|
return count
|
|
|
|
def generate_masks(self, noncompliant):
|
|
""" Generate all possible password masks matching the policy """
|
|
|
|
total_count = 0
|
|
sample_count = 0
|
|
|
|
# NOTE: It is better to collect total complexity
|
|
# not to lose precision when dividing by pps
|
|
total_complexity = 0
|
|
sample_complexity = 0
|
|
|
|
# TODO: Randomize or even statistically arrange matching masks
|
|
for length in range(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
|
|
elif char == "u":
|
|
uppercount += 1
|
|
elif char == "d":
|
|
digitcount += 1
|
|
elif char == "s":
|
|
specialcount += 1
|
|
|
|
# Filter according to password policy
|
|
# NOTE: Perform exact opposite (XOR) operation if noncompliant
|
|
# flag was set when calling the function.
|
|
if ((self.minlower == None or lowercount >= self.minlower) and
|
|
(self.maxlower == None or lowercount <= self.maxlower) and
|
|
(self.minupper == None or uppercount >= self.minupper) and
|
|
(self.maxupper == None or uppercount <= self.maxupper) and
|
|
(self.mindigit == None or digitcount >= self.mindigit) and
|
|
(self.maxdigit == None or digitcount <= self.maxdigit) and
|
|
(self.minspecial == None or specialcount >= self.minspecial) and
|
|
(self.maxspecial == None 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)
|
|
|
|
total_count += total_length_count
|
|
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 += " PolicyGen %s | |\n" % VERSION
|
|
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("-o", "--outputmasks", dest="output_masks",
|
|
help="Save masks to a file", metavar="masks.hcmask")
|
|
parser.add_option("--pps", dest="pps", help="Passwords per Second",
|
|
type="int", metavar="1000000000")
|
|
parser.add_option("--showmasks", dest="showmasks",
|
|
help="Show matching masks", action="store_true", default=False)
|
|
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.add_option("--minlength", dest="minlength", type="int", metavar="8", default=8,
|
|
help="Minimum password length")
|
|
group.add_option("--maxlength", dest="maxlength", type="int", metavar="8", default=8,
|
|
help="Maximum password length")
|
|
group.add_option("--mindigit", dest="mindigit", type="int",
|
|
metavar="1", help="Minimum number of digits")
|
|
group.add_option("--minlower", dest="minlower", type="int", metavar="1",
|
|
help="Minimum number of lower-case characters")
|
|
group.add_option("--minupper", dest="minupper", type="int", metavar="1",
|
|
help="Minimum number of upper-case characters")
|
|
group.add_option("--minspecial", dest="minspecial", type="int", metavar="1",
|
|
help="Minimum number of special characters")
|
|
group.add_option("--maxdigit", dest="maxdigit", type="int",
|
|
metavar="3", help="Maximum number of digits")
|
|
group.add_option("--maxlower", dest="maxlower", type="int", metavar="3",
|
|
help="Maximum number of lower-case characters")
|
|
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("-q", "--quiet", action="store_true",
|
|
dest="quiet", default=False, help="Don't show headers.")
|
|
|
|
(options, args) = parser.parse_args()
|
|
|
|
# Print program header
|
|
if not options.quiet:
|
|
print()
|
|
header
|
|
|
|
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 != None:
|
|
policygen.minlength = options.minlength
|
|
if options.maxlength != None:
|
|
policygen.maxlength = options.maxlength
|
|
if options.mindigit != None:
|
|
policygen.mindigit = options.mindigit
|
|
if options.minlower != None:
|
|
policygen.minlower = options.minlower
|
|
if options.minupper != None:
|
|
policygen.minupper = options.minupper
|
|
if options.minspecial != None:
|
|
policygen.minspecial = options.minspecial
|
|
if options.maxdigit != None:
|
|
policygen.maxdigits = options.maxdigit
|
|
if options.maxlower != None:
|
|
policygen.maxlower = options.maxlower
|
|
if options.maxupper != None:
|
|
policygen.maxupper = options.maxupper
|
|
if options.maxspecial != None:
|
|
policygen.maxspecial = options.maxspecial
|
|
|
|
# Misc
|
|
if options.pps:
|
|
policygen.pps = options.pps
|
|
if options.showmasks:
|
|
policygen.showmasks = options.showmasks
|
|
|
|
print("[*] Using {:,d} keys/sec for calculations.".format(policygen.pps))
|
|
|
|
# Print current password policy
|
|
print("[*] Password policy:")
|
|
print(" Pass Lengths: min:%d max:%d" % (
|
|
policygen.minlength, policygen.maxlength))
|
|
print(" Min strength: l:%s u:%s d:%s s:%s" % (
|
|
policygen.minlower, policygen.minupper, policygen.mindigit, policygen.minspecial))
|
|
print(" Max strength: l:%s u:%s d:%s s:%s" % (
|
|
policygen.maxlower, policygen.maxupper, policygen.maxdigit, policygen.maxspecial))
|
|
|
|
print("[*] Generating [%s] masks." % (
|
|
"compliant" if not options.noncompliant else "non-compliant"))
|
|
policygen.generate_masks(options.noncompliant)
|