Created
November 18, 2015 16:18
-
-
Save jamcut/a84878911d2aae2c79fa to your computer and use it in GitHub Desktop.
Python script for auditing passwords against length and complexity requirements.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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