TLS decryption: refactoring, more comments, less bare pointers

This commit refactors TLS decryption, adds more comments in scripts and
in C++ source-code, and removes use of bare pointers, instead relying
more on stl data types.
This commit is contained in:
Johanna Amann 2022-01-17 12:15:40 +00:00
parent 689b06d9bd
commit b78f30339f
7 changed files with 186 additions and 120 deletions

View file

@ -1,4 +1,10 @@
#! Decrypt SSL/TLS payloads ##! This script allows for the decryption of certain TLS 1.2 connection, if the user is in possession
##! of the private key material for the session. Key material can either be provided via a file (useful
##! for processing trace files) or via sending events via broker (for live decoding).
##!
##! Please note that this feature is experimental and can change without guarantees to our typical
##! deprecation tieline. Please also note that currently only TLS 1.2 connections that use the
##! TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 cipher suite are supported.
@load base/frameworks/input @load base/frameworks/input
@load base/frameworks/notice @load base/frameworks/notice
@ -7,49 +13,69 @@
module SSL; module SSL;
export {
const keylog_file = getenv("ZEEK_TLS_KEYLOG_FILE") &redef;
global secrets: table[string] of string = {} &redef;
global keys: table[string] of string = {} &redef;
global add_keys: event(client_random: string, keys: string);
global add_secret: event(client_random: string, secret: string);
}
# Do not disable analyzers after detection - otherwise we will not receive # Do not disable analyzers after detection - otherwise we will not receive
# encrypted packets. # encrypted packets.
redef SSL::disable_analyzer_after_detection = F; redef SSL::disable_analyzer_after_detection = F;
export {
## This can be set to a file that contains the session secrets for decryption, when parsing a pcap file.
## Please note that, when using this feature, you probably want to pause processing of data till this
## file has been read.
const keylog_file = getenv("ZEEK_TLS_KEYLOG_FILE") &redef;
## Secrets expire after this time of not being used.
const secret_expiration = 5 mins &redef;
## This event can be triggered, e.g., via broker to add known keys to the TLS key database.
##
## client_random: client random for which the key is set
##
## keys: key material
global add_keys: event(client_random: string, keys: string);
## This event can be triggered, e.g., via broker to add known secrets to the TLS secret datbase.
##
## client_random: client random for which the secret is set
##
## secrets: derived TLS secrets material
global add_secret: event(client_random: string, secret: string);
}
@if ( keylog_file == "" )
# If a keylog file was given via an environment variable, let's disable secret expiration - that does not
# make sense for pcaps.
global secrets: table[string] of string = {} &redef;
global keys: table[string] of string = {} &redef;
@else
#global secrets: table[string] of string = {} &read_expire=secret_expiration &redef;
#global keys: table[string] of string = {} &read_expire=secret_expiration &redef;
@endif
redef record SSL::Info += { redef record SSL::Info += {
# Decryption uses client_random as identifier # Decryption uses client_random as identifier
client_random: string &log &optional; client_random: string &optional;
}; };
type Idx: record { type SecretsIdx: record {
client_random: string; client_random: string;
}; };
type Val: record { type SecretsVal: record {
secret: string; secret: string;
}; };
const input_stream_name = "input-tls-keylog-file"; const tls_decrypt_stream_name = "tls-keylog-file";
event zeek_init() event zeek_init()
{ {
# listen for secrets # listen for secrets
Broker::subscribe("/zeek/tls/decryption"); Broker::subscribe("/zeek/tls/decryption");
# FIXME: is such a functionality helpful?
# ingest keylog file if the environment is set
if ( keylog_file != "" ) if ( keylog_file != "" )
{ {
suspend_processing(); Input::add_table([$name=tls_decrypt_stream_name, $source=keylog_file, $destination=secrets, $idx=SecretsIdx, $val=SecretsVal, $want_record=F]);
Input::remove(tls_decrypt_stream_name);
Input::add_table([$name=input_stream_name, $source=keylog_file, $destination=secrets, $idx=Idx, $val=Val, $want_record=F]);
Input::remove(input_stream_name);
} }
} }
@ -66,6 +92,7 @@ event SSL::add_secret(client_random: string, val: string)
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) 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; c$ssl$client_random = client_random;
print "Client random!";
if ( client_random in keys ) if ( client_random in keys )
set_keys(c, keys[client_random]); set_keys(c, keys[client_random]);
@ -73,7 +100,7 @@ event ssl_client_hello(c: connection, version: count, record_version: count, pos
set_secret(c, secrets[client_random]); set_secret(c, secrets[client_random]);
} }
event ssl_encrypted_data(c: connection, is_orig: bool, record_version: count, content_type: count, length: count) event ssl_change_cipher_spec(c: connection, is_orig: bool)
{ {
if ( c$ssl?$client_random ) if ( c$ssl?$client_random )
{ {
@ -81,20 +108,5 @@ event ssl_encrypted_data(c: connection, is_orig: bool, record_version: count, co
set_keys(c, keys[c$ssl$client_random]); set_keys(c, keys[c$ssl$client_random]);
else if ( c$ssl$client_random in secrets ) else if ( c$ssl$client_random in secrets )
set_secret(c, secrets[c$ssl$client_random]); set_secret(c, secrets[c$ssl$client_random]);
else
{
# FIXME: perhaps report that we could not decrypt the session
}
} }
} }
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();
}

