From d476c654eea7cfa47c31f1d0a9901c1759c2da5e Mon Sep 17 00:00:00 2001 From: Pierre Chifflier Date: Wed, 9 Apr 2014 09:52:00 +0200 Subject: [PATCH] TLS: add detection for malicious heartbeats (AKA heartbleed) The OpenSSL implementation of RFC 6520 (Heartbeat extension) does not check the payload length correctly, resulting in a copy of at most 64k of memory from the server (ref: CVE-2014-0160). This patch adds support for decoding heartbeat messages (if not encrypted), and checking several parts (type, length and padding). When an anomaly is detected, a TLS event is raised. --- rules/tls-events.rules | 5 ++- src/app-layer-ssl.c | 72 ++++++++++++++++++++++++++++++++++++++++++ src/app-layer-ssl.h | 3 ++ 3 files changed, 79 insertions(+), 1 deletion(-) diff --git a/rules/tls-events.rules b/rules/tls-events.rules index 560d55bd34..7c9ae6f09c 100644 --- a/rules/tls-events.rules +++ b/rules/tls-events.rules @@ -18,5 +18,8 @@ alert tls any any -> any any (msg:"SURICATA TLS certificate invalid string"; flo alert tls any any -> any any (msg:"SURICATA TLS error message encountered"; flow:established; app-layer-event:tls.error_message_encountered; flowint:tls.anomaly.count,+,1; classtype:protocol-command-decode; sid:2230009; rev:1;) alert tls any any -> any any (msg:"SURICATA TLS invalid record/traffic"; flow:established; app-layer-event:tls.invalid_ssl_record; flowint:tls.anomaly.count,+,1; classtype:protocol-command-decode; sid:2230010; rev:1;) -#next sid is 2230011 +alert tls any any -> any any (msg:"SURICATA TLS heartbeat encountered"; flow:established; app-layer-event:tls.heartbeat_message; flowint:tls.anomaly.count,+,1; classtype:protocol-command-decode; sid:2230011; rev:1;) +alert tls any any -> any any (msg:"SURICATA TLS overflow heartbeat encountered, possible exploit attempt (heartbleed)"; flow:established; app-layer-event:tls.overflow_heartbeat_message; flowint:tls.anomaly.count,+,1; classtype:protocol-command-decode; reference:cve,2014-0160; sid:2230012; rev:1;) +alert tls any any -> any any (msg:"SURICATA TLS invalid heartbeat encountered, possible exploit attempt (heartbleed)"; flow:established; app-layer-event:tls.invalid_heartbeat_message; flowint:tls.anomaly.count,+,1; classtype:protocol-command-decode; reference:cve,2014-0160; sid:2230013; rev:1;) +#next sid is 2230014 diff --git a/src/app-layer-ssl.c b/src/app-layer-ssl.c index 889377ef19..6e0c001cfe 100644 --- a/src/app-layer-ssl.c +++ b/src/app-layer-ssl.c @@ -60,6 +60,9 @@ SCEnumCharMap tls_decoder_event_table[ ] = { { "INVALID_TLS_HEADER", TLS_DECODER_EVENT_INVALID_TLS_HEADER }, { "INVALID_RECORD_TYPE", TLS_DECODER_EVENT_INVALID_RECORD_TYPE }, { "INVALID_HANDSHAKE_MESSAGE", TLS_DECODER_EVENT_INVALID_HANDSHAKE_MESSAGE }, + { "HEARTBEAT_MESSAGE", TLS_DECODER_EVENT_HEARTBEAT }, + { "INVALID_HEARTBEAT_MESSAGE", TLS_DECODER_EVENT_INVALID_HEARTBEAT }, + { "OVERFLOW_HEARTBEAT_MESSAGE", TLS_DECODER_EVENT_OVERFLOW_HEARTBEAT }, /* Certificates decoding messages */ { "INVALID_CERTIFICATE", TLS_DECODER_EVENT_INVALID_CERTIFICATE }, { "CERTIFICATE_MISSING_ELEMENT", TLS_DECODER_EVENT_CERTIFICATE_MISSING_ELEMENT }, @@ -83,6 +86,7 @@ SslConfig ssl_config; #define SSLV3_ALERT_PROTOCOL 21 #define SSLV3_HANDSHAKE_PROTOCOL 22 #define SSLV3_APPLICATION_PROTOCOL 23 +#define SSLV3_HEARTBEAT_PROTOCOL 24 /* SSLv3 handshake protocol types */ #define SSLV3_HS_HELLO_REQUEST 0 @@ -113,6 +117,10 @@ SslConfig ssl_config; #define SSLV3_RECORD_HDR_LEN 5 #define SSLV3_MESSAGE_HDR_LEN 4 +/* TLS heartbeat protocol types */ +#define TLS_HB_REQUEST 1 +#define TLS_HB_RESPONSE 2 + static void SSLParserReset(SSLState *ssl_state) { ssl_state->curr_connp->bytes_processed = 0; @@ -322,6 +330,59 @@ static int SSLv3ParseHandshakeProtocol(SSLState *ssl_state, uint8_t *input, return (input - initial_input); } +/** + * \internal + * \brief TLS Heartbeat parser (see RFC 6520) + * + * \param sslstate Pointer to the SSL state. + * \param input Pointer the received input data. + * \param input_len Length in bytes of the received data. + * + * \retval The number of bytes parsed on success, 0 if nothing parsed, -1 on failure. + */ +static int SSLv3ParseHeartbeatProtocol(SSLState *ssl_state, uint8_t *input, + uint32_t input_len) +{ + uint8_t hb_type; + uint16_t payload_len; + uint16_t padding_len; + + // expect at least 3 bytes, heartbeat type (1) + length (2) + if (input_len < 3) { + return 0; + } + + hb_type = *input++; + + if (!(hb_type == TLS_HB_REQUEST || hb_type == TLS_HB_RESPONSE)) { + AppLayerDecoderEventsSetEvent(ssl_state->f, TLS_DECODER_EVENT_INVALID_HEARTBEAT); + return -1; + } + + payload_len = (*input++) << 8; + payload_len |= (*input++); + + // check that the requested payload length is really present in record (CVE-2014-0160) + if ((uint32_t)(payload_len+3) > ssl_state->curr_connp->record_length) { + AppLayerDecoderEventsSetEvent(ssl_state->f, TLS_DECODER_EVENT_OVERFLOW_HEARTBEAT); + return -1; + } + + // check the padding length + // it must be at least 16 bytes (RFC 6520, section 4) + padding_len = ssl_state->curr_connp->record_length - payload_len - 3; + if (padding_len < 16) { + AppLayerDecoderEventsSetEvent(ssl_state->f, TLS_DECODER_EVENT_INVALID_HEARTBEAT); + return -1; + } + + if (input_len < payload_len+padding_len) // we don't have the payload + return 0; + + // skip the heartbeat, 3 bytes were already parsed, e.g |18 03 02| for TLS 1.2 + return (ssl_state->curr_connp->record_length - 3); +} + static int SSLv3ParseRecord(uint8_t direction, SSLState *ssl_state, uint8_t *input, uint32_t input_len) { @@ -762,6 +823,17 @@ static int SSLv3Decode(uint8_t direction, SSLState *ssl_state, break; + case SSLV3_HEARTBEAT_PROTOCOL: + if (ssl_state->flags & SSL_AL_FLAG_CHANGE_CIPHER_SPEC) { + // stream is encrypted, so we cannot check the handshake :( + //AppLayerDecoderEventsSetEvent(ssl_state->f, TLS_DECODER_EVENT_HEARTBEAT); + break; + } + retval = SSLv3ParseHeartbeatProtocol(ssl_state, input + parsed, input_len); + if (retval < 0) + return -1; + break; + default: /* \todo fix the event from invalid rule to unknown rule */ AppLayerDecoderEventsSetEvent(ssl_state->f, TLS_DECODER_EVENT_INVALID_RECORD_TYPE); diff --git a/src/app-layer-ssl.h b/src/app-layer-ssl.h index d138200d05..5047eb10f2 100644 --- a/src/app-layer-ssl.h +++ b/src/app-layer-ssl.h @@ -35,6 +35,9 @@ enum { TLS_DECODER_EVENT_INVALID_TLS_HEADER, TLS_DECODER_EVENT_INVALID_RECORD_TYPE, TLS_DECODER_EVENT_INVALID_HANDSHAKE_MESSAGE, + TLS_DECODER_EVENT_HEARTBEAT, + TLS_DECODER_EVENT_INVALID_HEARTBEAT, + TLS_DECODER_EVENT_OVERFLOW_HEARTBEAT, /* Certificates decoding messages */ TLS_DECODER_EVENT_INVALID_CERTIFICATE, TLS_DECODER_EVENT_CERTIFICATE_MISSING_ELEMENT, -- 2.47.2