Merge remote-tracking branch 'origin/topic/bernhard/ssl-analyzer'

* origin/topic/bernhard/ssl-analyzer:
  Fix a few failing tests
  Add very basic ocsp stapling support.
  Add documentation, consts and tests for the new events.
  Support parsing of several TLS extensions.
  Make SSL/TLS version detection less brittle.
  Nicer notices for heartbleed.
  rip out state handline from ssl analyzer.
  enable detection of encrypted heartbleeds.
  also extract payload data in ssl_heartbeat
  add to local.bro, add disclaimer
  make tls heartbeat messages a bit better.
  fix tabs.
  polish script and probably detect encrypted attacks too.
  detect and alert on simple case of heartbleed
  default to TLS when not being able to determine version
  add is_orig to heartbeat event
  Throw new event for heartbeat messages.

BIT-1178 #merged
This commit is contained in:
Robin Sommer 2014-04-24 16:57:54 -07:00
commit 201fc7b25a
28 changed files with 800 additions and 376 deletions

20
CHANGES
View file

@ -1,4 +1,24 @@
2.2-377 | 2014-04-24 16:57:54 -0700
* A larger set of SSL improvements and extensions. Addresses
BIT-1178. (Bernhard Amann)
- Fixes TLS protocol version detection. It also should
bail-out correctly on non-tls-connections now
- Adds support for a few TLS extensions, including
server_name, alpn, and ec-curves.
- Adds support for the heartbeat events.
- Add Heartbleed detector script.
- Adds basic support for OCSP stapling.
* Fix parsing of DNS TXT RRs w/ multiple character-strings.
Addresses BIT-1156. (Jon Siwek)
2.2-353 | 2014-04-24 16:12:30 -0700
* Adapt HTTP partial content to cache file analysis IDs. (Jon Siwek)

5
NEWS
View file

@ -40,6 +40,11 @@ New Functionality
magic matches and their corresponding strength against a given chunk
of data.
- The SSL analyzer now has support heartbeats as well as for a few
extensions, including server_name, alpn, and ec-curves.
- The SSL analyzer comes with Heartbleed detector script in
protocols/ssl/heartbleed.bro.
Changed Functionality
---------------------

View file

@ -1 +1 @@
2.2-353
2.2-377

View file

@ -185,6 +185,7 @@ export {
["RPC_underflow"] = ACTION_LOG,
["RST_storm"] = ACTION_LOG,
["RST_with_data"] = ACTION_LOG,
["SSL_many_server_names"] = ACTION_LOG,
["simultaneous_open"] = ACTION_LOG_PER_CONN,
["spontaneous_FIN"] = ACTION_IGNORE,
["spontaneous_RST"] = ACTION_IGNORE,

View file

@ -94,6 +94,49 @@ export {
[65281] = "renegotiation_info"
} &default=function(i: count):string { return fmt("unknown-%d", i); };
## Mapping between numeric codes and human readable string for SSL/TLS elliptic curves.
# See http://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-8
const ec_curves: table[count] of string = {
[1] = "sect163k1",
[2] = "sect163r1",
[3] = "sect163r2",
[4] = "sect193r1",
[5] = "sect193r2",
[6] = "sect233k1",
[7] = "sect233r1",
[8] = "sect239k1",
[9] = "sect283k1",
[10] = "sect283r1",
[11] = "sect409k1",
[12] = "sect409r1",
[13] = "sect571k1",
[14] = "sect571r1",
[15] = "secp160k1",
[16] = "secp160r1",
[17] = "secp160r2",
[18] = "secp192k1",
[19] = "secp192r1",
[20] = "secp224k1",
[21] = "secp224r1",
[22] = "secp256k1",
[23] = "secp256r1",
[24] = "secp384r1",
[25] = "secp521r1",
[26] = "brainpoolP256r1",
[27] = "brainpoolP384r1",
[28] = "brainpoolP512r1",
[0xFF01] = "arbitrary_explicit_prime_curves",
[0xFF02] = "arbitrary_explicit_char2_curves"
} &default=function(i: count):string { return fmt("unknown-%d", i); };
## Mapping between numeric codes and human readable string for SSL/TLC EC point formats.
# See http://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-9
const ec_point_formats: table[count] of string = {
[0] = "uncompressed",
[1] = "ansiX962_compressed_prime",
[2] = "ansiX962_compressed_char2"
} &default=function(i: count):string { return fmt("unknown-%d", i); };
# SSLv2
const SSLv20_CK_RC4_128_WITH_MD5 = 0x010080;
const SSLv20_CK_RC4_128_EXPORT40_WITH_MD5 = 0x020080;
@ -821,42 +864,4 @@ export {
[TLS_EMPTY_RENEGOTIATION_INFO_SCSV] = "TLS_EMPTY_RENEGOTIATION_INFO_SCSV",
} &default=function(i: count):string { return fmt("unknown-%d", i); };
## Mapping between the constants and string values for SSL/TLS errors.
const x509_errors: table[count] of string = {
[0] = "ok",
[1] = "unable to get issuer cert",
[2] = "unable to get crl",
[3] = "unable to decrypt cert signature",
[4] = "unable to decrypt crl signature",
[5] = "unable to decode issuer public key",
[6] = "cert signature failure",
[7] = "crl signature failure",
[8] = "cert not yet valid",
[9] = "cert has expired",
[10] = "crl not yet valid",
[11] = "crl has expired",
[12] = "error in cert not before field",
[13] = "error in cert not after field",
[14] = "error in crl last update field",
[15] = "error in crl next update field",
[16] = "out of mem",
[17] = "depth zero self signed cert",
[18] = "self signed cert in chain",
[19] = "unable to get issuer cert locally",
[20] = "unable to verify leaf signature",
[21] = "cert chain too long",
[22] = "cert revoked",
[23] = "invalid ca",
[24] = "path length exceeded",
[25] = "invalid purpose",
[26] = "cert untrusted",
[27] = "cert rejected",
[28] = "subject issuer mismatch",
[29] = "akid skid mismatch",
[30] = "akid issuer serial mismatch",
[31] = "keyusage no certsign",
[32] = "unable to get crl issuer",
[33] = "unhandled critical extension",
} &default=function(i: count):string { return fmt("unknown-%d", i); };
}

View file

@ -159,12 +159,16 @@ event ssl_server_hello(c: connection, version: count, possible_ts: time, server_
c$ssl$cipher = cipher_desc[cipher];
}
event ssl_extension(c: connection, is_orig: bool, code: count, val: string) &priority=5
event ssl_extension_server_name(c: connection, is_orig: bool, names: string_vec) &priority=5
{
set_session(c);
if ( is_orig && extensions[code] == "server_name" )
c$ssl$server_name = sub_bytes(val, 6, |val|);
if ( is_orig && |names| > 0 )
{
c$ssl$server_name = names[0];
if ( |names| > 1 )
event conn_weird("SSL_many_server_names", c, cat(names));
}
}
event ssl_alert(c: connection, is_orig: bool, level: count, desc: count) &priority=5

View file

@ -0,0 +1,121 @@
##! Detect the TLS heartbleed attack. See http://heartbleed.com for more.
@load base/protocols/ssl
@load base/frameworks/notice
module Heartbleed;
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
};
}
# Do not disable analyzers after detection - otherwhise we will not notice
# encrypted attacks.
redef SSL::disable_analyzer_after_detection=F;
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;
};
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;
}
}

