Skip to content
This repository has been archived by the owner on Dec 6, 2023. It is now read-only.

Commit

Permalink
Added new data handler "sunsetsha1"
Browse files Browse the repository at this point in the history
The new handler "sunsetsha1" detects TLS certificates using the SHA-1 signature algorithm which expire during or after the Google Chrome SHA-1 sunset period. See Google security blog entry:
https://googleonlinesecurity.blogspot.com.au/2014/09/gradually-sunsetting-sha-1.html.

Note. The signature algorithm is checked for the "leaf" and "intermediate" certificates in the chain.
  • Loading branch information
yzninja committed Jan 23, 2016
1 parent 7e998a5 commit eaa3fec
Show file tree
Hide file tree
Showing 5 changed files with 184 additions and 0 deletions.
66 changes: 66 additions & 0 deletions docs/gce/_update_dev.sh
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ public class AttacksPreferenceFragment extends PreferenceFragment {
BUNDLED_SUPPORTED_DATA_ATTACK_IDS.add("httpdetection");
BUNDLED_SUPPORTED_DATA_ATTACK_IDS.add("imagereplace");
BUNDLED_SUPPORTED_DATA_ATTACK_IDS.add("sslstrip");
BUNDLED_SUPPORTED_DATA_ATTACK_IDS.add("sunsetsha1");
}

private static final String ATTACK_ENABLED_PREF_KEY_PREFIX = "attack_enabled_";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,8 @@
<string name="vuln_sslstrip">Downgrade of HTTPS to HTTP</string>
<!-- Vulnerability: XMPP STARTTLS strip -->
<string name="vuln_xmppstarttlsstrip">Downgrade of STARTTLS-protected XMPP to cleartext</string>
<!-- Vulnerability: Certificate uses SHA-1 and expires after a Chrome sunset date -->
<string name="vuln_sunsetsha1">Certificate uses SHA-1 and expires after a Chrome sunset date</string>

<string name="notifications_pref_screen_title">Notifications</string>
<string name="vuln_notifications_enabled_pref_title">Notifications</string>
Expand Down Expand Up @@ -159,6 +161,8 @@
<string name="attack_summary_xmppauthdetection">XMPP credentials/auth token compromise</string>
<string name="attack_title_xmppstarttlsstrip">XMPP STARTTLS strip</string>
<string name="attack_summary_xmppstarttlsstrip">Downgrade of STARTTLS-protected XMPP to cleartext</string>
<string name="attack_title_sunsetsha1">Certificate uses SHA-1 and expires after sunset date</string>
<string name="attack_summary_sunsetsha1">Certificate uses SHA-1 and expires after a Chrome sunset date</string>

<string name="advanced_pref_screen_title">Advanced</string>

Expand Down
112 changes: 112 additions & 0 deletions nogotofail/mitm/connection/handlers/data/ssl.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,12 @@
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
from datetime import datetime
import OpenSSL.crypto


class _TlsRecordHandler(DataHandler):
"""Base class for a handler that acts on TlsRecords in a Tls connection.
Expand Down Expand Up @@ -172,3 +177,110 @@ 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(_TlsRecordHandler):
name = "sunsetsha1"
description = (
"Detects TLS certificates using the SHA-1 signature algorithm that "
"expire during of after the Google Chrome sunset period.")

# Certificate type constants
LEAF_CERT = 1
INTERMEDIATE_CERT = 2

def on_tls_response(self, record):
CRT_DATE_FORMAT = "%Y%m%d%H%M%SZ"
try:
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
crt_chain_length = len(certificate.certificates)
crt_chain_index = 0
# Loop through certificate chain starting from the leaf
# cert to the last intermediate cert (before the root).
for crt_byte_string in certificate.certificates[
:crt_chain_length-1]:
if (crt_chain_index == 0):
crt_type = self.LEAF_CERT
else:
crt_type = self.INTERMEDIATE_CERT
a_cert = OpenSSL.crypto.load_certificate(
OpenSSL.crypto.FILETYPE_ASN1, crt_byte_string)
# Check certificates in chain for SHA-1 sunset issue
crt_signature_algorithm = \
a_cert.get_signature_algorithm()
if ("sha1" in crt_signature_algorithm):
crt_CN = a_cert.get_subject().CN
crt_not_before = a_cert.get_notBefore()
crt_not_after = a_cert.get_notAfter()
debug_message = \
["Certicate using SHA-1 with attributes ",
"- CN \"", crt_CN, "\", notBefore \"",
str(crt_not_before or ''),
"\", notAfter \"", str(crt_not_after or ''),
"\", signature_algorithm \"",
str(crt_signature_algorithm or ''), "\""]
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,
crt_type)
crt_chain_index += 1
except AttributeError:
pass
return record.to_bytes()

def _alert_on_sunset_sha1(self, crt_not_after, crt_CN, crt_type):
""" 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
logging_level = logging.WARNING
CRT_DATE_FORMAT = "%d-%m-%Y"
sunset_critical_date = datetime.strptime("31-12-2016", CRT_DATE_FORMAT)
sunset_error_date = datetime.strptime("30-06-2016", CRT_DATE_FORMAT)
sunset_warning_date = datetime.strptime("31-12-2015", CRT_DATE_FORMAT)

if (crt_type == self.LEAF_CERT):
crt_type_name = "Leaf"
elif (crt_type == self.INTERMEDIATE_CERT):
crt_type_name = "Intermediate"

if (crt_not_after > sunset_critical_date):
if (crt_type == self.LEAF_CERT):
logging_level = logging.CRITICAL
elif (crt_type == self.INTERMEDIATE_CERT):
logging_level = logging.WARNING
log_message = \
[crt_type_name, " certificate with CN ", crt_CN,
" uses SHA-1 and expires after 31 Dec 2016"]
self.log(logging_level, "".join(log_message))
self.log_event(logging_level, connection.AttackEvent(
self.connection, self.name, True, ""))
self.connection.vuln_notify(util.vuln.VULN_SUNSET_SHA1)
elif (crt_not_after > sunset_error_date):
if (crt_type == self.LEAF_CERT):
logging_level = logging.ERROR
elif (crt_type == self.INTERMEDIATE_CERT):
logging_level = logging.WARNING
log_message = \
[crt_type_name, " certificate with CN ", crt_CN,
" uses SHA-1 and expires after 30 Jun 2016"]
self.log(logging_level, "".join(log_message))
self.log_event(logging_level, connection.AttackEvent(
self.connection, self.name, True, ""))
self.connection.vuln_notify(util.vuln.VULN_SUNSET_SHA1)
elif (crt_not_after > sunset_warning_date):
logging_level = logging.WARNING
log_message = \
[crt_type_name, " certificate with CN ", crt_CN,
" uses SHA-1 and expires after 31 Dec 2015"]
self.log(logging_level, "".join(log_message))
self.log_event(logging_level, connection.AttackEvent(
self.connection, self.name, True, ""))
self.connection.vuln_notify(util.vuln.VULN_SUNSET_SHA1)
1 change: 1 addition & 0 deletions nogotofail/mitm/util/vuln.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,4 @@
VULN_WEAK_TLS_VERSION = "weaktlsversion"
VULN_TLS_SERVER_KEY_REPLACEMENT = "serverkeyreplace"
VULN_TLS_SUPERFISH_TRUSTED = "superfishca"
VULN_SUNSET_SHA1 = "sunsetsha1"

0 comments on commit eaa3fec

Please sign in to comment.