From beb92ca5062f1c5972d65a87be1ebbdd5b45ecf3 Mon Sep 17 00:00:00 2001 From: rotarydrone Date: Tue, 18 Sep 2018 10:44:47 -0400 Subject: [PATCH 1/5] Add support for file inputs --- ssl_checker.py | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/ssl_checker.py b/ssl_checker.py index 6bd1715..6b8d364 100755 --- a/ssl_checker.py +++ b/ssl_checker.py @@ -223,8 +223,16 @@ def get_args(): """Set argparse options.""" parser = ArgumentParser(prog='ssl_checker.py', add_help=False, description="""Collects useful information about given host's SSL certificates.""") - parser.add_argument('-H', '--host', dest='hosts', nargs='*', required=True, + + + group = parser.add_mutually_exclusive_group() + + group.add_argument('-H', '--host', dest='hosts', nargs='*', required=False, help='Hosts as input separated by space') + + group.add_argument('-f', '--host-file', dest='host_file', required=False, + help='Hosts as input from file') + parser.add_argument('-s', '--socks', dest='socks', default=False, metavar='HOST:PORT', help='Enable SOCKS proxy for connection') @@ -246,6 +254,12 @@ def get_args(): args = parser.parse_args() + # Get hosts from file if provided + if args.host_file: + hosts_file = open(args.host_file, 'r') + args.hosts = hosts_file.readlines() + hosts_file.close() + # Checks hosts list if isinstance(args.hosts, list): if len(args.hosts) == 0: From 85d461942f7a921cf1d30295c161097e4e2e5869 Mon Sep 17 00:00:00 2001 From: rotarydrone Date: Tue, 18 Sep 2018 11:38:08 -0400 Subject: [PATCH 2/5] Add support for Subject Alt Names --- ssl_checker.py | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/ssl_checker.py b/ssl_checker.py index 6b8d364..9e45240 100755 --- a/ssl_checker.py +++ b/ssl_checker.py @@ -8,7 +8,7 @@ from ssl import PROTOCOL_TLSv1 from time import sleep try: - from OpenSSL import SSL + from OpenSSL import SSL, crypto except ImportError: print('Required module does not exist. Install: pip install pyopenssl') sys.exit(1) @@ -86,6 +86,19 @@ def analyze_ssl(host, context): return context +def get_cert_sans(x509cert): + ''' + Get Subject Alt Names from Certificate. Shameless taken from stack overflow: + https://stackoverflow.com/users/4547691/anatolii-chmykhalo + ''' + + san = '' + ext_count = x509cert.get_extension_count() + for i in range(0, ext_count): + ext = x509cert.get_extension(i) + if 'subjectAltName' in str(ext.get_short_name()): + san = ext.__str__() + return san def get_cert_info(host, cert): """Get all the information about cert and create a JSON file.""" @@ -102,6 +115,7 @@ def get_cert_info(host, cert): context['cert_sn'] = cert.get_serial_number() context['cert_alg'] = cert.get_signature_algorithm().decode() context['cert_ver'] = cert.get_version() + context['cert_sans'] = get_cert_sans(cert) context['cert_exp'] = cert.has_expired() # Valid from @@ -144,7 +158,10 @@ def print_status(host, context, analyze=False): print('\t\tLogjam vulnerability: {}'.format(context[host]['logjam_vuln'])) print('\t\tDrown vulnerability: {}'.format(context[host]['drownVulnerable'])) - print('\t\tExpired: {}\n'.format(context[host]['cert_exp'])) + print('\t\tExpired: {}'.format(context[host]['cert_exp'])) + print('\t\tCertificate SANs: ') + for san in context[host]['cert_sans'].split(','): print('\t\t\t{}'.format(san)) + def show_result(user_args): @@ -223,16 +240,13 @@ def get_args(): """Set argparse options.""" parser = ArgumentParser(prog='ssl_checker.py', add_help=False, description="""Collects useful information about given host's SSL certificates.""") - - + group = parser.add_mutually_exclusive_group() group.add_argument('-H', '--host', dest='hosts', nargs='*', required=False, help='Hosts as input separated by space') - group.add_argument('-f', '--host-file', dest='host_file', required=False, help='Hosts as input from file') - parser.add_argument('-s', '--socks', dest='socks', default=False, metavar='HOST:PORT', help='Enable SOCKS proxy for connection') From 3d66f1ada8c3276355947b72e7f34fbd5a6c4a70 Mon Sep 17 00:00:00 2001 From: rotarydrone Date: Tue, 18 Sep 2018 12:19:09 -0400 Subject: [PATCH 3/5] Fix CSV output to write into rows instead of columns --- ssl_checker.py | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/ssl_checker.py b/ssl_checker.py index 9e45240..43bfbd1 100755 --- a/ssl_checker.py +++ b/ssl_checker.py @@ -6,6 +6,7 @@ from argparse import ArgumentParser, SUPPRESS from datetime import datetime from ssl import PROTOCOL_TLSv1 from time import sleep +from csv import DictWriter try: from OpenSSL import SSL, crypto @@ -98,6 +99,8 @@ def get_cert_sans(x509cert): ext = x509cert.get_extension(i) if 'subjectAltName' in str(ext.get_short_name()): san = ext.__str__() + # replace commas to not break csv output + san = san.replace(',', ';') return san def get_cert_info(host, cert): @@ -160,7 +163,7 @@ def print_status(host, context, analyze=False): print('\t\tExpired: {}'.format(context[host]['cert_exp'])) print('\t\tCertificate SANs: ') - for san in context[host]['cert_sans'].split(','): print('\t\t\t{}'.format(san)) + for san in context[host]['cert_sans'].split(';'): print('\t\t\t{}'.format(san)) @@ -216,15 +219,14 @@ def show_result(user_args): else: print(context) - def export_csv(context, filename): - """Export all context results to CSV file.""" + """ Export all context results to CSV file.""" + # prepend dict keys to write column headers with open(filename, 'w') as csv_file: + csv_writer = DictWriter(csv_file, context.items()[0][1].keys()) + csv_writer.writeheader() for host in context.keys(): - csv_file.write('{}\n'.format(host)) - for key, value in context[host].items(): - csv_file.write('{},{}\n'.format(key, value)) - + csv_writer.writerow(context[host]) def filter_hostname(host): """Remove unused characters and split by address and port.""" From 8080775e389be447ce24624e58fc2147789b6d30 Mon Sep 17 00:00:00 2001 From: rotarydrone Date: Tue, 18 Sep 2018 12:45:11 -0400 Subject: [PATCH 4/5] Add support for cert SHA1 fingerprint --- ssl_checker.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ssl_checker.py b/ssl_checker.py index 43bfbd1..82ba0d1 100755 --- a/ssl_checker.py +++ b/ssl_checker.py @@ -116,6 +116,7 @@ def get_cert_info(host, cert): context['issuer_ou'] = cert.get_issuer().organizationalUnitName context['issuer_cn'] = cert.get_issuer().commonName context['cert_sn'] = cert.get_serial_number() + context['cert_sha1'] = cert.digest("sha1") context['cert_alg'] = cert.get_signature_algorithm().decode() context['cert_ver'] = cert.get_version() context['cert_sans'] = get_cert_sans(cert) @@ -149,6 +150,7 @@ def print_status(host, context, analyze=False): print('\t\tValid to: {} ({} days left)'.format(context[host]['valid_till'], days_left)) print('\t\tValidity days: {}'.format(context[host]['validity_days'])) print('\t\tCertificate S/N: {}'.format(context[host]['cert_sn'])) + print('\t\tCertificate SHA1 FP: {}'.format(context[host]['cert_sha1'])) print('\t\tCertificate version: {}'.format(context[host]['cert_ver'])) print('\t\tCertificate algorithm: {}'.format(context[host]['cert_alg'])) From d1cb4d99710ee3ecf8c399228ca58740a1705455 Mon Sep 17 00:00:00 2001 From: rotarydrone Date: Tue, 18 Sep 2018 12:45:24 -0400 Subject: [PATCH 5/5] Update README.md --- README.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 106123e..e186d26 100644 --- a/README.md +++ b/README.md @@ -17,14 +17,16 @@ You only need to installl pyOpenSSL: ``` ./ssl_checker.py -h -usage: ssl_checker.py -H [HOSTS [HOSTS ...]] [-s HOST:PORT] [-c FILENAME.CSV] - [-j] [-a] [-p] [-h] +usage: ssl_checker.py [-H [HOSTS [HOSTS ...]] | -f HOST_FILE] [-s HOST:PORT] + [-c FILENAME.CSV] [-j] [-a] [-p] [-h] Collects useful information about given host's SSL certificates. optional arguments: -H [HOSTS [HOSTS ...]], --host [HOSTS [HOSTS ...]] Hosts as input separated by space + -f HOST_FILE, --host-file HOST_FILE + Hosts as input from file -s HOST:PORT, --socks HOST:PORT Enable SOCKS proxy for connection -c FILENAME.CSV, --csv FILENAME.CSV @@ -39,6 +41,8 @@ optional arguments: Port is optional here. The script will use 443 if not specified. +`-f, --host-file` File containing hostnames for input + `-H, --host ` Enter the hosts separated by space `-s, --socks ` Enable connection through SOCKS server