mirror of
https://github.com/zeek/zeek.git
synced 2025-10-02 06:38:20 +00:00
Updates to improve SSL scripts.
- Certificate validation volume has been greatly cut down by caching results. - Cert hashing is now done in one place instead of being repeated everywhere a cert hash was needed. - Some small cleanups for notice suppression that should greatly reduce duplicate notice volume about invalid certificates.
This commit is contained in:
parent
e6a3dbfb5d
commit
549661bd11
5 changed files with 59 additions and 19 deletions
21
scripts/policy/protocols/ssl/cert-hash.bro
Normal file
21
scripts/policy/protocols/ssl/cert-hash.bro
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
##! This script calculates MD5 sums for server DER formatted certificates.
|
||||||
|
|
||||||
|
@load base/protocols/ssl
|
||||||
|
|
||||||
|
module SSL;
|
||||||
|
|
||||||
|
export {
|
||||||
|
redef record Info += {
|
||||||
|
cert_hash: string &log &optional;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
event x509_certificate(c: connection, cert: X509, is_server: bool, chain_idx: count, chain_len: count, der_cert: string) &priority=10
|
||||||
|
{
|
||||||
|
# We aren't tracking client certificates yet and we are also only tracking
|
||||||
|
# the primary cert.
|
||||||
|
if ( ! is_server || chain_idx != 0 )
|
||||||
|
return;
|
||||||
|
|
||||||
|
c$ssl$cert_hash = md5_hash(der_cert);
|
||||||
|
}
|
|
@ -7,6 +7,8 @@
|
||||||
@load base/frameworks/notice
|
@load base/frameworks/notice
|
||||||
@load base/utils/directions-and-hosts
|
@load base/utils/directions-and-hosts
|
||||||
|
|
||||||
|
@load protocols/ssl/cert-hash
|
||||||
|
|
||||||
module SSL;
|
module SSL;
|
||||||
|
|
||||||
export {
|
export {
|
||||||
|
@ -31,10 +33,6 @@ export {
|
||||||
const notify_when_cert_expiring_in = 30days &redef;
|
const notify_when_cert_expiring_in = 30days &redef;
|
||||||
}
|
}
|
||||||
|
|
||||||
redef Notice::type_suppression_intervals += {
|
|
||||||
[[Certificate_Expired, Certificate_Expires_Soon, Certificate_Not_Valid_Yet]] = 1day
|
|
||||||
};
|
|
||||||
|
|
||||||
event x509_certificate(c: connection, cert: X509, is_server: bool, chain_idx: count, chain_len: count, der_cert: string) &priority=5
|
event x509_certificate(c: connection, cert: X509, is_server: bool, chain_idx: count, chain_len: count, der_cert: string) &priority=5
|
||||||
{
|
{
|
||||||
# If this isn't the host cert or we aren't interested in the server, just return.
|
# If this isn't the host cert or we aren't interested in the server, just return.
|
||||||
|
@ -45,17 +43,17 @@ event x509_certificate(c: connection, cert: X509, is_server: bool, chain_idx: co
|
||||||
NOTICE([$note=Certificate_Not_Valid_Yet,
|
NOTICE([$note=Certificate_Not_Valid_Yet,
|
||||||
$conn=c, $suppress_for=1day,
|
$conn=c, $suppress_for=1day,
|
||||||
$msg=fmt("Certificate %s isn't valid until %T", cert$subject, cert$not_valid_before),
|
$msg=fmt("Certificate %s isn't valid until %T", cert$subject, cert$not_valid_before),
|
||||||
$identifier=fmt("%s:%d-%s", c$id$resp_h, c$id$resp_p, md5_hash(der_cert))]);
|
$identifier=cat(c$id$resp_h, c$id$resp_p, c$ssl$cert_hash)]);
|
||||||
|
|
||||||
else if ( cert$not_valid_after < network_time() )
|
else if ( cert$not_valid_after < network_time() )
|
||||||
NOTICE([$note=Certificate_Expired,
|
NOTICE([$note=Certificate_Expired,
|
||||||
$conn=c, $suppress_for=1day,
|
$conn=c, $suppress_for=1day,
|
||||||
$msg=fmt("Certificate %s expired at %T", cert$subject, cert$not_valid_after),
|
$msg=fmt("Certificate %s expired at %T", cert$subject, cert$not_valid_after),
|
||||||
$identifier=fmt("%s:%d-%s", c$id$resp_h, c$id$resp_p, md5_hash(der_cert))]);
|
$identifier=cat(c$id$resp_h, c$id$resp_p, c$ssl$cert_hash)]);
|
||||||
|
|
||||||
else if ( cert$not_valid_after - notify_when_cert_expiring_in < network_time() )
|
else if ( cert$not_valid_after - notify_when_cert_expiring_in < network_time() )
|
||||||
NOTICE([$note=Certificate_Expires_Soon,
|
NOTICE([$note=Certificate_Expires_Soon,
|
||||||
$msg=fmt("Certificate %s is going to expire at %T", cert$subject, cert$not_valid_after),
|
$msg=fmt("Certificate %s is going to expire at %T", cert$subject, cert$not_valid_after),
|
||||||
$conn=c, $suppress_for=1day,
|
$conn=c, $suppress_for=1day,
|
||||||
$identifier=fmt("%s:%d-%s", c$id$resp_h, c$id$resp_p, md5_hash(der_cert))]);
|
$identifier=cat(c$id$resp_h, c$id$resp_p, c$ssl$cert_hash)]);
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,19 +30,18 @@ export {
|
||||||
## the certificate.
|
## the certificate.
|
||||||
global extracted_certs: set[string] = set() &read_expire=1hr &redef;
|
global extracted_certs: set[string] = set() &read_expire=1hr &redef;
|
||||||
|
|
||||||
event ssl_established(c: connection)
|
event ssl_established(c: connection) &priority=5
|
||||||
{
|
{
|
||||||
if ( ! c$ssl?$cert )
|
if ( ! c$ssl?$cert )
|
||||||
return;
|
return;
|
||||||
if ( ! addr_matches_host(c$id$resp_h, extract_certs_pem) )
|
if ( ! addr_matches_host(c$id$resp_h, extract_certs_pem) )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
local cert_hash = md5_hash(c$ssl$cert);
|
if ( c$ssl$cert_hash in extracted_certs )
|
||||||
if ( cert_hash in extracted_certs )
|
|
||||||
# If we already extracted this cert, don't do it again.
|
# If we already extracted this cert, don't do it again.
|
||||||
return;
|
return;
|
||||||
|
|
||||||
add extracted_certs[cert_hash];
|
add extracted_certs[c$ssl$cert_hash];
|
||||||
local side = Site::is_local_addr(c$id$resp_h) ? "local" : "remote";
|
local side = Site::is_local_addr(c$id$resp_h) ? "local" : "remote";
|
||||||
local cmd = fmt("%s x509 -inform DER -outform PEM >> certs-%s.pem", openssl_util, side);
|
local cmd = fmt("%s x509 -inform DER -outform PEM >> certs-%s.pem", openssl_util, side);
|
||||||
piped_exec(cmd, c$ssl$cert);
|
piped_exec(cmd, c$ssl$cert);
|
||||||
|
|
|
@ -1,5 +1,8 @@
|
||||||
@load base/utils/directions-and-hosts
|
@load base/utils/directions-and-hosts
|
||||||
|
|
||||||
|
@load base/protocols/ssl
|
||||||
|
@load protocols/ssl/cert-hash
|
||||||
|
|
||||||
module Known;
|
module Known;
|
||||||
|
|
||||||
export {
|
export {
|
||||||
|
@ -39,7 +42,7 @@ event bro_init() &priority=5
|
||||||
Log::create_stream(Known::CERTS_LOG, [$columns=CertsInfo, $ev=log_known_certs]);
|
Log::create_stream(Known::CERTS_LOG, [$columns=CertsInfo, $ev=log_known_certs]);
|
||||||
}
|
}
|
||||||
|
|
||||||
event x509_certificate(c: connection, cert: X509, is_server: bool, chain_idx: count, chain_len: count, der_cert: string)
|
event x509_certificate(c: connection, cert: X509, is_server: bool, chain_idx: count, chain_len: count, der_cert: string) &priority=5
|
||||||
{
|
{
|
||||||
# We aren't tracking client certificates yet.
|
# We aren't tracking client certificates yet.
|
||||||
if ( ! is_server ) return;
|
if ( ! is_server ) return;
|
||||||
|
@ -53,6 +56,7 @@ event x509_certificate(c: connection, cert: X509, is_server: bool, chain_idx: co
|
||||||
Log::write(Known::CERTS_LOG, [$ts=network_time(), $host=host,
|
Log::write(Known::CERTS_LOG, [$ts=network_time(), $host=host,
|
||||||
$port_num=c$id$resp_p, $subject=cert$subject,
|
$port_num=c$id$resp_p, $subject=cert$subject,
|
||||||
$issuer_subject=cert$issuer,
|
$issuer_subject=cert$issuer,
|
||||||
$serial=cert$serial]);
|
$serial=cert$serial,
|
||||||
|
$identifier=cat(c$id$resp_h,c$id$resp_p,c$ssl$cert_hash)]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,10 @@
|
||||||
|
##! Perform full certificate chain validation for SSL certificates.
|
||||||
|
|
||||||
@load base/frameworks/notice/main
|
@load base/frameworks/notice/main
|
||||||
@load base/protocols/ssl/main
|
@load base/protocols/ssl/main
|
||||||
|
|
||||||
|
@load protocols/ssl/cert-hash
|
||||||
|
|
||||||
module SSL;
|
module SSL;
|
||||||
|
|
||||||
export {
|
export {
|
||||||
|
@ -11,22 +15,36 @@ export {
|
||||||
redef record Info += {
|
redef record Info += {
|
||||||
validation_status: string &log &optional;
|
validation_status: string &log &optional;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
## MD5 hash values for recently validated certs along with the validation
|
||||||
|
## status message are kept in this table so avoid constant validation
|
||||||
|
## everytime the same certificate is seen.
|
||||||
|
global recently_validated_certs: table[string] of string = table()
|
||||||
|
&read_expire=5mins &synchronized;
|
||||||
}
|
}
|
||||||
|
|
||||||
event ssl_established(c: connection) &priority=3
|
event ssl_established(c: connection) &priority=3
|
||||||
{
|
{
|
||||||
# If there aren't any certs we can't very well do certificate validation.
|
# If there aren't any certs we can't very well do certificate validation.
|
||||||
if ( !c$ssl?$cert || !c$ssl?$cert_chain )
|
if ( ! c$ssl?$cert || ! c$ssl?$cert_chain )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
if ( c$ssl?$cert_hash && c$ssl$cert_hash in recently_validated_certs )
|
||||||
|
{
|
||||||
|
c$ssl$validation_status = recently_validated_certs[c$ssl$cert_hash];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
local result = x509_verify(c$ssl$cert, c$ssl$cert_chain, root_certs);
|
||||||
|
c$ssl$validation_status = x509_err2str(result);
|
||||||
|
}
|
||||||
|
|
||||||
local result = x509_verify(c$ssl$cert, c$ssl$cert_chain, root_certs);
|
if ( c$ssl$validation_status != "ok" )
|
||||||
c$ssl$validation_status = x509_err2str(result);
|
|
||||||
if ( result != 0 )
|
|
||||||
{
|
{
|
||||||
local message = fmt("SSL certificate validation failed with (%s)", c$ssl$validation_status);
|
local message = fmt("SSL certificate validation failed with (%s)", c$ssl$validation_status);
|
||||||
NOTICE([$note=Invalid_Server_Cert, $msg=message,
|
NOTICE([$note=Invalid_Server_Cert, $msg=message,
|
||||||
$sub=c$ssl$subject, $conn=c]);
|
$sub=c$ssl$subject, $conn=c,
|
||||||
|
$identifier=cat(c$id$resp_h,c$id$resp_p,c$ssl$validation_status,c$ssl$cert_hash)]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue