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(socks)
add_subdirectory(ssh)
#add_subdirectory(ssl)
add_subdirectory(ssl)
add_subdirectory(syslog)
add_subdirectory(tcp)
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);
had_gap = false;
ssl = nullptr;
}
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
if ( interp->encryption_method() > 0x00 )
{
if ( ! ssl )
{
ssl = new analyzer::ssl::SSL_Analyzer(Conn());
if ( ! AddChildAnalyzer(ssl) )
{
reporter->AnalyzerError(this, "failed to add TCP child analyzer "
"to RDP analyzer: already exists");
return;
}
}
/* if ( ! ssl )
{
ssl = new analyzer::ssl::SSL_Analyzer(Conn());
if ( ! AddChildAnalyzer(ssl) )
{
reporter->AnalyzerError(this, "failed to add TCP child analyzer "
"to RDP analyzer: already exists");
return;
}
}
ForwardStream(len, data, orig);
ForwardStream(len, data, orig); */
}
else
{

View file

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

View file

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

View file

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

View file

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

View file

@ -2,13 +2,17 @@ protocol analyzer TLS over TCP:
parse with TLS::Message,
port 443/tcp;
# protocol analyzer DTLS over UDP:
# parse with TLS::DTLSMessage,
# port 443/udp;
import TLS;
import zeek;
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::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);
@ -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::RecordFragment::ccs -> event ssl_change_cipher_spec($conn, $is_orig);
on TLS::RecordFragment::ccs if ( msg.context().ccs_seen == 2 ) -> event ssl_established($conn);
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::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);
@ -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::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::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_cipher: uint16;
var ccs_seen: uint8;
var invalid_dtls_version_count: uint32;
var skipping: bool;
on %init {
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 {
%context = Share;
@ -552,33 +564,67 @@ public type Message = unit {
sink alertsink;
var record_version: uint16;
var encrypted : bool = False;
var dtls : bool = False;
on %init {
self.handshakesink.connect(new Handshake(self));
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($$);
version: uint16;
length: uint16;
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;
};
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 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 {
print "Unhandled content type", self.content_type;
print "Unhandled content type", content_type;
}
on ccs {
# ignore duplicate ccs
@ -589,12 +635,9 @@ type RecordFragment = unit(handshakesink: sink, alertsink: sink, inout msg: Mess
handshakesink.close();
alertsink.close();
msg.encrypted = True;
# msg.context().ccs_seen++;
msg.context().ccs_seen++;
}
}
on version {
msg.record_version = self.version;
}
};
type Heartbeat = unit(length: uint16) {
@ -613,8 +656,7 @@ type Alert_message = unit {
description: uint8; # &convert=AlertDescription($$);
};
public type Handshake = unit(inout msg: Message) {
%context = Share;
type Handshake = unit(inout msg: Message) {
handshakes: Handshake_message(msg)[];
};
@ -640,12 +682,12 @@ type Handshake_message = unit(inout msg: Message) {
* -> unhandled : bytes &size=self.length;
};
on msg_type {
print self.msg_type;
}
on %done {
print self;
}
on msg_type {
print "Handshake message", self.msg_type;
}
on unhandled {
print "Unhandled handshake message of type ", self.msg_type;
}
@ -674,7 +716,7 @@ type HelloVerifyRequest = 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);
response: bytes &size=self.length;
};
@ -699,7 +741,8 @@ type ClientHello = unit(len: uint64, msg: Message) {
type ServerHello = unit(len: uint64, inout msg: Message) {
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: bytes &size=self.session_id_length;
cipher_suite: uint16;
@ -729,7 +772,7 @@ type Extension = unit(client_hello: bool) {
Extensions::renegotiation_info -> renegotiation_info: RenegotiationInfo;
Extensions::server_name -> server_name: ServerNameList if ( self.length > 0 );
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::key_share_old -> key_share_old: 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;
};
on code {
print "Extension", self.code;
}
on unknown {
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
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;
}
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> {
local out: vector<uint16>;
@ -1503,6 +1554,13 @@ public function convert_protocol_name_list(pns: TLS::ProtocolNameList) : vector<
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 {
spicy::accept_input();
}
@ -1524,10 +1582,10 @@ on TLS::Certificate::%done {
for ( i in self.certificate_list )
{
if ( first )
zeek::file_begin("application/x-x509-user-cert");
else
zeek::file_begin("application/x-x509-ca-cert");
zeek::file_data_in(i.cert);
zeek::file_begin("application/x-x509-user-cert");
else
zeek::file_begin("application/x-x509-ca-cert");
zeek::file_data_in(i.cert);
zeek::file_end();
first = False;
}