mirror of
https://github.com/mozilla/cipherscan.git
synced 2024-11-05 07:23:42 +01:00
122 lines
5.6 KiB
Python
122 lines
5.6 KiB
Python
|
#!/usr/bin/env python
|
||
|
# This Source Code Form is subject to the terms of the Mozilla Public
|
||
|
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||
|
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||
|
#
|
||
|
# Contributor: Julien Vehent jvehent@mozilla.com [:ulfr]
|
||
|
|
||
|
import fileinput
|
||
|
import sys
|
||
|
import json
|
||
|
import subprocess
|
||
|
from collections import defaultdict
|
||
|
|
||
|
def is_fubar(results):
|
||
|
fubar_ciphers = set(all_ciphers) - set(old_ciphers)
|
||
|
for conn in results['ciphersuite']:
|
||
|
if conn['cipher'] in fubar_ciphers:
|
||
|
return True
|
||
|
if 'SSLv2' in conn['protocols']:
|
||
|
return True
|
||
|
if conn['pubkey'] < 2048:
|
||
|
return True
|
||
|
return False
|
||
|
|
||
|
def is_old(results):
|
||
|
for conn in results['ciphersuite']:
|
||
|
if conn['cipher'] not in old_ciphers:
|
||
|
return False
|
||
|
if 'SSLv3' not in conn['protocols']:
|
||
|
return False
|
||
|
if 'sha1WithRSAEncryption' not in conn['sigalg']:
|
||
|
return False
|
||
|
return True
|
||
|
|
||
|
def is_intermediate(results):
|
||
|
for conn in results['ciphersuite']:
|
||
|
if conn['cipher'] not in intermediate_ciphers:
|
||
|
return False
|
||
|
if len(set(conn['protocols']) - set(['TLSv1', 'TLSv1.1', 'TLSv1.2'])) > 0:
|
||
|
return False
|
||
|
return True
|
||
|
|
||
|
def is_modern(results):
|
||
|
for conn in results['ciphersuite']:
|
||
|
if conn['cipher'] not in modern_ciphers:
|
||
|
errors["modern"]["ciphers"].append(conn['cipher'])
|
||
|
return False
|
||
|
if len(set(conn['protocols']) - set(['TLSv1.1', 'TLSv1.2'])) > 0:
|
||
|
# deprecated protocols are supported
|
||
|
return False
|
||
|
return True
|
||
|
|
||
|
def is_ordered(results, ciphersuite):
|
||
|
return True
|
||
|
|
||
|
def evaluate(results):
|
||
|
status = "obscure unknown ssl"
|
||
|
|
||
|
if len(results['ciphersuite']) == 0:
|
||
|
status = "no ssl"
|
||
|
|
||
|
if is_modern(results):
|
||
|
if is_ordered(results, modern_ciphers):
|
||
|
status = "modern tls"
|
||
|
else:
|
||
|
status = "modern tls with bad ordering"
|
||
|
|
||
|
if is_intermediate(results):
|
||
|
if is_ordered(results, intermediate_ciphers):
|
||
|
status = "intermediate tls"
|
||
|
else:
|
||
|
status = "intermediate tls with bad ordering"
|
||
|
|
||
|
if is_old(results):
|
||
|
if is_ordered(results, old_ciphers):
|
||
|
status = "old ssl"
|
||
|
else:
|
||
|
status = "old ssl with bad ordering"
|
||
|
|
||
|
if is_fubar(results):
|
||
|
status = "fubar ssl"
|
||
|
|
||
|
return status
|
||
|
|
||
|
def process_results(data):
|
||
|
results = dict()
|
||
|
try:
|
||
|
results = json.loads(data)
|
||
|
except ValueError, e:
|
||
|
print("invalid json data")
|
||
|
try:
|
||
|
if results:
|
||
|
print(results['target'] + " has " + evaluate(results))
|
||
|
except TypeError, e:
|
||
|
pass
|
||
|
|
||
|
def main():
|
||
|
global all_ciphers, old_ciphers, intermediate_ciphers, modern_ciphers, errors
|
||
|
all_ciphers = subprocess.check_output(['./openssl', 'ciphers', all_ciphersuite]).rstrip().split(':')
|
||
|
old_ciphers = subprocess.check_output(['./openssl', 'ciphers', old_ciphersuite]).rstrip().split(':')
|
||
|
intermediate_ciphers = subprocess.check_output(['./openssl', 'ciphers', intermediate_ciphersuite]).rstrip().split(':')
|
||
|
modern_ciphers = subprocess.check_output(['./openssl', 'ciphers', modern_ciphersuite]).rstrip().split(':')
|
||
|
if len(sys.argv) > 1:
|
||
|
# evaluate target specified as argument
|
||
|
data = subprocess.check_output(['./cipherscan', '-j', sys.argv[1]])
|
||
|
process_results(data)
|
||
|
else:
|
||
|
# take input from stdin
|
||
|
for data in fileinput.input():
|
||
|
if data:
|
||
|
process_results(data)
|
||
|
print errors
|
||
|
# from https://wiki.mozilla.org/Security/Server_Side_TLS
|
||
|
all_ciphersuite = "ALL:COMPLEMENTOFALL:+aRSA"
|
||
|
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"
|
||
|
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"
|
||
|
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"
|
||
|
errors = defaultdict(str)
|
||
|
|
||
|
if __name__ == "__main__":
|
||
|
main()
|