From 43b04693c5ab73265a7f070614423c8ab62f5bdd Mon Sep 17 00:00:00 2001 From: yzninja Date: Tue, 8 Sep 2015 21:11:35 +1000 Subject: [PATCH] The sunsetsha1 handler was moved to the data/ssl.py file. It was modified to be a passive data handler. --- docs/gce/_update_dev.sh | 66 +++++++++++ .../handlers/connection/__init__.py | 2 +- .../mitm/connection/handlers/data/ssl.py | 106 ++++++++++++++++++ 3 files changed, 173 insertions(+), 1 deletion(-) create mode 100644 docs/gce/_update_dev.sh diff --git a/docs/gce/_update_dev.sh b/docs/gce/_update_dev.sh new file mode 100644 index 00000000..2c2befae --- /dev/null +++ b/docs/gce/_update_dev.sh @@ -0,0 +1,66 @@ +#!/bin/sh + +set -e + +# Directory paths used for nogotofail. +INSTALL_DIR=/opt/nogotofail +CONFIG_DIR=/etc/nogotofail +LOG_DIR=/var/log/nogotofail + +# Stop the nogotofail-mitm and other associated services if they're running. +if (ps ax | grep -v grep | grep nogotofail-mitm > /dev/null) then +sudo /etc/init.d/nogotofail-mitm stop +fi +if (ps ax | grep -v grep | grep dnsmasq > /dev/null) then +sudo /etc/init.d/dnsmasq stop +fi +if (ps ax | grep -v grep | grep openvpn > /dev/null) then +sudo /etc/init.d/openvpn stop +fi +# Remove Python files and compiled versions i.e. *.py and *.pyc files. +# TODO: Find a more elegant method for uninstalling a Python program. +#rm -rf $INSTALL_DIR +#rm -rf $CONFIG_DIR +#rm -rf $LOG_DIR +find $INSTALL_DIR -type f -name '*.py' -delete +find $INSTALL_DIR -type f -name '*.pyc' -delete + +# Install toolchain dependencies +sudo apt-get update +sudo apt-get -y upgrade +#sudo apt-get -y install patch make gcc libssl-dev python-openssl liblzo2-dev libpam-dev + +# Install OpenVPN and dnsmasq +#sudo apt-get -y install openvpn dnsmasq + +# Build and install a patched version of OpenVPN. +# This is needed because the OpenVPN 2.3.x still does not properly handle +# floating clients (those whose source IP address as seen by the server changes +# from time to time) which is a regular occurrence in the mobile world. +# OpenVPN 2.4 might ship with proper support out of the box. In that case, this +# kludge can be removed. +#./build_openvpn.sh + +# Build and install a patched version of dnsmasq. +# This is needed because GCE does not support IPv6. We thus blackhole IPv6 +# traffic from clients so that they are forced to use IPv4. However, default +# DNS servers will still resolve hostnames to IPv6 addresses causing clients to +# attempt IPv6. To avoid clients attempting IPv6, we run a patched dnsmasq DNS +# server which empties AAAA records thus causing clients to go for A records +# which provide IPv4 addresses. +#./build_dnsmasq.sh + +# Set up OpenVPN server +#sudo ./setup_openvpn.sh + +# Set up the MiTM daemons +sudo ./setup_mitm.sh + +# Move dev mitm.conf file into /etc/nogotofail directory +sudo cp /home/michael/noseyp_setup/ngtf_mitm.conf /etc/nogotofail/mitm.conf + +# Restart all the relevant daemons +sudo /etc/init.d/dnsmasq start +sudo /etc/init.d/openvpn start +#sudo /etc/init.d/nogotofail-mitm stop || true +sudo /etc/init.d/nogotofail-mitm start diff --git a/nogotofail/mitm/connection/handlers/connection/__init__.py b/nogotofail/mitm/connection/handlers/connection/__init__.py index 63142b34..6310393b 100644 --- a/nogotofail/mitm/connection/handlers/connection/__init__.py +++ b/nogotofail/mitm/connection/handlers/connection/__init__.py @@ -26,4 +26,4 @@ from droptls import DropTLS from ccs import EarlyCCS from serverkeyreplace import ServerKeyReplacementMITM -from sunsetsha1 import SunsetSHA1 +# from sunsetsha1 import SunsetSHA1 diff --git a/nogotofail/mitm/connection/handlers/data/ssl.py b/nogotofail/mitm/connection/handlers/data/ssl.py index b3df9dcc..006abeba 100644 --- a/nogotofail/mitm/connection/handlers/data/ssl.py +++ b/nogotofail/mitm/connection/handlers/data/ssl.py @@ -19,7 +19,11 @@ from nogotofail.mitm.connection.handlers.data import DataHandler from nogotofail.mitm.connection.handlers.store import handler from nogotofail.mitm.event import connection +from nogotofail.mitm import util from nogotofail.mitm.util import ssl2, tls, vuln +from nogotofail.mitm.util.tls.types import HandshakeMessage, TlsRecord +from datetime import datetime + @handler.passive(handlers) class InsecureCipherDetectionHandler(DataHandler): @@ -90,3 +94,105 @@ def on_ssl(self, client_hello): self.log(logging.ERROR, "Client enabled SSLv3 protocol without TLS_FALLBACK_SCSV") self.log_attack_event(data="SSLv3") + + +@handler.passive(handlers) +class SunsetSHA1(DataHandler): + name = "sunsetsha1" + description = ( + "Detects TLS certificates using the SHA-1 signature algorithm that " + "expire during of after the Google Chrome sunset period.") + + buffer = "" + ssl = False + + def on_ssl(self, client_hello): + self.ssl = True + return True + + def on_response(self, response): + if not self.ssl: + return response + response = self.buffer + response + self.buffer = "" + CRT_DATE_FORMAT = "%Y%m%d%H%M%SZ" + try: + index = 0 + while index < len(response): + record, size = TlsRecord.from_stream(response[index:]) + for i, message in enumerate(record.messages): + # Check for Certificate message + if (isinstance(message, tls.types.HandshakeMessage) and + message.type == HandshakeMessage.TYPE.CERTIFICATE): + certificate = message.obj + # For each certificate in the certificate chain check + # the signature algorithm + for a_certificate in certificate.certificates: + self.log(logging.DEBUG, "Found a certificate in chain str - ." + + a_certificate) + """ + crt_signature_algorithm = \ + a_certificate.get_signature_algorithm() + if ("sha1" in crt_signature_algorithm): + crt_subject = a_certificate.get_subject() + crt_CN = crt_subject.CN + crt_not_before = a_certificate.get_notBefore() + crt_not_after = a_certificate.get_notAfter() + debug_message = \ + ["Certicate using SHA-1 with attributes - CN \"", + crt_CN, "\", notBefore \"", crt_not_before, + "\", notAfter \"", crt_not_after, + "\", signature_algorithm \"", + crt_signature_algorithm, "\""] + self.log(logging.DEBUG, "".join(debug_message)) + crt_not_after = datetime.strptime(crt_not_after, + CRT_DATE_FORMAT) + self._alert_on_sunset_sha1(crt_not_after, crt_CN) + """ + index += size + except ValueError: + # Failed to parse TLS, this is probably due to a short read of a TLS + # record. Buffer the response to try and get more data. + self.buffer = response + # But don't buffer too much, give up after 16k. + if len(self.buffer) > 2**14: + response = self.buffer + self.buffer = "" + return self.buffer + return "" + return response + + def _alert_on_sunset_sha1(self, crt_not_after, crt_CN): + """ Raises an alert if a certificate signature algorithm uses SHA-1 and + it expires during or after the Google Chrome sunset period. + """ + # SHA-1 sunset dates based on Google Chrome dates published dates. See + # http://googleonlinesecurity.blogspot.com/2014/09/gradually-sunsetting-sha-1.html + sunset_critical_date = datetime.strptime("31-12-2016", "%d-%m-%Y") + sunset_error_date = datetime.strptime("30-06-2016", "%d-%m-%Y") + sunset_warning_date = datetime.strptime("31-12-2015", "%d-%m-%Y") + + if (crt_not_after > sunset_critical_date): + log_message = \ + ["Certificate with CN ", crt_CN, + " uses SHA-1 and expires after 31 Dec 2016"] + self.log(logging.CRITICAL, "".join(log_message)) + self.log_event(logging.CRITICAL, connection.AttackEvent( + self.connection, self.name, True, "")) + self.connection.vuln_notify(util.vuln.VULN_SUNSET_SHA1) + elif (crt_not_after > sunset_error_date): + log_message = \ + ["Certificate with CN ", crt_CN, + " uses SHA-1 and expires after 30 Jun 2016"] + self.log(logging.ERROR, "".join(log_message)) + self.log_event(logging.ERROR, connection.AttackEvent( + self.connection, self.name, True, "")) + self.connection.vuln_notify(util.vuln.VULN_SUNSET_SHA1) + elif (crt_not_after > sunset_warning_date): + log_message = \ + ["Certificate with CN ", crt_CN, + " uses SHA-1 and expires after 31 Dec 2015"] + self.log(logging.WARNING, "".join(log_message)) + self.log_event(logging.WARNING, connection.AttackEvent( + self.connection, self.name, True, "")) + self.connection.vuln_notify(util.vuln.VULN_SUNSET_SHA1)