mirror of
https://github.com/zeek/zeek.git
synced 2025-10-13 03:58:20 +00:00
Merge branch 'topic/bernhard/heartbeat' into topic/bernhard/ssl-analyzer
Conflicts: src/analyzer/protocol/ssl/ssl-analyzer.pac src/analyzer/protocol/ssl/ssl-protocol.pac
This commit is contained in:
commit
8ce3cf65f2
6 changed files with 151 additions and 2 deletions
114
scripts/policy/protocols/ssl/heartbleed.bro
Normal file
114
scripts/policy/protocols/ssl/heartbleed.bro
Normal file
|
@ -0,0 +1,114 @@
|
||||||
|
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;
|
||||||
|
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,
|
||||||
|
## 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
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
event ssl_heartbeat(c: connection, is_orig: bool, length: count, heartbeat_type: count, payload_length: count, payload: string)
|
||||||
|
{
|
||||||
|
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=fmt("An TLS heartbleed attack was detected! Record length %d, payload length %d", length, payload_length),
|
||||||
|
$conn=c,
|
||||||
|
$identifier=cat(c$uid, length, payload_length)
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( heartbeat_type == 2 && c$ssl$heartbleed_detected )
|
||||||
|
{
|
||||||
|
NOTICE([$note=SSL_Heartbeat_Attack_Success,
|
||||||
|
$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
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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=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),
|
||||||
|
$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=fmt("Heartbeat message smaller than minimum required length. Probable attack. Message length: %d", length),
|
||||||
|
$conn=c,
|
||||||
|
$n=length,
|
||||||
|
$identifier=cat(c$uid, 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_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 )
|
||||||
|
{
|
||||||
|
c$ssl$last_responder_heartbeat_request_size = length;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( c$ssl?$last_originator_heartbeat_request_size )
|
||||||
|
delete c$ssl$last_originator_heartbeat_request_size;
|
||||||
|
}
|
||||||
|
}
|
|
@ -81,3 +81,5 @@
|
||||||
# Detect SHA1 sums in Team Cymru's Malware Hash Registry.
|
# Detect SHA1 sums in Team Cymru's Malware Hash Registry.
|
||||||
@load frameworks/files/detect-MHR
|
@load frameworks/files/detect-MHR
|
||||||
|
|
||||||
|
# Load heartbleed detection. Only superficially tested, might contain bugs.
|
||||||
|
@load policy/protocols/ssl/heartbleed
|
||||||
|
|
|
@ -138,3 +138,7 @@ event ssl_alert%(c: connection, is_orig: bool, level: count, desc: count%);
|
||||||
## .. bro:see:: ssl_client_hello ssl_established ssl_extension ssl_server_hello
|
## .. bro:see:: ssl_client_hello ssl_established ssl_extension ssl_server_hello
|
||||||
## ssl_alert
|
## ssl_alert
|
||||||
event ssl_session_ticket_handshake%(c: connection, ticket_lifetime_hint: count, ticket: string%);
|
event ssl_session_ticket_handshake%(c: connection, ticket_lifetime_hint: count, ticket: string%);
|
||||||
|
|
||||||
|
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, payload: string%);
|
||||||
|
|
|
@ -278,8 +278,21 @@ refine connection SSL_Conn += {
|
||||||
bro_analyzer()->Conn());
|
bro_analyzer()->Conn());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ( ${rec.content_type} == HEARTBEAT )
|
||||||
|
BifEvent::generate_ssl_encrypted_heartbeat(bro_analyzer(),
|
||||||
|
bro_analyzer()->Conn(), ${rec.is_orig}, ${rec.length});
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
%}
|
%}
|
||||||
|
|
||||||
|
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,
|
||||||
|
new StringVal(data.length(), (const char*) data.data()));
|
||||||
|
return true;
|
||||||
|
%}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#refine typeattr ChangeCipherSpec += &let {
|
#refine typeattr ChangeCipherSpec += &let {
|
||||||
|
@ -299,6 +312,10 @@ refine typeattr V2Error += &let {
|
||||||
# proc : bool = $context.connection.proc_application_data(rec);
|
# proc : bool = $context.connection.proc_application_data(rec);
|
||||||
#};
|
#};
|
||||||
|
|
||||||
|
refine typeattr Heartbeat += &let {
|
||||||
|
proc : bool = $context.connection.proc_heartbeat(rec, type, payload_length, data);
|
||||||
|
};
|
||||||
|
|
||||||
refine typeattr ClientHello += &let {
|
refine typeattr ClientHello += &let {
|
||||||
proc : bool = $context.connection.proc_client_hello(rec, client_version,
|
proc : bool = $context.connection.proc_client_hello(rec, client_version,
|
||||||
gmt_unix_time, random_bytes,
|
gmt_unix_time, random_bytes,
|
||||||
|
|
|
@ -12,6 +12,7 @@ enum ContentType {
|
||||||
ALERT = 21,
|
ALERT = 21,
|
||||||
HANDSHAKE = 22,
|
HANDSHAKE = 22,
|
||||||
APPLICATION_DATA = 23,
|
APPLICATION_DATA = 23,
|
||||||
|
HEARTBEAT = 24,
|
||||||
V2_ERROR = 300,
|
V2_ERROR = 300,
|
||||||
V2_CLIENT_HELLO = 301,
|
V2_CLIENT_HELLO = 301,
|
||||||
V2_CLIENT_MASTER_KEY = 302,
|
V2_CLIENT_MASTER_KEY = 302,
|
||||||
|
|
|
@ -39,13 +39,13 @@ type SSLRecord(is_orig: bool) = record {
|
||||||
$context.connection.determine_ssl_version(head0, head1, head2);
|
$context.connection.determine_ssl_version(head0, head1, head2);
|
||||||
|
|
||||||
content_type : int = case version of {
|
content_type : int = case version of {
|
||||||
UNKNOWN_VERSION -> 0;
|
# UNKNOWN_VERSION -> 0; assume tls on unknown version
|
||||||
SSLv20 -> head2+300;
|
SSLv20 -> head2+300;
|
||||||
default -> head0;
|
default -> head0;
|
||||||
};
|
};
|
||||||
|
|
||||||
length : int = case version of {
|
length : int = case version of {
|
||||||
UNKNOWN_VERSION -> 0;
|
# UNKNOWN_VERSION -> 0; assume tls on unknown version
|
||||||
SSLv20 -> (((head0 & 0x7f) << 8) | head1) - 3;
|
SSLv20 -> (((head0 & 0x7f) << 8) | head1) - 3;
|
||||||
default -> (head3 << 8) | head4;
|
default -> (head3 << 8) | head4;
|
||||||
};
|
};
|
||||||
|
@ -62,6 +62,7 @@ type PlaintextRecord(rec: SSLRecord) = case rec.content_type of {
|
||||||
CHANGE_CIPHER_SPEC -> ch_cipher : ChangeCipherSpec(rec);
|
CHANGE_CIPHER_SPEC -> ch_cipher : ChangeCipherSpec(rec);
|
||||||
ALERT -> alert : Alert(rec);
|
ALERT -> alert : Alert(rec);
|
||||||
HANDSHAKE -> handshake : Handshake(rec);
|
HANDSHAKE -> handshake : Handshake(rec);
|
||||||
|
HEARTBEAT -> heartbeat: Heartbeat(rec);
|
||||||
APPLICATION_DATA -> app_data : ApplicationData(rec);
|
APPLICATION_DATA -> app_data : ApplicationData(rec);
|
||||||
V2_ERROR -> v2_error : V2Error(rec);
|
V2_ERROR -> v2_error : V2Error(rec);
|
||||||
V2_CLIENT_HELLO -> v2_client_hello : V2ClientHello(rec);
|
V2_CLIENT_HELLO -> v2_client_hello : V2ClientHello(rec);
|
||||||
|
@ -164,6 +165,16 @@ type ApplicationData(rec: SSLRecord) = record {
|
||||||
data : bytestring &restofdata &transient;
|
data : bytestring &restofdata &transient;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
######################################################################
|
||||||
|
# V3 Heartbeat
|
||||||
|
######################################################################
|
||||||
|
|
||||||
|
type Heartbeat(rec: SSLRecord) = record {
|
||||||
|
type : uint8;
|
||||||
|
payload_length : uint16;
|
||||||
|
data : bytestring &restofdata;
|
||||||
|
};
|
||||||
|
|
||||||
######################################################################
|
######################################################################
|
||||||
# Handshake Protocol (7.4.)
|
# Handshake Protocol (7.4.)
|
||||||
######################################################################
|
######################################################################
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue