From 39fe43812ead4585b441d236c12243ee8efe2cbb Mon Sep 17 00:00:00 2001 From: Narbeh Arakil Date: Sun, 30 Jun 2019 17:28:25 +0430 Subject: [PATCH] Add Duration and Fix bugs --- README.md | 78 +++++++++++++++++++++++++++++--------------------- ssl_checker.py | 28 +++++++++--------- 2 files changed, 61 insertions(+), 45 deletions(-) diff --git a/README.md b/README.md index d1226bd..38d0d43 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,7 @@ optional arguments: -c FILENAME.CSV, --csv FILENAME.CSV Enable CSV file export -j, --json Enable JSON in the output - -a, --analyze Enable SSL security analysis on the host + -a, --analyze Enable SSL security analysis on the host. -p, --pretty Print pretty and more human readable Json -h, --help Show this help message and exit ``` @@ -51,7 +51,7 @@ Port is optional here. The script will use 443 if not specified. `-j, --json ` Use this if you want to only have the result in JSON -`-a, --analyze` This argument will include security analyze on the certificate. It will take more time. +`-a, --analyze` This argument will include security analyze on the certificate. It will take more time. No result means failed to analyze. `-p, --pretty ` Use this with `-j` to print indented and human readable JSON @@ -69,7 +69,9 @@ narbeh@narbeh-xps:~/ssl-checker$ ./ssl_checker.py -H facebook.com [-] facebook.com Failed: [Errno 111] Connection refused -0 successful and 1 failed ++------------------------------------------------------+ +| Successful: 0 | Failed: 1 | Duration: 0:00:00.710470 | ++------------------------------------------------------+ narbeh@narbeh-xps:~/ssl-checker$ ./ssl_checker.py -H facebook.com -s localhost:9050 +-------------------+ @@ -88,7 +90,9 @@ narbeh@narbeh-xps:~/ssl-checker$ ./ssl_checker.py -H facebook.com -s localhost:9 Certificate algorithm: sha256WithRSAEncryption Expired: False -1 successful and 0 failed ++------------------------------------------------------+ +| Successful: 1 | Failed: 0 | Duration: 0:00:00.710470 | ++------------------------------------------------------+ ``` @@ -98,39 +102,48 @@ narbeh@narbeh-xps:~/ssl-checker$ ./ssl_checker.py -H facebook.com -s localhost:9 ## Example ``` -narbeh@narbeh-xps:~/ssl-checker$ ./ssl_checker.py -H narbeh.org google.com:443 -+-------------------+ -|Analyzing 2 host(s)| -+-------------------+ - - [+] narbeh.org - - Issued domain: narbeh.org +narbeh@narbeh-laptop:~/ssl-checker$ ./ssl_checker.py -H time.com github.com:443 ++---------------------+ +| Analyzing 2 host(s) | ++---------------------+ + [+] time.com + ------------- + Issued domain: time.com Issued to: None - Issued by: Let's Encrypt (US) - Valid from: 2018-04-21 - Valid to: 2018-07-20 (88 days left) - Validity days: 90 - Certificate S/N: 338163108483756707389368573553026254634358 + Issued by: Amazon (US) + Valid from: 2018-11-07 + Valid to: 2019-12-07 (159 days left) + Validity days: 395 + Certificate S/N: 10018094209647532371913518187860771165 + Certificate SHA1 FP: 64:C4:2E:AF:38:2A:28:64:A0:A8:B8:6B:02:05:86:1F:E7:F6:E5:FF Certificate version: 2 Certificate algorithm: sha256WithRSAEncryption Expired: False + Certificate SAN's: + \_ DNS:time.com + \_ DNS:*.time.com - [+] google.com - Issued domain: *.google.com - Issued to: Google Inc - Issued by: Google Inc (US) - Valid from: 2018-03-28 - Valid to: 2018-06-20 (58 days left) - Validity days: 83 - Certificate S/N: 2989116342670522968 + [+] github.com + --------------- + Issued domain: github.com + Issued to: GitHub, Inc. + Issued by: DigiCert Inc (US) + Valid from: 2018-05-08 + Valid to: 2020-06-03 (338 days left) + Validity days: 757 + Certificate S/N: 13324412563135569597699362973539517727 + Certificate SHA1 FP: CA:06:F5:6B:25:8B:7A:0D:4F:2B:05:47:09:39:47:86:51:15:19:84 Certificate version: 2 Certificate algorithm: sha256WithRSAEncryption Expired: False + Certificate SAN's: + \_ DNS:github.com + \_ DNS:www.github.com - -2 successful and 0 failed ++------------------------------------------------------+ +| Successful: 2 | Failed: 0 | Duration: 0:00:01.429145 | ++------------------------------------------------------+ ``` @@ -141,9 +154,9 @@ By passing `-a/--analyze` to the script, it will scan the certificate for securi ``` narbeh@narbeh-xps:~/ssl-checker$ ./ssl_checker.py -j -p -H narbeh.org:443 -a -+-------------------+ -|Analyzing 2 host(s)| -+-------------------+ ++---------------------+ +| Analyzing 1 host(s) | ++---------------------+ Warning: -a/--analyze is enabled. It takes more time... @@ -167,8 +180,9 @@ Warning: -a/--analyze is enabled. It takes more time... Drown vulnerability: False Expired: False - -1 successful and 0 failed ++------------------------------------------------------+ +| Successful: 1 | Failed: 0 | Duration: 0:00:01.429145 | ++------------------------------------------------------+ ``` diff --git a/ssl_checker.py b/ssl_checker.py index 7dee0a6..9606114 100755 --- a/ssl_checker.py +++ b/ssl_checker.py @@ -64,20 +64,20 @@ def analyze_ssl(host, context): api_url = 'https://api.ssllabs.com/api/v3/' while True: main_request = loads(urlopen(api_url + 'analyze?host={}'.format(host)).read().decode('utf-8')) - if main_request['status'] == 'DNS': - print('Analyzing the security of {}. Please wait...'.format(host)) + if main_request['status'] in ('DNS', 'IN_PROGRESS'): sleep(5) continue - if main_request['status'] == 'IN_PROGRESS': - # We can find a way to show the progress - sleep(5) - pass elif main_request['status'] == 'READY': break - context[host]['grade'] = main_request['endpoints'][0]['grade'] - endpoint_data = loads(urlopen(api_url + 'getEndpointData?host={}&s={}'.format(host, main_request['endpoints'][0]['ipAddress'])).read().decode('utf-8')) + endpoint_data = loads(urlopen(api_url + 'getEndpointData?host={}&s={}'.format( + host, main_request['endpoints'][0]['ipAddress'])).read().decode('utf-8')) + # if the certificate is invalid + if endpoint_data['statusMessage'] == 'Certificate not valid for domain name': + return context + + context[host]['grade'] = main_request['endpoints'][0]['grade'] context[host]['poodle_vuln'] = endpoint_data['details']['poodle'] context[host]['heartbleed_vuln'] = endpoint_data['details']['heartbleed'] context[host]['heartbeat_vuln'] = endpoint_data['details']['heartbeat'] @@ -117,7 +117,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_sha1'] = cert.digest('sha1').decode() context['cert_alg'] = cert.get_signature_algorithm().decode() context['cert_ver'] = cert.get_version() context['cert_sans'] = get_cert_sans(cert) @@ -143,7 +143,7 @@ def print_status(host, context, analyze=False): """Print all the usefull info about host.""" days_left = (datetime.strptime(context[host]['valid_till'], '%Y-%m-%d') - datetime.now()).days - print('\t{}[+]{} {}\n'.format(Clr.GREEN, Clr.RST, host)) + print('\t{}[+]{} {}\n\t{}'.format(Clr.GREEN, Clr.RST, host, '-' * (len(host) + 5))) print('\t\tIssued domain: {}'.format(context[host]['issued_to'])) print('\t\tIssued to: {}'.format(context[host]['issued_o'])) print('\t\tIssued by: {} ({})'.format(context[host]['issuer_o'], context[host]['issuer_c'])) @@ -155,7 +155,7 @@ def print_status(host, context, analyze=False): print('\t\tCertificate version: {}'.format(context[host]['cert_ver'])) print('\t\tCertificate algorithm: {}'.format(context[host]['cert_alg'])) - if analyze: + if analyze and context.get('grade', False): # If analyze part fails print('\t\tCertificate grade: {}'.format(context[host]['grade'])) print('\t\tPoodle vulnerability: {}'.format(context[host]['poodle_vuln'])) print('\t\tHeartbleed vulnerability: {}'.format(context[host]['heartbleed_vuln'])) @@ -177,6 +177,7 @@ def show_result(user_args): """Get the context.""" context = {} failed_cnt = 0 + start_time = datetime.now() hosts = user_args.hosts if not user_args.json_true: @@ -211,7 +212,8 @@ def show_result(user_args): sys.exit(1) if not user_args.json_true: - border_msg(' {} successful and {} failed '.format(len(hosts) - failed_cnt, failed_cnt)) + border_msg(' Successful: {} | Failed: {} | Duration: {} '.format( + len(hosts) - failed_cnt, failed_cnt, datetime.now() - start_time)) # CSV export if -c/--csv is specified if user_args.csv_enabled: @@ -230,7 +232,7 @@ def export_csv(context, filename): """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 = DictWriter(csv_file, list(context.items())[0][1].keys()) csv_writer.writeheader() for host in context.keys(): csv_writer.writerow(context[host])