diff --git a/scripts/policy/protocols/ssl/decryption.zeek b/scripts/policy/protocols/ssl/decryption.zeek new file mode 100644 index 0000000000..a3608d81e7 --- /dev/null +++ b/scripts/policy/protocols/ssl/decryption.zeek @@ -0,0 +1,106 @@ +#! Decrypt SSL/TLS payloads + +@load base/frameworks/input +@load base/frameworks/notice +@load base/protocols/conn +@load base/protocols/ssl + +module SSL; + +# Local +const input_stream_name = "input-tls-keylog-file"; + +type Idx: record { + client_random: string; +}; + +type Val: record { + secret: string; +}; + +global randoms: table[string] of string = {}; + +export { + redef record Info += { + # decryption uses client_random as identifier + client_random: string &log &optional; + }; + + const keylog_file = getenv("ZEEK_TLS_KEYLOG_FILE") &redef; + + global secrets: table[string] of string = {} &redef; + global keys: table[string] of string = {} &redef; + + event SSL::add_keys(client_random: string, val: string) + { + SSL::keys[client_random] = val; + } + + event SSL::add_secret(client_random: string, val: string) + { + SSL::secrets[client_random] = val; + } +} + +event ssl_client_hello(c: connection, version: count, record_version: count, possible_ts: time, client_random: string, session_id: string, ciphers: index_vec, comp_methods: index_vec) + { + c$ssl$client_random = client_random; + + if ( client_random in keys ) + { + set_keys(c, keys[client_random]); + } + else if ( client_random in secrets ) + { + set_secret(c, secrets[client_random]); + } + } + +event ssl_encrypted_data(c: connection, is_orig: bool, record_version: count, content_type: count, length: count, payload: string) + { + if ( c$ssl?$client_random ) + { + if ( c$ssl$client_random in keys ) + { + set_keys(c, keys[c$ssl$client_random]); + } + else if ( c$ssl$client_random in secrets ) + { + set_secret(c, secrets[c$ssl$client_random]); + } + else + { + # FIXME: should this be moved to reporter.log or removed completely? + #print "No suitable key or secret found for random:", randoms[c$uid]; + } + } + } + +event SSL::tls_input_done() + { + continue_processing(); + } + +event Input::end_of_data(name: string, source: string) + { + if ( name == input_stream_name ) + { + event SSL::tls_input_done(); + } +} + +event zeek_init() + { + # listen for secrets + Broker::subscribe("/zeek/tls/decryption"); + + # FIXME: is such a functionality helpful? + # ingest keylog file if the environment is set + if ( keylog_file != "" ) + { + suspend_processing(); + + Input::add_table([$name=input_stream_name, $source=keylog_file, $destination=secrets, $idx=Idx, $val=Val, $want_record=F]); + Input::remove(input_stream_name); + } +} diff --git a/scripts/policy/protocols/ssl/heartbleed.zeek b/scripts/policy/protocols/ssl/heartbleed.zeek index aabafbff14..40bc800b8c 100644 --- a/scripts/policy/protocols/ssl/heartbleed.zeek +++ b/scripts/policy/protocols/ssl/heartbleed.zeek @@ -223,7 +223,7 @@ event ssl_encrypted_heartbeat(c: connection, is_orig: bool, length: count) } } -event ssl_encrypted_data(c: connection, is_orig: bool, record_version: count, content_type: count, length: count) +event ssl_encrypted_data(c: connection, is_orig: bool, record_version: count, content_type: count, length: count, payload: string) { if ( !c?$ssl ) return; diff --git a/scripts/test-all-policy.zeek b/scripts/test-all-policy.zeek index 879d1fbd9b..0b421e7c21 100644 --- a/scripts/test-all-policy.zeek +++ b/scripts/test-all-policy.zeek @@ -101,6 +101,7 @@ @load protocols/ssh/geo-data.zeek @load protocols/ssh/interesting-hostnames.zeek @load protocols/ssh/software.zeek +#@load protocols/ssl/decryption.zeek @load protocols/ssl/expiring-certs.zeek @load protocols/ssl/extract-certs-pem.zeek @load protocols/ssl/heartbleed.zeek diff --git a/scripts/zeekygen/__load__.zeek b/scripts/zeekygen/__load__.zeek index 64494037b8..524c12a609 100644 --- a/scripts/zeekygen/__load__.zeek +++ b/scripts/zeekygen/__load__.zeek @@ -1,6 +1,7 @@ @load test-all-policy.zeek # Scripts which are commented out in test-all-policy.zeek. +@load protocols/ssl/decryption.zeek @load protocols/ssl/notary.zeek @load frameworks/control/controllee.zeek @load frameworks/control/controller.zeek diff --git a/src/analyzer/protocol/ssl/CMakeLists.txt b/src/analyzer/protocol/ssl/CMakeLists.txt index 47093a978e..2a9179ce64 100644 --- a/src/analyzer/protocol/ssl/CMakeLists.txt +++ b/src/analyzer/protocol/ssl/CMakeLists.txt @@ -10,8 +10,8 @@ zeek_plugin_bif(events.bif) zeek_plugin_bif(functions.bif) zeek_plugin_bif(consts.bif) zeek_plugin_pac(tls-handshake.pac tls-handshake-protocol.pac tls-handshake-analyzer.pac ssl-defs.pac - proc-client-hello.pac - proc-server-hello.pac + proc-client-hello-tls.pac + proc-server-hello-tls.pac proc-certificate.pac tls-handshake-signed_certificate_timestamp.pac ) diff --git a/src/analyzer/protocol/ssl/DTLS.cc b/src/analyzer/protocol/ssl/DTLS.cc index 09f9e7c305..ee823148bd 100644 --- a/src/analyzer/protocol/ssl/DTLS.cc +++ b/src/analyzer/protocol/ssl/DTLS.cc @@ -72,4 +72,9 @@ void DTLS_Analyzer::SendHandshake(uint16_t raw_tls_version, uint8_t msg_type, ui } } +bool DTLS_Analyzer::TryDecryptApplicationData(int len, const u_char* data, bool is_orig, uint8_t content_type, uint16_t raw_tls_version) + { + // noop for now as DTLS decryption is currently not supported + } + } // namespace zeek::analyzer::dtls diff --git a/src/analyzer/protocol/ssl/DTLS.h b/src/analyzer/protocol/ssl/DTLS.h index 1554c1bb6e..7c956f0860 100644 --- a/src/analyzer/protocol/ssl/DTLS.h +++ b/src/analyzer/protocol/ssl/DTLS.h @@ -27,6 +27,8 @@ public: static analyzer::Analyzer* Instantiate(Connection* conn) { return new DTLS_Analyzer(conn); } + bool TryDecryptApplicationData(int len, const u_char* data, bool is_orig, uint8_t content_type, uint16_t raw_tls_version); + protected: binpac::DTLS::SSL_Conn* interp; binpac::TLSHandshake::Handshake_Conn* handshake_interp; diff --git a/src/analyzer/protocol/ssl/SSL.cc b/src/analyzer/protocol/ssl/SSL.cc index d5088a6746..6fd2349fa1 100644 --- a/src/analyzer/protocol/ssl/SSL.cc +++ b/src/analyzer/protocol/ssl/SSL.cc @@ -1,5 +1,6 @@ #include "zeek/analyzer/protocol/ssl/SSL.h" +#include "zeek/analyzer/Manager.h" #include "zeek/analyzer/protocol/tcp/TCP_Reassembler.h" #include "zeek/Reporter.h" #include "zeek/util.h" @@ -8,6 +9,73 @@ #include "zeek/analyzer/protocol/ssl/ssl_pac.h" #include "zeek/analyzer/protocol/ssl/tls-handshake_pac.h" +#include +#include +#include + +#define MSB(a) ((a>>8)&0xff) +#define LSB(a) (a&0xff) + +static void fmt_seq(uint32_t num, u_char* buf) + { + memset(buf, 0, 8); + uint32_t netnum = htonl(num); + memcpy(buf+4, &netnum, 4); + } + +static void print_hex(std::string name, u_char* data, int len) + { + int i = 0; + printf("%s (%d): ", name.c_str(), len); + if (len > 0) + printf("0x%02x", data[0]); + + for (i = 1; i < len; i++) + { + printf(" 0x%02x", data[i]); + } + printf("\n"); + } + +static bool tls1_prf(const u_char *secret, int secret_len, const char* label, + const u_char* rnd1, int rnd1_len, const u_char* rnd2, int rnd2_len, u_char* out, size_t out_len) + { + // alloc buffers + EVP_PKEY_CTX *pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_TLS1_PRF, NULL); + size_t seed_len = strlen(label) + rnd1_len + rnd2_len; + u_char *seed = (u_char*) malloc(seed_len); + u_char *ptr = seed; + + // seed = label + rnd1 + rnd2 + memcpy(ptr, label, strlen(label)); + ptr += strlen(label); + memcpy(ptr, rnd1, rnd1_len); + ptr += rnd1_len; + memcpy(ptr, rnd2, rnd2_len); + ptr += rnd2_len; + + if (EVP_PKEY_derive_init(pctx) <= 0) + goto abort; /* Error */ + // FIXME: sha384 should not be hardcoded + if (EVP_PKEY_CTX_set_tls1_prf_md(pctx, EVP_sha384()) <= 0) + goto abort; /* Error */ + if (EVP_PKEY_CTX_set1_tls1_prf_secret(pctx, secret, secret_len) <= 0) + goto abort; /* Error */ + if (EVP_PKEY_CTX_add1_tls1_prf_seed(pctx, seed, seed_len) <= 0) + goto abort; /* Error */ + if (EVP_PKEY_derive(pctx, out, &out_len) <= 0) + goto abort; /* Error */ + + EVP_PKEY_CTX_free(pctx); + free(seed); + return true; + +abort: + EVP_PKEY_CTX_free(pctx); + free(seed); + return false; + } + namespace zeek::analyzer::ssl { SSL_Analyzer::SSL_Analyzer(Connection* c) @@ -16,12 +84,20 @@ SSL_Analyzer::SSL_Analyzer(Connection* c) interp = new binpac::SSL::SSL_Conn(this); handshake_interp = new binpac::TLSHandshake::Handshake_Conn(this); had_gap = false; + c_seq = 0; + s_seq = 0; + // FIXME: should this be initialized to nullptr? + secret = new zeek::StringVal(0, ""); + keys = new zeek::StringVal(0, ""); + pia = nullptr; } SSL_Analyzer::~SSL_Analyzer() { delete interp; delete handshake_interp; + delete secret; + delete keys; } void SSL_Analyzer::Done() @@ -98,4 +174,166 @@ void SSL_Analyzer::Undelivered(uint64_t seq, int len, bool orig) interp->NewGap(orig, len); } +void SSL_Analyzer::SetSecret(const u_char* data, int len) + { + // FIXME: Is this the proper way to initialize a zeek::StringVal? + // the alternative requires a cast: new zeek::StringVal(len, data) + secret = new zeek::StringVal(new zeek::String(data, len, true)); + } + +void SSL_Analyzer::SetKeys(const u_char* data, int len) + { + keys = new zeek::StringVal(new zeek::String(data, len, true)); + } + +bool SSL_Analyzer::TryDecryptApplicationData(int len, const u_char* data, bool is_orig, uint8_t content_type, uint16_t raw_tls_version) + { + // Unsupported cipher suite. Currently supported: + // - TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 == 0xC030 + auto cipher = handshake_interp->chosen_cipher(); + if ( cipher != 0xC030 ) + { + //printf("Unsupported cipher suite: %d\n", cipher); + return false; + } + + // Neither secret or key present: abort + if ( secret->Len() == 0 && keys->Len() == 0 ) + { + // FIXME: this is just for debugging + printf("Could not decrypt packet (missing key):\n"); + print_hex("->client_random:", handshake_interp->client_random().data(), handshake_interp->client_random().length()); + return false; + } + + // Secret present, but no keys derived yet: derive keys + if ( secret->Len() != 0 && keys->Len() == 0 ) + { + uint32_t ts = htonl((uint32_t) handshake_interp->gmt_unix_time()); + + u_char crand[32] = {0x00}; + u_char keybuf[72]; + + auto c_rnd = handshake_interp->client_random(); + auto s_rnd = handshake_interp->server_random(); + memcpy(crand, &(ts), 4); + memcpy(crand + 4, c_rnd.data(), c_rnd.length()); + + tls1_prf(secret->Bytes(), secret->Len(), "key expansion", s_rnd.data(), s_rnd.length(), + crand, sizeof(crand), keybuf, sizeof(keybuf)); + + // save derived keys + SetKeys(keybuf, sizeof(keybuf)); + } + + // Keys present: decrypt TLS application data + if ( keys->Len() != 0 ) + { + // session keys & AEAD data + u_char c_wk[32]; + u_char s_wk[32]; + u_char c_iv[4]; + u_char s_iv[4]; + u_char s_aead_nonce[12]; + u_char s_aead_tag[13]; + + memcpy(c_wk, keys->Bytes(), 32); + memcpy(s_wk, &(keys->Bytes()[32]), 32); + memcpy(c_iv, &(keys->Bytes()[64]), 4); + memcpy(s_iv, &(keys->Bytes()[68]), 4); + + // FIXME: should we change types here? + u_char* encrypted = (u_char*)data; + size_t encrypted_len = len; + + // FIXME: should this be moved to SSL_Conn? + if ( is_orig ) + c_seq++; + else + s_seq++; + + if ( is_orig ) + memcpy(s_aead_nonce, c_iv, 4); + else + memcpy(s_aead_nonce, s_iv, 4); + memcpy(&(s_aead_nonce[4]), encrypted, 8); + + EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new(); + EVP_CIPHER_CTX_init(ctx); + EVP_CipherInit(ctx, EVP_aes_256_gcm(), NULL, NULL, 0); + + encrypted += 8; + // FIXME: is this because of nonce and aead tag? + encrypted_len -= 8; + encrypted_len -= 16; + + if (is_orig) + EVP_DecryptInit(ctx, EVP_aes_256_gcm(), c_wk, s_aead_nonce); + else + EVP_DecryptInit(ctx, EVP_aes_256_gcm(), s_wk, s_aead_nonce); + EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_TAG, 16, encrypted + encrypted_len); + + if (is_orig) + fmt_seq(c_seq, s_aead_tag); + else + fmt_seq(s_seq, s_aead_tag); + + s_aead_tag[8] = content_type; + s_aead_tag[9] = MSB(raw_tls_version); + s_aead_tag[10] = LSB(raw_tls_version); + s_aead_tag[11] = MSB(encrypted_len); + s_aead_tag[12] = LSB(encrypted_len); + + u_char *decrypted = (u_char*)malloc(encrypted_len); + int decrypted_len = 0; + + EVP_DecryptUpdate(ctx, NULL, &decrypted_len, s_aead_tag, 13); + EVP_DecryptUpdate(ctx, decrypted, &decrypted_len, (const u_char*) encrypted, encrypted_len); + + int res = 0; + if ( ! (res = EVP_DecryptFinal(ctx, NULL, &res)) ) + { + printf("Decryption failed with return code %d. Invalid key?\n", res); + EVP_CIPHER_CTX_free(ctx); + free(decrypted); + return false; + } + + EVP_CIPHER_CTX_free(ctx); + ForwardDecryptedData(decrypted_len, reinterpret_cast(decrypted), is_orig); + + free(decrypted); + return true; + } + + // This is only reached if key derivation somehow failed + return false; + } + +void SSL_Analyzer::ForwardDecryptedData(int len, const u_char* data, bool is_orig) + { + if ( ! pia ) + { + pia = new analyzer::pia::PIA_TCP(Conn()); + if ( AddChildAnalyzer(pia) ) + { + pia->FirstPacket(true, nullptr); + pia->FirstPacket(false, nullptr); + } + else + reporter->FatalError("Could not initialize PIA"); + + // FIXME: Also statically add HTTP/H2 at the moment. + // We should move this bit to scriptland + auto http = analyzer_mgr->InstantiateAnalyzer("HTTP", Conn()); + if ( http ) + AddChildAnalyzer(http); + auto http2 = analyzer_mgr->InstantiateAnalyzer("HTTP2", Conn()); + if ( http2 ) + AddChildAnalyzer(http2); + } + + ForwardStream(len, data, is_orig); + } + } // namespace zeek::analyzer::ssl diff --git a/src/analyzer/protocol/ssl/SSL.h b/src/analyzer/protocol/ssl/SSL.h index 12dd2ce57c..4eac7e77a6 100644 --- a/src/analyzer/protocol/ssl/SSL.h +++ b/src/analyzer/protocol/ssl/SSL.h @@ -1,5 +1,6 @@ #pragma once +#include "zeek/analyzer/protocol/pia/PIA.h" #include "zeek/analyzer/protocol/tcp/TCP.h" #include "zeek/analyzer/protocol/ssl/events.bif.h" @@ -33,11 +34,24 @@ public: static analyzer::Analyzer* Instantiate(Connection* conn) { return new SSL_Analyzer(conn); } + // Key material for decryption + void SetSecret(const u_char* data, int len); + void SetKeys(const u_char* data, int len); + + bool TryDecryptApplicationData(int len, const u_char* data, bool is_orig, uint8_t content_type, uint16_t raw_tls_version); + void ForwardDecryptedData(int len, const u_char* data, bool is_orig); + protected: binpac::SSL::SSL_Conn* interp; binpac::TLSHandshake::Handshake_Conn* handshake_interp; bool had_gap; + // FIXME: should this be moved into the connection? + int c_seq; + int s_seq; + zeek::StringVal *secret; + zeek::StringVal *keys; + zeek::analyzer::pia::PIA_TCP *pia; }; } // namespace zeek::analyzer::ssl diff --git a/src/analyzer/protocol/ssl/events.bif b/src/analyzer/protocol/ssl/events.bif index 25bc34398b..9192babfdf 100644 --- a/src/analyzer/protocol/ssl/events.bif +++ b/src/analyzer/protocol/ssl/events.bif @@ -558,9 +558,11 @@ event ssl_plaintext_data%(c: connection, is_orig: bool, record_version: count, c ## ## length: length of the entire message. ## +## payload: encrypted payload of the SSL/TLS message +## ## .. zeek:see:: ssl_client_hello ssl_established ssl_extension ssl_server_hello ## ssl_alert ssl_heartbeat ssl_probable_encrypted_handshake_message -event ssl_encrypted_data%(c: connection, is_orig: bool, record_version: count, content_type: count, length: count%); +event ssl_encrypted_data%(c: connection, is_orig: bool, record_version: count, content_type: count, length: count, payload: string%); ## This event is generated for application data records of TLS 1.3 connections of which ## we suspect that they contain handshake messages. diff --git a/src/analyzer/protocol/ssl/functions.bif b/src/analyzer/protocol/ssl/functions.bif index 2d72a4a741..b04bf19b3f 100644 --- a/src/analyzer/protocol/ssl/functions.bif +++ b/src/analyzer/protocol/ssl/functions.bif @@ -16,3 +16,25 @@ function set_ssl_established%(c: connection%): any static_cast(sa)->StartEncryption(); return nullptr; %} + +function set_secret%(c: connection, secret: string%): bool + %{ + analyzer::Analyzer* sa = c->FindAnalyzer("SSL"); + if ( sa ) + { + static_cast(sa)->SetSecret(secret->Bytes(), secret->Len()); + return zeek::val_mgr->True(); + } + return zeek::val_mgr->False(); + %} + +function set_keys%(c: connection, keys: string%): bool + %{ + analyzer::Analyzer* sa = c->FindAnalyzer("SSL"); + if ( sa ) + { + static_cast(sa)->SetKeys(keys->Bytes(), keys->Len()); + return zeek::val_mgr->True(); + } + return zeek::val_mgr->False(); + %} diff --git a/src/analyzer/protocol/ssl/proc-client-hello-tls.pac b/src/analyzer/protocol/ssl/proc-client-hello-tls.pac new file mode 100644 index 0000000000..a06be74e36 --- /dev/null +++ b/src/analyzer/protocol/ssl/proc-client-hello-tls.pac @@ -0,0 +1,56 @@ + function proc_client_hello( + version : uint16, ts : double, + client_random : bytestring, + session_id : uint8[], + cipher_suites16 : uint16[], + cipher_suites24 : uint24[], + compression_methods: uint8[]) : bool + %{ + if ( ! version_ok(version) ) + { + zeek_analyzer()->ProtocolViolation(zeek::util::fmt("unsupported client SSL version 0x%04x", version)); + zeek_analyzer()->SetSkip(true); + } + else + zeek_analyzer()->ProtocolConfirmation(); + + if ( ssl_client_hello ) + { + vector cipher_suites; + + if ( cipher_suites16 ) + std::copy(cipher_suites16->begin(), cipher_suites16->end(), std::back_inserter(cipher_suites)); + else + std::transform(cipher_suites24->begin(), cipher_suites24->end(), std::back_inserter(cipher_suites), to_int()); + + auto cipher_vec = zeek::make_intrusive(zeek::id::index_vec); + + for ( unsigned int i = 0; i < cipher_suites.size(); ++i ) + { + auto ciph = zeek::val_mgr->Count(cipher_suites[i]); + cipher_vec->Assign(i, ciph); + } + + auto comp_vec = zeek::make_intrusive(zeek::id::index_vec); + + if ( compression_methods ) + { + for ( unsigned int i = 0; i < compression_methods->size(); ++i ) + { + auto comp = zeek::val_mgr->Count((*compression_methods)[i]); + comp_vec->Assign(i, comp); + } + } + + set_client_random(client_random); + set_gmt_unix_time(ts); + zeek::BifEvent::enqueue_ssl_client_hello(zeek_analyzer(), zeek_analyzer()->Conn(), + version, record_version(), ts, + zeek::make_intrusive(client_random.length(), + (const char*) client_random.data()), + {zeek::AdoptRef{}, to_string_val(session_id)}, + std::move(cipher_vec), std::move(comp_vec)); + } + + return true; + %} diff --git a/src/analyzer/protocol/ssl/proc-server-hello-tls.pac b/src/analyzer/protocol/ssl/proc-server-hello-tls.pac new file mode 100644 index 0000000000..456cc9bcd0 --- /dev/null +++ b/src/analyzer/protocol/ssl/proc-server-hello-tls.pac @@ -0,0 +1,41 @@ + function proc_server_hello( + version : uint16, v2 : bool, + server_random : bytestring, + session_id : uint8[], + cipher_suites16 : uint16[], + cipher_suites24 : uint24[], + comp_method : uint8) : bool + %{ + if ( ! version_ok(version) ) + { + zeek_analyzer()->ProtocolViolation(zeek::util::fmt("unsupported server SSL version 0x%04x", version)); + zeek_analyzer()->SetSkip(true); + } + + if ( ssl_server_hello ) + { + vector* ciphers = new vector(); + + if ( cipher_suites16 ) + std::copy(cipher_suites16->begin(), cipher_suites16->end(), std::back_inserter(*ciphers)); + else + std::transform(cipher_suites24->begin(), cipher_suites24->end(), std::back_inserter(*ciphers), to_int()); + + uint32 ts = 0; + if ( v2 == 0 && server_random.length() >= 4 ) + ts = ntohl(*((uint32*)server_random.data())); + + set_server_random(server_random); + zeek::BifEvent::enqueue_ssl_server_hello(zeek_analyzer(), + zeek_analyzer()->Conn(), + version, record_version(), ts, + zeek::make_intrusive(server_random.length(), + (const char*) server_random.data()), + {zeek::AdoptRef{}, to_string_val(session_id)}, + ciphers->size()==0 ? 0 : ciphers->at(0), comp_method); + + delete ciphers; + } + + return true; + %} diff --git a/src/analyzer/protocol/ssl/ssl-dtls-analyzer.pac b/src/analyzer/protocol/ssl/ssl-dtls-analyzer.pac index f086fae609..8cb076ad6b 100644 --- a/src/analyzer/protocol/ssl/ssl-dtls-analyzer.pac +++ b/src/analyzer/protocol/ssl/ssl-dtls-analyzer.pac @@ -43,7 +43,7 @@ refine connection SSL_Conn += { return true; %} - function proc_ciphertext_record(rec : SSLRecord) : bool + function proc_ciphertext_record(rec : SSLRecord, cont: bytestring) : bool %{ if ( established_ == false && determine_tls13() == 1 ) { @@ -62,8 +62,15 @@ refine connection SSL_Conn += { } if ( ssl_encrypted_data ) + { zeek::BifEvent::enqueue_ssl_encrypted_data(zeek_analyzer(), - zeek_analyzer()->Conn(), ${rec.is_orig}, ${rec.raw_tls_version}, ${rec.content_type}, ${rec.length}); + zeek_analyzer()->Conn(), ${rec.is_orig}, ${rec.raw_tls_version}, ${rec.content_type}, ${rec.length}, + zeek::make_intrusive(cont.length(), (const char*) cont.data())); + if (rec->content_type() == APPLICATION_DATA) + { + zeek_analyzer()->TryDecryptApplicationData(cont.length(), cont.data(), rec->is_orig(), rec->content_type(), rec->raw_tls_version()); + } + } return true; %} @@ -123,7 +130,7 @@ refine typeattr UnknownRecord += &let { }; refine typeattr CiphertextRecord += &let { - proc : bool = $context.connection.proc_ciphertext_record(rec); + proc : bool = $context.connection.proc_ciphertext_record(rec, cont); } refine typeattr PlaintextRecord += &let { diff --git a/src/analyzer/protocol/ssl/ssl-dtls-protocol.pac b/src/analyzer/protocol/ssl/ssl-dtls-protocol.pac index ad2b869ae8..156c1bf6f4 100644 --- a/src/analyzer/protocol/ssl/ssl-dtls-protocol.pac +++ b/src/analyzer/protocol/ssl/ssl-dtls-protocol.pac @@ -96,7 +96,7 @@ type UnknownRecord(rec: SSLRecord) = record { }; type CiphertextRecord(rec: SSLRecord) = record { - cont : bytestring &restofdata &transient; + cont : bytestring &restofdata; }; ###################################################################### diff --git a/src/analyzer/protocol/ssl/tls-handshake-analyzer.pac b/src/analyzer/protocol/ssl/tls-handshake-analyzer.pac index 961eb8eaf3..9559ba0151 100644 --- a/src/analyzer/protocol/ssl/tls-handshake-analyzer.pac +++ b/src/analyzer/protocol/ssl/tls-handshake-analyzer.pac @@ -25,8 +25,8 @@ refine connection Handshake_Conn += { - %include proc-client-hello.pac - %include proc-server-hello.pac + %include proc-client-hello-tls.pac + %include proc-server-hello-tls.pac %include proc-certificate.pac function proc_session_ticket_handshake(rec: SessionTicketHandshake, is_orig: bool): bool diff --git a/src/analyzer/protocol/ssl/tls-handshake-protocol.pac b/src/analyzer/protocol/ssl/tls-handshake-protocol.pac index e37a0c512a..628dd3da48 100644 --- a/src/analyzer/protocol/ssl/tls-handshake-protocol.pac +++ b/src/analyzer/protocol/ssl/tls-handshake-protocol.pac @@ -943,6 +943,9 @@ refine connection Handshake_Conn += { uint32 chosen_cipher_; uint16 chosen_version_; uint16 record_version_; + bytestring client_random_; + bytestring server_random_; + uint32 gmt_unix_time_; %} %init{ @@ -950,6 +953,10 @@ refine connection Handshake_Conn += { chosen_version_ = UNKNOWN_VERSION; record_version_ = 0; + // FIXME: How should bytestrings be initialized? + // client_random_ = ?? + // server_random_ = ?? + gmt_unix_time_ = 0; %} function chosen_cipher() : int %{ return chosen_cipher_; %} @@ -983,5 +990,29 @@ refine connection Handshake_Conn += { record_version_ = version; return true; %} + + function client_random() : bytestring %{ return client_random_; %} + + function set_client_random(client_random: bytestring) : bool + %{ + client_random_.init(client_random.data(), client_random.length()); + return true; + %} + + function server_random() : bytestring %{ return server_random_; %} + + function set_server_random(server_random: bytestring) : bool + %{ + server_random_.init(server_random.data(), server_random.length()); + return true; + %} + + function gmt_unix_time() : uint32 %{ return gmt_unix_time_; %} + + function set_gmt_unix_time(ts: uint32) : bool + %{ + gmt_unix_time_ = ts; + return true; + %} }; diff --git a/testing/btest/Baseline/scripts.policy.misc.dump-events/really-all-events.log b/testing/btest/Baseline/scripts.policy.misc.dump-events/really-all-events.log index 0d937690c2..e20dbfb687 100644 --- a/testing/btest/Baseline/scripts.policy.misc.dump-events/really-all-events.log +++ b/testing/btest/Baseline/scripts.policy.misc.dump-events/really-all-events.log @@ -9079,6 +9079,7 @@ XXXXXXXXXX.XXXXXX ssl_encrypted_data [2] record_version: count = 771 [3] content_type: count = 22 [4] length: count = 32 + [5] payload: string = \x1c\x1c\x84S/9\x14e\xb6'\xe5,\x03\x0fY\xdf\x1b\xcfu\xc84\xae\x1a"\xea]9j'\xbeZ\xa7 XXXXXXXXXX.XXXXXX raw_packet [0] p: raw_pkt_hdr = [l2=[encap=LINK_ETHERNET, len=91, cap_len=91, src=58:b0:35:86:54:8d, dst=cc:b2:55:f4:62:92, vlan=, inner_vlan=, eth_type=2048, proto=L3_IPV4], ip=[hl=20, tos=0, len=77, id=51331, ttl=64, p=6, src=192.168.133.100, dst=17.167.150.73], ip6=, tcp=[sport=49655/tcp, dport=443/tcp, seq=3289393854, ack=2319612745, hl=20, dl=37, reserved=0, flags=24, win=8192], udp=, icmp=] @@ -9176,6 +9177,7 @@ XXXXXXXXXX.XXXXXX ssl_encrypted_data [2] record_version: count = 771 [3] content_type: count = 22 [4] length: count = 32 + [5] payload: string = Z\x99\x17~d\x06\xbd;\xb4\xdf\xe2\xb3~9,|\xac\xdb\xb4\xeb\xcc\x95.\x17\xd2Q\x8a\x96\xdb\x13\x09! XXXXXXXXXX.XXXXXX raw_packet [0] p: raw_pkt_hdr = [l2=[encap=LINK_ETHERNET, len=97, cap_len=97, src=cc:b2:55:f4:62:92, dst=58:b0:35:86:54:8d, vlan=, inner_vlan=, eth_type=2048, proto=L3_IPV4], ip=[hl=20, tos=0, len=83, id=50807, ttl=243, p=6, src=17.167.150.73, dst=192.168.133.100], ip6=, tcp=[sport=443/tcp, dport=49655/tcp, seq=2319612745, ack=3289393891, hl=20, dl=43, reserved=0, flags=24, win=3626], udp=, icmp=] diff --git a/testing/btest/scripts/base/protocols/ssl/handshake-events.test b/testing/btest/scripts/base/protocols/ssl/handshake-events.test index 0b45bebc02..0c694bfaa1 100644 --- a/testing/btest/scripts/base/protocols/ssl/handshake-events.test +++ b/testing/btest/scripts/base/protocols/ssl/handshake-events.test @@ -27,7 +27,7 @@ event ssl_plaintext_data(c: connection, is_orig: bool, record_version: count, co print "Plaintext data", c$id$orig_h, c$id$resp_h, is_orig, SSL::version_strings[record_version], content_type, length; } -event ssl_encrypted_data(c: connection, is_orig: bool, record_version: count, content_type: count, length: count) +event ssl_encrypted_data(c: connection, is_orig: bool, record_version: count, content_type: count, length: count, payload: string) { print "Encrypted data", c$id$orig_h, c$id$resp_h, is_orig, SSL::version_strings[record_version], content_type, length; } diff --git a/testing/btest/scripts/base/protocols/ssl/tls13.test b/testing/btest/scripts/base/protocols/ssl/tls13.test index 875149ce80..f1f03cd5df 100644 --- a/testing/btest/scripts/base/protocols/ssl/tls13.test +++ b/testing/btest/scripts/base/protocols/ssl/tls13.test @@ -37,7 +37,7 @@ event ssl_established(c: connection) print "established", c$id; } -event ssl_encrypted_data(c: connection, is_orig: bool, record_version: count, content_type: count, length: count) +event ssl_encrypted_data(c: connection, is_orig: bool, record_version: count, content_type: count, length: count, payload: string) { print "encrypted", c$id, is_orig, SSL::version_strings[record_version], content_type; } diff --git a/testing/btest/scripts/base/protocols/ssl/tls13_encrypted_handshake_events.test b/testing/btest/scripts/base/protocols/ssl/tls13_encrypted_handshake_events.test index 3293315723..08936cee56 100644 --- a/testing/btest/scripts/base/protocols/ssl/tls13_encrypted_handshake_events.test +++ b/testing/btest/scripts/base/protocols/ssl/tls13_encrypted_handshake_events.test @@ -6,7 +6,7 @@ redef SSL::disable_analyzer_after_detection=F; -event ssl_encrypted_data(c: connection, is_orig: bool, record_version: count, content_type: count, length: count) +event ssl_encrypted_data(c: connection, is_orig: bool, record_version: count, content_type: count, length: count, payload: string) { print "encrypted", c$id, is_orig, SSL::version_strings[record_version], content_type; }