mirror of
https://github.com/mozilla/cipherscan.git
synced 2024-11-22 14:23:41 +01:00
improved configuration analysis
This commit is contained in:
parent
2858ef8116
commit
405b104583
337
analyze.py
Normal file → Executable file
337
analyze.py
Normal file → Executable file
@ -5,59 +5,218 @@
|
|||||||
#
|
#
|
||||||
# Contributor: Julien Vehent jvehent@mozilla.com [:ulfr]
|
# Contributor: Julien Vehent jvehent@mozilla.com [:ulfr]
|
||||||
|
|
||||||
import fileinput
|
import sys, os, json, subprocess, logging, argparse
|
||||||
import sys
|
from collections import namedtuple
|
||||||
import json
|
|
||||||
import subprocess
|
|
||||||
from collections import defaultdict
|
|
||||||
|
|
||||||
|
# is_fubar assumes that a configuration is not completely messed up
|
||||||
|
# and looks for reasons to think otherwise. it will return True if
|
||||||
|
# it finds one of these reason
|
||||||
def is_fubar(results):
|
def is_fubar(results):
|
||||||
|
fubar = False
|
||||||
fubar_ciphers = set(all_ciphers) - set(old_ciphers)
|
fubar_ciphers = set(all_ciphers) - set(old_ciphers)
|
||||||
for conn in results['ciphersuite']:
|
for conn in results['ciphersuite']:
|
||||||
if conn['cipher'] in fubar_ciphers:
|
if conn['cipher'] in fubar_ciphers:
|
||||||
return True
|
logging.debug(conn['cipher'] + ' is in the list of fubar ciphers')
|
||||||
|
fubar = True
|
||||||
if 'SSLv2' in conn['protocols']:
|
if 'SSLv2' in conn['protocols']:
|
||||||
return True
|
logging.debug('SSLv2 is in the list of fubar protocols')
|
||||||
|
fubar = True
|
||||||
if conn['pubkey'] < 2048:
|
if conn['pubkey'] < 2048:
|
||||||
return True
|
logging.debug(conn['pubkey'] + ' is a fubar pubkey size')
|
||||||
return False
|
fubar = True
|
||||||
|
if 'md5WithRSAEncryption' in conn['sigalg']:
|
||||||
|
logging.debug(conn['sigalg']+ ' is a fubar cert signature')
|
||||||
|
fubar = True
|
||||||
|
if conn['trusted'] == 'False':
|
||||||
|
logging.debug('The certificate is not trusted, which is quite fubar')
|
||||||
|
fubar = True
|
||||||
|
return fubar
|
||||||
|
|
||||||
|
# is_old assumes a configuration *is* old, and will return False if
|
||||||
|
# the parameters of an old configuration are not found. Those parameters
|
||||||
|
# are defined in https://wiki.mozilla.org/Security/Server_Side_TLS#Old_backward_compatibility
|
||||||
def is_old(results):
|
def is_old(results):
|
||||||
|
lvl = 'old'
|
||||||
|
old = True
|
||||||
|
has_sslv3 = False
|
||||||
|
has_3des = False
|
||||||
|
has_sha1 = True
|
||||||
|
has_dhparam = True
|
||||||
|
has_ocsp = True
|
||||||
|
all_proto = []
|
||||||
for conn in results['ciphersuite']:
|
for conn in results['ciphersuite']:
|
||||||
|
# flag unwanted ciphers
|
||||||
if conn['cipher'] not in old_ciphers:
|
if conn['cipher'] not in old_ciphers:
|
||||||
return False
|
logging.debug(conn['cipher'] + ' is not in the list of old ciphers')
|
||||||
if 'SSLv3' not in conn['protocols']:
|
failures[lvl].append("remove cipher " + conn['cipher'])
|
||||||
return False
|
old = False
|
||||||
|
# verify required 3des cipher is present
|
||||||
|
if conn['cipher'] == 'DES-CBC3-SHA':
|
||||||
|
has_3des = True
|
||||||
|
# verify required ssl3 protocol is present
|
||||||
|
if 'SSLv3' in conn['protocols']:
|
||||||
|
has_sslv3 = True
|
||||||
|
for proto in conn['protocols']:
|
||||||
|
if proto not in all_proto:
|
||||||
|
all_proto.append(proto)
|
||||||
|
# verify required sha1 signature is used
|
||||||
if 'sha1WithRSAEncryption' not in conn['sigalg']:
|
if 'sha1WithRSAEncryption' not in conn['sigalg']:
|
||||||
return False
|
logging.debug(conn['sigalg'][0] + ' is a not an old signature')
|
||||||
return True
|
old = False
|
||||||
|
has_sha1 = False
|
||||||
|
# verify required pfs parameter is used
|
||||||
|
if conn['cipher'][0:2] == 'DHE':
|
||||||
|
if conn['pfs'] != 'DH,1024bits':
|
||||||
|
logging.debug(conn['pfs']+ ' is not a good DH parameters for the old configuration')
|
||||||
|
old = False
|
||||||
|
has_dhparam = False
|
||||||
|
if conn['ocsp_stapling'] == 'False':
|
||||||
|
has_ocsp = False
|
||||||
|
missing_ciphers = set(old_ciphers) - set(all_ciphers)
|
||||||
|
for cipher in missing_ciphers:
|
||||||
|
logging.debug("missing cipher " + cipher + " wanted in the " + lvl + " configuration")
|
||||||
|
failures[lvl].append('add cipher ' + cipher)
|
||||||
|
extra_proto = set(all_proto) - set(['SSLv3', 'TLSv1', 'TLSv1.1', 'TLSv1.2'])
|
||||||
|
for proto in extra_proto:
|
||||||
|
logging.debug("found protocol not wanted in the old configuration:" + proto)
|
||||||
|
failures[lvl].append('disable ' + proto)
|
||||||
|
modern = False
|
||||||
|
missing_proto = set(['SSLv3', 'TLSv1', 'TLSv1.1', 'TLSv1.2']) - set(all_proto)
|
||||||
|
for proto in missing_proto:
|
||||||
|
logging.debug("missing protocol wanted in the old configuration:" + proto)
|
||||||
|
failures[lvl].append('enable ' + proto)
|
||||||
|
if not has_sslv3:
|
||||||
|
logging.debug("SSLv3 is not supported and required by the old configuration")
|
||||||
|
old = False
|
||||||
|
if not has_3des:
|
||||||
|
logging.debug("DES-CBC3-SHA is not supported and required by the old configuration")
|
||||||
|
failures[lvl].append("add cipher DES-CBC3-SHA")
|
||||||
|
old = False
|
||||||
|
if not has_sha1:
|
||||||
|
failures[lvl].append("use a certificate with sha1WithRSAEncryption signature")
|
||||||
|
old = False
|
||||||
|
if not has_dhparam:
|
||||||
|
failures[lvl].append("use a DH parameter of 1024 bits")
|
||||||
|
old = False
|
||||||
|
if not has_ocsp:
|
||||||
|
failures[lvl].append("enable OCSP Stapling")
|
||||||
|
return old
|
||||||
|
|
||||||
|
# is_intermediate is similar to is_old but for intermediate configuration from
|
||||||
|
# https://wiki.mozilla.org/Security/Server_Side_TLS#Intermediate_compatibility_.28default.29
|
||||||
def is_intermediate(results):
|
def is_intermediate(results):
|
||||||
|
lvl = 'intermediate'
|
||||||
|
inter = True
|
||||||
|
has_tls1 = False
|
||||||
|
has_aes = False
|
||||||
|
has_dhparam = True
|
||||||
|
has_sha256 = True
|
||||||
|
has_ocsp = True
|
||||||
|
all_proto = []
|
||||||
for conn in results['ciphersuite']:
|
for conn in results['ciphersuite']:
|
||||||
if conn['cipher'] not in intermediate_ciphers:
|
if conn['cipher'] not in intermediate_ciphers:
|
||||||
return False
|
logging.debug(conn['cipher'] + ' is not in the list of intermediate ciphers')
|
||||||
if len(set(conn['protocols']) - set(['TLSv1', 'TLSv1.1', 'TLSv1.2'])) > 0:
|
failures[lvl].append("remove cipher " + conn['cipher'])
|
||||||
return False
|
inter = False
|
||||||
return True
|
if conn['cipher'] == 'AES128-SHA':
|
||||||
|
has_aes = True
|
||||||
|
for proto in conn['protocols']:
|
||||||
|
if proto not in all_proto:
|
||||||
|
all_proto.append(proto)
|
||||||
|
if 'TLSv1' in conn['protocols']:
|
||||||
|
has_tls1 = True
|
||||||
|
if 'sha256WithRSAEncryption' not in conn['sigalg']:
|
||||||
|
logging.debug(conn['sigalg'][0] + ' is a not an intermediate signature')
|
||||||
|
inter = False
|
||||||
|
has_sha256 = False
|
||||||
|
if conn['cipher'][0:2] == 'DHE':
|
||||||
|
if conn['pfs'] != 'DH,2048bits':
|
||||||
|
logging.debug(conn['pfs']+ ' is not a good DH parameters for the old configuration')
|
||||||
|
inter = False
|
||||||
|
has_dhparam = False
|
||||||
|
if conn['ocsp_stapling'] == 'False':
|
||||||
|
has_ocsp = False
|
||||||
|
extra_proto = set(all_proto) - set(['TLSv1', 'TLSv1.1', 'TLSv1.2'])
|
||||||
|
for proto in extra_proto:
|
||||||
|
logging.debug("found protocol not wanted in the intermediate configuration:" + proto)
|
||||||
|
failures[lvl].append('disable ' + proto)
|
||||||
|
modern = False
|
||||||
|
missing_proto = set(['TLSv1', 'TLSv1.1', 'TLSv1.2']) - set(all_proto)
|
||||||
|
for proto in missing_proto:
|
||||||
|
logging.debug("missing protocol wanted in the intermediate configuration:" + proto)
|
||||||
|
failures[lvl].append('enable ' + proto)
|
||||||
|
if not has_tls1:
|
||||||
|
logging.debug("TLSv1 is not supported and required by the old configuration")
|
||||||
|
inter = False
|
||||||
|
if not has_aes:
|
||||||
|
logging.debug("AES128-SHA is not supported and required by the intermediate configuration")
|
||||||
|
failures[lvl].append("add cipher AES128-SHA")
|
||||||
|
inter = False
|
||||||
|
if not has_sha256:
|
||||||
|
failures[lvl].append("use a certificate with sha256WithRSAEncryption signature")
|
||||||
|
inter = False
|
||||||
|
if not has_dhparam:
|
||||||
|
failures[lvl].append("use a DH parameter of 2048 bits")
|
||||||
|
inter = False
|
||||||
|
if not has_ocsp:
|
||||||
|
failures[lvl].append("enable OCSP Stapling")
|
||||||
|
return inter
|
||||||
|
|
||||||
|
# is_modern is similar to is_old but for modern configuration from
|
||||||
|
# https://wiki.mozilla.org/Security/Server_Side_TLS#Modern_compatibility
|
||||||
def is_modern(results):
|
def is_modern(results):
|
||||||
|
lvl = 'modern'
|
||||||
|
modern = True
|
||||||
|
has_dhparam = True
|
||||||
|
has_sha256 = True
|
||||||
|
has_ocsp = True
|
||||||
|
all_proto = []
|
||||||
for conn in results['ciphersuite']:
|
for conn in results['ciphersuite']:
|
||||||
if conn['cipher'] not in modern_ciphers:
|
if conn['cipher'] not in modern_ciphers:
|
||||||
errors["modern"]["ciphers"].append(conn['cipher'])
|
logging.debug(conn['cipher'] + ' is not in the list of modern ciphers')
|
||||||
return False
|
failures[lvl].append("remove cipher " + conn['cipher'])
|
||||||
if len(set(conn['protocols']) - set(['TLSv1.1', 'TLSv1.2'])) > 0:
|
modern = False
|
||||||
# deprecated protocols are supported
|
for proto in conn['protocols']:
|
||||||
return False
|
if proto not in all_proto:
|
||||||
return True
|
all_proto.append(proto)
|
||||||
|
if 'sha256WithRSAEncryption' not in conn['sigalg']:
|
||||||
|
logging.debug(conn['sigalg'][0] + ' is a not an intermediate signature')
|
||||||
|
inter = False
|
||||||
|
has_sha256 = False
|
||||||
|
if conn['cipher'][0:2] == 'DHE':
|
||||||
|
if conn['pfs'] != 'DH,2048bits':
|
||||||
|
logging.debug(conn['pfs']+ ' is not a good DH parameters for the old configuration')
|
||||||
|
inter = False
|
||||||
|
has_dhparam = False
|
||||||
|
if conn['ocsp_stapling'] == 'False':
|
||||||
|
has_ocsp = False
|
||||||
|
extra_proto = set(all_proto) - set(['TLSv1.1', 'TLSv1.2'])
|
||||||
|
for proto in extra_proto:
|
||||||
|
logging.debug("found protocol not wanted in the modern configuration:" + proto)
|
||||||
|
failures[lvl].append('disable ' + proto)
|
||||||
|
modern = False
|
||||||
|
missing_proto = set(['TLSv1.1', 'TLSv1.2']) - set(all_proto)
|
||||||
|
for proto in missing_proto:
|
||||||
|
logging.debug("missing protocol wanted in the modern configuration:" + proto)
|
||||||
|
failures[lvl].append('enable ' + proto)
|
||||||
|
if not has_sha256:
|
||||||
|
failures[lvl].append("use a certificate with sha256WithRSAEncryption signature")
|
||||||
|
modern = False
|
||||||
|
if not has_dhparam:
|
||||||
|
failures[lvl].append("use a DH parameter of 2048 bits")
|
||||||
|
modern = False
|
||||||
|
if not has_ocsp:
|
||||||
|
failures[lvl].append("enable OCSP Stapling")
|
||||||
|
return modern
|
||||||
|
|
||||||
def is_ordered(results, ciphersuite):
|
def is_ordered(results, ciphersuite):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def evaluate(results):
|
def evaluate_all(results):
|
||||||
status = "obscure unknown ssl"
|
status = "obscure unknown ssl"
|
||||||
|
|
||||||
if len(results['ciphersuite']) == 0:
|
if len(results['ciphersuite']) == 0:
|
||||||
status = "no ssl"
|
return "no ssl"
|
||||||
|
|
||||||
if is_modern(results):
|
if is_modern(results):
|
||||||
if is_ordered(results, modern_ciphers):
|
if is_ordered(results, modern_ciphers):
|
||||||
@ -78,44 +237,122 @@ def evaluate(results):
|
|||||||
status = "old ssl with bad ordering"
|
status = "old ssl with bad ordering"
|
||||||
|
|
||||||
if is_fubar(results):
|
if is_fubar(results):
|
||||||
status = "fubar ssl"
|
return "fubar ssl"
|
||||||
|
|
||||||
return status
|
return status
|
||||||
|
|
||||||
def process_results(data):
|
def process_results(data, level=None):
|
||||||
results = dict()
|
results = dict()
|
||||||
|
# initialize the failures struct
|
||||||
|
global failures
|
||||||
|
failures = dict()
|
||||||
|
failures['old'] = []
|
||||||
|
failures['intermediate'] = []
|
||||||
|
failures['modern'] = []
|
||||||
try:
|
try:
|
||||||
results = json.loads(data)
|
results = json.loads(data)
|
||||||
except ValueError, e:
|
except ValueError, e:
|
||||||
print("invalid json data")
|
print("invalid json data")
|
||||||
try:
|
try:
|
||||||
if results:
|
if results:
|
||||||
print(results['target'] + " has " + evaluate(results))
|
print(results['target'] + " has " + evaluate_all(results))
|
||||||
except TypeError, e:
|
except TypeError, e:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def main():
|
# print failures
|
||||||
global all_ciphers, old_ciphers, intermediate_ciphers, modern_ciphers, errors
|
if level:
|
||||||
all_ciphers = subprocess.check_output(['./openssl', 'ciphers', all_ciphersuite]).rstrip().split(':')
|
if len(failures[level]) > 0:
|
||||||
old_ciphers = subprocess.check_output(['./openssl', 'ciphers', old_ciphersuite]).rstrip().split(':')
|
print("\nFailed to pass " + level + " level. The following items are failing:")
|
||||||
intermediate_ciphers = subprocess.check_output(['./openssl', 'ciphers', intermediate_ciphersuite]).rstrip().split(':')
|
for failure in failures[level]:
|
||||||
modern_ciphers = subprocess.check_output(['./openssl', 'ciphers', modern_ciphersuite]).rstrip().split(':')
|
print("* " + failure)
|
||||||
if len(sys.argv) > 1:
|
|
||||||
# evaluate target specified as argument
|
|
||||||
data = subprocess.check_output(['./cipherscan', '-j', sys.argv[1]])
|
|
||||||
process_results(data)
|
|
||||||
else:
|
else:
|
||||||
# take input from stdin
|
for lvl in ['old', 'intermediate', 'modern']:
|
||||||
for data in fileinput.input():
|
if len(failures[lvl]) > 0:
|
||||||
if data:
|
print("\nFailed to pass " + lvl + " level. The following items are failing:")
|
||||||
process_results(data)
|
for failure in failures[lvl]:
|
||||||
print errors
|
print("* " + failure)
|
||||||
# from https://wiki.mozilla.org/Security/Server_Side_TLS
|
|
||||||
all_ciphersuite = "ALL:COMPLEMENTOFALL:+aRSA"
|
def build_ciphers_lists():
|
||||||
old_ciphersuite = "ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128:AES256:AES:DES-CBC3-SHA:HIGH:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA"
|
global all_ciphers, old_ciphers, intermediate_ciphers, modern_ciphers, errors
|
||||||
intermediate_ciphersuite = "ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128:AES256:AES:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA"
|
# from https://wiki.mozilla.org/Security/Server_Side_TLS
|
||||||
modern_ciphersuite = "ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!3DES:!MD5:!PSK"
|
allC = 'ALL:COMPLEMENTOFALL:+aRSA'
|
||||||
errors = defaultdict(str)
|
oldC = 'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-S' \
|
||||||
|
'HA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM' \
|
||||||
|
'-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-' \
|
||||||
|
'AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA' \
|
||||||
|
'384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AE' \
|
||||||
|
'S128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-' \
|
||||||
|
'AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128:AES256:AES:DES-CBC3-SHA:HI' \
|
||||||
|
'GH:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-R' \
|
||||||
|
'SA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA'
|
||||||
|
intC = 'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-S' \
|
||||||
|
'HA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM' \
|
||||||
|
'-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-' \
|
||||||
|
'AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA' \
|
||||||
|
'384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AE' \
|
||||||
|
'S128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-' \
|
||||||
|
'AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128:AES256:AES:CAMELLIA!aNULL:' \
|
||||||
|
'!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC' \
|
||||||
|
'3-SHA:!KRB5-DES-CBC3-SHA'
|
||||||
|
modernC = 'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-S' \
|
||||||
|
'HA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM' \
|
||||||
|
'-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-' \
|
||||||
|
'AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA' \
|
||||||
|
'384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AE' \
|
||||||
|
'S128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-' \
|
||||||
|
'AES256-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!3DES:!MD5:!PSK'
|
||||||
|
logging.debug('Loading all ciphers: ' + allC)
|
||||||
|
all_ciphers = subprocess.check_output(
|
||||||
|
['./openssl', 'ciphers', allC]).rstrip().split(':')
|
||||||
|
logging.debug('Loading old ciphers: ' + oldC)
|
||||||
|
old_ciphers = subprocess.check_output(
|
||||||
|
['./openssl', 'ciphers', oldC]).rstrip().split(':')
|
||||||
|
logging.debug('Loading intermediate ciphers: ' + intC)
|
||||||
|
intermediate_ciphers = subprocess.check_output(
|
||||||
|
['./openssl', 'ciphers', intC]).rstrip().split(':')
|
||||||
|
logging.debug('Loading modern ciphers: ' + modernC)
|
||||||
|
modern_ciphers = subprocess.check_output(
|
||||||
|
['./openssl', 'ciphers', modernC]).rstrip().split(':')
|
||||||
|
|
||||||
|
def main():
|
||||||
|
parser = argparse.ArgumentParser(
|
||||||
|
description='Analyze cipherscan results and provides guidelines to improve configuration.',
|
||||||
|
usage='\n* Analyze a single target, invokes cipherscan: $ ./analyze.py -t [target]' \
|
||||||
|
'\n* Evaluate json results passed through stdin: $ python analyze.py < target_results.json' \
|
||||||
|
'\nexample: ./analyze.py mozilla.org',
|
||||||
|
epilog='Julien Vehent [:ulfr] - 2014')
|
||||||
|
parser.add_argument('-d', dest='debug', action='store_true',
|
||||||
|
help='debug output')
|
||||||
|
parser.add_argument('infile', nargs='?', type=argparse.FileType('r'),
|
||||||
|
default=sys.stdin, help='cipherscan json results')
|
||||||
|
parser.add_argument('outfile', nargs='?', type=argparse.FileType('w'),
|
||||||
|
default=sys.stdout, help='json formatted analysis')
|
||||||
|
parser.add_argument('-l', dest='level',
|
||||||
|
help='target configuration level [old, intermediate, modern]')
|
||||||
|
parser.add_argument('-t', dest='target',
|
||||||
|
help='analyze a <target>, invokes cipherscan')
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
if args.debug:
|
||||||
|
logging.basicConfig(stream=sys.stderr, level=logging.DEBUG)
|
||||||
|
else:
|
||||||
|
logging.basicConfig(stream=sys.stderr, level=logging.INFO)
|
||||||
|
|
||||||
|
build_ciphers_lists()
|
||||||
|
|
||||||
|
if args.target:
|
||||||
|
# evaluate target specified as argument
|
||||||
|
logging.debug('Invoking cipherscan with target: ' + args.target)
|
||||||
|
data = subprocess.check_output(['./cipherscan', '-j', args.target])
|
||||||
|
process_results(data, args.level)
|
||||||
|
else:
|
||||||
|
if os.fstat(args.infile.fileno()).st_size < 2:
|
||||||
|
logging.error("invalid input file")
|
||||||
|
parser.print_help()
|
||||||
|
sys.exit(1)
|
||||||
|
data = args.infile.readline()
|
||||||
|
logging.debug('Evaluating results from stdin: ' + data)
|
||||||
|
process_results(data, args.level)
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
main()
|
main()
|
||||||
|
Loading…
Reference in New Issue
Block a user