View file

@ -116,7 +116,7 @@
@load protocols/ssh/geo-data.zeek @load protocols/ssh/geo-data.zeek
@load protocols/ssh/interesting-hostnames.zeek @load protocols/ssh/interesting-hostnames.zeek
@load protocols/ssh/software.zeek @load protocols/ssh/software.zeek
#@load protocols/ssl/decryption.zeek @load protocols/ssl/decryption.zeek
@load protocols/ssl/expiring-certs.zeek @load protocols/ssl/expiring-certs.zeek
# @load protocols/ssl/extract-certs-pem.zeek # @load protocols/ssl/extract-certs-pem.zeek
@load protocols/ssl/heartbleed.zeek @load protocols/ssl/heartbleed.zeek

View file

@ -26,11 +26,14 @@ namespace zeek::analyzer::ssl
#define MSB(a) ((a >> 8) & 0xff) #define MSB(a) ((a >> 8) & 0xff)
#define LSB(a) (a & 0xff) #define LSB(a) (a & 0xff)
static void fmt_seq(uint32_t num, u_char* buf) static std::basic_string<unsigned char> fmt_seq(uint32_t num)
{ {
memset(buf, 0, 8); std::basic_string<unsigned char> out(4, '\0');
out.reserve(13);
uint32_t netnum = htonl(num); uint32_t netnum = htonl(num);
memcpy(buf + 4, &netnum, 4); out.append(reinterpret_cast<u_char*>(&netnum), 4);
out.append(5, '\0');
return out;
} }
SSL_Analyzer::SSL_Analyzer(Connection* c) : analyzer::tcp::TCP_ApplicationAnalyzer("SSL", c) SSL_Analyzer::SSL_Analyzer(Connection* c) : analyzer::tcp::TCP_ApplicationAnalyzer("SSL", c)
@ -124,9 +127,9 @@ void SSL_Analyzer::Undelivered(uint64_t seq, int len, bool orig)
interp->NewGap(orig, len); interp->NewGap(orig, len);
} }
void SSL_Analyzer::SetSecret(zeek::StringVal* secret) void SSL_Analyzer::SetSecret(const zeek::StringVal& secret)
{ {
SetSecret(secret->Len(), secret->Bytes()); SetSecret(secret.Len(), secret.Bytes());
} }
void SSL_Analyzer::SetSecret(size_t len, const u_char* data) void SSL_Analyzer::SetSecret(size_t len, const u_char* data)
@ -135,11 +138,11 @@ void SSL_Analyzer::SetSecret(size_t len, const u_char* data)
secret.append((const char*)data, len); secret.append((const char*)data, len);
} }
void SSL_Analyzer::SetKeys(zeek::StringVal* nkeys) void SSL_Analyzer::SetKeys(const zeek::StringVal& nkeys)
{ {
keys.clear(); keys.clear();
keys.reserve(nkeys->Len()); keys.reserve(nkeys.Len());
std::copy(nkeys->Bytes(), nkeys->Bytes() + nkeys->Len(), std::back_inserter(keys)); std::copy(nkeys.Bytes(), nkeys.Bytes() + nkeys.Len(), std::back_inserter(keys));
} }
void SSL_Analyzer::SetKeys(const std::vector<u_char> newkeys) void SSL_Analyzer::SetKeys(const std::vector<u_char> newkeys)
@ -282,40 +285,38 @@ bool SSL_Analyzer::TryDecryptApplicationData(int len, const u_char* data, bool i
// FIXME: could also print keys or conn id here // FIXME: could also print keys or conn id here
DBG_LOG(DBG_ANALYZER, "Decrypting application data"); DBG_LOG(DBG_ANALYZER, "Decrypting application data");
// NOTE: you must not call functions that invalidate keys.data() on keys during the
// remainder of this function. (Given that we do not manipulate the key material in this
// function that should not be hard)
// client write_key // client write_key
u_char c_wk[32]; const u_char* c_wk = keys.data();
// server write_key // server write_key
u_char s_wk[32]; const u_char* s_wk = keys.data() + 32;
// client IV // client IV
u_char c_iv[4]; const u_char* c_iv = keys.data() + 64;
// server IV // server IV
u_char s_iv[4]; const u_char* s_iv = keys.data() + 68;
// AEAD nonce & tag
u_char s_aead_nonce[12];
u_char s_aead_tag[13];
// FIXME: there should be a better way to do these copies
memcpy(c_wk, keys.data(), 32);
memcpy(s_wk, keys.data() + 32, 32);
memcpy(c_iv, keys.data() + 64, 4);
memcpy(s_iv, keys.data() + 68, 4);
// FIXME: should we change types here? // FIXME: should we change types here?
u_char* encrypted = (u_char*)data; u_char* encrypted = (u_char*)data;
size_t encrypted_len = len; size_t encrypted_len = len;
// FIXME: should this be moved to SSL_Conn?
if ( is_orig ) if ( is_orig )
c_seq++; c_seq++;
else else
s_seq++; s_seq++;
// AEAD nonce, length 12
std::basic_string<unsigned char> s_aead_nonce;
if ( is_orig ) if ( is_orig )
memcpy(s_aead_nonce, c_iv, 4); s_aead_nonce.assign(c_iv, 4);
else else
memcpy(s_aead_nonce, s_iv, 4); s_aead_nonce.assign(s_iv, 4);
memcpy(&(s_aead_nonce[4]), encrypted, 8);
// this should be the explicit counter
s_aead_nonce.append(encrypted, 8);
assert(s_aead_nonce.size() == 12);
EVP_CIPHER_CTX* ctx = EVP_CIPHER_CTX_new(); EVP_CIPHER_CTX* ctx = EVP_CIPHER_CTX_new();
EVP_CIPHER_CTX_init(ctx); EVP_CIPHER_CTX_init(ctx);
@ -323,47 +324,60 @@ bool SSL_Analyzer::TryDecryptApplicationData(int len, const u_char* data, bool i
encrypted += 8; encrypted += 8;
// FIXME: is this because of nonce and aead tag? // FIXME: is this because of nonce and aead tag?
if ( encrypted_len <= (16 + 8) )
{
DBG_LOG(DBG_ANALYZER, "Invalid encrypted length encountered during TLS decryption");
EVP_CIPHER_CTX_free(ctx);
return false;
}
encrypted_len -= 8; encrypted_len -= 8;
encrypted_len -= 16; encrypted_len -= 16;
// FIXME: aes_256_gcm should not be hardcoded here ;) // FIXME: aes_256_gcm should not be hardcoded here ;)
if ( is_orig ) if ( is_orig )
EVP_DecryptInit(ctx, EVP_aes_256_gcm(), c_wk, s_aead_nonce); EVP_DecryptInit(ctx, EVP_aes_256_gcm(), c_wk, s_aead_nonce.data());
else else
EVP_DecryptInit(ctx, EVP_aes_256_gcm(), s_wk, s_aead_nonce); EVP_DecryptInit(ctx, EVP_aes_256_gcm(), s_wk, s_aead_nonce.data());
EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_TAG, 16, encrypted + encrypted_len); EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_TAG, 16, encrypted + encrypted_len);
// AEAD tag
std::basic_string<unsigned char> s_aead_tag;
if ( is_orig ) if ( is_orig )
fmt_seq(c_seq, s_aead_tag); s_aead_tag = fmt_seq(c_seq);
else else
fmt_seq(s_seq, s_aead_tag); s_aead_tag = fmt_seq(s_seq);
s_aead_tag[8] = content_type; s_aead_tag[8] = content_type;
s_aead_tag[9] = MSB(raw_tls_version); s_aead_tag[9] = MSB(raw_tls_version);
s_aead_tag[10] = LSB(raw_tls_version); s_aead_tag[10] = LSB(raw_tls_version);
s_aead_tag[11] = MSB(encrypted_len); s_aead_tag[11] = MSB(encrypted_len);
s_aead_tag[12] = LSB(encrypted_len); s_aead_tag[12] = LSB(encrypted_len);
assert(s_aead_tag.size() == 13);
u_char* decrypted = new u_char[encrypted_len]; auto decrypted = std::vector<u_char>(
encrypted_len +
16); // see OpenSSL manpage - 16 is the block size for the supported cipher
int decrypted_len = 0; int decrypted_len = 0;
EVP_DecryptUpdate(ctx, NULL, &decrypted_len, s_aead_tag, 13); EVP_DecryptUpdate(ctx, NULL, &decrypted_len, s_aead_tag.data(), s_aead_tag.size());
EVP_DecryptUpdate(ctx, decrypted, &decrypted_len, (const u_char*)encrypted, encrypted_len); EVP_DecryptUpdate(ctx, decrypted.data(), &decrypted_len, (const u_char*)encrypted,
encrypted_len);
assert(decrypted_len <= decrypted.size());
decrypted.resize(decrypted_len);
int res = 0; int res = 0;
if ( ! (res = EVP_DecryptFinal(ctx, NULL, &res)) ) if ( ! (res = EVP_DecryptFinal(ctx, NULL, &res)) )
{ {
DBG_LOG(DBG_ANALYZER, "Decryption failed with return code: %d. Invalid key?\n", res); DBG_LOG(DBG_ANALYZER, "Decryption failed with return code: %d. Invalid key?\n", res);
EVP_CIPHER_CTX_free(ctx); EVP_CIPHER_CTX_free(ctx);
delete[] decrypted;
return false; return false;
} }
DBG_LOG(DBG_ANALYZER, "Successfully decrypted %d bytes.", decrypted_len); DBG_LOG(DBG_ANALYZER, "Successfully decrypted %d bytes.", decrypted_len);
EVP_CIPHER_CTX_free(ctx); EVP_CIPHER_CTX_free(ctx);
ForwardDecryptedData(decrypted_len, reinterpret_cast<const u_char*>(decrypted), is_orig); ForwardDecryptedData(decrypted, is_orig);
delete[] decrypted;
return true; return true;
} }
@ -371,7 +385,7 @@ bool SSL_Analyzer::TryDecryptApplicationData(int len, const u_char* data, bool i
return false; return false;
} }
void SSL_Analyzer::ForwardDecryptedData(int len, const u_char* data, bool is_orig) void SSL_Analyzer::ForwardDecryptedData(const std::vector<u_char>& data, bool is_orig)
{ {
if ( ! pia ) if ( ! pia )
{ {
@ -383,18 +397,9 @@ void SSL_Analyzer::ForwardDecryptedData(int len, const u_char* data, bool is_ori
} }
else else
reporter->FatalError("Could not initialize PIA"); 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); ForwardStream(data.size(), data.data(), is_orig);
} }
} // namespace zeek::analyzer::ssl } // namespace zeek::analyzer::ssl

View file

@ -25,6 +25,9 @@ namespace zeek::analyzer::ssl
class SSL_Analyzer final : public analyzer::tcp::TCP_ApplicationAnalyzer class SSL_Analyzer final : public analyzer::tcp::TCP_ApplicationAnalyzer
{ {
// let binpac forward encryppted TLS application data to us.
friend class binpac::SSL::SSL_Conn;
public: public:
explicit SSL_Analyzer(Connection* conn); explicit SSL_Analyzer(Connection* conn);
~SSL_Analyzer() override; ~SSL_Analyzer() override;
@ -50,14 +53,20 @@ public:
* Set the secret that should be used to derive keys for the * Set the secret that should be used to derive keys for the
* connection. (For TLS 1.2 this is the pre-master secret) * connection. (For TLS 1.2 this is the pre-master secret)
* *
* Please note that these functions currently are hardcoded to only work with a single TLS 1.2
* cuphersuite (TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384).
*
* @param secret The secret to set * @param secret The secret to set
*/ */
void SetSecret(StringVal* secret); void SetSecret(const StringVal& secret);
/** /**
* Set the secret that should be used to derive keys for the * Set the secret that should be used to derive keys for the
* connection. (For TLS 1.2 this is the pre-master secret) * connection. (For TLS 1.2 this is the pre-master secret)
* *
* Please note that these functions currently are hardcoded to only work with a single TLS 1.2
* cuphersuite (TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384).
*
* @param len Length of the secret bytes * @param len Length of the secret bytes
* *
* @param data Pointer to the secret bytes * @param data Pointer to the secret bytes
@ -68,24 +77,32 @@ public:
* 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.
* *
* Please note that these functions currently are hardcoded to only work with a single TLS 1.2
* cuphersuite (TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384).
*
* @param keys The key buffer as derived via TLS PRF (for * @param keys The key buffer as derived via TLS PRF (for
* AES_GCM this should be 72 bytes in length) * AES_GCM this should be 72 bytes in length)
*/ */
void SetKeys(StringVal* keys); void SetKeys(const StringVal& keys);
/** /**
* 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.
* *
* @param len Length of the key buffer (for AES_GCM this should * Please note that these functions currently are hardcoded to only work with a single TLS 1.2
* be 72) * cuphersuite (TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384).
* *
* @param data Pointer to the key buffer as derived via TLS PRF * @param keys The key buffer as derived via TLS PRF (for
* AES_GCM this should be 72 bytes in length)
*/ */
void SetKeys(const std::vector<u_char> newkeys); void SetKeys(const std::vector<u_char> newkeys);
protected:
/** /**
* Try to decrypt TLS application data from a packet. Requires secret or keys to be set prior * Try to decrypt TLS application data from a packet. Requires secret or keys to be set prior.
*
* Please note that these functions currently are hardcoded to only work with a single TLS 1.2
* cuphersuite (TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384).
* *
* @param len Length of the encrypted bytes to decrypt * @param len Length of the encrypted bytes to decrypt
* *
@ -96,48 +113,46 @@ public:
* @param content_type Content type as given in the TLS packet * @param content_type Content type as given in the TLS packet
* *
* @param raw_tls_version Raw TLS version as given in the TLS packets * @param raw_tls_version Raw TLS version as given in the TLS packets
*
* @return True if decryption succeeded and data was forwarded.
*/ */
bool TryDecryptApplicationData(int len, const u_char* data, bool is_orig, uint8_t content_type, bool TryDecryptApplicationData(int len, const u_char* data, bool is_orig, uint8_t content_type,
uint16_t raw_tls_version); uint16_t raw_tls_version);
/** /**
* TLS 1.2 pseudo random function (PRF) used to expand the pre-master secret and derive keys. * TLS 1.2 pseudo random function (PRF) used to expand the pre-master secret and derive keys.
* The seed is obtained by concatinating rnd1 and rnd2 * The seed is obtained by concatinating rnd1 and rnd2.
*
* Please note that these functions currently are hardcoded to only work with a single TLS 1.2
* cuphersuite (TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384).
* *
* @param secret Secret as defined in the TLS RFC * @param secret Secret as defined in the TLS RFC
* *
* @param label Label as defined in the TLS RFC * @param label Label as defined in the TLS RFC
* *
* @param rnd1 Pointer to the first part of the seed * @param First part of the seed
* *
* @param rnd1_len Length of the first part of the seed * @param rnd2 Second part of the seed
*
* @param rnd2 Pointer to the second part of the seed
* *
* @param rnd2_len Length of the second part of the seed * @param rnd2_len Length of the second part of the seed
* *
* @param out Pointer to the derived bytes * @param requested_len Length indicating how many bytes should be derived
* *
* @param out_len Length indicating how many bytes should be derived * @return The derived bytes, if the operation succeeds.
*
* @return True, if the operation completed successfully, false otherwise
*/ */
std::optional<std::vector<u_char>> TLS12_PRF(const std::string& secret, std::optional<std::vector<u_char>> TLS12_PRF(const std::string& secret,
const std::string& label, const std::string& rnd1, const std::string& label, const std::string& rnd1,
const std::string& rnd2, size_t requested_len); const std::string& rnd2, size_t requested_len);
/** /**
* Forward decrypted TLS application data to child analyzers * Forward decrypted TLS application data to child analyzers.
* *
* @param len Length of the data to forward * @param data Data to forward
*
* @param data Pointer to the data to forward
* *
* @param is_orig Direction of the connection * @param is_orig Direction of the connection
*/ */
void ForwardDecryptedData(int len, const u_char* data, bool is_orig); void ForwardDecryptedData(const std::vector<u_char>& data, bool is_orig);
protected:
binpac::SSL::SSL_Conn* interp; binpac::SSL::SSL_Conn* interp;
binpac::TLSHandshake::Handshake_Conn* handshake_interp; binpac::TLSHandshake::Handshake_Conn* handshake_interp;
bool had_gap; bool had_gap;

View file

@ -9,32 +9,59 @@
## finished succesfully). ## finished succesfully).
## ##
## c: The SSL connection. ## c: The SSL connection.
function set_ssl_established%(c: connection%): any ##
## Returns: T on success, F on failure.
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 nullptr; 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 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
## 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 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();
%} %}

View file

@ -13,10 +13,12 @@ refine connection SSL_Conn += {
%member{ %member{
int established_; int established_;
int decryption_failed_;
%} %}
%init{ %init{
established_ = false; established_ = false;
decryption_failed_ = false;
%} %}
%cleanup{ %cleanup{
@ -65,10 +67,13 @@ refine connection SSL_Conn += {
{ {
zeek::BifEvent::enqueue_ssl_encrypted_data(zeek_analyzer(), 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});
if (rec->content_type() == APPLICATION_DATA) }
{
zeek_analyzer()->TryDecryptApplicationData(cont.length(), cont.data(), rec->is_orig(), rec->content_type(), rec->raw_tls_version()); if ( rec->content_type() == APPLICATION_DATA && decryption_failed_ == false )
} {
// If decryption of one packet fails, do not try to decrypt future packets.
if ( ! zeek_analyzer()->TryDecryptApplicationData(cont.length(), cont.data(), rec->is_orig(), rec->content_type(), rec->raw_tls_version()) )
decryption_failed_ = true;
} }
return true; return true;

View file

@ -1,11 +1,13 @@
# @TEST-REQUIRES: grep -q "#define OPENSSL_HAVE_KDF_H" $BUILD/zeek-config.h # @TEST-REQUIRES: grep -q "#define OPENSSL_HAVE_KDF_H" $BUILD/zeek-config.h
# @TEST-EXEC: zeek -b -C -r $TRACES/tls/tls12-decryption.pcap %INPUT # @TEST-EXEC: zeek -B dpd -C -r $TRACES/tls/tls12-decryption.pcap %INPUT
# @TEST-EXEC: btest-diff http.log # @TEST-EXEC: btest-diff http.log
@load protocols/ssl/decryption @load protocols/ssl/decryption
@load base/protocols/http @load base/protocols/http
module SSL;
redef SSL::secrets += { redef SSL::secrets += {
["\xb4\x0a\x24\x4b\x48\xe4\x2e\xac\x28\x71\x44\xb1\xb7\x39\x30\x57\xca\xa1\x31\xf9\x61\xa7\x8e\x38\xb0\xe7\x7c\x1e"] = "\xbd\x01\xe5\x89\xd1\x05\x19\x9e\x9a\xb5\xfc\x9b\xd7\x58\xb5\xf2\x88\xdb\x28\xfd\x80\xaa\x02\x26\x1e\x47\x65\xac\x13\x57\xd0\x07\xfd\x08\xc7\xbd\xab\x45\x45\x0e\x01\x5a\x01\xd0\x8e\x5e\x7c\xa6", ["\xb4\x0a\x24\x4b\x48\xe4\x2e\xac\x28\x71\x44\xb1\xb7\x39\x30\x57\xca\xa1\x31\xf9\x61\xa7\x8e\x38\xb0\xe7\x7c\x1e"] = "\xbd\x01\xe5\x89\xd1\x05\x19\x9e\x9a\xb5\xfc\x9b\xd7\x58\xb5\xf2\x88\xdb\x28\xfd\x80\xaa\x02\x26\x1e\x47\x65\xac\x13\x57\xd0\x07\xfd\x08\xc7\xbd\xab\x45\x45\x0e\x01\x5a\x01\xd0\x8e\x5e\x7c\xa6",
}; };