View file

@ -81,3 +81,6 @@
# Detect SHA1 sums in Team Cymru's Malware Hash Registry.
@load frameworks/files/detect-MHR
# Uncomment the following line to enable detection of the heartbleed attack. Enabling
# this might impact performance a bit.
# @load policy/protocols/ssl/heartbleed

View file

@ -85,6 +85,7 @@
@load protocols/ssh/software.bro
@load protocols/ssl/expiring-certs.bro
@load protocols/ssl/extract-certs-pem.bro
@load protocols/ssl/heartbleed.bro
@load protocols/ssl/known-certs.bro
@load protocols/ssl/log-hostcerts-only.bro
#@load protocols/ssl/notary.bro

View file

@ -66,6 +66,8 @@ event ssl_server_hello%(c: connection, version: count, possible_ts: time, server
## information out of that as it can. This event provides access to any
## extensions either side sends as part of an extended *hello* message.
##
## Note that Bro offers more specialized events for a few extensions.
##
## c: The connection.
##
## is_orig: True if event is raised for originator side of the connection.
@ -77,9 +79,81 @@ event ssl_server_hello%(c: connection, version: count, possible_ts: time, server
## val: The raw extension value that was sent in the message.
##
## .. bro:see:: ssl_alert ssl_client_hello ssl_established ssl_server_hello
## ssl_session_ticket_handshake
## ssl_session_ticket_handshake ssl_extension_ec_point_formats
## ssl_extension_elliptic_curves ssl_extension_application_layer_protocol_negotiation
## ssl_extension_server_name
event ssl_extension%(c: connection, is_orig: bool, code: count, val: string%);
## Generated for an SSL/TLS Elliptic Curves extension. This TLS extension is
## defined in :rfc:`4492` and sent by the client in the initial handshake. It gives
## the list of elliptic curves supported by the client.
##
## c: The connection.
##
## is_orig: True if event is raised for originator side of the connection.
##
## curves: List of supported elliptic curves.
##
## .. bro:see:: ssl_alert ssl_client_hello ssl_established ssl_server_hello
## ssl_session_ticket_handshake ssl_extension
## ssl_extension_ec_point_formats ssl_extension_application_layer_protocol_negotiation
## ssl_extension_server_name
event ssl_extension_elliptic_curves%(c: connection, is_orig: bool, curves: index_vec%);
## Generated for an SSL/TLS Supported Point Formats extension. This TLS extension
## is defined in :rfc:`4492` and sent by the client and/or server in the initial
## handshake. It gives the list of elliptic curve point formats supported by the
## client.
##
## c: The connection.
##
## is_orig: True if event is raised for originator side of the connection.
##
## point_formats: List of supported point formats.
##
## .. bro:see:: ssl_alert ssl_client_hello ssl_established ssl_server_hello
## ssl_session_ticket_handshake ssl_extension
## ssl_extension_elliptic_curves ssl_extension_application_layer_protocol_negotiation
## ssl_extension_server_name
event ssl_extension_ec_point_formats%(c: connection, is_orig: bool, point_formats: index_vec%);
## Generated for an SSL/TLS Application-Layer Protocol Negotiation extension.
## This TLS extension is defined in draft-ietf-tls-applayerprotoneg and sent in
## the initial handshake. It contains the list of client supported application
## protocols by the client or the server, respectovely.
##
## At the moment it is mostly used to negotiate the use of SPDY / HTTP2-drafts.
##
## c: The connection.
##
## is_orig: True if event is raised for originator side of the connection.
##
## protocols: List of supported application layer protocols.
##
## .. bro:see:: ssl_alert ssl_client_hello ssl_established ssl_server_hello
## ssl_session_ticket_handshake ssl_extension
## ssl_extension_elliptic_curves ssl_extension_ec_point_formats
## ssl_extension_server_name
event ssl_extension_application_layer_protocol_negotiation%(c: connection, is_orig: bool, protocols: string_vec%);
## Generated for an SSL/TLS Server Name extension. This SSL/TLS extension is
## defined in :rfc:`3546` and sent by the client in the initial handshake. It
## contains the name of the server it is contacting. This information can be used
## by the server to choose the correct certificate for the host the client wants to
## contact.
##
## c: The connection.
##
## is_orig: True if event is raised for originator side of the connection.
##
## protocols: List of supported application layer protocols.
##
## .. bro:see:: ssl_alert ssl_client_hello ssl_established ssl_server_hello
## ssl_session_ticket_handshake ssl_extension
## ssl_extension_elliptic_curves ssl_extension_ec_point_formats
## ssl_extension_application_layer_protocol_negotiation
event ssl_extension_server_name%(c: connection, is_orig: bool, names: string_vec%);
## Generated at the end of an SSL/TLS handshake. SSL/TLS sessions start with
## an unencrypted handshake, and Bro extracts as much information out of that
## as it can. This event signals the time when an SSL/TLS has finished the
@ -138,3 +212,52 @@ 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%);
## Generated for SSL/TLS heartbeat messages that are sent before session encryption
## starts. Generally heartbeat messages should rarely be seen in normal TLS traffic.
## Heartbeats are described in :rfc:`6520`.
##
## c: The connection.
##
## is_orig: True if event is raised for originator side of the connection.
##
## length: length of the entire heartbeat message.
##
## heartbeat_type: type of the heartbeat message. Per RFC, 1 = request, 2 = response
##
## payload_length: length of the payload of the heartbeat message, according to packet field
##
## payload: payload contained in the heartbeat message. Size can differ from payload_length,
## if payload_length and actual packet length disagree.
##
## .. bro:see:: ssl_client_hello ssl_established ssl_extension ssl_server_hello
## ssl_alert ssl_encrypted_heartbeat
event ssl_heartbeat%(c: connection, is_orig: bool, length: count, heartbeat_type: count, payload_length: count, payload: string%);
## Generated for SSL/TLS heartbeat messages that are sent after session encryption
## started. Generally heartbeat messages should rarely be seen in normal TLS traffic.
## Heartbeats are described in :rfc:`6520`.
##
## Note that :bro:id:`SSL::disable_analyzer_after_detection` has to be set to false.
## Otherwhise this event will never be thrown.
##
## c: The connection.
##
## is_orig: True if event is raised for originator side of the connection.
##
## length: length of the entire heartbeat message.
##
## .. bro:see:: ssl_client_hello ssl_established ssl_extension ssl_server_hello
## ssl_alert ssl_heartbeat
event ssl_encrypted_heartbeat%(c: connection, is_orig: bool, length: count%);
## This event contains the OCSP response contained in a Certificate Status Request
## message, when the client requested OCSP stapling and the server supports it. See
## description in :rfc:`6066`
##
## c: The connection.
##
## is_orig: True if event is raised for originator side of the connection.
##
## response: OCSP data.
event ssl_stapled_ocsp%(c: connection, is_orig: bool, response: string%);

View file

@ -86,46 +86,9 @@ function version_ok(vers : uint16) : bool
refine connection SSL_Conn += {
%member{
int eof;
%}
%init{
eof=0;
%}
#%eof{
# if ( ! eof &&
# state_ != STATE_CONN_ESTABLISHED &&
# state_ != STATE_TRACK_LOST &&
# state_ != STATE_INITIAL )
# bro_analyzer()->ProtocolViolation(fmt("unexpected end of connection in state %s",
# state_label(state_).c_str()));
# ++eof;
#%}
%cleanup{
%}
function proc_change_cipher_spec(rec: SSLRecord) : bool
%{
if ( state_ == STATE_TRACK_LOST )
bro_analyzer()->ProtocolViolation(fmt("unexpected ChangeCipherSpec from %s at state %s",
orig_label(${rec.is_orig}).c_str(),
state_label(old_state_).c_str()));
return true;
%}
function proc_application_data(rec: SSLRecord) : bool
%{
if ( state_ != STATE_CONN_ESTABLISHED &&
(state_ != STATE_CLIENT_FINISHED && ! ${rec.is_orig}) )
bro_analyzer()->ProtocolViolation(fmt("unexpected ApplicationData from %s at state %s",
orig_label(${rec.is_orig}).c_str(),
state_label(old_state_).c_str()));
return true;
%}
function proc_alert(rec: SSLRecord, level : int, desc : int) : bool
%{
BifEvent::generate_ssl_alert(bro_analyzer(), bro_analyzer()->Conn(),
@ -217,12 +180,106 @@ refine connection SSL_Conn += {
return true;
%}
function proc_ssl_extension(rec: SSLRecord, type: int, data: bytestring) : bool
function proc_ssl_extension(rec: SSLRecord, type: int, sourcedata: const_bytestring) : bool
%{
// We cheat a little bit here. We want to throw this event
// for every extension we encounter, even those that are
// handled by more specialized events later. To access the
// parsed data, we use sourcedata, which contains the whole
// data blob of the extension, including headers. We skip
// over those (4 bytes).
size_t length = sourcedata.length();
if ( length < 4 )
{
// This should be impossible due to the binpac parser
// and protocol description
bro_analyzer()->ProtocolViolation(fmt("Impossible extension length: %lu", length));
return true;
}
length -= 4;
const unsigned char* data = sourcedata.begin() + 4;
if ( ssl_extension )
BifEvent::generate_ssl_extension(bro_analyzer(),
bro_analyzer()->Conn(), ${rec.is_orig}, type,
new StringVal(data.length(), (const char*) data.data()));
new StringVal(length, reinterpret_cast<const char*>(data)));
return true;
%}
function proc_ec_point_formats(rec: SSLRecord, point_format_list: uint8[]) : bool
%{
VectorVal* points = new VectorVal(internal_type("index_vec")->AsVectorType());
if ( point_format_list )
{
for ( unsigned int i = 0; i < point_format_list->size(); ++i )
points->Assign(i, new Val((*point_format_list)[i], TYPE_COUNT));
}
BifEvent::generate_ssl_extension_ec_point_formats(bro_analyzer(), bro_analyzer()->Conn(),
${rec.is_orig}, points);
return true;
%}
function proc_elliptic_curves(rec: SSLRecord, list: uint16[]) : bool
%{
VectorVal* curves = new VectorVal(internal_type("index_vec")->AsVectorType());
if ( list )
{
for ( unsigned int i = 0; i < list->size(); ++i )
curves->Assign(i, new Val((*list)[i], TYPE_COUNT));
}
BifEvent::generate_ssl_extension_elliptic_curves(bro_analyzer(), bro_analyzer()->Conn(),
${rec.is_orig}, curves);
return true;
%}
function proc_apnl(rec: SSLRecord, protocols: ProtocolName[]) : bool
%{
VectorVal* plist = new VectorVal(internal_type("string_vec")->AsVectorType());
if ( protocols )
{
for ( unsigned int i = 0; i < protocols->size(); ++i )
plist->Assign(i, new StringVal((*protocols)[i]->name().length(), (const char*) (*protocols)[i]->name().data()));
}
BifEvent::generate_ssl_extension_application_layer_protocol_negotiation(bro_analyzer(), bro_analyzer()->Conn(),
${rec.is_orig}, plist);
return true;
%}
function proc_server_name(rec: SSLRecord, list: ServerName[]) : bool
%{
VectorVal* servers = new VectorVal(internal_type("string_vec")->AsVectorType());
if ( list )
{
for ( unsigned int i = 0, j = 0; i < list->size(); ++i )
{
ServerName* servername = (*list)[i];
if ( servername->name_type() != 0 )
{
bro_analyzer()->Weird(fmt("Encountered unknown type in server name ssl extension: %d", servername->name_type()));
continue;
}
if ( servername->host_name() )
servers->Assign(j++, new StringVal(servername->host_name()->host_name().length(), (const char*) servername->host_name()->host_name().data()));
else
bro_analyzer()->Weird("Empty server_name extension in ssl connection");
}
}
BifEvent::generate_ssl_extension_server_name(bro_analyzer(), bro_analyzer()->Conn(),
${rec.is_orig}, servers);
return true;
%}
@ -263,9 +320,9 @@ refine connection SSL_Conn += {
return ret;
%}
function proc_v3_certificate(rec: SSLRecord, cl : CertificateList) : bool
function proc_v3_certificate(rec: SSLRecord, cl : X509Certificate[]) : bool
%{
vector<X509Certificate*>* certs = cl->val();
vector<X509Certificate*>* certs = cl;
vector<bytestring>* cert_list = new vector<bytestring>();
std::transform(certs->begin(), certs->end(),
@ -278,11 +335,6 @@ refine connection SSL_Conn += {
function proc_v2_client_master_key(rec: SSLRecord, cipher_kind: int) : bool
%{
if ( state_ == STATE_TRACK_LOST )
bro_analyzer()->ProtocolViolation(fmt("unexpected v2 client master key message from %s in state %s",
orig_label(${rec.is_orig}).c_str(),
state_label(old_state_).c_str()));
BifEvent::generate_ssl_established(bro_analyzer(),
bro_analyzer()->Conn());
@ -296,17 +348,6 @@ refine connection SSL_Conn += {
return true;
%}
function proc_handshake(hs: Handshake, is_orig: bool) : bool
%{
if ( state_ == STATE_TRACK_LOST )
bro_analyzer()->ProtocolViolation(fmt("unexpected Handshake message %s from %s in state %s",
handshake_type_label(${hs.msg_type}).c_str(),
orig_label(is_orig).c_str(),
state_label(old_state_).c_str()));
return true;
%}
function proc_unknown_record(rec: SSLRecord) : bool
%{
bro_analyzer()->ProtocolViolation(fmt("unknown SSL record type (%d) from %s",
@ -317,25 +358,48 @@ refine connection SSL_Conn += {
function proc_ciphertext_record(rec : SSLRecord) : bool
%{
if ( state_ == STATE_TRACK_LOST )
bro_analyzer()->ProtocolViolation(fmt("unexpected ciphertext record from %s in state %s",
orig_label(${rec.is_orig}).c_str(),
state_label(old_state_).c_str()));
else if ( state_ == STATE_CONN_ESTABLISHED &&
old_state_ == STATE_COMM_ENCRYPTED )
if ( client_state_ == STATE_ENCRYPTED &&
server_state_ == STATE_ENCRYPTED )
{
BifEvent::generate_ssl_established(bro_analyzer(),
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;
%}
};
refine typeattr ChangeCipherSpec += &let {
proc : bool = $context.connection.proc_change_cipher_spec(rec)
&requires(state_changed);
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;
%}
function proc_check_v2_server_hello_version(version: uint16) : bool
%{
if ( version != SSLv20 )
bro_analyzer()->ProtocolViolation(fmt("Invalid version in SSL server hello. Version: %d", version));
return true;
%}
function proc_certificate_status(rec : SSLRecord, status_type: uint8, response: bytestring) : bool
%{
if ( status_type == 1 ) // ocsp
{
BifEvent::generate_ssl_stapled_ocsp(bro_analyzer(),
bro_analyzer()->Conn(), ${rec.is_orig},
new StringVal(response.length(),
(const char*) response.data()));
}
return true;
%}
};
refine typeattr Alert += &let {
@ -346,57 +410,49 @@ refine typeattr V2Error += &let {
proc : bool = $context.connection.proc_alert(rec, -1, error_code);
};
refine typeattr ApplicationData += &let {
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 {
proc : bool = $context.connection.proc_client_hello(rec, client_version,
gmt_unix_time, random_bytes,
session_id, csuits, 0)
&requires(state_changed);
session_id, csuits, 0);
};
refine typeattr V2ClientHello += &let {
proc : bool = $context.connection.proc_client_hello(rec, client_version, 0,
challenge, session_id, 0, ciphers)
&requires(state_changed);
challenge, session_id, 0, ciphers);
};
refine typeattr ServerHello += &let {
proc : bool = $context.connection.proc_server_hello(rec, server_version,
gmt_unix_time, random_bytes, session_id, cipher_suite, 0,
compression_method)
&requires(state_changed);
compression_method);
};
refine typeattr V2ServerHello += &let {
proc : bool = $context.connection.proc_server_hello(rec, server_version, 0,
conn_id_data, 0, 0, ciphers, 0)
&requires(state_changed);
conn_id_data, 0, 0, ciphers, 0);
check_v2 : bool = $context.connection.proc_check_v2_server_hello_version(server_version);
cert : bool = $context.connection.proc_v2_certificate(rec, cert_data)
&requires(proc);
};
refine typeattr Certificate += &let {
proc : bool = $context.connection.proc_v3_certificate(rec, certificates)
&requires(state_changed);
proc : bool = $context.connection.proc_v3_certificate(rec, certificates);
};
refine typeattr V2ClientMasterKey += &let {
proc : bool = $context.connection.proc_v2_client_master_key(rec, cipher_kind)
&requires(state_changed);
proc : bool = $context.connection.proc_v2_client_master_key(rec, cipher_kind);
};
refine typeattr UnknownHandshake += &let {
proc : bool = $context.connection.proc_unknown_handshake(hs, is_orig);
};
refine typeattr Handshake += &let {
proc : bool = $context.connection.proc_handshake(this, rec.is_orig);
};
refine typeattr SessionTicketHandshake += &let {
proc : bool = $context.connection.proc_session_ticket_handshake(this, rec.is_orig);
}
@ -410,5 +466,25 @@ refine typeattr CiphertextRecord += &let {
}
refine typeattr SSLExtension += &let {
proc : bool = $context.connection.proc_ssl_extension(rec, type, data);
proc : bool = $context.connection.proc_ssl_extension(rec, type, sourcedata);
};
refine typeattr EcPointFormats += &let {
proc : bool = $context.connection.proc_ec_point_formats(rec, point_format_list);
};
refine typeattr EllipticCurves += &let {
proc : bool = $context.connection.proc_elliptic_curves(rec, elliptic_curve_list);
};
refine typeattr ApplicationLayerProtocolNegotiationExtension += &let {
proc : bool = $context.connection.proc_apnl(rec, protocol_name_list);
};
refine typeattr ServerNameExt += &let {
proc : bool = $context.connection.proc_server_name(rec, server_names);
};
refine typeattr CertificateStatus += &let {
proc : bool = $context.connection.proc_certificate_status(rec, status_type, response);
};

View file

@ -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,
@ -19,6 +20,7 @@ enum ContentType {
UNKNOWN_OR_V2_ENCRYPTED = 400
};
# If you add a new TLS version here, do not forget to also adjust the DPD signature.
enum SSLVersions {
UNKNOWN_VERSION = 0x0000,
SSLv20 = 0x0002,
@ -27,3 +29,34 @@ enum SSLVersions {
TLSv11 = 0x0302,
TLSv12 = 0x0303
};
enum SSLExtensions {
EXT_SERVER_NAME = 0,
EXT_MAX_FRAGMENT_LENGTH = 1,
EXT_CLIENT_CERTIFICATE_URL = 2,
EXT_TRUSTED_CA_KEYS = 3,
EXT_TRUNCATED_HMAC = 4,
EXT_STATUS_REQUEST = 5,
EXT_USER_MAPPING = 6,
EXT_CLIENT_AUTHZ = 7,
EXT_SERVER_AUTHZ = 8,
EXT_CERT_TYPE = 9,
EXT_ELLIPTIC_CURVES = 10,
EXT_EC_POINT_FORMATS = 11,
EXT_SRP = 12,
EXT_SIGNATURE_ALGORITHMS = 13,
EXT_USE_SRTP = 14,
EXT_HEARTBEAT = 15,
EXT_APPLICATION_LAYER_PROTOCOL_NEGOTIATION = 16,
EXT_STATUS_REQUEST_V2 = 17,
EXT_SIGNED_CERTIFICATE_TIMESTAMP = 18,
EXT_SESSIONTICKET_TLS = 35,
EXT_EXTENDED_RANDOM = 40,
EXT_NEXT_PROTOCOL_NEGOTIATION = 13172,
EXT_ORIGIN_BOUND_CERTIFICATES = 13175,
EXT_ENCRYPTED_CLIENT_CERTIFICATES = 13180,
EXT_CHANNEL_ID = 30031,
EXT_CHANNEL_ID_NEW = 30032,
EXT_PADDING = 35655,
EXT_RENEGOTIATION_INFO = 65281
};

View file

@ -34,26 +34,23 @@ type SSLRecord(is_orig: bool) = record {
head4 : uint8;
rec : RecordText(this)[] &length=length, &requires(content_type);
} &length = length+5, &byteorder=bigendian,
&let {
&let {
version : int =
$context.connection.determine_ssl_version(head0, head1, head2);
$context.connection.determine_ssl_record_layer(head0, head1, head2, head3, head4);
content_type : int = case version of {
UNKNOWN_VERSION -> 0;
SSLv20 -> head2+300;
default -> head0;
};
length : int = case version of {
UNKNOWN_VERSION -> 0;
SSLv20 -> (((head0 & 0x7f) << 8) | head1) - 3;
default -> (head3 << 8) | head4;
};
};
type RecordText(rec: SSLRecord) = case $context.connection.state() of {
STATE_ABBREV_SERVER_ENCRYPTED, STATE_CLIENT_ENCRYPTED,
STATE_COMM_ENCRYPTED, STATE_CONN_ESTABLISHED
type RecordText(rec: SSLRecord) = case $context.connection.state(rec.is_orig) of {
STATE_ENCRYPTED
-> ciphertext : CiphertextRecord(rec);
default
-> plaintext : PlaintextRecord(rec);
@ -63,6 +60,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);
@ -71,75 +69,98 @@ type PlaintextRecord(rec: SSLRecord) = case rec.content_type of {
default -> unknown_record : UnknownRecord(rec);
};
######################################################################
# TLS Extensions
######################################################################
type SSLExtension(rec: SSLRecord) = record {
type: uint16;
data_len: uint16;
data: bytestring &length=data_len;
# Pretty code ahead. Deal with the fact that perhaps extensions are
# not really present and we do not want to fail because of that.
ext: case type of {
EXT_APPLICATION_LAYER_PROTOCOL_NEGOTIATION -> apnl: ApplicationLayerProtocolNegotiationExtension(rec)[] &until($element == 0 || $element != 0);
EXT_ELLIPTIC_CURVES -> elliptic_curves: EllipticCurves(rec)[] &until($element == 0 || $element != 0);
EXT_EC_POINT_FORMATS -> ec_point_formats: EcPointFormats(rec)[] &until($element == 0 || $element != 0);
# EXT_STATUS_REQUEST -> status_request: StatusRequest(rec)[] &until($element == 0 || $element != 0);
EXT_SERVER_NAME -> server_name: ServerNameExt(rec)[] &until($element == 0 || $element != 0);
default -> data: bytestring &restofdata;
};
} &length=data_len+4 &exportsourcedata;
type ServerNameHostName() = record {
length: uint16;
host_name: bytestring &length=length;
};
type ServerName() = record {
name_type: uint8; # has to be 0 for host-name
name: case name_type of {
0 -> host_name: ServerNameHostName;
default -> data : bytestring &restofdata; # unknown name
};
};
type ServerNameExt(rec: SSLRecord) = record {
length: uint16;
server_names: ServerName[] &until($input.length() == 0);
} &length=length+2;
# Do not parse for now. Structure is correct, but only contains asn.1 data that we would not use further.
#type OcspStatusRequest(rec: SSLRecord) = record {
# responder_id_list_length: uint16;
# responder_id_list: bytestring &length=responder_id_list_length;
# request_extensions_length: uint16;
# request_extensions: bytestring &length=request_extensions_length;
#};
#
#type StatusRequest(rec: SSLRecord) = record {
# status_type: uint8; # 1 -> ocsp
# req: case status_type of {
# 1 -> ocsp_status_request: OcspStatusRequest(rec);
# default -> data : bytestring &restofdata; # unknown
# };
#};
type EcPointFormats(rec: SSLRecord) = record {
length: uint8;
point_format_list: uint8[length];
};
type EllipticCurves(rec: SSLRecord) = record {
length: uint16;
elliptic_curve_list: uint16[length/2];
};
type ProtocolName() = record {
length: uint8;
name: bytestring &length=length;
};
type ApplicationLayerProtocolNegotiationExtension(rec: SSLRecord) = record {
length: uint16;
protocol_name_list: ProtocolName[] &until($input.length() == 0);
} &length=length+2;
######################################################################
# state management according to Section 7.3. in spec
# Encryption Tracking
######################################################################
enum AnalyzerState {
STATE_INITIAL,
STATE_CLIENT_HELLO_RCVD,
STATE_IN_SERVER_HELLO,
STATE_SERVER_HELLO_DONE,
STATE_CLIENT_CERT,
STATE_CLIENT_KEY_WITH_CERT,
STATE_CLIENT_KEY_NO_CERT,
STATE_CLIENT_CERT_VERIFIED,
STATE_CLIENT_ENCRYPTED,
STATE_CLIENT_FINISHED,
STATE_ABBREV_SERVER_ENCRYPTED,
STATE_ABBREV_SERVER_FINISHED,
STATE_COMM_ENCRYPTED,
STATE_CONN_ESTABLISHED,
STATE_V2_CL_MASTER_KEY_EXPECTED,
STATE_TRACK_LOST,
STATE_ANY
STATE_CLEAR,
STATE_ENCRYPTED
};
%code{
string state_label(int state_nr)
{
switch ( state_nr ) {
case STATE_INITIAL:
return string("INITIAL");
case STATE_CLIENT_HELLO_RCVD:
return string("CLIENT_HELLO_RCVD");
case STATE_IN_SERVER_HELLO:
return string("IN_SERVER_HELLO");
case STATE_SERVER_HELLO_DONE:
return string("SERVER_HELLO_DONE");
case STATE_CLIENT_CERT:
return string("CLIENT_CERT");
case STATE_CLIENT_KEY_WITH_CERT:
return string("CLIENT_KEY_WITH_CERT");
case STATE_CLIENT_KEY_NO_CERT:
return string("CLIENT_KEY_NO_CERT");
case STATE_CLIENT_CERT_VERIFIED:
return string("CLIENT_CERT_VERIFIED");
case STATE_CLIENT_ENCRYPTED:
return string("CLIENT_ENCRYPTED");
case STATE_CLIENT_FINISHED:
return string("CLIENT_FINISHED");
case STATE_ABBREV_SERVER_ENCRYPTED:
return string("ABBREV_SERVER_ENCRYPTED");
case STATE_ABBREV_SERVER_FINISHED:
return string("ABBREV_SERVER_FINISHED");
case STATE_COMM_ENCRYPTED:
return string("COMM_ENCRYPTED");
case STATE_CONN_ESTABLISHED:
return string("CONN_ESTABLISHED");
case STATE_V2_CL_MASTER_KEY_EXPECTED:
return string("STATE_V2_CL_MASTER_KEY_EXPECTED");
case STATE_TRACK_LOST:
return string("TRACK_LOST");
case STATE_ANY:
return string("ANY");
case STATE_CLEAR:
return string("CLEAR");
case STATE_ENCRYPTED:
return string("ENCRYPTED");
default:
return string(fmt("UNKNOWN (%d)", state_nr));
@ -176,21 +197,7 @@ type ChangeCipherSpec(rec: SSLRecord) = record {
type : uint8;
} &length = 1, &let {
state_changed : bool =
$context.connection.transition(STATE_CLIENT_FINISHED,
STATE_COMM_ENCRYPTED, rec.is_orig, false) ||
$context.connection.transition(STATE_IN_SERVER_HELLO,
STATE_ABBREV_SERVER_ENCRYPTED, rec.is_orig, false) ||
$context.connection.transition(STATE_CLIENT_KEY_NO_CERT,
STATE_CLIENT_ENCRYPTED, rec.is_orig, true) ||
$context.connection.transition(STATE_CLIENT_CERT_VERIFIED,
STATE_CLIENT_ENCRYPTED, rec.is_orig, true) ||
$context.connection.transition(STATE_CLIENT_CERT,
STATE_CLIENT_ENCRYPTED, rec.is_orig, true) ||
$context.connection.transition(STATE_CLIENT_KEY_WITH_CERT,
STATE_CLIENT_ENCRYPTED, rec.is_orig, true) ||
$context.connection.transition(STATE_ABBREV_SERVER_FINISHED,
STATE_COMM_ENCRYPTED, rec.is_orig, true) ||
$context.connection.lost_track();
$context.connection.startEncryption(rec.is_orig);
};
@ -209,7 +216,7 @@ type Alert(rec: SSLRecord) = record {
######################################################################
type V2Error(rec: SSLRecord) = record {
data: bytestring &restofdata &transient;
data : bytestring &restofdata &transient;
} &let {
error_code : uint16 = ((rec.head3 << 8) | rec.head4);
};
@ -226,17 +233,21 @@ type ApplicationData(rec: SSLRecord) = record {
};
######################################################################
# Handshake Protocol (7.4.)
# V3 Heartbeat
######################################################################
type Heartbeat(rec: SSLRecord) = record {
type : uint8;
payload_length : uint16;
data : bytestring &restofdata;
};
######################################################################
# V3 Hello Request (7.4.1.1.)
######################################################################
# Hello Request is empty
type HelloRequest(rec: SSLRecord) = empty &let {
hr: bool = $context.connection.set_hello_requested(true);
};
type HelloRequest(rec: SSLRecord) = empty;
######################################################################
@ -257,16 +268,8 @@ type ClientHello(rec: SSLRecord) = record {
# of the following fields.
ext_len: uint16[] &until($element == 0 || $element != 0);
extensions : SSLExtension(rec)[] &until($input.length() == 0);
} &let {
state_changed : bool =
$context.connection.transition(STATE_INITIAL,
STATE_CLIENT_HELLO_RCVD, rec.is_orig, true) ||
($context.connection.hello_requested() &&
$context.connection.transition(STATE_ANY, STATE_CLIENT_HELLO_RCVD, rec.is_orig, true)) ||
$context.connection.lost_track();
};
######################################################################
# V2 Client Hello (SSLv2 2.5.)
######################################################################
@ -279,13 +282,6 @@ type V2ClientHello(rec: SSLRecord) = record {
session_id : uint8[session_len];
challenge : bytestring &length = chal_len;
} &length = 6 + csuit_len + session_len + chal_len, &let {
state_changed : bool =
$context.connection.transition(STATE_INITIAL,
STATE_CLIENT_HELLO_RCVD, rec.is_orig, true) ||
($context.connection.hello_requested() &&
$context.connection.transition(STATE_ANY, STATE_CLIENT_HELLO_RCVD, rec.is_orig, true)) ||
$context.connection.lost_track();
client_version : int = rec.version;
};
@ -306,11 +302,6 @@ type ServerHello(rec: SSLRecord) = record {
# of the following fields.
ext_len: uint16[] &until($element == 0 || $element != 0);
extensions : SSLExtension(rec)[] &until($input.length() == 0);
} &let {
state_changed : bool =
$context.connection.transition(STATE_CLIENT_HELLO_RCVD,
STATE_IN_SERVER_HELLO, rec.is_orig, false) ||
$context.connection.lost_track();
};
@ -329,14 +320,6 @@ type V2ServerHello(rec: SSLRecord) = record {
ciphers : uint24[ciph_len/3];
conn_id_data : bytestring &length = conn_id_len;
} &let {
state_changed : bool =
(session_id_hit > 0 ?
$context.connection.transition(STATE_CLIENT_HELLO_RCVD,
STATE_CONN_ESTABLISHED, rec.is_orig, false) :
$context.connection.transition(STATE_CLIENT_HELLO_RCVD,
STATE_V2_CL_MASTER_KEY_EXPECTED, rec.is_orig, false)) ||
$context.connection.lost_track();
session_id_hit : uint8 = rec.head3;
cert_type : uint8 = rec.head4;
};
@ -351,20 +334,18 @@ type X509Certificate = record {
certificate : bytestring &length = to_int()(length);
};
type CertificateList = X509Certificate[] &until($input.length() == 0);
type Certificate(rec: SSLRecord) = record {
length : uint24;
certificates : CertificateList &length = to_int()(length);
} &let {
state_changed : bool =
$context.connection.transition(STATE_IN_SERVER_HELLO,
STATE_IN_SERVER_HELLO, rec.is_orig, false) ||
$context.connection.transition(STATE_SERVER_HELLO_DONE,
STATE_CLIENT_CERT, rec.is_orig, true) ||
$context.connection.lost_track();
};
certificates : X509Certificate[] &until($input.length() == 0);
} &length = to_int()(length)+3;
# OCSP Stapling
type CertificateStatus(rec: SSLRecord) = record {
status_type: uint8; # 1 = ocsp, everything else is undefined
length : uint24;
response: bytestring &restofdata;
};
######################################################################
# V3 Server Key Exchange Message (7.4.3.)
@ -373,11 +354,6 @@ type Certificate(rec: SSLRecord) = record {
# For now ignore details; just eat up complete message
type ServerKeyExchange(rec: SSLRecord) = record {
key : bytestring &restofdata &transient;
} &let {
state_changed : bool =
$context.connection.transition(STATE_IN_SERVER_HELLO,
STATE_IN_SERVER_HELLO, rec.is_orig, false) ||
$context.connection.lost_track();
};
@ -388,11 +364,6 @@ type ServerKeyExchange(rec: SSLRecord) = record {
# For now, ignore Certificate Request Details; just eat up message.
type CertificateRequest(rec: SSLRecord) = record {
cont : bytestring &restofdata &transient;
} &let {
state_changed : bool =
$context.connection.transition(STATE_IN_SERVER_HELLO,
STATE_IN_SERVER_HELLO, rec.is_orig, false) ||
$context.connection.lost_track();
};
@ -401,12 +372,7 @@ type CertificateRequest(rec: SSLRecord) = record {
######################################################################
# Server Hello Done is empty
type ServerHelloDone(rec: SSLRecord) = empty &let {
state_changed : bool =
$context.connection.transition(STATE_IN_SERVER_HELLO,
STATE_SERVER_HELLO_DONE, rec.is_orig, false) ||
$context.connection.lost_track();
};
type ServerHelloDone(rec: SSLRecord) = empty;
######################################################################
@ -425,15 +391,6 @@ type ServerHelloDone(rec: SSLRecord) = empty &let {
# encrypted anyway); just eat up message.
type ClientKeyExchange(rec: SSLRecord) = record {
key : bytestring &restofdata &transient;
} &let {
state_changed : bool =
$context.connection.transition(STATE_SERVER_HELLO_DONE,
STATE_CLIENT_KEY_NO_CERT, rec.is_orig, true) ||
$context.connection.transition(STATE_CLIENT_CERT,
STATE_CLIENT_KEY_WITH_CERT, rec.is_orig, true) ||
$context.connection.transition(STATE_CLIENT_CERT,
STATE_CLIENT_KEY_WITH_CERT, rec.is_orig, true) ||
$context.connection.lost_track();
};
######################################################################
@ -449,12 +406,10 @@ type V2ClientMasterKey(rec: SSLRecord) = record {
en_key_data : bytestring &length = en_key_len &transient;
key_arg_data : bytestring &length = key_arg_len &transient;
} &length = 7 + cl_key_len + en_key_len + key_arg_len, &let {
state_changed : bool =
$context.connection.transition(STATE_V2_CL_MASTER_KEY_EXPECTED,
STATE_CONN_ESTABLISHED, rec.is_orig, true) ||
$context.connection.lost_track();
cipher_kind : int = (((rec.head3 << 16) | (rec.head4 << 8)) | cipher_kind_8);
# encryption starts for both sides after this message.
state_changed_client : bool = $context.connection.startEncryption(true);
state_changed_server : bool = $context.connection.startEncryption(false);
};
@ -465,11 +420,6 @@ type V2ClientMasterKey(rec: SSLRecord) = record {
# For now, ignore Certificate Verify; just eat up the message.
type CertificateVerify(rec: SSLRecord) = record {
cont : bytestring &restofdata &transient;
} &let {
state_changed : bool =
$context.connection.transition(STATE_CLIENT_KEY_WITH_CERT,
STATE_CLIENT_CERT_VERIFIED, rec.is_orig, true) ||
$context.connection.lost_track();
};
@ -481,13 +431,6 @@ type CertificateVerify(rec: SSLRecord) = record {
# so we will not be able to read those messages.
type Finished(rec: SSLRecord) = record {
cont : bytestring &restofdata &transient;
} &let {
state_changed : bool =
$context.connection.transition(STATE_SERVER_HELLO_DONE,
STATE_COMM_ENCRYPTED, rec.is_orig, true) ||
$context.connection.transition(STATE_CLIENT_FINISHED,
STATE_COMM_ENCRYPTED, rec.is_orig, false) ||
$context.connection.lost_track();
};
type SessionTicketHandshake(rec: SSLRecord) = record {
@ -499,10 +442,8 @@ type SessionTicketHandshake(rec: SSLRecord) = record {
# V3 Handshake Protocol (7.)
######################################################################
type UnknownHandshake(hs: Handshake, is_orig: bool) = record {
type UnknownHandshake(hs: Handshake, is_orig: bool) = record {
data : bytestring &restofdata &transient;
} &let {
state_changed : bool = $context.connection.lost_track();
};
type Handshake(rec: SSLRecord) = record {
@ -522,7 +463,7 @@ type Handshake(rec: SSLRecord) = record {
CLIENT_KEY_EXCHANGE -> client_key_exchange : ClientKeyExchange(rec);
FINISHED -> finished : Finished(rec);
CERTIFICATE_URL -> certificate_url : bytestring &restofdata &transient;
CERTIFICATE_STATUS -> certificate_status : bytestring &restofdata &transient;
CERTIFICATE_STATUS -> certificate_status : CertificateStatus(rec);
default -> unknown_handshake : UnknownHandshake(this, rec.is_orig);
} &length = to_int()(length);
};
@ -532,33 +473,12 @@ type Handshake(rec: SSLRecord) = record {
# Fragmentation (6.2.1.)
######################################################################
type UnknownRecord(rec: SSLRecord) = record {
type UnknownRecord(rec: SSLRecord) = record {
cont : bytestring &restofdata &transient;
} &let {
state_changed : bool = $context.connection.lost_track();
};
type CiphertextRecord(rec: SSLRecord) = record {
cont : bytestring &restofdata &transient;
} &let {
state_changed : bool =
$context.connection.transition(STATE_CLIENT_FINISHED,
STATE_CLIENT_FINISHED, rec.is_orig, false) ||
$context.connection.transition(STATE_CLIENT_FINISHED,
STATE_CLIENT_FINISHED, rec.is_orig, true) ||
$context.connection.transition(STATE_ABBREV_SERVER_ENCRYPTED,
STATE_ABBREV_SERVER_FINISHED, rec.is_orig, false) ||
$context.connection.transition(STATE_CLIENT_ENCRYPTED,
STATE_CLIENT_FINISHED, rec.is_orig, true) ||
$context.connection.transition(STATE_COMM_ENCRYPTED,
STATE_CONN_ESTABLISHED, rec.is_orig, false) ||
$context.connection.transition(STATE_COMM_ENCRYPTED,
STATE_CONN_ESTABLISHED, rec.is_orig, true) ||
$context.connection.transition(STATE_CONN_ESTABLISHED,
STATE_CONN_ESTABLISHED, rec.is_orig, false) ||
$context.connection.transition(STATE_CONN_ESTABLISHED,
STATE_CONN_ESTABLISHED, rec.is_orig, true) ||
$context.connection.lost_track();
};
@ -578,67 +498,88 @@ type SSLPDU(is_orig: bool) = record {
refine connection SSL_Conn += {
%member{
int state_;
int old_state_;
bool hello_requested_;
int client_state_;
int server_state_;
int record_layer_version_;
%}
%init{
state_ = STATE_INITIAL;
old_state_ = STATE_INITIAL;
hello_requested_ = false;
server_state_ = STATE_CLEAR;
client_state_ = STATE_CLEAR;
record_layer_version_ = UNKNOWN_VERSION;
%}
function determine_ssl_version(head0 : uint8, head1 : uint8,
head2 : uint8) : int
function determine_ssl_record_layer(head0 : uint8, head1 : uint8,
head2 : uint8, head3: uint8, head4: uint8) : int
%{
if ( head0 >= 20 && head0 <= 23 &&
head1 == 0x03 && head2 <= 0x03 )
// This is most probably SSL version 3.
return (head1 << 8) | head2;
if ( record_layer_version_ != UNKNOWN_VERSION )
return record_layer_version_;
else if ( head0 >= 128 && head2 < 5 && head2 != 3 )
// Not very strong evidence, but we suspect
// this to be SSLv2.
return SSLv20;
if ( head0 & 0x80 )
{
if ( head2 == 0x01 ) // SSLv2 client hello.
{
uint16 version = (head3 << 8) | head4;
if ( version != SSLv20 && version != SSLv30 && version != TLSv10 &&
version != TLSv11 && version != TLSv12 )
{
bro_analyzer()->ProtocolViolation(fmt("Invalid version in SSL client hello. Version: %d", version));
return UNKNOWN_VERSION;
}
else
else
return SSLv20;
}
else if ( head2 == 0x04 ) // SSLv2 server hello. This connection will continue using SSLv2.
{
record_layer_version_ = SSLv20;
return SSLv20;
}
else // this is not SSL or TLS.
{
bro_analyzer()->ProtocolViolation(fmt("Invalid headers in SSL connection. Head1: %d, head2: %d, head3: %d", head1, head2, head3));
return UNKNOWN_VERSION;
}
}
uint16 version = (head1<<8) | head2;
if ( version != SSLv30 && version != TLSv10 &&
version != TLSv11 && version != TLSv12 )
{
bro_analyzer()->ProtocolViolation(fmt("Invalid version in TLS connection. Version: %d", version));
return UNKNOWN_VERSION;
}
if ( head0 >=20 && head0 <= 30 )
{ // ok, set record layer version, this never can be downgraded to v2
record_layer_version_ = version;
return version;
}
bro_analyzer()->ProtocolViolation(fmt("Invalid type in TLS connection. Version: %d, Type: %d", version, head0));
return UNKNOWN_VERSION;
%}
function state() : int %{ return state_; %}
function old_state() : int %{ return old_state_; %}
function client_state() : int %{ return client_state_; %}
function transition(olds : AnalyzerState, news : AnalyzerState,
current_record_is_orig : bool, is_orig : bool) : bool
function server_state() : int %{ return client_state_; %}
function state(is_orig: bool) : int
%{
if ( (olds != STATE_ANY && olds != state_) ||
current_record_is_orig != is_orig )
return false;
if ( is_orig )
return client_state_;
else
return server_state_;
%}
old_state_ = state_;
state_ = news;
//printf("transitioning from %s to %s\n", state_label(old_state()).c_str(), state_label(state()).c_str());
function startEncryption(is_orig: bool) : bool
%{
if ( is_orig )
client_state_ = STATE_ENCRYPTED;
else
server_state_ = STATE_ENCRYPTED;
return true;
%}
function lost_track() : bool
%{
state_ = STATE_TRACK_LOST;
return false;
%}
function hello_requested() : bool
%{
bool ret = hello_requested_;
hello_requested_ = false;
return ret;
%}
function set_hello_requested(val : bool) : bool
%{
hello_requested_ = val;
return val;
%}
};

View file

@ -0,0 +1 @@
F, 1995

View file

@ -0,0 +1,13 @@
server_name, 192.168.4.149, 74.125.239.152, [google.de]
Curves, 192.168.4.149, 74.125.239.152
secp256r1
secp384r1
secp521r1
Point formats, 192.168.4.149, 74.125.239.152, T
uncompressed
ALPN, 192.168.4.149, 74.125.239.152, [spdy/3, spdy/3.1, http/1.1]
Point formats, 192.168.4.149, 74.125.239.152, F
uncompressed
ansiX962_compressed_prime
ansiX962_compressed_char2
ALPN, 192.168.4.149, 74.125.239.152, [spdy/3.1]

View file

@ -0,0 +1,10 @@
#separator \x09
#set_separator ,
#empty_field (empty)
#unset_field -
#path notice
#open 2014-04-24-19-05-00
#fields ts uid id.orig_h id.orig_p id.resp_h id.resp_p fuid file_mime_type file_desc proto note msg sub src dst p n peer_descr actions suppress_for dropped remote_location.country_code remote_location.region remote_location.city remote_location.latitude remote_location.longitude
#types time string addr port addr port string string string enum enum string string addr addr port count string set[enum] interval bool string string string double double
1397169549.895057 CXWv6p3arKYeMETxOg 192.168.4.149 59676 107.170.241.107 443 - - - tcp Heartbleed::SSL_Heartbeat_Attack_Success An Encrypted TLS heartbleed attack was probably detected! First packet client record length 1, first packet server record length 32 - 192.168.4.149 107.170.241.107 443 - bro Notice::ACTION_LOG 3600.000000 F - - - - -
#close 2014-04-24-19-05-00

View file

@ -0,0 +1,11 @@
#separator \x09
#set_separator ,
#empty_field (empty)
#unset_field -
#path notice
#open 2014-04-24-18-30-54
#fields ts uid id.orig_h id.orig_p id.resp_h id.resp_p fuid file_mime_type file_desc proto note msg sub src dst p n peer_descr actions suppress_for dropped remote_location.country_code remote_location.region remote_location.city remote_location.latitude remote_location.longitude
#types time string addr port addr port string string string enum enum string string addr addr port count string set[enum] interval bool string string string double double
1396976220.863714 CXWv6p3arKYeMETxOg 173.203.79.216 41459 107.170.241.107 443 - - - tcp Heartbleed::SSL_Heartbeat_Attack An TLS heartbleed attack was detected! Record length 16368, payload length 16365 - 173.203.79.216 107.170.241.107 443 - bro Notice::ACTION_LOG 3600.000000 F - - - - -
1396976220.918017 CXWv6p3arKYeMETxOg 173.203.79.216 41459 107.170.241.107 443 - - - tcp Heartbleed::SSL_Heartbeat_Attack_Success An TLS heartbleed attack detected before was probably exploited. Transmitted payload length in first packet: 16365 - 173.203.79.216 107.170.241.107 443 - bro Notice::ACTION_LOG 3600.000000 F - - - - -
#close 2014-04-24-18-30-54

View file

@ -0,0 +1,10 @@
#separator \x09
#set_separator ,
#empty_field (empty)
#unset_field -
#path notice
#open 2014-04-24-18-29-46
#fields ts uid id.orig_h id.orig_p id.resp_h id.resp_p fuid file_mime_type file_desc proto note msg sub src dst p n peer_descr actions suppress_for dropped remote_location.country_code remote_location.region remote_location.city remote_location.latitude remote_location.longitude
#types time string addr port addr port string string string enum enum string string addr addr port count string set[enum] interval bool string string string double double
1396973486.753913 CXWv6p3arKYeMETxOg 173.203.79.216 46592 162.219.2.166 443 - - - tcp Heartbleed::SSL_Heartbeat_Attack An TLS heartbleed attack was detected! Record length 16368, payload length 16365 - 173.203.79.216 162.219.2.166 443 - bro Notice::ACTION_LOG 3600.000000 F - - - - -
#close 2014-04-24-18-29-46

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -5,7 +5,7 @@
# @TEST-REQUIRES: bro --help 2>&1 | grep -q mem-leaks
#
# @TEST-EXEC: HEAP_CHECK_DUMP_DIRECTORY=. HEAPCHECK=local btest-bg-run bro bro -b -m -r $TRACES/http/connect-with-smtp.trace %INPUT
# @TEST-EXEC: btest-bg-wait 15
# @TEST-EXEC: btest-bg-wait 30
@load base/protocols/conn
@load base/protocols/http

View file

@ -5,7 +5,7 @@
# @TEST-REQUIRES: bro --help 2>&1 | grep -q mem-leaks
#
# @TEST-EXEC: HEAP_CHECK_DUMP_DIRECTORY=. HEAPCHECK=local btest-bg-run bro bro -b -m -r $TRACES/tls/tls-expired-cert.trace %INPUT
# @TEST-EXEC: btest-bg-wait 15
# @TEST-EXEC: btest-bg-wait 30
@load base/protocols/ssl

View file

@ -0,0 +1,7 @@
# @TEST-EXEC: bro -C -r $TRACES/tls/ocsp-stapling.trace %INPUT
# @TEST-EXEC: btest-diff .stdout
event ssl_stapled_ocsp(c: connection, is_orig: bool, response: string)
{
print is_orig, |response|;
}

View file

@ -0,0 +1,26 @@
# @TEST-EXEC: bro -C -r $TRACES/tls/chrome-34-google.trace %INPUT
# @TEST-EXEC: btest-diff .stdout
event ssl_extension_elliptic_curves(c: connection, is_orig: bool, curves: index_vec)
{
print "Curves", c$id$orig_h, c$id$resp_h;
for ( i in curves )
print SSL::ec_curves[curves[i]];
}
event ssl_extension_ec_point_formats(c: connection, is_orig: bool, point_formats: index_vec)
{
print "Point formats", c$id$orig_h, c$id$resp_h, is_orig;
for ( i in point_formats )
print SSL::ec_point_formats[point_formats[i]];
}
event ssl_extension_application_layer_protocol_negotiation(c: connection, is_orig: bool, protocols: string_vec)
{
print "ALPN", c$id$orig_h, c$id$resp_h, protocols;
}
event ssl_extension_server_name(c: connection, is_orig: bool, names: string_vec)
{
print "server_name", c$id$orig_h, c$id$resp_h, names;
}

View file

@ -0,0 +1,13 @@
# TEST-EXEC: bro -C -r $TRACES/tls/heartbleed.pcap %INPUT
# TEST-EXEC: mv notice.log notice-heartbleed.log
# TEST-EXEC: btest-diff notice-heartbleed.log
# @TEST-EXEC: bro -C -r $TRACES/tls/heartbleed-success.pcap %INPUT
# @TEST-EXEC: mv notice.log notice-heartbleed-success.log
# @TEST-EXEC: btest-diff notice-heartbleed-success.log
# @TEST-EXEC: bro -C -r $TRACES/tls/heartbleed-encrypted-success.pcap %INPUT
# @TEST-EXEC: mv notice.log notice-encrypted.log
# @TEST-EXEC: btest-diff notice-encrypted.log
@load protocols/ssl/heartbleed