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. Only the signature algorithm of the "leaf" certificate in the chain is checked.
  • Loading branch information
yzninja committed Dec 1, 2015
1 parent 7e998a5 commit d6e79fb
Show file tree
Hide file tree
Showing 4 changed files with 108 additions and 0 deletions.
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
102 changes: 102 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,100 @@ 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:
record, remaining = tls.parse_tls(response)
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
if certificate.certificates:
# Check leaf certificate in chain for SHA-1 sunset issue
cert_byte_string = certificate.certificates[0]
leaf_cert = OpenSSL.crypto.load_certificate(
OpenSSL.crypto.FILETYPE_ASN1, cert_byte_string)
crt_signature_algorithm = \
leaf_cert.get_signature_algorithm()
if ("sha1" in crt_signature_algorithm):
crt_CN = leaf_cert.get_subject().CN
crt_not_before = leaf_cert.get_notBefore()
crt_not_after = leaf_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)
except AttributeError:
# 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)
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 d6e79fb

Please sign in to comment.