Skip to content

Instantly share code, notes, and snippets.

@jamcut
Created November 18, 2015 16:18
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jamcut/a84878911d2aae2c79fa to your computer and use it in GitHub Desktop.
Save jamcut/a84878911d2aae2c79fa to your computer and use it in GitHub Desktop.
Python script for auditing passwords against length and complexity requirements.
#!/usr/bin/env python
"""This script performs analysis on passwords. It will compare passwords to length and complexity
requirements supplied by the user. It also allows the user to provide a regular expression
in order to identify the number of passwords that match a spceific schema.
"""
import argparse
import re
import sys
try:
from termcolor import colored
COLORS = True
except ImportError:
print('[-] Could not import termcolor. For colored output, please run')
print('pip install termcolor')
def color_print_error(msg):
"""This function prints a string in 'error format'"""
if COLORS:
error_symbol = colored('[-] ', 'red', attrs=['bold'])
print(error_symbol + msg)
else:
print('[-] {0}').format(msg)
def color_print_good(msg):
"""This function prints a string in 'good format'"""
if COLORS:
error_symbol = colored('[+] ', 'yellow', attrs=['bold'])
print(error_symbol + msg)
else:
print('[+] {0}').format(msg)
def color_print_status(msg):
"""This function prints a string in 'status format'"""
if COLORS:
error_symbol = colored('[*] ', 'blue', attrs=['bold'])
print(error_symbol + msg)
else:
print('[*] {0}').format(msg)
def color_print_warn(msg):
"""This function prints a string in 'warn format'"""
if COLORS:
error_symbol = colored('[!] ', 'green', attrs=['bold'])
print(error_symbol + msg)
else:
print('[!] {0}').format(msg)
# main analysis function
def password_analysis(userpass_file, output_file, verbose, password_complexity, password_length, delimiter, **kwargs):
"""This function breaks up the input file based on the delimiter provided, and passes
the individual passwords on to other functions for analysis.
"""
use_regex = False
if kwargs:
obj_regex = kwargs['obj_regex']
use_regex = True
regex_match = []
regex_failed = []
line_num = 1
failed_length_check = []
failed_complexity_check = []
compliant_passwords = []
userpass_list = []
temp_userpass_list = userpass_file.readlines()
for my_line in temp_userpass_list:
my_line = my_line.strip()
try:
username, password = my_line.split(delimiter)
if username == "":
continue
elif password == "":
continue
else:
userpass_list.append(my_line)
# if there is more than one delimiter in the line it will throw a ValueError
except ValueError:
color_print_warn('Multiple delimiters on same line. Skipping input file line: {0}.'.format(str(line_num)))
line_num += 1
continue
line_num += 1
color_print_status("Analyzing {0} passwords for length and complexity...".format(str(len(userpass_list))))
if use_regex:
color_print_status('Comparing {0} passwords against regex: {1}'.format(str(len(userpass_list)), obj_regex.pattern))
for my_line in userpass_list:
complexity_test = False
length_test = False
username, password = my_line.split(delimiter)
if username == "":
continue
if password == "":
continue
complexity_test = complexity_check(password_complexity, password)
length_test = length_check(password_length, password)
if complexity_test == False:
failed_complexity_check.append(username)
if length_test == False:
failed_length_check.append(username)
if complexity_test == True:
if length_test == True:
compliant_passwords.append(username)
if use_regex:
check_regex = compare_regex(obj_regex, password)
if check_regex:
regex_match.append(username)
else:
regex_failed.append(username)
if use_regex:
kwargs['regex'] = obj_regex
kwargs['regex_match'] = regex_match
display_output(failed_length_check, failed_complexity_check, compliant_passwords, verbose, output_file, **kwargs)
else:
display_output(failed_length_check, failed_complexity_check, compliant_passwords, verbose, output_file)
return
# check password complexity
def complexity_check(password_complexity, password):
"""This function checks the complexit of the password based on the
four availble requirements: upper case alpha, lower case alpha,
numbers, and symbols.
"""
complexity_count = 0
complexity_test = False
alpha_lower = ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"]
alpha_upper = ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "V", "U", "W", "X", "Y", "Z"]
numerals = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"]
special_chars = ["!", "@", "#", "$", "%", "^", "&", "*", "(", ")", "-", "=", "_", "+", "[", "]", "{", "}", ";", "'", ":", '"', "<", ">", ".", "?", "/", "|", "'", ",", " ", "`", "~", "\\"]
for char in alpha_lower:
if char in password:
complexity_count += 1
break
for char in alpha_upper:
if char in password:
complexity_count += 1
break
for char in numerals:
if char in password:
complexity_count += 1
break
for char in special_chars:
if char in password:
complexity_count += 1
break
if complexity_count >= password_complexity:
complexity_test = True
return complexity_test
def length_check(password_length, password):
"""This function checks the length of the password against the provided length
requirement.
"""
length_test = False
if len(password) >= password_length:
length_test = True
return length_test
def compare_regex(obj_regex, password):
"""This function compares the password against a supplied regex."""
# check if password matches provided regex
match = obj_regex.match(password)
if match:
return True
else:
return False
def display_output(failed_length_check, failed_complexity_check, compliant_passwords, verbose, output_file, **kwargs):
"""This function displays the results,and optionally writes them to a file."""
# display/write number of failing length passwords
length_count_msg = "Total number of passwords that failed the length requirement: {0}".format(str(len(failed_length_check)))
color_print_good(length_count_msg)
if verbose:
color_print_status("Users with passwords less than the required length:\n")
for failed_length in failed_length_check:
print(failed_length)
if output_file:
output_file.write(length_count_msg + "\n")
output_file.write("Users with passwords less than the required length:\n")
for failed_length in failed_length_check:
output_file.write(failed_length + "\n")
# display/write number of complexity failing passwords
complexity_count_msg = "Total number of passwords that failed the complexity requirement: {0}".format(str(len(failed_complexity_check)))
color_print_good(complexity_count_msg)
if verbose:
color_print_status("Users with passwords that did not meet the complexity requirements:\n")
for failed_complexity in failed_complexity_check:
print(failed_complexity)
if output_file:
output_file.write("\n\n" + complexity_count_msg + "\n")
output_file.write("Users with passwords that did not meet the complexity requirements:\n")
for failed_complexity in failed_complexity_check:
output_file.write(failed_complexity + "\n")
# display/write number of compliant passwords
compliant_count_msg = "Total number of passwords that are compliant: {0}".format(str(len(compliant_passwords)))
color_print_good(compliant_count_msg)
if verbose:
color_print_status("Users with compliant passwords:\n")
for compliant_password in compliant_passwords:
print(compliant_password)
if output_file:
output_file.write("\n\n" + compliant_count_msg + "\n")
output_file.write("Users with compliant passwords:\n")
for compliant_password in compliant_passwords:
output_file.write(compliant_password + "\n")
if kwargs:
obj_regex = kwargs['obj_regex']
regex_match = kwargs['regex_match']
regex_match_msg = 'Total number of passwords that matched the regex {0}: {1}'.format(obj_regex.pattern, str(len(regex_match)))
color_print_good(regex_match_msg)
if verbose:
color_print_status('Users with passwords matching regex:\n')
for user in regex_match:
print(user)
if output_file:
output_file.write("\n\n" + regex_match_msg + "\n")
output_file.write("Users with passwords matching regex:\n")
for user in regex_match:
output_file.write(user + "\n")
return
def main():
"""This function accepts the arguments and initializes exerything"""
# setup arguments
parser = argparse.ArgumentParser(description='This script will analyze a userpass file and provide analysis of passwords')
parser.add_argument('-f', '--file', help='Userpass file in the format of: username,password', required=True)
parser.add_argument('-d', '--delimiter', help='character used to separate username and password in <file>', default=':')
parser.add_argument('-c', '--complexity', help='the required number of character types to meet complexity requirements', choices=['0', '1', '2', '3', '4'], default='3',)
parser.add_argument('-l', '--length', help='minimum password length', default='8')
parser.add_argument('-r', '--regex', help='regular expression to match against, in quotes', default=None)
parser.add_argument('-o', '--output', help='file to write output to')
parser.add_argument('-v', '--verbose', help='show verbose output', action='store_true')
# parse arguments
args = parser.parse_args()
try:
userpass_file = open(args.file, 'r')
except IOError:
color_print_error("Could not open file: {0}".format(args.file))
sys.exit(0)
if args.output:
try:
output_file = open(args.output, 'w')
except IOError:
color_print_error("Could not open file: {0}".format(args.output))
color_print_status('Continuing without output file...')
output_file = None
else:
output_file = None
verbose = args.verbose
password_complexity = int(args.complexity)
password_length = int(args.length)
delimiter = args.delimiter
# verify the supplied regex
kwargs = None
if args.regex is not None:
regex = args.regex
kwargs = {}
try:
obj_regex = re.compile(regex, re.IGNORECASE)
except (TypeError, SyntaxError):
color_print_error('Invalid regex provided: {0}'.format(regex))
sys.exit(0)
kwargs['obj_regex'] = obj_regex
password_analysis(userpass_file, output_file, verbose, password_complexity, password_length, delimiter, **kwargs)
else:
password_analysis(userpass_file, output_file, verbose, password_complexity, password_length, delimiter)
userpass_file.close()
if output_file:
output_file.close()
sys.exit(0)
if __name__ == '__main__':
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment