Merge remote-tracking branch 'origin/master' into topic/seth/file-entropy

# Conflicts:
#	scripts/test-all-policy.bro
#	testing/btest/Baseline/coverage.default-load-baseline/canonified_loaded_scripts.log
This commit is contained in:
Seth Hall 2016-03-21 11:39:15 -04:00
commit 89b4d79f93
1081 changed files with 38403 additions and 11012 deletions

View file

@ -4,7 +4,7 @@
##!
##! It's intended to be used from the command line like this::
##!
##! bro <scripts> frameworks/control/controller Control::host=<host_addr> Control::port=<host_port> Control::cmd=<command> [Control::arg=<arg>]
##! bro <scripts> frameworks/control/controller Control::host=<host_addr> Control::host_port=<host_port> Control::cmd=<command> [Control::arg=<arg>]
@load base/frameworks/control
@load base/frameworks/communication

View file

@ -0,0 +1,8 @@
##! Extract all files to disk.
@load base/files/extract
event file_new(f: fa_file)
{
Files::add_analyzer(f, Files::ANALYZER_EXTRACT);
}

View file

@ -1,5 +1,7 @@
##! Perform MD5 and SHA1 hashing on all files.
@load base/files/hash
event file_new(f: fa_file)
{
Files::add_analyzer(f, Files::ANALYZER_MD5);

View file

@ -0,0 +1,11 @@
@load base/frameworks/intel
@load ./where-locations
event ssh_server_host_key(c: connection, hash: string)
{
local seen = Intel::Seen($indicator=hash,
$indicator_type=Intel::PUBKEY_HASH,
$conn=c,
$where=SSH::IN_SERVER_HOST_KEY);
Intel::seen(seen);
}

View file

@ -10,3 +10,16 @@ event ssl_extension_server_name(c: connection, is_orig: bool, names: string_vec)
$conn=c,
$where=SSL::IN_SERVER_NAME]);
}
event ssl_established(c: connection)
{
if ( ! c$ssl?$cert_chain || |c$ssl$cert_chain| == 0 ||
! c$ssl$cert_chain[0]?$x509 )
return;
if ( c$ssl$cert_chain[0]$x509?$certificate && c$ssl$cert_chain[0]$x509$certificate?$cn )
Intel::seen([$indicator=c$ssl$cert_chain[0]$x509$certificate$cn,
$indicator_type=Intel::DOMAIN,
$conn=c,
$where=X509::IN_CERT]);
}

View file

@ -21,6 +21,7 @@ export {
SMTP::IN_REPLY_TO,
SMTP::IN_X_ORIGINATING_IP_HEADER,
SMTP::IN_MESSAGE,
SSH::IN_SERVER_HOST_KEY,
SSL::IN_SERVER_NAME,
SMTP::IN_HEADER,
X509::IN_CERT,

View file

@ -2,6 +2,18 @@
@load base/files/x509
@load ./where-locations
event x509_ext_subject_alternative_name(f: fa_file, ext: X509::SubjectAlternativeName)
{
if ( ext?$dns )
{
for ( i in ext$dns )
Intel::seen([$indicator=ext$dns[i],
$indicator_type=Intel::DOMAIN,
$f=f,
$where=X509::IN_CERT]);
}
}
event x509_certificate(f: fa_file, cert_ref: opaque of x509, cert: X509::Certificate)
{
if ( /emailAddress=/ in cert$subject )

View file

@ -53,7 +53,7 @@ export {
event HTTP::log_http(rec: HTTP::Info) &priority=5
{
if ( rec?$host && rec?$user_agent && rec$host == "crl.microsoft.com" &&
if ( rec?$host && rec?$user_agent && /crl.microsoft.com/ in rec$host &&
/Microsoft-CryptoAPI\// in rec$user_agent )
{
if ( rec$user_agent !in crypto_api_mapping )

View file

@ -23,7 +23,7 @@ export {
event bro_init() &priority=5
{
Log::create_stream(Barnyard2::LOG, [$columns=Info]);
Log::create_stream(Barnyard2::LOG, [$columns=Info, $path="barnyard2"]);
}

View file

@ -38,7 +38,7 @@ global add_sumstats: hook(id: conn_id, hostname: string, size: count);
event bro_init() &priority=3
{
Log::create_stream(AppStats::LOG, [$columns=Info]);
Log::create_stream(AppStats::LOG, [$columns=Info, $path="app_stats"]);
local r1: SumStats::Reducer = [$stream="apps.bytes", $apply=set(SumStats::SUM)];
local r2: SumStats::Reducer = [$stream="apps.hits", $apply=set(SumStats::UNIQUE)];

View file

@ -76,7 +76,7 @@ event CaptureLoss::take_measurement(last_ts: time, last_acks: count, last_gaps:
event bro_init() &priority=5
{
Log::create_stream(LOG, [$columns=Info]);
Log::create_stream(LOG, [$columns=Info, $path="capture_loss"]);
# We only schedule the event if we are capturing packets.
if ( reading_live_traffic() || reading_traces() )

View file

@ -55,7 +55,7 @@ export {
event bro_init() &priority=5
{
Log::create_stream(Traceroute::LOG, [$columns=Info, $ev=log_traceroute]);
Log::create_stream(Traceroute::LOG, [$columns=Info, $ev=log_traceroute, $path="traceroute"]);
local r1: SumStats::Reducer = [$stream="traceroute.time_exceeded", $apply=set(SumStats::UNIQUE)];
local r2: SumStats::Reducer = [$stream="traceroute.low_ttl_packet", $apply=set(SumStats::SUM)];

View file

@ -38,5 +38,5 @@ export {
event bro_init()
{
Log::create_stream(Known::DEVICES_LOG, [$columns=DevicesInfo, $ev=log_known_devices]);
Log::create_stream(Known::DEVICES_LOG, [$columns=DevicesInfo, $ev=log_known_devices, $path="known_devices"]);
}

View file

@ -30,7 +30,7 @@ const depth: table[count] of string = {
event bro_init() &priority=5
{
Log::create_stream(LoadedScripts::LOG, [$columns=Info]);
Log::create_stream(LoadedScripts::LOG, [$columns=Info, $path="loaded_scripts"]);
}
event bro_script_loaded(path: string, level: count)

View file

@ -39,6 +39,9 @@ export {
## Number of packets seen on the link since the last stats
## interval if reading live traffic.
pkts_link: count &log &optional;
## Number of bytes received since the last stats interval if
## reading live traffic.
bytes_recv: count &log &optional;
};
## Event to catch stats as they are written to the logging stream.
@ -47,7 +50,7 @@ export {
event bro_init() &priority=5
{
Log::create_stream(Stats::LOG, [$columns=Info, $ev=log_stats]);
Log::create_stream(Stats::LOG, [$columns=Info, $ev=log_stats, $path="stats"]);
}
event check_stats(last_ts: time, last_ns: NetStats, last_res: bro_resources)
@ -74,6 +77,7 @@ event check_stats(last_ts: time, last_ns: NetStats, last_res: bro_resources)
info$pkts_recv = ns$pkts_recvd - last_ns$pkts_recvd;
info$pkts_dropped = ns$pkts_dropped - last_ns$pkts_dropped;
info$pkts_link = ns$pkts_link - last_ns$pkts_link;
info$bytes_recv = ns$bytes_recvd - last_ns$bytes_recvd;
}
Log::write(Stats::LOG, info);

View file

@ -38,7 +38,7 @@ export {
event bro_init()
{
Log::create_stream(Known::HOSTS_LOG, [$columns=HostsInfo, $ev=log_known_hosts]);
Log::create_stream(Known::HOSTS_LOG, [$columns=HostsInfo, $ev=log_known_hosts, $path="known_hosts"]);
}
event connection_established(c: connection) &priority=5

View file

@ -49,7 +49,8 @@ redef record connection += {
event bro_init() &priority=5
{
Log::create_stream(Known::SERVICES_LOG, [$columns=ServicesInfo,
$ev=log_known_services]);
$ev=log_known_services,
$path="known_services"]);
}
event log_it(ts: time, a: addr, p: port, services: set[string])

View file

@ -0,0 +1,26 @@
##! This script add VLAN information to the connection logs
@load base/protocols/conn
module Conn;
redef record Info += {
## The outer VLAN for this connection, if applicable.
vlan: int &log &optional;
## The inner VLAN for this connection, if applicable.
inner_vlan: int &log &optional;
};
# Add the VLAN information to the Conn::Info structure after the connection
# has been removed. This ensures it's only done once, and is done before the
# connection information is written to the log.
event connection_state_remove(c: connection)
{
if ( c?$vlan )
c$conn$vlan = c$vlan;
if ( c?$inner_vlan )
c$conn$inner_vlan = c$inner_vlan;
}

View file

@ -19,12 +19,12 @@ export {
};
}
event rexmit_inconsistency(c: connection, t1: string, t2: string)
event rexmit_inconsistency(c: connection, t1: string, t2: string, tcp_flags: string)
{
NOTICE([$note=Retransmission_Inconsistency,
$conn=c,
$msg=fmt("%s rexmit inconsistency (%s) (%s)",
id_string(c$id), t1, t2),
$msg=fmt("%s rexmit inconsistency (%s) (%s) [%s]",
id_string(c$id), t1, t2, tcp_flags),
$identifier=fmt("%s", c$id)]);
}

View file

@ -26,20 +26,25 @@ export {
event http_header(c: connection, is_orig: bool, name: string, value: string) &priority=3
{
if ( ! is_orig || ! c?$http )
if ( ! c?$http )
return;
if ( log_client_header_names )
if ( is_orig )
{
if ( ! c$http?$client_header_names )
c$http$client_header_names = vector();
c$http$client_header_names[|c$http$client_header_names|] = name;
if ( log_client_header_names )
{
if ( ! c$http?$client_header_names )
c$http$client_header_names = vector();
c$http$client_header_names[|c$http$client_header_names|] = name;
}
}
if ( log_server_header_names )
else
{
if ( ! c$http?$server_header_names )
c$http$server_header_names = vector();
c$http$server_header_names[|c$http$server_header_names|] = name;
if ( log_server_header_names )
{
if ( ! c$http?$server_header_names )
c$http$server_header_names = vector();
c$http$server_header_names[|c$http$server_header_names|] = name;
}
}
}

View file

@ -1,4 +1,4 @@
##! Detect browser plugins as they leak through requests to Omniture
##! Detect browser plugins as they leak through requests to Omniture
##! advertising servers.
@load base/protocols/http
@ -10,8 +10,10 @@ export {
redef record Info += {
## Indicates if the server is an omniture advertising server.
omniture: bool &default=F;
## The unparsed Flash version, if detected.
flash_version: string &optional;
};
redef enum Software::Type += {
## Identifier for browser plugins in the software framework.
BROWSER_PLUGIN
@ -22,12 +24,20 @@ event http_header(c: connection, is_orig: bool, name: string, value: string) &pr
{
if ( is_orig )
{
if ( name == "X-FLASH-VERSION" )
switch ( name )
{
# Flash doesn't include it's name so we'll add it here since it
# simplifies the version parsing.
value = cat("Flash/", value);
Software::found(c$id, [$unparsed_version=value, $host=c$id$orig_h, $software_type=BROWSER_PLUGIN]);
case "X-FLASH-VERSION":
# Flash doesn't include it's name so we'll add it here since it
# simplifies the version parsing.
c$http$flash_version = cat("Flash/", value);
break;
case "X-REQUESTED-WITH":
# This header is usually used to indicate AJAX requests (XMLHttpRequest),
# but Chrome uses this header also to indicate the use of Flash.
if ( /Flash/ in value )
c$http$flash_version = value;
break;
}
}
else
@ -38,9 +48,26 @@ event http_header(c: connection, is_orig: bool, name: string, value: string) &pr
}
}
event http_message_done(c: connection, is_orig: bool, stat: http_message_stat)
{
# If a Flash was detected, it has to be logged considering the user agent.
if ( is_orig && c$http?$flash_version )
{
# AdobeAIR contains a seperate Flash, which should be emphasized.
# Note: We assume that the user agent header was not reset by the app.
if( c$http?$user_agent )
{
if ( /AdobeAIR/ in c$http$user_agent )
c$http$flash_version = cat("AdobeAIR-", c$http$flash_version);
}
Software::found(c$id, [$unparsed_version=c$http$flash_version, $host=c$id$orig_h, $software_type=BROWSER_PLUGIN]);
}
}
event log_http(rec: Info)
{
# We only want to inspect requests that were sent to omniture advertising
# We only want to inspect requests that were sent to omniture advertising
# servers.
if ( rec$omniture && rec?$uri )
{
@ -48,11 +75,11 @@ event log_http(rec: Info)
local parts = split_string_n(rec$uri, /&p=([^&]{5,});&/, T, 1);
if ( 1 in parts )
{
# We do sub_bytes here just to remove the extra extracted
# We do sub_bytes here just to remove the extra extracted
# characters from the regex split above.
local sw = sub_bytes(parts[1], 4, |parts[1]|-5);
local plugins = split_string(sw, /[[:blank:]]*;[[:blank:]]*/);
for ( i in plugins )
Software::found(rec$id, [$unparsed_version=plugins[i], $host=rec$id$orig_h, $software_type=BROWSER_PLUGIN]);
}

View file

@ -35,7 +35,7 @@ export {
event bro_init() &priority=5
{
Log::create_stream(Known::MODBUS_LOG, [$columns=ModbusInfo, $ev=log_known_modbus]);
Log::create_stream(Known::MODBUS_LOG, [$columns=ModbusInfo, $ev=log_known_modbus, $path="known_modbus"]);
}
event modbus_message(c: connection, headers: ModbusHeaders, is_orig: bool)

View file

@ -54,7 +54,7 @@ redef record Modbus::Info += {
event bro_init() &priority=5
{
Log::create_stream(Modbus::REGISTER_CHANGE_LOG, [$columns=MemmapInfo]);
Log::create_stream(Modbus::REGISTER_CHANGE_LOG, [$columns=MemmapInfo, $path="modbus_register_change"]);
}
event modbus_read_holding_registers_request(c: connection, headers: ModbusHeaders, start_address: count, quantity: count)

View file

@ -0,0 +1,22 @@
##! If an RDP session is "upgraded" to SSL, this will be indicated
##! with this script in a new field added to the RDP log.
@load base/protocols/rdp
@load base/protocols/ssl
module RDP;
export {
redef record RDP::Info += {
## Flag the connection if it was seen over SSL.
ssl: bool &log &default=F;
};
}
event ssl_established(c: connection)
{
if ( c?$rdp )
{
c$rdp$ssl = T;
}
}

View file

@ -12,11 +12,11 @@ export {
redef enum Notice::Type += {
## Indicates that a host has been identified as crossing the
## :bro:id:`SSH::password_guesses_limit` threshold with
## heuristically determined failed logins.
## failed logins.
Password_Guessing,
## Indicates that a host previously identified as a "password
## guesser" has now had a heuristically successful login
## attempt. This is not currently implemented.
## guesser" has now had a successful login
## attempt. This is not currently implemented.
Login_By_Password_Guesser,
};
@ -34,8 +34,7 @@ export {
const guessing_timeout = 30 mins &redef;
## This value can be used to exclude hosts or entire networks from being
## tracked as potential "guessers". There are cases where the success
## heuristic fails and this acts as the whitelist. The index represents
## tracked as potential "guessers". The index represents
## client subnets and the yield value represents server subnets.
const ignore_guessers: table[subnet] of subnet &redef;
}
@ -70,7 +69,7 @@ event bro_init()
}]);
}
event SSH::heuristic_successful_login(c: connection)
event ssh_auth_successful(c: connection, auth_method_none: bool)
{
local id = c$id;
@ -79,7 +78,7 @@ event SSH::heuristic_successful_login(c: connection)
$where=SSH::SUCCESSFUL_LOGIN]);
}
event SSH::heuristic_failed_login(c: connection)
event ssh_auth_failed(c: connection)
{
local id = c$id;

View file

@ -12,14 +12,14 @@ export {
## notice will be generated.
Watched_Country_Login,
};
redef record Info += {
## Add geographic data related to the "remote" host of the
## connection.
remote_location: geo_location &log &optional;
};
## The set of countries for which you'd like to generate notices upon
## The set of countries for which you'd like to generate notices upon
## successful login.
const watched_countries: set[string] = {"RO"} &redef;
}
@ -30,23 +30,29 @@ function get_location(c: connection): geo_location
return lookup_location(lookup_ip);
}
event SSH::heuristic_successful_login(c: connection) &priority=5
event ssh_auth_successful(c: connection, auth_method_none: bool) &priority=3
{
if ( ! c$ssh?$direction )
return;
# Add the location data to the SSH record.
c$ssh$remote_location = get_location(c);
if ( c$ssh$remote_location?$country_code && c$ssh$remote_location$country_code in watched_countries )
{
NOTICE([$note=Watched_Country_Login,
$conn=c,
$msg=fmt("SSH login %s watched country: %s",
(c$ssh$direction == OUTBOUND) ? "to" : "from",
$msg=fmt("SSH login %s watched country: %s",
(c$ssh$direction == OUTBOUND) ? "to" : "from",
c$ssh$remote_location$country_code)]);
}
}
event SSH::heuristic_failed_login(c: connection) &priority=5
event ssh_auth_failed(c: connection) &priority=3
{
if ( ! c$ssh?$direction )
return;
# Add the location data to the SSH record.
c$ssh$remote_location = get_location(c);
}

View file

@ -27,7 +27,7 @@ export {
/^ftp[0-9]*\./ &redef;
}
event SSH::heuristic_successful_login(c: connection)
event ssh_auth_successful(c: connection, auth_method_none: bool)
{
for ( host in set(c$id$orig_h, c$id$resp_h) )
{

View file

@ -43,7 +43,7 @@ export {
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, $path="known_certs"]);
}
event ssl_established(c: connection) &priority=3

View file

@ -1,4 +1,7 @@
##! Perform full certificate chain validation for SSL certificates.
#
# Also caches all intermediate certificates encountered so far and use them
# for future validations.
@load base/frameworks/notice
@load base/protocols/ssl
@ -12,19 +15,114 @@ export {
## invalid.
Invalid_Server_Cert
};
redef record Info += {
## Result of certificate validation for this connection.
validation_status: string &log &optional;
};
## MD5 hash values for recently validated chains along with the
## validation status message are kept in this table to avoid constant
## validation status are kept in this table to avoid constant
## validation every time the same certificate chain is seen.
global recently_validated_certs: table[string] of string = table()
&read_expire=5mins &synchronized &redef;
global recently_validated_certs: table[string] of string = table()
&read_expire=5mins &redef;
## Use intermediate CA certificate caching when trying to validate
## certificates. When this is enabled, Bro keeps track of all valid
## intermediate CA certificates that it has seen in the past. When
## encountering a host certificate that cannot be validated because
## of missing intermediate CA certificate, the cached list is used
## to try to validate the cert. This is similar to how Firefox is
## doing certificate validation.
##
## Disabling this will usually greatly increase the number of validation warnings
## that you encounter. Only disable if you want to find misconfigured servers.
global ssl_cache_intermediate_ca: bool = T &redef;
## Event from a worker to the manager that it has encountered a new
## valid intermediate.
global intermediate_add: event(key: string, value: vector of opaque of x509);
## Event from the manager to the workers that a new intermediate chain
## is to be added.
global new_intermediate: event(key: string, value: vector of opaque of x509);
}
global intermediate_cache: table[string] of vector of opaque of x509;
@if ( Cluster::is_enabled() )
@load base/frameworks/cluster
redef Cluster::manager2worker_events += /SSL::intermediate_add/;
redef Cluster::worker2manager_events += /SSL::new_intermediate/;
@endif
function add_to_cache(key: string, value: vector of opaque of x509)
{
intermediate_cache[key] = value;
@if ( Cluster::is_enabled() )
event SSL::new_intermediate(key, value);
@endif
}
@if ( Cluster::is_enabled() && Cluster::local_node_type() != Cluster::MANAGER )
event SSL::intermediate_add(key: string, value: vector of opaque of x509)
{
intermediate_cache[key] = value;
}
@endif
@if ( Cluster::is_enabled() && Cluster::local_node_type() == Cluster::MANAGER )
event SSL::new_intermediate(key: string, value: vector of opaque of x509)
{
if ( key in intermediate_cache )
return;
intermediate_cache[key] = value;
event SSL::intermediate_add(key, value);
}
@endif
function cache_validate(chain: vector of opaque of x509): string
{
local chain_hash: vector of string = vector();
for ( i in chain )
chain_hash[i] = sha1_hash(x509_get_certificate_string(chain[i]));
local chain_id = join_string_vec(chain_hash, ".");
# If we tried this certificate recently, just return the cached result.
if ( chain_id in recently_validated_certs )
return recently_validated_certs[chain_id];
local result = x509_verify(chain, root_certs);
recently_validated_certs[chain_id] = result$result_string;
# if we have a working chain where we did not store the intermediate certs
# in our cache yet - do so
if ( ssl_cache_intermediate_ca &&
result$result_string == "ok" &&
result?$chain_certs &&
|result$chain_certs| > 2 )
{
local result_chain = result$chain_certs;
local icert = x509_parse(result_chain[1]);
if ( icert$subject !in intermediate_cache )
{
local cachechain: vector of opaque of x509;
for ( i in result_chain )
{
if ( i >=1 && i<=|result_chain|-2 )
cachechain[i-1] = result_chain[i];
}
add_to_cache(icert$subject, cachechain);
}
}
return result$result_string;
}
event ssl_established(c: connection) &priority=3
{
# If there aren't any certs we can't very well do certificate validation.
@ -32,8 +130,31 @@ event ssl_established(c: connection) &priority=3
! c$ssl$cert_chain[0]?$x509 )
return;
local chain_id = join_string_vec(c$ssl$cert_chain_fuids, ".");
local intermediate_chain: vector of opaque of x509 = vector();
local issuer = c$ssl$cert_chain[0]$x509$certificate$issuer;
local hash = c$ssl$cert_chain[0]$sha1;
local result: string;
# Look if we already have a working chain for the issuer of this cert.
# If yes, try this chain first instead of using the chain supplied from
# the server.
if ( ssl_cache_intermediate_ca && issuer in intermediate_cache )
{
intermediate_chain[0] = c$ssl$cert_chain[0]$x509$handle;
for ( i in intermediate_cache[issuer] )
intermediate_chain[i+1] = intermediate_cache[issuer][i];
result = cache_validate(intermediate_chain);
if ( result == "ok" )
{
c$ssl$validation_status = result;
return;
}
}
# Validation with known chains failed or there was no fitting intermediate
# in our store.
# Fall back to validating the certificate with the server-supplied chain.
local chain: vector of opaque of x509 = vector();
for ( i in c$ssl$cert_chain )
{
@ -41,24 +162,14 @@ event ssl_established(c: connection) &priority=3
chain[i] = c$ssl$cert_chain[i]$x509$handle;
}
if ( chain_id in recently_validated_certs )
{
c$ssl$validation_status = recently_validated_certs[chain_id];
}
else
{
local result = x509_verify(chain, root_certs);
c$ssl$validation_status = result$result_string;
recently_validated_certs[chain_id] = result$result_string;
}
result = cache_validate(chain);
c$ssl$validation_status = result;
if ( c$ssl$validation_status != "ok" )
if ( result != "ok" )
{
local message = fmt("SSL certificate validation failed with (%s)", c$ssl$validation_status);
NOTICE([$note=Invalid_Server_Cert, $msg=message,
$sub=c$ssl$subject, $conn=c,
$identifier=cat(c$id$resp_h,c$id$resp_p,c$ssl$validation_status)]);
$identifier=cat(c$id$resp_h,c$id$resp_p,hash,c$ssl$validation_status)]);
}
}

View file

@ -34,9 +34,10 @@ event ssl_stapled_ocsp(c: connection, is_orig: bool, response: string) &priority
event ssl_established(c: connection) &priority=3
{
if ( ! c$ssl?$cert_chain || |c$ssl$cert_chain| == 0 || !c$ssl?$ocsp_response )
if ( ! c$ssl?$cert_chain || |c$ssl$cert_chain| == 0 || ! c$ssl$cert_chain[0]?$x509 || !c$ssl?$ocsp_response )
return;
local hash = c$ssl$cert_chain[0]$sha1;
local chain: vector of opaque of x509 = vector();
for ( i in c$ssl$cert_chain )
{

View file

@ -1,5 +1,5 @@
##! Generate notices when SSL/TLS connections use certificates or DH parameters
##! that have potentially unsafe key lengths.
##! Generate notices when SSL/TLS connections use certificates, DH parameters,
##! or cipher suites that are deemed to be insecure.
@load base/protocols/ssl
@load base/frameworks/notice
@ -11,17 +11,20 @@ export {
redef enum Notice::Type += {
## Indicates that a server is using a potentially unsafe key.
Weak_Key,
## Indicates that a server is using a potentially unsafe version
Old_Version,
## Indicates that a server is using a potentially unsafe cipher
Weak_Cipher
};
## The category of hosts you would like to be notified about which have
## certificates that are going to be expiring soon. By default, these
## notices will be suppressed by the notice framework for 1 day after a particular
## certificate has had a notice generated. Choices are: LOCAL_HOSTS, REMOTE_HOSTS,
## ALL_HOSTS, NO_HOSTS
## The category of hosts you would like to be notified about which are using weak
## keys/ciphers/protocol_versions. By default, these notices will be suppressed
## by the notice framework for 1 day after a particular host has had a notice
## generated. Choices are: LOCAL_HOSTS, REMOTE_HOSTS, ALL_HOSTS, NO_HOSTS
const notify_weak_keys = LOCAL_HOSTS &redef;
## The minimal key length in bits that is considered to be safe. Any shorter
## (non-EC) key lengths will trigger the notice.
## (non-EC) key lengths will trigger a notice.
const notify_minimal_key_length = 2048 &redef;
## Warn if the DH key length is smaller than the certificate key length. This is
@ -29,6 +32,17 @@ export {
## certificate key length. However, it is very common and cannot be avoided in some
## settings (e.g. with old jave clients).
const notify_dh_length_shorter_cert_length = T &redef;
## Warn if a server negotiates a SSL session with a protocol version smaller than
## the specified version. By default, the minimal version is TLSv10 because SSLv2
## and v3 have serious security issued.
## See https://tools.ietf.org/html/draft-thomson-sslv3-diediedie-00
## To disable, set to SSLv20
const tls_minimum_version = TLSv10 &redef;
## Warn if a server negotiates an unsafe cipher suite. By default, we only warn when
## encountering old export cipher suites, or RC4 (see RFC7465).
const unsafe_ciphers_regex = /(_EXPORT_)|(_RC4_)/ &redef;
}
# We check key lengths only for DSA or RSA certificates. For others, we do
@ -43,6 +57,7 @@ event ssl_established(c: connection) &priority=3
local fuid = c$ssl$cert_chain_fuids[0];
local cert = c$ssl$cert_chain[0]$x509$certificate;
local hash = c$ssl$cert_chain[0]$sha1;
if ( !cert?$key_type || !cert?$key_length )
return;
@ -56,7 +71,32 @@ event ssl_established(c: connection) &priority=3
NOTICE([$note=Weak_Key,
$msg=fmt("Host uses weak certificate with %d bit key", key_length),
$conn=c, $suppress_for=1day,
$identifier=cat(c$id$resp_h, c$id$resp_h, key_length)
$identifier=cat(c$id$resp_h, c$id$resp_h, hash, key_length)
]);
}
# Check for old SSL versions and weak connection keys
event ssl_server_hello(c: connection, version: count, possible_ts: time, server_random: string, session_id: string, cipher: count, comp_method: count) &priority=3
{
if ( ! addr_matches_host(c$id$resp_h, notify_weak_keys) )
return;
if ( version < tls_minimum_version )
{
local minimum_string = version_strings[tls_minimum_version];
local host_string = version_strings[version];
NOTICE([$note=Old_Version,
$msg=fmt("Host uses protocol version %s which is lower than the safe minimum %s", host_string, minimum_string),
$conn=c, $suppress_for=1day,
$identifier=cat(c$id$resp_h, c$id$resp_h)
]);
}
if ( unsafe_ciphers_regex in c$ssl$cipher )
NOTICE([$note=Weak_Cipher,
$msg=fmt("Host established connection using unsafe ciper suite %s", c$ssl$cipher),
$conn=c, $suppress_for=1day,
$identifier=cat(c$id$resp_h, c$id$resp_h, c$ssl$cipher)
]);
}