mirror of
https://github.com/zeek/zeek.git
synced 2025-10-02 14:48:21 +00:00
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:
parent
689b06d9bd
commit
b78f30339f
7 changed files with 186 additions and 120 deletions
|
@ -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();
|
|
||||||
}
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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();
|
||||||
%}
|
%}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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",
|
||||||
};
|
};
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue