From ffd4711a41ba0e9ea0f8cfd3097aadbe68912eb4 Mon Sep 17 00:00:00 2001 From: Bernhard Amann Date: Tue, 8 Apr 2014 07:41:08 -0700 Subject: [PATCH 01/11] Throw new event for heartbeat messages. Not tested. --- src/analyzer/protocol/ssl/events.bif | 2 ++ src/analyzer/protocol/ssl/ssl-analyzer.pac | 17 +++++++++++++++++ src/analyzer/protocol/ssl/ssl-defs.pac | 1 + src/analyzer/protocol/ssl/ssl-protocol.pac | 11 +++++++++++ 4 files changed, 31 insertions(+) diff --git a/src/analyzer/protocol/ssl/events.bif b/src/analyzer/protocol/ssl/events.bif index 054d9c672f..c85e911ee8 100644 --- a/src/analyzer/protocol/ssl/events.bif +++ b/src/analyzer/protocol/ssl/events.bif @@ -138,3 +138,5 @@ event ssl_alert%(c: connection, is_orig: bool, level: count, desc: count%); ## .. bro:see:: ssl_client_hello ssl_established ssl_extension ssl_server_hello ## ssl_alert event ssl_session_ticket_handshake%(c: connection, ticket_lifetime_hint: count, ticket: string%); + +event ssl_heartbeat%(c: connection, length: count%); diff --git a/src/analyzer/protocol/ssl/ssl-analyzer.pac b/src/analyzer/protocol/ssl/ssl-analyzer.pac index 49104fa549..e6ea1628a1 100644 --- a/src/analyzer/protocol/ssl/ssl-analyzer.pac +++ b/src/analyzer/protocol/ssl/ssl-analyzer.pac @@ -306,6 +306,10 @@ refine connection SSL_Conn += { function proc_ciphertext_record(rec : SSLRecord) : bool %{ + if ( ${rec.content_type} == HEARTBEAT ) + BifEvent::generate_ssl_heartbeat(bro_analyzer(), + bro_analyzer()->Conn(), ${rec.length}); + if ( state_ == STATE_TRACK_LOST ) bro_analyzer()->ProtocolViolation(fmt("unexpected ciphertext record from %s in state %s", orig_label(${rec.is_orig}).c_str(), @@ -320,6 +324,15 @@ refine connection SSL_Conn += { return true; %} + + function proc_heartbeat(rec : SSLRecord) : bool + %{ + BifEvent::generate_ssl_heartbeat(bro_analyzer(), + bro_analyzer()->Conn(), ${rec.length}); + + return true; + %} + }; refine typeattr ChangeCipherSpec += &let { @@ -339,6 +352,10 @@ refine typeattr ApplicationData += &let { proc : bool = $context.connection.proc_application_data(rec); }; +refine typeattr Heartbeat += &let { + proc : bool = $context.connection.proc_heartbeat(rec); +}; + refine typeattr ClientHello += &let { proc : bool = $context.connection.proc_client_hello(rec, client_version, gmt_unix_time, random_bytes, diff --git a/src/analyzer/protocol/ssl/ssl-defs.pac b/src/analyzer/protocol/ssl/ssl-defs.pac index c35fc56e85..23fa7abce5 100644 --- a/src/analyzer/protocol/ssl/ssl-defs.pac +++ b/src/analyzer/protocol/ssl/ssl-defs.pac @@ -12,6 +12,7 @@ enum ContentType { ALERT = 21, HANDSHAKE = 22, APPLICATION_DATA = 23, + HEARTBEAT = 24, V2_ERROR = 300, V2_CLIENT_HELLO = 301, V2_CLIENT_MASTER_KEY = 302, diff --git a/src/analyzer/protocol/ssl/ssl-protocol.pac b/src/analyzer/protocol/ssl/ssl-protocol.pac index 9368122eaa..c12130abf8 100644 --- a/src/analyzer/protocol/ssl/ssl-protocol.pac +++ b/src/analyzer/protocol/ssl/ssl-protocol.pac @@ -63,6 +63,7 @@ type PlaintextRecord(rec: SSLRecord) = case rec.content_type of { CHANGE_CIPHER_SPEC -> ch_cipher : ChangeCipherSpec(rec); ALERT -> alert : Alert(rec); HANDSHAKE -> handshake : Handshake(rec); + HEARTBEAT -> heartbeat: Heartbeat(rec); APPLICATION_DATA -> app_data : ApplicationData(rec); V2_ERROR -> v2_error : V2Error(rec); V2_CLIENT_HELLO -> v2_client_hello : V2ClientHello(rec); @@ -225,6 +226,16 @@ type ApplicationData(rec: SSLRecord) = record { data : bytestring &restofdata &transient; }; +###################################################################### +# V3 Heartbeat +###################################################################### + +# Heartbeats should basically always be encrypted, so we should not +# reach this point. +type Heartbeat(rec: SSLRecord) = record { + data : bytestring &restofdata &transient; +}; + ###################################################################### # Handshake Protocol (7.4.) ###################################################################### From 902d52e261069addbf6647537b1a5db9a171f79d Mon Sep 17 00:00:00 2001 From: Bernhard Amann Date: Tue, 8 Apr 2014 08:43:38 -0700 Subject: [PATCH 02/11] add is_orig to heartbeat event --- src/analyzer/protocol/ssl/events.bif | 2 +- src/analyzer/protocol/ssl/ssl-analyzer.pac | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/analyzer/protocol/ssl/events.bif b/src/analyzer/protocol/ssl/events.bif index c85e911ee8..3f780bdd60 100644 --- a/src/analyzer/protocol/ssl/events.bif +++ b/src/analyzer/protocol/ssl/events.bif @@ -139,4 +139,4 @@ event ssl_alert%(c: connection, is_orig: bool, level: count, desc: count%); ## ssl_alert event ssl_session_ticket_handshake%(c: connection, ticket_lifetime_hint: count, ticket: string%); -event ssl_heartbeat%(c: connection, length: count%); +event ssl_heartbeat%(c: connection, is_orig: bool, length: count%); diff --git a/src/analyzer/protocol/ssl/ssl-analyzer.pac b/src/analyzer/protocol/ssl/ssl-analyzer.pac index e6ea1628a1..1730ce8ce5 100644 --- a/src/analyzer/protocol/ssl/ssl-analyzer.pac +++ b/src/analyzer/protocol/ssl/ssl-analyzer.pac @@ -308,7 +308,7 @@ refine connection SSL_Conn += { %{ if ( ${rec.content_type} == HEARTBEAT ) BifEvent::generate_ssl_heartbeat(bro_analyzer(), - bro_analyzer()->Conn(), ${rec.length}); + bro_analyzer()->Conn(), ${rec.is_orig}, ${rec.length}); if ( state_ == STATE_TRACK_LOST ) bro_analyzer()->ProtocolViolation(fmt("unexpected ciphertext record from %s in state %s", @@ -328,7 +328,7 @@ refine connection SSL_Conn += { function proc_heartbeat(rec : SSLRecord) : bool %{ BifEvent::generate_ssl_heartbeat(bro_analyzer(), - bro_analyzer()->Conn(), ${rec.length}); + bro_analyzer()->Conn(), ${rec.is_orig}, ${rec.length}); return true; %} From 018735a5744d8a82bbd7cee48fd828b4018d257f Mon Sep 17 00:00:00 2001 From: Bernhard Amann Date: Tue, 8 Apr 2014 09:49:00 -0700 Subject: [PATCH 03/11] default to TLS when not being able to determine version --- src/analyzer/protocol/ssl/ssl-protocol.pac | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/analyzer/protocol/ssl/ssl-protocol.pac b/src/analyzer/protocol/ssl/ssl-protocol.pac index c12130abf8..944f081060 100644 --- a/src/analyzer/protocol/ssl/ssl-protocol.pac +++ b/src/analyzer/protocol/ssl/ssl-protocol.pac @@ -39,13 +39,13 @@ type SSLRecord(is_orig: bool) = record { $context.connection.determine_ssl_version(head0, head1, head2); content_type : int = case version of { - UNKNOWN_VERSION -> 0; + # UNKNOWN_VERSION -> 0; assume tls on unknown version SSLv20 -> head2+300; default -> head0; }; length : int = case version of { - UNKNOWN_VERSION -> 0; + # UNKNOWN_VERSION -> 0; assume tls on unknown version SSLv20 -> (((head0 & 0x7f) << 8) | head1) - 3; default -> (head3 << 8) | head4; }; From 335a30b08f10ecc6243182d71f786ac104f31897 Mon Sep 17 00:00:00 2001 From: Bernhard Amann Date: Tue, 8 Apr 2014 11:03:12 -0700 Subject: [PATCH 04/11] detect and alert on simple case of heartbleed --- scripts/policy/protocols/ssl/heartbleed.bro | 44 +++++++++++++++++++++ src/analyzer/protocol/ssl/events.bif | 4 +- src/analyzer/protocol/ssl/ssl-analyzer.pac | 8 ++-- src/analyzer/protocol/ssl/ssl-protocol.pac | 4 +- 4 files changed, 53 insertions(+), 7 deletions(-) create mode 100644 scripts/policy/protocols/ssl/heartbleed.bro diff --git a/scripts/policy/protocols/ssl/heartbleed.bro b/scripts/policy/protocols/ssl/heartbleed.bro new file mode 100644 index 0000000000..b3f81034b0 --- /dev/null +++ b/scripts/policy/protocols/ssl/heartbleed.bro @@ -0,0 +1,44 @@ +module Heartbleed; + +redef record SSL::Info += { +# last_originator_heartbeat_request_size: count &optional; +# originator_heartbeats: count &default=0; +# responder_heartbeats: count &default=0; + heartbleed_detected: bool &default=F; + }; + +export { + redef enum Notice::Type += { + ## Indicates that a host performing a heartbleed attack. + SSL_Heartbeat_Attack, + ## Indicates that a host performing a heartbleed attack was probably successful. + SSL_Heartbeat_Attack_Success, + }; +} + +event ssl_heartbeat(c: connection, is_orig: bool, length: count, heartbeat_type: count, payload_length: count) + { + if ( heartbeat_type == 1 ) + { + + local checklength: count = (length<(3+16)) ? length : (length - 3 - 16); + + + if ( payload_length > checklength ) + { + c$ssl$heartbleed_detected = T; + NOTICE([$note=SSL_Heartbeat_Attack, + $msg="An TLS heartbleed attack was detected!", + $conn=c + ]); + } + } + + if ( heartbeat_type == 2 && c$ssl$heartbleed_detected ) + { + NOTICE([$note=SSL_Heartbeat_Attack_Success, + $msg="An TLS heartbleed attack was detected and probably exploited", + $conn=c + ]); + } + } diff --git a/src/analyzer/protocol/ssl/events.bif b/src/analyzer/protocol/ssl/events.bif index 3f780bdd60..e720089f45 100644 --- a/src/analyzer/protocol/ssl/events.bif +++ b/src/analyzer/protocol/ssl/events.bif @@ -139,4 +139,6 @@ event ssl_alert%(c: connection, is_orig: bool, level: count, desc: count%); ## ssl_alert event ssl_session_ticket_handshake%(c: connection, ticket_lifetime_hint: count, ticket: string%); -event ssl_heartbeat%(c: connection, is_orig: bool, length: count%); +event ssl_encrypted_heartbeat%(c: connection, is_orig: bool, length: count%); + +event ssl_heartbeat%(c: connection, is_orig: bool, length: count, heartbeat_type: count, payload_length: count%); diff --git a/src/analyzer/protocol/ssl/ssl-analyzer.pac b/src/analyzer/protocol/ssl/ssl-analyzer.pac index 1730ce8ce5..d0ac88a5a1 100644 --- a/src/analyzer/protocol/ssl/ssl-analyzer.pac +++ b/src/analyzer/protocol/ssl/ssl-analyzer.pac @@ -307,7 +307,7 @@ refine connection SSL_Conn += { function proc_ciphertext_record(rec : SSLRecord) : bool %{ if ( ${rec.content_type} == HEARTBEAT ) - BifEvent::generate_ssl_heartbeat(bro_analyzer(), + BifEvent::generate_ssl_encrypted_heartbeat(bro_analyzer(), bro_analyzer()->Conn(), ${rec.is_orig}, ${rec.length}); if ( state_ == STATE_TRACK_LOST ) @@ -325,10 +325,10 @@ refine connection SSL_Conn += { return true; %} - function proc_heartbeat(rec : SSLRecord) : bool + function proc_heartbeat(rec : SSLRecord, type: uint8, payload_length: uint16) : bool %{ BifEvent::generate_ssl_heartbeat(bro_analyzer(), - bro_analyzer()->Conn(), ${rec.is_orig}, ${rec.length}); + bro_analyzer()->Conn(), ${rec.is_orig}, ${rec.length}, type, payload_length); return true; %} @@ -353,7 +353,7 @@ refine typeattr ApplicationData += &let { }; refine typeattr Heartbeat += &let { - proc : bool = $context.connection.proc_heartbeat(rec); + proc : bool = $context.connection.proc_heartbeat(rec, type, payload_length); }; refine typeattr ClientHello += &let { diff --git a/src/analyzer/protocol/ssl/ssl-protocol.pac b/src/analyzer/protocol/ssl/ssl-protocol.pac index 944f081060..acded2dbf9 100644 --- a/src/analyzer/protocol/ssl/ssl-protocol.pac +++ b/src/analyzer/protocol/ssl/ssl-protocol.pac @@ -230,9 +230,9 @@ type ApplicationData(rec: SSLRecord) = record { # V3 Heartbeat ###################################################################### -# Heartbeats should basically always be encrypted, so we should not -# reach this point. type Heartbeat(rec: SSLRecord) = record { + type : uint8; + payload_length : uint16; data : bytestring &restofdata &transient; }; From c41810a33780b1263f2e8a1b6939d398a267fa0f Mon Sep 17 00:00:00 2001 From: Bernhard Amann Date: Tue, 8 Apr 2014 11:19:30 -0700 Subject: [PATCH 05/11] polish script and probably detect encrypted attacks too. --- scripts/policy/protocols/ssl/heartbleed.bro | 65 +++++++++++++++++++-- 1 file changed, 60 insertions(+), 5 deletions(-) diff --git a/scripts/policy/protocols/ssl/heartbleed.bro b/scripts/policy/protocols/ssl/heartbleed.bro index b3f81034b0..c9a9622e2c 100644 --- a/scripts/policy/protocols/ssl/heartbleed.bro +++ b/scripts/policy/protocols/ssl/heartbleed.bro @@ -1,9 +1,11 @@ module Heartbleed; redef record SSL::Info += { -# last_originator_heartbeat_request_size: count &optional; -# originator_heartbeats: count &default=0; -# responder_heartbeats: count &default=0; + last_originator_heartbeat_request_size: count &optional; + last_responder_heartbeat_request_size: count &optional; + originator_heartbeats: count &default=0; + responder_heartbeats: count &default=0; + heartbleed_detected: bool &default=F; }; @@ -11,8 +13,14 @@ export { redef enum Notice::Type += { ## Indicates that a host performing a heartbleed attack. SSL_Heartbeat_Attack, - ## Indicates that a host performing a heartbleed attack was probably successful. + ## Indicates that a host performing a heartbleed attack was successful. SSL_Heartbeat_Attack_Success, + ## Indivcates that a host performing a heartbleed attack after encryption was started was probably successful + SSL_Heartbeat_Encrypted_Attack_Success, + ## Indicates we saw heartbeet requests with odd length. Probably an attack. + SSL_Heartbeat_Odd_Length, + ## Indicates we saw many heartbeat requests without an reply. Might be an attack. + SSL_Heartbeat_Many_Requests }; } @@ -20,7 +28,6 @@ event ssl_heartbeat(c: connection, is_orig: bool, length: count, heartbeat_type: { if ( heartbeat_type == 1 ) { - local checklength: count = (length<(3+16)) ? length : (length - 3 - 16); @@ -42,3 +49,51 @@ event ssl_heartbeat(c: connection, is_orig: bool, length: count, heartbeat_type: ]); } } + +event ssl_encrypted_heartbeat(c: connection, is_orig: bool, length: count) + { + if ( is_orig ) + ++c$ssl$originator_heartbeats; + else + ++c$ssl$responder_heartbeats; + + if ( c$ssl$originator_heartbeats > c$ssl$responder_heartbeats + 3 ) + NOTICE([$note=SSL_Heartbeat_Many_Requests, + $msg="Seeing more than 3 heartbeat requests without replies from server. Possible attack?", + $conn=c + ]); + + if ( is_orig && length < 19 ) + NOTICE([$note=SSL_Heartbeat_Odd_Length, + $msg="Heartbeat message smaller than minimum length. Probable attack.", + $conn=c + ]); + + if ( is_orig ) + { + if ( c$ssl?$last_responder_heartbeat_request_size ) + { + # server originated heartbeat. Ignore & continue + delete c$ssl$last_responder_heartbeat_request_size; + } + else + c$ssl$last_originator_heartbeat_request_size = length; + } + else + { + if ( c$ssl?$last_originator_heartbeat_request_size && c$ssl$last_originator_heartbeat_request_size > length ) + { + NOTICE([$note=SSL_Heartbeat_Encrypted_Attack_Success, + $msg="An Encrypted TLS heartbleed attack was probably detected!", + $conn=c + ]); + } + else if ( ! c$ssl?$last_originator_heartbeat_request_size ) + { + c$ssl$last_responder_heartbeat_request_size = length; + } + + if ( c$ssl?$last_originator_heartbeat_request_size ) + delete c$ssl$last_originator_heartbeat_request_size; + } + } From 4d33bdbb1ebe531a238803ef612395d8a39fc6e8 Mon Sep 17 00:00:00 2001 From: Bernhard Amann Date: Tue, 8 Apr 2014 11:28:13 -0700 Subject: [PATCH 06/11] fix tabs. --- scripts/policy/protocols/ssl/heartbleed.bro | 146 ++++++++++---------- 1 file changed, 73 insertions(+), 73 deletions(-) diff --git a/scripts/policy/protocols/ssl/heartbleed.bro b/scripts/policy/protocols/ssl/heartbleed.bro index c9a9622e2c..7089758e93 100644 --- a/scripts/policy/protocols/ssl/heartbleed.bro +++ b/scripts/policy/protocols/ssl/heartbleed.bro @@ -6,94 +6,94 @@ redef record SSL::Info += { originator_heartbeats: count &default=0; responder_heartbeats: count &default=0; - heartbleed_detected: bool &default=F; + heartbleed_detected: bool &default=F; }; export { redef enum Notice::Type += { ## Indicates that a host performing a heartbleed attack. SSL_Heartbeat_Attack, - ## Indicates that a host performing a heartbleed attack was successful. - SSL_Heartbeat_Attack_Success, - ## Indivcates that a host performing a heartbleed attack after encryption was started was probably successful - SSL_Heartbeat_Encrypted_Attack_Success, - ## Indicates we saw heartbeet requests with odd length. Probably an attack. - SSL_Heartbeat_Odd_Length, - ## Indicates we saw many heartbeat requests without an reply. Might be an attack. - SSL_Heartbeat_Many_Requests - }; + ## Indicates that a host performing a heartbleed attack was successful. + SSL_Heartbeat_Attack_Success, + ## Indivcates that a host performing a heartbleed attack after encryption was started was probably successful + SSL_Heartbeat_Encrypted_Attack_Success, + ## Indicates we saw heartbeet requests with odd length. Probably an attack. + SSL_Heartbeat_Odd_Length, + ## Indicates we saw many heartbeat requests without an reply. Might be an attack. + SSL_Heartbeat_Many_Requests + }; } event ssl_heartbeat(c: connection, is_orig: bool, length: count, heartbeat_type: count, payload_length: count) - { - if ( heartbeat_type == 1 ) - { - local checklength: count = (length<(3+16)) ? length : (length - 3 - 16); + { + if ( heartbeat_type == 1 ) + { + local checklength: count = (length<(3+16)) ? length : (length - 3 - 16); - if ( payload_length > checklength ) - { - c$ssl$heartbleed_detected = T; - NOTICE([$note=SSL_Heartbeat_Attack, - $msg="An TLS heartbleed attack was detected!", - $conn=c - ]); - } - } + if ( payload_length > checklength ) + { + c$ssl$heartbleed_detected = T; + NOTICE([$note=SSL_Heartbeat_Attack, + $msg="An TLS heartbleed attack was detected!", + $conn=c + ]); + } + } - if ( heartbeat_type == 2 && c$ssl$heartbleed_detected ) - { - NOTICE([$note=SSL_Heartbeat_Attack_Success, - $msg="An TLS heartbleed attack was detected and probably exploited", - $conn=c - ]); - } - } + if ( heartbeat_type == 2 && c$ssl$heartbleed_detected ) + { + NOTICE([$note=SSL_Heartbeat_Attack_Success, + $msg="An TLS heartbleed attack was detected and probably exploited", + $conn=c + ]); + } + } event ssl_encrypted_heartbeat(c: connection, is_orig: bool, length: count) - { - if ( is_orig ) - ++c$ssl$originator_heartbeats; - else - ++c$ssl$responder_heartbeats; + { + if ( is_orig ) + ++c$ssl$originator_heartbeats; + else + ++c$ssl$responder_heartbeats; - if ( c$ssl$originator_heartbeats > c$ssl$responder_heartbeats + 3 ) - NOTICE([$note=SSL_Heartbeat_Many_Requests, - $msg="Seeing more than 3 heartbeat requests without replies from server. Possible attack?", - $conn=c - ]); + if ( c$ssl$originator_heartbeats > c$ssl$responder_heartbeats + 3 ) + NOTICE([$note=SSL_Heartbeat_Many_Requests, + $msg="Seeing more than 3 heartbeat requests without replies from server. Possible attack?", + $conn=c + ]); - if ( is_orig && length < 19 ) - NOTICE([$note=SSL_Heartbeat_Odd_Length, - $msg="Heartbeat message smaller than minimum length. Probable attack.", - $conn=c - ]); + if ( is_orig && length < 19 ) + NOTICE([$note=SSL_Heartbeat_Odd_Length, + $msg="Heartbeat message smaller than minimum length. Probable attack.", + $conn=c + ]); - if ( is_orig ) - { - if ( c$ssl?$last_responder_heartbeat_request_size ) - { - # server originated heartbeat. Ignore & continue - delete c$ssl$last_responder_heartbeat_request_size; - } - else - c$ssl$last_originator_heartbeat_request_size = length; - } - else - { - if ( c$ssl?$last_originator_heartbeat_request_size && c$ssl$last_originator_heartbeat_request_size > length ) - { - NOTICE([$note=SSL_Heartbeat_Encrypted_Attack_Success, - $msg="An Encrypted TLS heartbleed attack was probably detected!", - $conn=c - ]); - } - else if ( ! c$ssl?$last_originator_heartbeat_request_size ) - { - c$ssl$last_responder_heartbeat_request_size = length; - } + if ( is_orig ) + { + if ( c$ssl?$last_responder_heartbeat_request_size ) + { + # server originated heartbeat. Ignore & continue + delete c$ssl$last_responder_heartbeat_request_size; + } + else + c$ssl$last_originator_heartbeat_request_size = length; + } + else + { + if ( c$ssl?$last_originator_heartbeat_request_size && c$ssl$last_originator_heartbeat_request_size > length ) + { + NOTICE([$note=SSL_Heartbeat_Encrypted_Attack_Success, + $msg="An Encrypted TLS heartbleed attack was probably detected!", + $conn=c + ]); + } + else if ( ! c$ssl?$last_originator_heartbeat_request_size ) + { + c$ssl$last_responder_heartbeat_request_size = length; + } - if ( c$ssl?$last_originator_heartbeat_request_size ) - delete c$ssl$last_originator_heartbeat_request_size; - } - } + if ( c$ssl?$last_originator_heartbeat_request_size ) + delete c$ssl$last_originator_heartbeat_request_size; + } + } From cb87f834f9360aae450f778b0a9b49cee4102a4e Mon Sep 17 00:00:00 2001 From: Bernhard Amann Date: Tue, 8 Apr 2014 11:40:48 -0700 Subject: [PATCH 07/11] make tls heartbeat messages a bit better. --- scripts/policy/protocols/ssl/heartbleed.bro | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/scripts/policy/protocols/ssl/heartbleed.bro b/scripts/policy/protocols/ssl/heartbleed.bro index 7089758e93..0e5abc7ab3 100644 --- a/scripts/policy/protocols/ssl/heartbleed.bro +++ b/scripts/policy/protocols/ssl/heartbleed.bro @@ -30,12 +30,11 @@ event ssl_heartbeat(c: connection, is_orig: bool, length: count, heartbeat_type: { local checklength: count = (length<(3+16)) ? length : (length - 3 - 16); - if ( payload_length > checklength ) { c$ssl$heartbleed_detected = T; NOTICE([$note=SSL_Heartbeat_Attack, - $msg="An TLS heartbleed attack was detected!", + $msg=fmt("An TLS heartbleed attack was detected! Record length %d, payload length %d", length, payload_length), $conn=c ]); } @@ -60,13 +59,15 @@ event ssl_encrypted_heartbeat(c: connection, is_orig: bool, length: count) if ( c$ssl$originator_heartbeats > c$ssl$responder_heartbeats + 3 ) NOTICE([$note=SSL_Heartbeat_Many_Requests, $msg="Seeing more than 3 heartbeat requests without replies from server. Possible attack?", - $conn=c + $conn=c, + $n=(c$ssl$originator_heartbeats-c$ssl$responder_heartbeats) ]); if ( is_orig && length < 19 ) NOTICE([$note=SSL_Heartbeat_Odd_Length, $msg="Heartbeat message smaller than minimum length. Probable attack.", - $conn=c + $conn=c, + $n=length ]); if ( is_orig ) From f2c2da92c6505a2a979051e553ff4043f1317a0e Mon Sep 17 00:00:00 2001 From: Bernhard Amann Date: Tue, 8 Apr 2014 11:53:01 -0700 Subject: [PATCH 08/11] add to local.bro, add disclaimer --- scripts/policy/protocols/ssl/heartbleed.bro | 2 ++ scripts/site/local.bro | 2 ++ 2 files changed, 4 insertions(+) diff --git a/scripts/policy/protocols/ssl/heartbleed.bro b/scripts/policy/protocols/ssl/heartbleed.bro index 0e5abc7ab3..d66ff4df2a 100644 --- a/scripts/policy/protocols/ssl/heartbleed.bro +++ b/scripts/policy/protocols/ssl/heartbleed.bro @@ -1,5 +1,7 @@ module Heartbleed; +# Please note - this is not well tested. Use at your own risk. + redef record SSL::Info += { last_originator_heartbeat_request_size: count &optional; last_responder_heartbeat_request_size: count &optional; diff --git a/scripts/site/local.bro b/scripts/site/local.bro index e1a3574424..bb2cc73a53 100644 --- a/scripts/site/local.bro +++ b/scripts/site/local.bro @@ -81,3 +81,5 @@ # Detect SHA1 sums in Team Cymru's Malware Hash Registry. @load frameworks/files/detect-MHR +# Load heartbleed detection. Only superficially tested, might contain bugs. +@load policy/protocols/ssl/heartbleed From 2942a26280866c5ecad13ce8d7a63a706dc9e1af Mon Sep 17 00:00:00 2001 From: Bernhard Amann Date: Tue, 8 Apr 2014 12:44:51 -0700 Subject: [PATCH 09/11] also extract payload data in ssl_heartbeat --- scripts/policy/protocols/ssl/heartbleed.bro | 2 +- src/analyzer/protocol/ssl/events.bif | 2 +- src/analyzer/protocol/ssl/ssl-analyzer.pac | 8 ++++---- src/analyzer/protocol/ssl/ssl-protocol.pac | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/scripts/policy/protocols/ssl/heartbleed.bro b/scripts/policy/protocols/ssl/heartbleed.bro index d66ff4df2a..19728b1d0c 100644 --- a/scripts/policy/protocols/ssl/heartbleed.bro +++ b/scripts/policy/protocols/ssl/heartbleed.bro @@ -26,7 +26,7 @@ export { }; } -event ssl_heartbeat(c: connection, is_orig: bool, length: count, heartbeat_type: count, payload_length: count) +event ssl_heartbeat(c: connection, is_orig: bool, length: count, heartbeat_type: count, payload_length: count, payload: string) { if ( heartbeat_type == 1 ) { diff --git a/src/analyzer/protocol/ssl/events.bif b/src/analyzer/protocol/ssl/events.bif index e720089f45..a11e7bcc68 100644 --- a/src/analyzer/protocol/ssl/events.bif +++ b/src/analyzer/protocol/ssl/events.bif @@ -141,4 +141,4 @@ event ssl_session_ticket_handshake%(c: connection, ticket_lifetime_hint: count, event ssl_encrypted_heartbeat%(c: connection, is_orig: bool, length: count%); -event ssl_heartbeat%(c: connection, is_orig: bool, length: count, heartbeat_type: count, payload_length: count%); +event ssl_heartbeat%(c: connection, is_orig: bool, length: count, heartbeat_type: count, payload_length: count, payload: string%); diff --git a/src/analyzer/protocol/ssl/ssl-analyzer.pac b/src/analyzer/protocol/ssl/ssl-analyzer.pac index d0ac88a5a1..29240161d1 100644 --- a/src/analyzer/protocol/ssl/ssl-analyzer.pac +++ b/src/analyzer/protocol/ssl/ssl-analyzer.pac @@ -325,11 +325,11 @@ refine connection SSL_Conn += { return true; %} - function proc_heartbeat(rec : SSLRecord, type: uint8, payload_length: uint16) : bool + function proc_heartbeat(rec : SSLRecord, type: uint8, payload_length: uint16, data: bytestring) : bool %{ BifEvent::generate_ssl_heartbeat(bro_analyzer(), - bro_analyzer()->Conn(), ${rec.is_orig}, ${rec.length}, type, payload_length); - + bro_analyzer()->Conn(), ${rec.is_orig}, ${rec.length}, type, payload_length, + new StringVal(data.length(), (const char*) data.data())); return true; %} @@ -353,7 +353,7 @@ refine typeattr ApplicationData += &let { }; refine typeattr Heartbeat += &let { - proc : bool = $context.connection.proc_heartbeat(rec, type, payload_length); + proc : bool = $context.connection.proc_heartbeat(rec, type, payload_length, data); }; refine typeattr ClientHello += &let { diff --git a/src/analyzer/protocol/ssl/ssl-protocol.pac b/src/analyzer/protocol/ssl/ssl-protocol.pac index acded2dbf9..f8645410dc 100644 --- a/src/analyzer/protocol/ssl/ssl-protocol.pac +++ b/src/analyzer/protocol/ssl/ssl-protocol.pac @@ -233,7 +233,7 @@ type ApplicationData(rec: SSLRecord) = record { type Heartbeat(rec: SSLRecord) = record { type : uint8; payload_length : uint16; - data : bytestring &restofdata &transient; + data : bytestring &restofdata; }; ###################################################################### From 2414aaf4bb9e1a9ba9aafbf748f2905311f8cd8e Mon Sep 17 00:00:00 2001 From: Bernhard Amann Date: Tue, 8 Apr 2014 21:57:37 -0700 Subject: [PATCH 10/11] enable detection of encrypted heartbleeds. --- scripts/policy/protocols/ssl/heartbleed.bro | 2 +- src/analyzer/protocol/ssl/ssl-protocol.pac | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/policy/protocols/ssl/heartbleed.bro b/scripts/policy/protocols/ssl/heartbleed.bro index 19728b1d0c..0049c4c51f 100644 --- a/scripts/policy/protocols/ssl/heartbleed.bro +++ b/scripts/policy/protocols/ssl/heartbleed.bro @@ -84,7 +84,7 @@ event ssl_encrypted_heartbeat(c: connection, is_orig: bool, length: count) } else { - if ( c$ssl?$last_originator_heartbeat_request_size && c$ssl$last_originator_heartbeat_request_size > length ) + if ( c$ssl?$last_originator_heartbeat_request_size && c$ssl$last_originator_heartbeat_request_size < length ) { NOTICE([$note=SSL_Heartbeat_Encrypted_Attack_Success, $msg="An Encrypted TLS heartbleed attack was probably detected!", diff --git a/src/analyzer/protocol/ssl/ssl-protocol.pac b/src/analyzer/protocol/ssl/ssl-protocol.pac index f8645410dc..b8e5c9f624 100644 --- a/src/analyzer/protocol/ssl/ssl-protocol.pac +++ b/src/analyzer/protocol/ssl/ssl-protocol.pac @@ -52,7 +52,7 @@ type SSLRecord(is_orig: bool) = record { }; type RecordText(rec: SSLRecord) = case $context.connection.state() of { - STATE_ABBREV_SERVER_ENCRYPTED, STATE_CLIENT_ENCRYPTED, + STATE_ABBREV_SERVER_ENCRYPTED, STATE_CLIENT_ENCRYPTED, STATE_CLIENT_FINISHED, STATE_COMM_ENCRYPTED, STATE_CONN_ESTABLISHED -> ciphertext : CiphertextRecord(rec); default From ef41cc7189cf199d8d229aa47acd58b37e6d0eb4 Mon Sep 17 00:00:00 2001 From: Bernhard Amann Date: Wed, 16 Apr 2014 10:48:22 -0700 Subject: [PATCH 11/11] Nicer notices for heartbleed. Duplicates are now excluded and the notice texts contain a bit more useful information. --- scripts/policy/protocols/ssl/heartbleed.bro | 40 +++++++++++++-------- 1 file changed, 26 insertions(+), 14 deletions(-) diff --git a/scripts/policy/protocols/ssl/heartbleed.bro b/scripts/policy/protocols/ssl/heartbleed.bro index 0049c4c51f..dc38c66f73 100644 --- a/scripts/policy/protocols/ssl/heartbleed.bro +++ b/scripts/policy/protocols/ssl/heartbleed.bro @@ -15,11 +15,9 @@ export { redef enum Notice::Type += { ## Indicates that a host performing a heartbleed attack. SSL_Heartbeat_Attack, - ## Indicates that a host performing a heartbleed attack was successful. + ## Indicates that a host performing a heartbleed attack was probably successful. SSL_Heartbeat_Attack_Success, - ## Indivcates that a host performing a heartbleed attack after encryption was started was probably successful - SSL_Heartbeat_Encrypted_Attack_Success, - ## Indicates we saw heartbeet requests with odd length. Probably an attack. + ## Indicates we saw heartbeat requests with odd length. Probably an attack. SSL_Heartbeat_Odd_Length, ## Indicates we saw many heartbeat requests without an reply. Might be an attack. SSL_Heartbeat_Many_Requests @@ -37,7 +35,8 @@ event ssl_heartbeat(c: connection, is_orig: bool, length: count, heartbeat_type: c$ssl$heartbleed_detected = T; NOTICE([$note=SSL_Heartbeat_Attack, $msg=fmt("An TLS heartbleed attack was detected! Record length %d, payload length %d", length, payload_length), - $conn=c + $conn=c, + $identifier=cat(c$uid, length, payload_length) ]); } } @@ -45,8 +44,9 @@ event ssl_heartbeat(c: connection, is_orig: bool, length: count, heartbeat_type: if ( heartbeat_type == 2 && c$ssl$heartbleed_detected ) { NOTICE([$note=SSL_Heartbeat_Attack_Success, - $msg="An TLS heartbleed attack was detected and probably exploited", - $conn=c + $msg=fmt("An TLS heartbleed attack detected before was probably exploited. Transmitted payload length in first packet: %d", payload_length), + $conn=c, + $identifier=c$uid ]); } } @@ -60,16 +60,26 @@ event ssl_encrypted_heartbeat(c: connection, is_orig: bool, length: count) if ( c$ssl$originator_heartbeats > c$ssl$responder_heartbeats + 3 ) NOTICE([$note=SSL_Heartbeat_Many_Requests, - $msg="Seeing more than 3 heartbeat requests without replies from server. Possible attack?", + $msg=fmt("Seeing more than 3 heartbeat requests without replies from server. Possible attack. Client count: %d, server count: %d", c$ssl$originator_heartbeats, c$ssl$responder_heartbeats), $conn=c, - $n=(c$ssl$originator_heartbeats-c$ssl$responder_heartbeats) + $n=(c$ssl$originator_heartbeats-c$ssl$responder_heartbeats), + $identifier=fmt("%s%d", c$uid, c$ssl$responder_heartbeats/1000) # re-throw every 1000 heartbeats + ]); + + if ( c$ssl$responder_heartbeats > c$ssl$originator_heartbeats + 3 ) + NOTICE([$note=SSL_Heartbeat_Many_Requests, + $msg=fmt("Server is sending more heartbleed responsed than requests were seen. Possible attack. Client count: %d, server count: %d", c$ssl$originator_heartbeats, c$ssl$responder_heartbeats), + $conn=c, + $n=(c$ssl$originator_heartbeats-c$ssl$responder_heartbeats), + $identifier=fmt("%s%d", c$uid, c$ssl$responder_heartbeats/1000) # re-throw every 1000 heartbeats ]); if ( is_orig && length < 19 ) NOTICE([$note=SSL_Heartbeat_Odd_Length, - $msg="Heartbeat message smaller than minimum length. Probable attack.", + $msg=fmt("Heartbeat message smaller than minimum required length. Probable attack. Message length: %d", length), $conn=c, - $n=length + $n=length, + $identifier=cat(c$uid, length) ]); if ( is_orig ) @@ -86,9 +96,11 @@ event ssl_encrypted_heartbeat(c: connection, is_orig: bool, length: count) { if ( c$ssl?$last_originator_heartbeat_request_size && c$ssl$last_originator_heartbeat_request_size < length ) { - NOTICE([$note=SSL_Heartbeat_Encrypted_Attack_Success, - $msg="An Encrypted TLS heartbleed attack was probably detected!", - $conn=c + NOTICE([$note=SSL_Heartbeat_Attack_Success, + $msg=fmt("An Encrypted TLS heartbleed attack was probably detected! First packet client record length %d, first packet server record length %d", + c$ssl?$last_originator_heartbeat_request_size, c$ssl$last_originator_heartbeat_request_size), + $conn=c, + $identifier=c$uid # only throw once per connection ]); } else if ( ! c$ssl?$last_originator_heartbeat_request_size )