mirror of
https://github.com/zeek/zeek.git
synced 2025-10-08 09:38:19 +00:00
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:
commit
89b4d79f93
1081 changed files with 38403 additions and 11012 deletions
|
@ -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
|
||||
|
|
8
scripts/policy/frameworks/files/extract-all-files.bro
Normal file
8
scripts/policy/frameworks/files/extract-all-files.bro
Normal 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);
|
||||
}
|
|
@ -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);
|
||||
|
|
11
scripts/policy/frameworks/intel/seen/pubkey-hashes.bro
Normal file
11
scripts/policy/frameworks/intel/seen/pubkey-hashes.bro
Normal 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);
|
||||
}
|
|
@ -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]);
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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 )
|
||||
|
|
|
@ -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 )
|
||||
|
|
|
@ -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"]);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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)];
|
||||
|
|
|
@ -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() )
|
||||
|
|
|
@ -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)];
|
||||
|
|
|
@ -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"]);
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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])
|
||||
|
|
26
scripts/policy/protocols/conn/vlan-logging.bro
Normal file
26
scripts/policy/protocols/conn/vlan-logging.bro
Normal 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;
|
||||
}
|
||||
|
|
@ -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)]);
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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]);
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
22
scripts/policy/protocols/rdp/indicate_ssl.bro
Normal file
22
scripts/policy/protocols/rdp/indicate_ssl.bro
Normal 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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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) )
|
||||
{
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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 )
|
||||
{
|
||||
|
|
|
@ -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)
|
||||
]);
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue