1""" 2Looks for certificates in SSL/TLS traffic and tries to find any hashes that 3match those in the abuse.ch blacklist. 4(https://sslbl.abuse.ch/blacklist/) 5""" 6 7# handy reference: 8# http://blog.fourthbit.com/2014/12/23/traffic-analysis-of-an-ssl-slash-tls-session 9 10import dshell.core 11from dshell.output.alertout import AlertOutput 12 13import hashlib 14import io 15import struct 16 17# SSLv3/TLS version 18SSL3_VERSION = 0x0300 19TLS1_VERSION = 0x0301 20TLS1_1_VERSION = 0x0302 21TLS1_2_VERSION = 0x0303 22 23# Record type 24SSL3_RT_CHANGE_CIPHER_SPEC = 20 25SSL3_RT_ALERT = 21 26SSL3_RT_HANDSHAKE = 22 27SSL3_RT_APPLICATION_DATA = 23 28 29# Handshake message type 30SSL3_MT_HELLO_REQUEST = 0 31SSL3_MT_CLIENT_HELLO = 1 32SSL3_MT_SERVER_HELLO = 2 33SSL3_MT_CERTIFICATE = 11 34SSL3_MT_SERVER_KEY_EXCHANGE = 12 35SSL3_MT_CERTIFICATE_REQUEST = 13 36SSL3_MT_SERVER_DONE = 14 37SSL3_MT_CERTIFICATE_VERIFY = 15 38SSL3_MT_CLIENT_KEY_EXCHANGE = 16 39SSL3_MT_FINISHED = 20 40 41 42class DshellPlugin(dshell.core.ConnectionPlugin): 43 44 def __init__(self): 45 super().__init__( 46 name="sslblacklist", 47 author="dev195", 48 bpf="tcp and (port 443 or port 993 or port 1443 or port 8531)", 49 description="Looks for certificate SHA1 matches in the abuse.ch blacklist", 50 longdescription=""" 51 Looks for certificates in SSL/TLS traffic and tries to find any hashes that 52 match those in the abuse.ch blacklist. 53 54 Requires downloading the blacklist CSV from abuse.ch: 55 https://sslbl.abuse.ch/blacklist/ 56 57 If the CSV is not in the current directory, use the --sslblacklist_csv 58 argument to provide a file path. 59""", 60 output=AlertOutput(label=__name__), 61 optiondict={ 62 "csv": { 63 "help": "filepath to the sslblacklist.csv file", 64 "default": "./sslblacklist.csv", 65 "metavar": "FILEPATH" 66 }, 67 } 68 ) 69 70 def premodule(self): 71 self.parse_blacklist_csv(self.csv) 72 73 def parse_blacklist_csv(self, filepath): 74 "parses the SSL blacklist CSV, given the 'filepath'" 75 # Python's standard csv module doesn't seem to handle it properly 76 self.hashes = {} 77 with open(filepath, 'r') as csv: 78 for line in csv: 79 line = line.split('#')[0] # ignore comments 80 line = line.strip() 81 try: 82 timestamp, sha1, reason = line.split(',', 3) 83 self.hashes[sha1] = reason 84 except ValueError: 85 continue 86 87 def blob_handler(self, conn, blob): 88 if blob.direction == 'cs': 89 return None 90 91 data = io.BytesIO(blob.data) 92 93 # Iterate over each layer of the connection, paying special attention to the certificate 94 while True: 95 try: 96 content_type, proto_version, record_len = struct.unpack("!BHH", data.read(5)) 97 except struct.error: 98 break 99 if proto_version not in (SSL3_VERSION, TLS1_VERSION, TLS1_1_VERSION, TLS1_2_VERSION): 100 return None 101 if content_type == SSL3_RT_HANDSHAKE: 102 handshake_type = struct.unpack("!B", data.read(1))[0] 103 handshake_len = struct.unpack("!I", b"\x00"+data.read(3))[0] 104 if handshake_type == SSL3_MT_CERTIFICATE: 105 # Process the certificate itself 106 cert_chain_len = struct.unpack("!I", b"\x00"+data.read(3))[0] 107 bytes_processed = 0 108 while (bytes_processed < cert_chain_len): 109 try: 110 cert_data_len = struct.unpack("!I", b"\x00"+data.read(3))[0] 111 cert_data = data.read(cert_data_len) 112 bytes_processed = 3 + cert_data_len 113 sha1 = hashlib.sha1(cert_data).hexdigest() 114 if sha1 in self.hashes: 115 bad_guy = self.hashes[sha1] 116 self.write("Certificate hash match: {}".format(bad_guy), **conn.info()) 117 except struct.error as e: 118 break 119 else: 120 # Ignore any layers that are not a certificate 121 data.read(handshake_len) 122 continue 123 124 return conn, blob 125