Spicy TLS: more extensions, start of DTLS support.

This commit is contained in:
Johanna Amann 2023-04-19 14:36:00 +01:00
parent 63a4cc824a
commit 82bcc2dbb3
8 changed files with 206 additions and 147 deletions

View file

@ -37,7 +37,7 @@ add_subdirectory(smtp)
add_subdirectory(snmp) add_subdirectory(snmp)
add_subdirectory(socks) add_subdirectory(socks)
add_subdirectory(ssh) add_subdirectory(ssh)
#add_subdirectory(ssl) add_subdirectory(ssl)
add_subdirectory(syslog) add_subdirectory(syslog)
add_subdirectory(tcp) add_subdirectory(tcp)
add_subdirectory(tls) add_subdirectory(tls)

View file

@ -13,7 +13,6 @@ RDP_Analyzer::RDP_Analyzer(Connection* c) : analyzer::tcp::TCP_ApplicationAnalyz
interp = new binpac::RDP::RDP_Conn(this); interp = new binpac::RDP::RDP_Conn(this);
had_gap = false; had_gap = false;
ssl = nullptr;
} }
RDP_Analyzer::~RDP_Analyzer() RDP_Analyzer::~RDP_Analyzer()
@ -54,18 +53,18 @@ void RDP_Analyzer::DeliverStream(int len, const u_char* data, bool orig)
// 0x03-0x04 is CredSSP which is effectively SSL/TLS // 0x03-0x04 is CredSSP which is effectively SSL/TLS
if ( interp->encryption_method() > 0x00 ) if ( interp->encryption_method() > 0x00 )
{ {
if ( ! ssl ) /* if ( ! ssl )
{ {
ssl = new analyzer::ssl::SSL_Analyzer(Conn()); ssl = new analyzer::ssl::SSL_Analyzer(Conn());
if ( ! AddChildAnalyzer(ssl) ) if ( ! AddChildAnalyzer(ssl) )
{ {
reporter->AnalyzerError(this, "failed to add TCP child analyzer " reporter->AnalyzerError(this, "failed to add TCP child analyzer "
"to RDP analyzer: already exists"); "to RDP analyzer: already exists");
return; return;
} }
} }
ForwardStream(len, data, orig); ForwardStream(len, data, orig); */
} }
else else
{ {

View file

@ -3,7 +3,6 @@
#include "zeek/analyzer/protocol/pia/PIA.h" #include "zeek/analyzer/protocol/pia/PIA.h"
#include "zeek/analyzer/protocol/rdp/events.bif.h" #include "zeek/analyzer/protocol/rdp/events.bif.h"
#include "zeek/analyzer/protocol/rdp/rdp_pac.h" #include "zeek/analyzer/protocol/rdp/rdp_pac.h"
#include "zeek/analyzer/protocol/ssl/SSL.h"
#include "zeek/analyzer/protocol/tcp/TCP.h" #include "zeek/analyzer/protocol/tcp/TCP.h"
namespace zeek::analyzer::rdp namespace zeek::analyzer::rdp
@ -31,7 +30,7 @@ protected:
binpac::RDP::RDP_Conn* interp; binpac::RDP::RDP_Conn* interp;
bool had_gap; bool had_gap;
analyzer::ssl::SSL_Analyzer* ssl; // analyzer::ssl::SSL_Analyzer* ssl;
}; };
} // namespace zeek::analyzer::rdp } // namespace zeek::analyzer::rdp

View file

@ -2,34 +2,34 @@ zeek_add_plugin(
Zeek Zeek
SSL SSL
SOURCES SOURCES
SSL.cc # SSL.cc
DTLS.cc # DTLS.cc
Plugin.cc Plugin.cc
BIFS BIFS
types.bif # types.bif
events.bif # events.bif
functions.bif functions.bif
consts.bif # consts.bif
PAC # PAC
tls-handshake.pac # tls-handshake.pac
tls-handshake-protocol.pac # tls-handshake-protocol.pac
tls-handshake-analyzer.pac # tls-handshake-analyzer.pac
ssl-defs.pac # ssl-defs.pac
proc-certificate.pac # proc-certificate.pac
tls-handshake-signed_certificate_timestamp.pac # tls-handshake-signed_certificate_timestamp.pac
PAC # PAC
ssl.pac # ssl.pac
ssl-dtls-analyzer.pac # ssl-dtls-analyzer.pac
ssl-analyzer.pac # ssl-analyzer.pac
ssl-dtls-protocol.pac # ssl-dtls-protocol.pac
ssl-protocol.pac # ssl-protocol.pac
ssl-defs.pac # ssl-defs.pac
proc-certificate.pac # proc-certificate.pac
PAC # PAC
dtls.pac # dtls.pac
ssl-dtls-analyzer.pac # ssl-dtls-analyzer.pac
dtls-analyzer.pac # dtls-analyzer.pac
ssl-dtls-protocol.pac # ssl-dtls-protocol.pac
dtls-protocol.pac # dtls-protocol.pac
ssl-defs.pac # ssl-defs.pac
) )

