1"""
2Looks for SSL alert messages
3"""
4
5# handy reference:
6# http://blog.fourthbit.com/2014/12/23/traffic-analysis-of-an-ssl-slash-tls-session
7
8import dshell.core
9from dshell.output.alertout import AlertOutput
10
11import hashlib
12import io
13import struct
14from pprint import pprint
15
16# SSLv3/TLS version
17SSL3_VERSION = 0x0300
18TLS1_VERSION = 0x0301
19TLS1_1_VERSION = 0x0302
20TLS1_2_VERSION = 0x0303
21
22# Record type
23SSL3_RT_CHANGE_CIPHER_SPEC = 20
24SSL3_RT_ALERT             = 21
25SSL3_RT_HANDSHAKE         = 22
26SSL3_RT_APPLICATION_DATA  = 23
27
28# Handshake message type
29SSL3_MT_HELLO_REQUEST           = 0
30SSL3_MT_CLIENT_HELLO            = 1
31SSL3_MT_SERVER_HELLO            = 2
32SSL3_MT_CERTIFICATE             = 11
33SSL3_MT_SERVER_KEY_EXCHANGE     = 12
34SSL3_MT_CERTIFICATE_REQUEST     = 13
35SSL3_MT_SERVER_DONE             = 14
36SSL3_MT_CERTIFICATE_VERIFY      = 15
37SSL3_MT_CLIENT_KEY_EXCHANGE     = 16
38SSL3_MT_FINISHED                = 20
39
40alert_types = {
41    0x00: "CLOSE_NOTIFY",
42    0x0a: "UNEXPECTED_MESSAGE",
43    0x14: "BAD_RECORD_MAC",
44    0x15: "DECRYPTION_FAILED",
45    0x16: "RECORD_OVERFLOW",
46    0x1e: "DECOMPRESSION_FAILURE",
47    0x28: "HANDSHAKE_FAILURE",
48    0x29: "NO_CERTIFICATE",
49    0x2a: "BAD_CERTIFICATE",
50    0x2b: "UNSUPPORTED_CERTIFICATE",
51    0x2c: "CERTIFICATE_REVOKED",
52    0x2d: "CERTIFICATE_EXPIRED",
53    0x2e: "CERTIFICATE_UNKNOWN",
54    0x2f: "ILLEGAL_PARAMETER",
55    0x30: "UNKNOWN_CA",
56    0x31: "ACCESS_DENIED",
57    0x32: "DECODE_ERROR",
58    0x33: "DECRYPT_ERROR",
59    0x3c: "EXPORT_RESTRICTION",
60    0x46: "PROTOCOL_VERSION",
61    0x47: "INSUFFICIENT_SECURITY",
62    0x50: "INTERNAL_ERROR",
63    0x5a: "USER_CANCELLED",
64    0x64: "NO_RENEGOTIATION",
65}
66
67alert_severities = {
68    0x01: "warning",
69    0x02: "fatal",
70}
71
72class DshellPlugin(dshell.core.ConnectionPlugin):
73
74    def __init__(self):
75        super().__init__(
76            name="sslalerts",
77            author="dev195",
78            bpf="tcp and (port 443 or port 993 or port 1443 or port 8531)",
79            description="Looks for SSL alert messages",
80            output=AlertOutput(label=__name__),
81        )
82
83    def blob_handler(self, conn, blob):
84        data = io.BytesIO(blob.data)
85        alert_seen = False
86        # Iterate over each layer of the connection, paying special attention to the certificate
87        while True:
88            try:
89                content_type, proto_version, record_len = struct.unpack("!BHH", data.read(5))
90            except struct.error:
91                break
92            if proto_version not in (SSL3_VERSION, TLS1_VERSION, TLS1_1_VERSION, TLS1_2_VERSION):
93                return None
94            if content_type == SSL3_RT_ALERT:
95                handshake_len = struct.unpack("!I", data.read(4))[0]
96#                assert handshake_len == 2  # TODO remove when live
97                severity = struct.unpack("!B", data.read(1))[0]
98                if severity not in alert_severities:
99                    continue
100                severity_msg = alert_severities.get(severity, severity)
101                alert_type = struct.unpack("!B", data.read(1))[0]
102                alert_msg = alert_types.get(alert_type, str(alert_type))
103                self.write("SSL alert: ({}) {}".format(severity_msg, alert_msg), **conn.info())
104                alert_seen = True
105
106        if alert_seen:
107            return conn, blob
108