View file

@ -2,9 +2,9 @@
#include "zeek/plugin/Plugin.h" #include "zeek/plugin/Plugin.h"
#include "zeek/analyzer/Component.h" // #include "zeek/analyzer/Component.h"
#include "zeek/analyzer/protocol/ssl/DTLS.h" //#include "zeek/analyzer/protocol/ssl/DTLS.h"
#include "zeek/analyzer/protocol/ssl/SSL.h" // #include "zeek/analyzer/protocol/ssl/SSL.h"
namespace zeek::plugin::detail::Zeek_SSL namespace zeek::plugin::detail::Zeek_SSL
{ {
@ -14,10 +14,10 @@ class Plugin : public zeek::plugin::Plugin
public: public:
zeek::plugin::Configuration Configure() override zeek::plugin::Configuration Configure() override
{ {
AddComponent( // AddComponent(
new zeek::analyzer::Component("SSL", zeek::analyzer::ssl::SSL_Analyzer::Instantiate)); // new zeek::analyzer::Component("SSL", zeek::analyzer::ssl::SSL_Analyzer::Instantiate));
AddComponent(new zeek::analyzer::Component( // AddComponent(new zeek::analyzer::Component(
"DTLS", zeek::analyzer::dtls::DTLS_Analyzer::Instantiate)); // "DTLS", zeek::analyzer::dtls::DTLS_Analyzer::Instantiate));
zeek::plugin::Configuration config; zeek::plugin::Configuration config;
config.name = "Zeek::SSL"; config.name = "Zeek::SSL";

View file

@ -1,70 +1,70 @@
%%{ %%{
#include <openssl/x509.h> #include <openssl/x509.h>
#include "zeek/analyzer/protocol/ssl/SSL.h" // #include "zeek/analyzer/protocol/ssl/SSL.h"
#include "zeek/Reporter.h" #include "zeek/Reporter.h"
%%} %%}
## Sets if the SSL analyzer should consider the connection established (handshake # ## Sets if the SSL analyzer should consider the connection established (handshake
## finished successfully). # ## finished successfully).
## # ##
## c: The SSL connection. # ## c: The SSL connection.
## # ##
## Returns: T on success, F on failure. # ## Returns: T on success, F on failure.
function set_ssl_established%(c: connection%): bool # function set_ssl_established%(c: connection%): bool
%{ # %{
zeek::analyzer::Analyzer* sa = c->FindAnalyzer("SSL"); # zeek::analyzer::Analyzer* sa = c->FindAnalyzer("SSL");
#
if ( sa ) # if ( sa )
{ # {
static_cast<zeek::analyzer::ssl::SSL_Analyzer*>(sa)->StartEncryption(); # static_cast<zeek::analyzer::ssl::SSL_Analyzer*>(sa)->StartEncryption();
return zeek::val_mgr->True(); # return zeek::val_mgr->True();
} # }
#
return zeek::val_mgr->False(); # return zeek::val_mgr->False();
%} # %}
#
## Set the secret that should be used to derive keys for the connection. # ## Set the secret that should be used to derive keys for the connection.
## (For TLS 1.2 this is the pre-master secret). # ## (For TLS 1.2 this is the pre-master secret).
## # ##
## c: The affected connection # ## c: The affected connection
## # ##
## secret: secret to set # ## secret: secret to set
## # ##
## Returns: T on success, F on failure. # ## Returns: T on success, F on failure.
function set_secret%(c: connection, secret: string%): bool # function set_secret%(c: connection, secret: string%): bool
%{ # %{
analyzer::Analyzer* sa = c->FindAnalyzer("SSL"); # analyzer::Analyzer* sa = c->FindAnalyzer("SSL");
#
if ( sa ) # if ( sa )
{ # {
static_cast<zeek::analyzer::ssl::SSL_Analyzer*>(sa)->SetSecret(*secret); # static_cast<zeek::analyzer::ssl::SSL_Analyzer*>(sa)->SetSecret(*secret);
return zeek::val_mgr->True(); # return zeek::val_mgr->True();
} # }
#
return zeek::val_mgr->False(); # return zeek::val_mgr->False();
%} # %}
#
## Set the decryption keys that should be used to decrypt # ## Set the decryption keys that should be used to decrypt
## TLS application data in the connection. # ## TLS application data in the connection.
## # ##
## c: The affected connection # ## c: The affected connection
## # ##
## keys: The key buffer as derived via TLS PRF. # ## keys: The key buffer as derived via TLS PRF.
## # ##
## Returns: T on success, F on failure. # ## Returns: T on success, F on failure.
function set_keys%(c: connection, keys: string%): bool # function set_keys%(c: connection, keys: string%): bool
%{ # %{
analyzer::Analyzer* sa = c->FindAnalyzer("SSL"); # analyzer::Analyzer* sa = c->FindAnalyzer("SSL");
#
if ( sa ) # if ( sa )
{ # {
static_cast<zeek::analyzer::ssl::SSL_Analyzer*>(sa)->SetKeys(*keys); # static_cast<zeek::analyzer::ssl::SSL_Analyzer*>(sa)->SetKeys(*keys);
return zeek::val_mgr->True(); # return zeek::val_mgr->True();
} # }
#
return zeek::val_mgr->False(); # return zeek::val_mgr->False();
%} # %}
## Decodes a DER-encoded distinguished name into an ASCII string, ## Decodes a DER-encoded distinguished name into an ASCII string,
## using the RFC2253 representation ## using the RFC2253 representation

View file

@ -2,13 +2,17 @@ protocol analyzer TLS over TCP:
parse with TLS::Message, parse with TLS::Message,
port 443/tcp; port 443/tcp;
# protocol analyzer DTLS over UDP:
# parse with TLS::DTLSMessage,
# port 443/udp;
import TLS; import TLS;
import zeek; import zeek;
import spicy; import spicy;
on TLS::ClientHello -> event ssl_client_hello($conn, self.client_version, msg.record_version, cast<time>(self.random.gmt_unix_time), self.random.random_bytes, self.session_id, self.cipher_suites, self.compression_methods); on TLS::ClientHello -> event ssl_client_hello($conn, self.client_version, msg.record_version, cast<time>(self.random.gmt_unix_time), self.random.random_bytes, self.session_id, self.cipher_suites, self.compression_methods);
on TLS::ServerHello -> event ssl_server_hello($conn, self.server_version, msg.record_version, cast<time>(self.random.gmt_unix_time), self.random.random_bytes, self.session_id, self.cipher_suite, self.compression_method); on TLS::ServerHello -> event ssl_server_hello($conn, self.server_version, msg.record_version, cast<time>(self.gmt_unix_time), self.random_bytes, self.session_id, self.cipher_suite, self.compression_method);
on TLS::EllipticCurveList -> event ssl_extension_elliptic_curves($conn, $is_orig, self.elliptic_curve_list); on TLS::EllipticCurveList -> event ssl_extension_elliptic_curves($conn, $is_orig, self.elliptic_curve_list);
@ -18,9 +22,10 @@ on TLS::ServerNameList -> event ssl_extension_server_name($conn, $is_orig, TLS::
on TLS::NewSessionTicket -> event ssl_session_ticket_handshake($conn, self.ticket_lifetime_hint, self.ticket); on TLS::NewSessionTicket -> event ssl_session_ticket_handshake($conn, self.ticket_lifetime_hint, self.ticket);
on TLS::RecordFragment::ccs -> event ssl_change_cipher_spec($conn, $is_orig); on TLS::PlaintextRecord::ccs -> event ssl_change_cipher_spec($conn, $is_orig);
on TLS::PlaintextRecord::ccs if ( msg.context().ccs_seen == 2 ) -> event ssl_established($conn);
on TLS::RecordFragment::ccs if ( msg.context().ccs_seen == 2 ) -> event ssl_established($conn); on TLS::PlaintextRecord::appdata if ( msg.encrypted == False ) -> event ssl_plaintext_data($conn, $is_orig, msg.record_version, content_type, self.length);
on TLS::PlaintextRecord::appdata if ( msg.encrypted == True ) -> event ssl_encrypted_data($conn, $is_orig, msg.record_version, content_type, self.length);
on TLS::Handshake_message -> event ssl_handshake_message($conn, $is_orig, self.msg_type, self.length); on TLS::Handshake_message -> event ssl_handshake_message($conn, $is_orig, self.msg_type, self.length);
@ -67,9 +72,7 @@ on TLS::Alert_message -> event ssl_alert($conn, $is_orig, self.level, self.descr
on TLS::Heartbeat -> event ssl_heartbeat($conn, $is_orig, length, self.tpe, self.payload_length, self.data); on TLS::Heartbeat -> event ssl_heartbeat($conn, $is_orig, length, self.tpe, self.payload_length, self.data);
on TLS::RecordFragment::appdata if ( msg.encrypted == False ) -> event ssl_plaintext_data($conn, $is_orig, self.version, self.content_type, self.length);
on TLS::RecordFragment::appdata if ( msg.encrypted == True ) -> event ssl_encrypted_data($conn, $is_orig, self.version, self.content_type, self.length);
on TLS::CertificateStatus -> event ssl_stapled_ocsp($conn, $is_orig, self.response); on TLS::CertificateStatus -> event ssl_stapled_ocsp($conn, $is_orig, self.response);
on TLS::CertificateRequest if ( TLS::uses_signature_and_hashalgorithm(msg) ) -> event ssl_certificate_request($conn, $is_orig, self.certificate_types, TLS::convert_signature_algorithms(self.supported_signature_algorithms), TLS::convert_certificate_authorities(self));
on TLS::CertificateRequest if ( ! TLS::uses_signature_and_hashalgorithm(msg) ) -> event ssl_certificate_request($conn, $is_orig, self.certificate_types, TLS::create_empty_sigmature_algorithms(), TLS::convert_certificate_authorities(self));

View file

@ -539,12 +539,24 @@ type Share = unit {
var chosen_version_sh: uint16; var chosen_version_sh: uint16;
var chosen_cipher: uint16; var chosen_cipher: uint16;
var ccs_seen: uint8; var ccs_seen: uint8;
var invalid_dtls_version_count: uint32;
var skipping: bool;
on %init { on %init {
self.ccs_seen = 0; self.ccs_seen = 0;
self.invalid_dtls_version_count = 0;
self.skipping = False;
} }
}; };
#public type TLSMessage = unit {
# m: Message(False);
#};
#
#public type DTLSMessage = unit {
# m: Message(True);
#};
public type Message = unit { public type Message = unit {
%context = Share; %context = Share;
@ -552,33 +564,67 @@ public type Message = unit {
sink alertsink; sink alertsink;
var record_version: uint16; var record_version: uint16;
var encrypted : bool = False; var encrypted : bool = False;
var dtls : bool = False;
on %init { on %init {
self.handshakesink.connect(new Handshake(self)); self.handshakesink.connect(new Handshake(self));
self.alertsink.connect(new Alert); self.alertsink.connect(new Alert);
} }
fragment : RecordFragment(self.handshakesink, self.alertsink, self)[]; fragment : RecordFragmentChoice(self.handshakesink, self.alertsink, self)[];
# : bytes &eod if ( self.context().skipping );
}; };
type RecordFragment = unit(handshakesink: sink, alertsink: sink, inout msg: Message) { type RecordFragmentChoice = unit(handshakesink: sink, alertsink: sink, inout msg: Message) {
switch ( msg.dtls ) {
True -> dtlsfragment : DTLSRecordFragment(handshakesink, alertsink, msg);
False -> tlsfragment : TLSRecordFragment(handshakesink, alertsink, msg);
};
};
type TLSRecordFragment = unit(handshakesink: sink, alertsink: sink, inout msg: Message) {
content_type: uint8; # &convert=ContentType($$); content_type: uint8; # &convert=ContentType($$);
version: uint16; version: uint16;
length: uint16; record: PlaintextRecord(self.content_type, handshakesink, alertsink, msg);
switch ( ContentType(self.content_type) ) {
ContentType::handshake -> : bytes &size=self.length -> handshakesink;
ContentType::application_data -> appdata : bytes &size=self.length;
ContentType::change_cipher_spec -> ccs : bytes &size=self.length;
ContentType::heartbeat -> hn: Heartbeat(self.length);
ContentType::alert -> :bytes &size=self.length -> alertsink;
* -> unhandled : bytes &size=self.length;
};
on content_type { on content_type {
print "Content type", self.content_type; print "Content type", self.content_type;
} }
on version {
msg.record_version = self.version;
}
};
type DTLSRecordFragment = unit(handshakesink: sink, alertsink: sink, inout msg: Message) {
content_type: uint8; # &convert=ContentType($$);
version: uint16;
# the epoch signalizes that a changecipherspec message has been received. Hence, everything with
# an epoch > 0 should be encrypted
epoch: uint16;
sequence_number: bytes &size=6; # uint48
record: PlaintextRecord(self.content_type, handshakesink, alertsink, msg);
on content_type {
print "Content type", self.content_type;
}
on version {
msg.record_version = self.version;
}
};
type PlaintextRecord = unit(content_type: uint8, handshakesink: sink, alertsink: sink, inout msg: Message) {
length: uint16;
switch ( ContentType(content_type) ) {
ContentType::handshake -> : bytes &size=self.length -> handshakesink;
ContentType::application_data -> appdata : bytes &size=self.length;
ContentType::change_cipher_spec -> ccs : bytes &size=self.length;
ContentType::heartbeat -> hn: Heartbeat(self.length);
ContentType::alert -> : bytes &size=self.length -> alertsink;
* -> unhandled : bytes &size=self.length;
};
on unhandled { on unhandled {
print "Unhandled content type", self.content_type; print "Unhandled content type", content_type;
} }
on ccs { on ccs {
# ignore duplicate ccs # ignore duplicate ccs
@ -589,12 +635,9 @@ type RecordFragment = unit(handshakesink: sink, alertsink: sink, inout msg: Mess
handshakesink.close(); handshakesink.close();
alertsink.close(); alertsink.close();
msg.encrypted = True; msg.encrypted = True;
# msg.context().ccs_seen++; msg.context().ccs_seen++;
} }
} }
on version {
msg.record_version = self.version;
}
}; };
type Heartbeat = unit(length: uint16) { type Heartbeat = unit(length: uint16) {
@ -613,8 +656,7 @@ type Alert_message = unit {
description: uint8; # &convert=AlertDescription($$); description: uint8; # &convert=AlertDescription($$);
}; };
public type Handshake = unit(inout msg: Message) { type Handshake = unit(inout msg: Message) {
%context = Share;
handshakes: Handshake_message(msg)[]; handshakes: Handshake_message(msg)[];
}; };
@ -640,12 +682,12 @@ type Handshake_message = unit(inout msg: Message) {
* -> unhandled : bytes &size=self.length; * -> unhandled : bytes &size=self.length;
}; };
on msg_type {
print self.msg_type;
}
on %done { on %done {
print self; print self;
} }
on msg_type {
print "Handshake message", self.msg_type;
}
on unhandled { on unhandled {
print "Unhandled handshake message of type ", self.msg_type; print "Unhandled handshake message of type ", self.msg_type;
} }
@ -674,7 +716,7 @@ type HelloVerifyRequest = unit {
}; };
type CertificateStatus = unit { type CertificateStatus = unit {
status_type: uint8; status_type: uint8; # 1 = ocsp, everything else is undefined
length: bytes &size=3 &convert=$$.to_uint(spicy::ByteOrder::Network); length: bytes &size=3 &convert=$$.to_uint(spicy::ByteOrder::Network);
response: bytes &size=self.length; response: bytes &size=self.length;
}; };
@ -699,7 +741,8 @@ type ClientHello = unit(len: uint64, msg: Message) {
type ServerHello = unit(len: uint64, inout msg: Message) { type ServerHello = unit(len: uint64, inout msg: Message) {
server_version: uint16; server_version: uint16;
random: Random; random_bytes: bytes &size=32;
gmt_unix_time: uint32 &parse-from=self.random_bytes;
session_id_length: uint8; session_id_length: uint8;
session_id: bytes &size=self.session_id_length; session_id: bytes &size=self.session_id_length;
cipher_suite: uint16; cipher_suite: uint16;
@ -729,7 +772,7 @@ type Extension = unit(client_hello: bool) {
Extensions::renegotiation_info -> renegotiation_info: RenegotiationInfo; Extensions::renegotiation_info -> renegotiation_info: RenegotiationInfo;
Extensions::server_name -> server_name: ServerNameList if ( self.length > 0 ); Extensions::server_name -> server_name: ServerNameList if ( self.length > 0 );
Extensions::application_layer_protocol_negotiation -> application_layer_protocol_negotiation: ProtocolNameList; Extensions::application_layer_protocol_negotiation -> application_layer_protocol_negotiation: ProtocolNameList;
Extensions::status_request -> status_request: StatusRequest(self.length); Extensions::status_request -> status_request: StatusRequest(self.length) if ( self.length > 0 );
Extensions::signed_certificate_timestamp -> signed_certificate_timestamp: SignedCertificateTimestampList if ( self.length > 0 ); Extensions::signed_certificate_timestamp -> signed_certificate_timestamp: SignedCertificateTimestampList if ( self.length > 0 );
Extensions::key_share_old -> key_share_old: KeyShare(client_hello, self.length); Extensions::key_share_old -> key_share_old: KeyShare(client_hello, self.length);
Extensions::key_share -> key_share: KeyShare(client_hello, self.length); Extensions::key_share -> key_share: KeyShare(client_hello, self.length);
@ -739,6 +782,9 @@ type Extension = unit(client_hello: bool) {
* -> unknown : bytes &size=self.length; * -> unknown : bytes &size=self.length;
}; };
on code {
print "Extension", self.code;
}
on unknown { on unknown {
print "Unknown extension", self.code; print "Unknown extension", self.code;
} }
@ -1125,7 +1171,7 @@ type EcdheServerKeyExchange = unit(len: uint64, msg: Message) {
}; };
}; };
function uses_signature_and_hashalgorithm(msg: Message) : bool public function uses_signature_and_hashalgorithm(msg: Message) : bool
{ {
# larger TLS11 and not DTLSv10 # larger TLS11 and not DTLSv10
if ( msg.context().chosen_version_sh > 0x0302 && msg.context().chosen_version_sh != 0xFEFF ) if ( msg.context().chosen_version_sh > 0x0302 && msg.context().chosen_version_sh != 0xFEFF )
@ -1471,6 +1517,11 @@ public function convert_signature_algorithms(sa: TLS::SignatureAlgorithms) : vec
return out; return out;
} }
public function create_empty_sigmature_algorithms() : vector<tuple<HashAlgorithm: uint8, SignatureAlgorithm: uint8>> {
local out: vector<tuple<uint8, uint8>>;
return out;
}
public function convert_clienthellokeyshare(ks: TLS::ClientHelloKeyShare) : vector<uint16> { public function convert_clienthellokeyshare(ks: TLS::ClientHelloKeyShare) : vector<uint16> {
local out: vector<uint16>; local out: vector<uint16>;
@ -1503,6 +1554,13 @@ public function convert_protocol_name_list(pns: TLS::ProtocolNameList) : vector<
return out; return out;
} }
public function convert_certificate_authorities(c: TLS::CertificateRequest) : vector<bytes> {
local out: vector<bytes>;
for ( i in c.certificate_authorities )
out.push_back(i.certificate_authority);
return out;
}
on TLS::ClientHello::%done { on TLS::ClientHello::%done {
spicy::accept_input(); spicy::accept_input();
} }
@ -1524,10 +1582,10 @@ on TLS::Certificate::%done {
for ( i in self.certificate_list ) for ( i in self.certificate_list )
{ {
if ( first ) if ( first )
zeek::file_begin("application/x-x509-user-cert"); zeek::file_begin("application/x-x509-user-cert");
else else
zeek::file_begin("application/x-x509-ca-cert"); zeek::file_begin("application/x-x509-ca-cert");
zeek::file_data_in(i.cert); zeek::file_data_in(i.cert);
zeek::file_end(); zeek::file_end();
first = False; first = False;
} }