mirror of
https://github.com/zeek/zeek.git
synced 2025-10-17 14:08:20 +00:00
Merge remote-tracking branch 'origin/master' into topic/seth/file-entropy
This commit is contained in:
commit
8e53e719f3
1894 changed files with 189157 additions and 279280 deletions
|
@ -1,8 +1,9 @@
|
|||
##! This is a utility script that implements the controller interface for the
|
||||
##! control framework. It's intended to be run to control a remote Bro
|
||||
##! control framework. It's intended to be run to control a remote Bro
|
||||
##! and then shutdown.
|
||||
##!
|
||||
##! 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>]
|
||||
|
||||
@load base/frameworks/control
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
##! This script enables logging of packet segment data when a protocol
|
||||
##! parsing violation is encountered. The amount of
|
||||
##! data from the packet logged is set by the packet_segment_size variable.
|
||||
##! This script enables logging of packet segment data when a protocol
|
||||
##! parsing violation is encountered. The amount of data from the
|
||||
##! packet logged is set by the :bro:see:`DPD::packet_segment_size` variable.
|
||||
##! A caveat to logging packet data is that in some cases, the packet may
|
||||
##! not be the packet that actually caused the protocol violation.
|
||||
|
||||
|
@ -10,8 +10,8 @@ module DPD;
|
|||
|
||||
export {
|
||||
redef record Info += {
|
||||
## A chunk of the payload the most likely resulted in the protocol
|
||||
## violation.
|
||||
## A chunk of the payload that most likely resulted in the
|
||||
## protocol violation.
|
||||
packet_segment: string &optional &log;
|
||||
};
|
||||
|
||||
|
|
|
@ -23,35 +23,50 @@ export {
|
|||
/application\/jar/ |
|
||||
/video\/mp4/ &redef;
|
||||
|
||||
## The malware hash registry runs each malware sample through several A/V engines.
|
||||
## Team Cymru returns a percentage to indicate how many A/V engines flagged the
|
||||
## sample as malicious. This threshold allows you to require a minimum detection
|
||||
## rate.
|
||||
## The Match notice has a sub message with a URL where you can get more
|
||||
## information about the file. The %s will be replaced with the SHA-1
|
||||
## hash of the file.
|
||||
const match_sub_url = "https://www.virustotal.com/en/search/?query=%s" &redef;
|
||||
|
||||
## The malware hash registry runs each malware sample through several
|
||||
## A/V engines. Team Cymru returns a percentage to indicate how
|
||||
## many A/V engines flagged the sample as malicious. This threshold
|
||||
## allows you to require a minimum detection rate.
|
||||
const notice_threshold = 10 &redef;
|
||||
}
|
||||
|
||||
event file_hash(f: fa_file, kind: string, hash: string)
|
||||
function do_mhr_lookup(hash: string, fi: Notice::FileInfo)
|
||||
{
|
||||
if ( kind=="sha1" && match_file_types in f$mime_type )
|
||||
local hash_domain = fmt("%s.malware.hash.cymru.com", hash);
|
||||
|
||||
when ( local MHR_result = lookup_hostname_txt(hash_domain) )
|
||||
{
|
||||
local hash_domain = fmt("%s.malware.hash.cymru.com", hash);
|
||||
when ( local MHR_result = lookup_hostname_txt(hash_domain) )
|
||||
# Data is returned as "<dateFirstDetected> <detectionRate>"
|
||||
local MHR_answer = split1(MHR_result, / /);
|
||||
|
||||
if ( |MHR_answer| == 2 )
|
||||
{
|
||||
# Data is returned as "<dateFirstDetected> <detectionRate>"
|
||||
local MHR_answer = split1(MHR_result, / /);
|
||||
if ( |MHR_answer| == 2 )
|
||||
local mhr_detect_rate = to_count(MHR_answer[2]);
|
||||
|
||||
if ( mhr_detect_rate >= notice_threshold )
|
||||
{
|
||||
local mhr_first_detected = double_to_time(to_double(MHR_answer[1]));
|
||||
local mhr_detect_rate = to_count(MHR_answer[2]);
|
||||
|
||||
local readable_first_detected = strftime("%Y-%m-%d %H:%M:%S", mhr_first_detected);
|
||||
if ( mhr_detect_rate >= notice_threshold )
|
||||
{
|
||||
local message = fmt("Malware Hash Registry Detection rate: %d%% Last seen: %s", mhr_detect_rate, readable_first_detected);
|
||||
local virustotal_url = fmt("https://www.virustotal.com/en/file/%s/analysis/", hash);
|
||||
NOTICE([$note=Match, $msg=message, $sub=virustotal_url, $f=f]);
|
||||
}
|
||||
local message = fmt("Malware Hash Registry Detection rate: %d%% Last seen: %s", mhr_detect_rate, readable_first_detected);
|
||||
local virustotal_url = fmt(match_sub_url, hash);
|
||||
# We don't have the full fa_file record here in order to
|
||||
# avoid the "when" statement cloning it (expensive!).
|
||||
local n: Notice::Info = Notice::Info($note=Match, $msg=message, $sub=virustotal_url);
|
||||
Notice::populate_file_info2(fi, n);
|
||||
NOTICE(n);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
event file_hash(f: fa_file, kind: string, hash: string)
|
||||
{
|
||||
if ( kind == "sha1" && f?$info && f$info?$mime_type &&
|
||||
match_file_types in f$info$mime_type )
|
||||
do_mhr_lookup(hash, Notice::create_file_info(f));
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
# Perform MD5 and SHA1 hashing on all files.
|
||||
##! Perform MD5 and SHA1 hashing on all files.
|
||||
|
||||
event file_new(f: fa_file)
|
||||
{
|
||||
|
|
|
@ -18,7 +18,7 @@ export {
|
|||
do_notice: bool &default=F;
|
||||
|
||||
## Restrictions on when notices are created to only create
|
||||
## them if the do_notice field is T and the notice was
|
||||
## them if the *do_notice* field is T and the notice was
|
||||
## seen in the indicated location.
|
||||
if_in: Intel::Where &optional;
|
||||
};
|
||||
|
|
1
scripts/policy/frameworks/intel/seen/README
Normal file
1
scripts/policy/frameworks/intel/seen/README
Normal file
|
@ -0,0 +1 @@
|
|||
Scripts that send data to the intelligence framework.
|
|
@ -1,8 +1,10 @@
|
|||
@load ./conn-established
|
||||
@load ./dns
|
||||
@load ./http-host-header
|
||||
@load ./file-hashes
|
||||
@load ./file-names
|
||||
@load ./http-headers
|
||||
@load ./http-url
|
||||
@load ./http-user-agents
|
||||
@load ./ssl
|
||||
@load ./smtp
|
||||
@load ./smtp-url-extraction
|
||||
@load ./smtp-url-extraction
|
||||
@load ./x509
|
||||
|
|
12
scripts/policy/frameworks/intel/seen/file-hashes.bro
Normal file
12
scripts/policy/frameworks/intel/seen/file-hashes.bro
Normal file
|
@ -0,0 +1,12 @@
|
|||
@load base/frameworks/intel
|
||||
@load ./where-locations
|
||||
|
||||
event file_hash(f: fa_file, kind: string, hash: string)
|
||||
{
|
||||
local seen = Intel::Seen($indicator=hash,
|
||||
$indicator_type=Intel::FILE_HASH,
|
||||
$f=f,
|
||||
$where=Files::IN_HASH);
|
||||
|
||||
Intel::seen(seen);
|
||||
}
|
11
scripts/policy/frameworks/intel/seen/file-names.bro
Normal file
11
scripts/policy/frameworks/intel/seen/file-names.bro
Normal file
|
@ -0,0 +1,11 @@
|
|||
@load base/frameworks/intel
|
||||
@load ./where-locations
|
||||
|
||||
event file_new(f: fa_file)
|
||||
{
|
||||
if ( f?$info && f$info?$filename )
|
||||
Intel::seen([$indicator=f$info$filename,
|
||||
$indicator_type=Intel::FILE_NAME,
|
||||
$f=f,
|
||||
$where=Files::IN_NAME]);
|
||||
}
|
53
scripts/policy/frameworks/intel/seen/http-headers.bro
Normal file
53
scripts/policy/frameworks/intel/seen/http-headers.bro
Normal file
|
@ -0,0 +1,53 @@
|
|||
@load base/frameworks/intel
|
||||
@load ./where-locations
|
||||
@load base/utils/addrs
|
||||
|
||||
event http_header(c: connection, is_orig: bool, name: string, value: string)
|
||||
{
|
||||
if ( is_orig )
|
||||
{
|
||||
switch ( name )
|
||||
{
|
||||
case "HOST":
|
||||
if ( is_valid_ip(value) )
|
||||
Intel::seen([$host=to_addr(value),
|
||||
$indicator_type=Intel::ADDR,
|
||||
$conn=c,
|
||||
$where=HTTP::IN_HOST_HEADER]);
|
||||
else
|
||||
Intel::seen([$indicator=value,
|
||||
$indicator_type=Intel::DOMAIN,
|
||||
$conn=c,
|
||||
$where=HTTP::IN_HOST_HEADER]);
|
||||
break;
|
||||
|
||||
case "REFERER":
|
||||
Intel::seen([$indicator=sub(value, /^.*:\/\//, ""),
|
||||
$indicator_type=Intel::URL,
|
||||
$conn=c,
|
||||
$where=HTTP::IN_REFERRER_HEADER]);
|
||||
break;
|
||||
|
||||
case "X-FORWARDED-FOR":
|
||||
if ( is_valid_ip(value) )
|
||||
{
|
||||
local addrs = find_ip_addresses(value);
|
||||
for ( i in addrs )
|
||||
{
|
||||
Intel::seen([$host=to_addr(addrs[i]),
|
||||
$indicator_type=Intel::ADDR,
|
||||
$conn=c,
|
||||
$where=HTTP::IN_X_FORWARDED_FOR_HEADER]);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case "USER-AGENT":
|
||||
Intel::seen([$indicator=value,
|
||||
$indicator_type=Intel::SOFTWARE,
|
||||
$conn=c,
|
||||
$where=HTTP::IN_USER_AGENT_HEADER]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
@load base/frameworks/intel
|
||||
@load ./where-locations
|
||||
|
||||
event http_header(c: connection, is_orig: bool, name: string, value: string)
|
||||
{
|
||||
if ( is_orig && name == "HOST" )
|
||||
Intel::seen([$indicator=value,
|
||||
$indicator_type=Intel::DOMAIN,
|
||||
$conn=c,
|
||||
$where=HTTP::IN_HOST_HEADER]);
|
||||
}
|
|
@ -1,12 +0,0 @@
|
|||
@load base/frameworks/intel
|
||||
@load ./where-locations
|
||||
|
||||
event http_header(c: connection, is_orig: bool, name: string, value: string)
|
||||
{
|
||||
if ( is_orig && name == "USER-AGENT" )
|
||||
Intel::seen([$indicator=value,
|
||||
$indicator_type=Intel::SOFTWARE,
|
||||
$conn=c,
|
||||
$where=HTTP::IN_USER_AGENT_HEADER]);
|
||||
}
|
||||
|
|
@ -2,31 +2,9 @@
|
|||
@load base/protocols/ssl
|
||||
@load ./where-locations
|
||||
|
||||
event x509_certificate(c: connection, is_orig: bool, cert: X509, chain_idx: count, chain_len: count, der_cert: string)
|
||||
event ssl_extension_server_name(c: connection, is_orig: bool, names: string_vec)
|
||||
{
|
||||
if ( chain_idx == 0 )
|
||||
{
|
||||
if ( /emailAddress=/ in cert$subject )
|
||||
{
|
||||
local email = sub(cert$subject, /^.*emailAddress=/, "");
|
||||
email = sub(email, /,.*$/, "");
|
||||
Intel::seen([$indicator=email,
|
||||
$indicator_type=Intel::EMAIL,
|
||||
$conn=c,
|
||||
$where=(is_orig ? SSL::IN_CLIENT_CERT : SSL::IN_SERVER_CERT)]);
|
||||
}
|
||||
|
||||
Intel::seen([$indicator=sha1_hash(der_cert),
|
||||
$indicator_type=Intel::CERT_HASH,
|
||||
$conn=c,
|
||||
$where=(is_orig ? SSL::IN_CLIENT_CERT : SSL::IN_SERVER_CERT)]);
|
||||
}
|
||||
}
|
||||
|
||||
event ssl_extension(c: connection, is_orig: bool, code: count, val: string)
|
||||
{
|
||||
if ( is_orig && SSL::extensions[code] == "server_name" &&
|
||||
c?$ssl && c$ssl?$server_name )
|
||||
if ( is_orig && c?$ssl && c$ssl?$server_name )
|
||||
Intel::seen([$indicator=c$ssl$server_name,
|
||||
$indicator_type=Intel::DOMAIN,
|
||||
$conn=c,
|
||||
|
|
|
@ -4,10 +4,14 @@ export {
|
|||
redef enum Intel::Where += {
|
||||
Conn::IN_ORIG,
|
||||
Conn::IN_RESP,
|
||||
Files::IN_HASH,
|
||||
Files::IN_NAME,
|
||||
DNS::IN_REQUEST,
|
||||
DNS::IN_RESPONSE,
|
||||
HTTP::IN_HOST_HEADER,
|
||||
HTTP::IN_REFERRER_HEADER,
|
||||
HTTP::IN_USER_AGENT_HEADER,
|
||||
HTTP::IN_X_FORWARDED_FOR_HEADER,
|
||||
HTTP::IN_URL,
|
||||
SMTP::IN_MAIL_FROM,
|
||||
SMTP::IN_RCPT_TO,
|
||||
|
@ -17,9 +21,8 @@ export {
|
|||
SMTP::IN_REPLY_TO,
|
||||
SMTP::IN_X_ORIGINATING_IP_HEADER,
|
||||
SMTP::IN_MESSAGE,
|
||||
SSL::IN_SERVER_CERT,
|
||||
SSL::IN_CLIENT_CERT,
|
||||
SSL::IN_SERVER_NAME,
|
||||
SMTP::IN_HEADER,
|
||||
X509::IN_CERT,
|
||||
};
|
||||
}
|
||||
|
|
16
scripts/policy/frameworks/intel/seen/x509.bro
Normal file
16
scripts/policy/frameworks/intel/seen/x509.bro
Normal file
|
@ -0,0 +1,16 @@
|
|||
@load base/frameworks/intel
|
||||
@load base/files/x509
|
||||
@load ./where-locations
|
||||
|
||||
event x509_certificate(f: fa_file, cert_ref: opaque of x509, cert: X509::Certificate)
|
||||
{
|
||||
if ( /emailAddress=/ in cert$subject )
|
||||
{
|
||||
local email = sub(cert$subject, /^.*emailAddress=/, "");
|
||||
email = sub(email, /,.*$/, "");
|
||||
Intel::seen([$indicator=email,
|
||||
$indicator_type=Intel::EMAIL,
|
||||
$f=f,
|
||||
$where=X509::IN_CERT]);
|
||||
}
|
||||
}
|
|
@ -8,23 +8,23 @@ export {
|
|||
const max_bpf_shunts = 100 &redef;
|
||||
|
||||
## Call this function to use BPF to shunt a connection (to prevent the
|
||||
## data packets from reaching Bro). For TCP connections, control packets
|
||||
## are still allowed through so that Bro can continue logging the connection
|
||||
## and it can stop shunting once the connection ends.
|
||||
## data packets from reaching Bro). For TCP connections, control
|
||||
## packets are still allowed through so that Bro can continue logging
|
||||
## the connection and it can stop shunting once the connection ends.
|
||||
global shunt_conn: function(id: conn_id): bool;
|
||||
|
||||
## This function will use a BPF expresssion to shunt traffic between
|
||||
## This function will use a BPF expression to shunt traffic between
|
||||
## the two hosts given in the `conn_id` so that the traffic is never
|
||||
## exposed to Bro's traffic processing.
|
||||
global shunt_host_pair: function(id: conn_id): bool;
|
||||
|
||||
## Remove shunting for a host pair given as a `conn_id`. The filter
|
||||
## is not immediately removed. It waits for the occassional filter
|
||||
## is not immediately removed. It waits for the occasional filter
|
||||
## update done by the `PacketFilter` framework.
|
||||
global unshunt_host_pair: function(id: conn_id): bool;
|
||||
|
||||
## Performs the same function as the `unshunt_host_pair` function, but
|
||||
## it forces an immediate filter update.
|
||||
## Performs the same function as the :bro:id:`PacketFilter::unshunt_host_pair`
|
||||
## function, but it forces an immediate filter update.
|
||||
global force_unshunt_host_pair: function(id: conn_id): bool;
|
||||
|
||||
## Retrieve the currently shunted connections.
|
||||
|
@ -34,12 +34,13 @@ export {
|
|||
global current_shunted_host_pairs: function(): set[conn_id];
|
||||
|
||||
redef enum Notice::Type += {
|
||||
## Indicative that :bro:id:`PacketFilter::max_bpf_shunts` connections
|
||||
## are already being shunted with BPF filters and no more are allowed.
|
||||
## Indicative that :bro:id:`PacketFilter::max_bpf_shunts`
|
||||
## connections are already being shunted with BPF filters and
|
||||
## no more are allowed.
|
||||
No_More_Conn_Shunts_Available,
|
||||
|
||||
## Limitations in BPF make shunting some connections with BPF impossible.
|
||||
## This notice encompasses those various cases.
|
||||
## Limitations in BPF make shunting some connections with BPF
|
||||
## impossible. This notice encompasses those various cases.
|
||||
Cannot_BPF_Shunt_Conn,
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
##! Provides the possibly to define software names that are interesting to
|
||||
##! Provides the possibility to define software names that are interesting to
|
||||
##! watch for changes. A notice is generated if software versions change on a
|
||||
##! host.
|
||||
|
||||
|
@ -9,15 +9,15 @@ module Software;
|
|||
|
||||
export {
|
||||
redef enum Notice::Type += {
|
||||
## For certain software, a version changing may matter. In that case,
|
||||
## this notice will be generated. Software that matters if the version
|
||||
## changes can be configured with the
|
||||
## For certain software, a version changing may matter. In that
|
||||
## case, this notice will be generated. Software that matters
|
||||
## if the version changes can be configured with the
|
||||
## :bro:id:`Software::interesting_version_changes` variable.
|
||||
Software_Version_Change,
|
||||
};
|
||||
|
||||
## Some software is more interesting when the version changes and this is
|
||||
## a set of all software that should raise a notice when a different
|
||||
## Some software is more interesting when the version changes and this
|
||||
## is a set of all software that should raise a notice when a different
|
||||
## version is seen on a host.
|
||||
const interesting_version_changes: set[string] = { } &redef;
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
##! Provides a variable to define vulnerable versions of software and if a
|
||||
##! a version of that software as old or older than the defined version a
|
||||
##! Provides a variable to define vulnerable versions of software and if
|
||||
##! a version of that software is as old or older than the defined version a
|
||||
##! notice will be generated.
|
||||
|
||||
@load base/frameworks/control
|
||||
|
@ -21,7 +21,7 @@ export {
|
|||
min: Software::Version &optional;
|
||||
## The maximum vulnerable version. This field is deliberately
|
||||
## not optional because a maximum vulnerable version must
|
||||
## always be defined. This assumption may become incorrent
|
||||
## always be defined. This assumption may become incorrect
|
||||
## if all future versions of some software are to be considered
|
||||
## vulnerable. :)
|
||||
max: Software::Version;
|
||||
|
|
|
@ -0,0 +1,69 @@
|
|||
##! Windows systems access a Microsoft Certificate Revocation List (CRL) periodically. The
|
||||
##! user agent for these requests reveals which version of Crypt32.dll installed on the system,
|
||||
##! which can uniquely identify the version of Windows that's running.
|
||||
##!
|
||||
##! This script will log the version of Windows that was identified to the Software framework.
|
||||
|
||||
@load base/protocols/http
|
||||
@load base/frameworks/software
|
||||
|
||||
module OS;
|
||||
|
||||
export {
|
||||
redef enum Software::Type += {
|
||||
## Identifier for Windows operating system versions
|
||||
WINDOWS,
|
||||
};
|
||||
|
||||
type Software::name_and_version: record {
|
||||
name : string;
|
||||
version: Software::Version;
|
||||
};
|
||||
|
||||
const crypto_api_mapping: table[string] of Software::name_and_version = {
|
||||
["Microsoft-CryptoAPI/5.131.2195.6661"] = [$name="Windows", $version=[$major=5, $minor=131, $minor2=2195, $minor3=6661, $addl="2000 SP4"]],
|
||||
["Microsoft-CryptoAPI/5.131.2195.6824"] = [$name="Windows", $version=[$major=5, $minor=131, $minor2=2195, $minor3=6824, $addl="2000 with MS04-11"]],
|
||||
["Microsoft-CryptoAPI/5.131.2195.6926"] = [$name="Windows", $version=[$major=5, $minor=131, $minor2=2195, $minor3=6926, $addl="2000 with Hotfix 98830"]],
|
||||
|
||||
["Microsoft-CryptoAPI/5.131.2600.0"] = [$name="Windows", $version=[$major=5, $minor=131, $minor2=2600, $minor3=0, $addl="XP SP0"]],
|
||||
["Microsoft-CryptoAPI/5.131.2600.1106"] = [$name="Windows", $version=[$major=5, $minor=131, $minor2=2600, $minor3=1106, $addl="XP SP1"]],
|
||||
["Microsoft-CryptoAPI/5.131.2600.2180"] = [$name="Windows", $version=[$major=5, $minor=131, $minor2=2600, $minor3=2180, $addl="XP SP2"]],
|
||||
["Microsoft-CryptoAPI/5.131.2600.3180"] = [$name="Windows", $version=[$major=5, $minor=131, $minor2=2600, $minor3=3180, $addl="XP SP3 Beta 1"]],
|
||||
["Microsoft-CryptoAPI/5.131.2600.3205"] = [$name="Windows", $version=[$major=5, $minor=131, $minor2=2600, $minor3=3205, $addl="XP SP3 Beta 2"]],
|
||||
["Microsoft-CryptoAPI/5.131.2600.3249"] = [$name="Windows", $version=[$major=5, $minor=131, $minor2=2600, $minor3=3249, $addl="XP SP3 RC Beta"]],
|
||||
["Microsoft-CryptoAPI/5.131.2600.3264"] = [$name="Windows", $version=[$major=5, $minor=131, $minor2=2600, $minor3=3264, $addl="XP SP3 RC1"]],
|
||||
["Microsoft-CryptoAPI/5.131.2600.3282"] = [$name="Windows", $version=[$major=5, $minor=131, $minor2=2600, $minor3=3282, $addl="XP SP3 RC1 Update"]],
|
||||
["Microsoft-CryptoAPI/5.131.2600.3300"] = [$name="Windows", $version=[$major=5, $minor=131, $minor2=2600, $minor3=3300, $addl="XP SP3 RC2"]],
|
||||
["Microsoft-CryptoAPI/5.131.2600.3311"] = [$name="Windows", $version=[$major=5, $minor=131, $minor2=2600, $minor3=3311, $addl="XP SP3 RC2 Update"]],
|
||||
["Microsoft-CryptoAPI/5.131.2600.5508"] = [$name="Windows", $version=[$major=5, $minor=131, $minor2=2600, $minor3=5508, $addl="XP SP3 RC2 Update 2"]],
|
||||
["Microsoft-CryptoAPI/5.131.2600.5512"] = [$name="Windows", $version=[$major=5, $minor=131, $minor2=2600, $minor3=5512, $addl="XP SP3"]],
|
||||
|
||||
["Microsoft-CryptoAPI/5.131.3790.0"] = [$name="Windows", $version=[$major=5, $minor=131, $minor2=3790, $minor3=0, $addl="XP x64 or Server 2003 SP0"]],
|
||||
["Microsoft-CryptoAPI/5.131.3790.1830"] = [$name="Windows", $version=[$major=5, $minor=131, $minor2=3790, $minor3=1830, $addl="XP x64 or Server 2003 SP1"]],
|
||||
["Microsoft-CryptoAPI/5.131.3790.3959"] = [$name="Windows", $version=[$major=5, $minor=131, $minor2=3790, $minor3=3959, $addl="XP x64 or Server 2003 SP2"]],
|
||||
["Microsoft-CryptoAPI/5.131.3790.5235"] = [$name="Windows", $version=[$major=5, $minor=131, $minor2=3790, $minor3=5235, $addl="XP x64 or Server 2003 with MS13-095"]],
|
||||
|
||||
["Microsoft-CryptoAPI/6.0"] = [$name="Windows", $version=[$major=6, $minor=0, $addl="Vista or Server 2008"]],
|
||||
["Microsoft-CryptoAPI/6.1"] = [$name="Windows", $version=[$major=6, $minor=1, $addl="7 or Server 2008 R2"]],
|
||||
["Microsoft-CryptoAPI/6.2"] = [$name="Windows", $version=[$major=6, $minor=2, $addl="8 or Server 2012"]],
|
||||
["Microsoft-CryptoAPI/6.3"] = [$name="Windows", $version=[$major=6, $minor=3, $addl="8.1 or Server 2012 R2"]],
|
||||
["Microsoft-CryptoAPI/6.4"] = [$name="Windows", $version=[$major=6, $minor=4, $addl="10 Technical Preview"]],
|
||||
} &redef;
|
||||
}
|
||||
|
||||
event HTTP::log_http(rec: HTTP::Info) &priority=5
|
||||
{
|
||||
if ( rec?$host && rec?$user_agent && rec$host == "crl.microsoft.com" &&
|
||||
/Microsoft-CryptoAPI\// in rec$user_agent )
|
||||
{
|
||||
if ( rec$user_agent !in crypto_api_mapping )
|
||||
{
|
||||
Software::found(rec$id, [$unparsed_version=sub(rec$user_agent, /Microsoft-CryptoAPI/, "Unknown CryptoAPI Version"), $host=rec$id$orig_h, $software_type=WINDOWS]);
|
||||
}
|
||||
else
|
||||
{
|
||||
local result = crypto_api_mapping[rec$user_agent];
|
||||
Software::found(rec$id, [$version=result$version, $name=result$name, $host=rec$id$orig_h, $software_type=WINDOWS]);
|
||||
}
|
||||
}
|
||||
}
|
1
scripts/policy/integration/barnyard2/README
Normal file
1
scripts/policy/integration/barnyard2/README
Normal file
|
@ -0,0 +1 @@
|
|||
Integration with Barnyard2.
|
|
@ -15,8 +15,8 @@ export {
|
|||
alert: AlertData &log;
|
||||
};
|
||||
|
||||
## This can convert a Barnyard :bro:type:`Barnyard2::PacketID` value to a
|
||||
## :bro:type:`conn_id` value in the case that you might need to index
|
||||
## This can convert a Barnyard :bro:type:`Barnyard2::PacketID` value to
|
||||
## a :bro:type:`conn_id` value in the case that you might need to index
|
||||
## into an existing data structure elsewhere within Bro.
|
||||
global pid2cid: function(p: PacketID): conn_id;
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@ export {
|
|||
generator_id: count; ##< Which generator generated the alert?
|
||||
signature_revision: count; ##< Sig revision for this id.
|
||||
classification_id: count; ##< Event classification.
|
||||
classification: string; ##< Descriptive classification string,
|
||||
classification: string; ##< Descriptive classification string.
|
||||
priority_id: count; ##< Event priority.
|
||||
event_id: count; ##< Event ID.
|
||||
} &log;
|
||||
|
|
|
@ -3,8 +3,8 @@
|
|||
|
||||
module Intel;
|
||||
|
||||
## These are some fields to add extended compatibility between Bro and the Collective
|
||||
## Intelligence Framework
|
||||
## These are some fields to add extended compatibility between Bro and the
|
||||
## Collective Intelligence Framework.
|
||||
redef record Intel::MetaData += {
|
||||
## Maps to the Impact field in the Collective Intelligence Framework.
|
||||
cif_impact: string &optional;
|
||||
|
|
1
scripts/policy/misc/app-stats/README
Normal file
1
scripts/policy/misc/app-stats/README
Normal file
|
@ -0,0 +1 @@
|
|||
AppStats collects information about web applications in use on the network.
|
|
@ -1,5 +1,5 @@
|
|||
#! AppStats collects information about web applications in use
|
||||
#! on the network.
|
||||
##! AppStats collects information about web applications in use
|
||||
##! on the network.
|
||||
|
||||
@load base/protocols/http
|
||||
@load base/protocols/ssl
|
||||
|
|
1
scripts/policy/misc/app-stats/plugins/README
Normal file
1
scripts/policy/misc/app-stats/plugins/README
Normal file
|
@ -0,0 +1 @@
|
|||
Plugins for AppStats.
|
|
@ -1,6 +1,6 @@
|
|||
@load ./facebook
|
||||
@load ./gmail
|
||||
@load ./google
|
||||
@load ./netflix
|
||||
@load ./pandora
|
||||
@load ./youtube
|
||||
#@load ./gmail
|
||||
#@load ./google
|
||||
#@load ./netflix
|
||||
#@load ./pandora
|
||||
#@load ./youtube
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
##! the packet capture or it could even be beyond the host. If you are
|
||||
##! capturing from a switch with a SPAN port, it's very possible that
|
||||
##! the switch itself could be overloaded and dropping packets.
|
||||
##! Reported loss is computed in terms of number of "gap events" (ACKs
|
||||
##! Reported loss is computed in terms of the number of "gap events" (ACKs
|
||||
##! for a sequence number that's above a gap).
|
||||
|
||||
@load base/frameworks/notice
|
||||
|
@ -26,7 +26,7 @@ export {
|
|||
## The time delay between this measurement and the last.
|
||||
ts_delta: interval &log;
|
||||
## In the event that there are multiple Bro instances logging
|
||||
## to the same host, this distinguishes each peer with it's
|
||||
## to the same host, this distinguishes each peer with its
|
||||
## individual name.
|
||||
peer: string &log;
|
||||
## Number of missed ACKs from the previous measurement interval.
|
||||
|
@ -34,7 +34,7 @@ export {
|
|||
## Total number of ACKs seen in the previous measurement interval.
|
||||
acks: count &log;
|
||||
## Percentage of ACKs seen where the data being ACKed wasn't seen.
|
||||
percent_lost: string &log;
|
||||
percent_lost: double &log;
|
||||
};
|
||||
|
||||
## The interval at which capture loss reports are created.
|
||||
|
@ -43,7 +43,7 @@ export {
|
|||
## The percentage of missed data that is considered "too much"
|
||||
## when the :bro:enum:`CaptureLoss::Too_Much_Loss` notice should be
|
||||
## generated. The value is expressed as a double between 0 and 1 with 1
|
||||
## being 100%
|
||||
## being 100%.
|
||||
const too_much_loss: double = 0.1 &redef;
|
||||
}
|
||||
|
||||
|
@ -64,7 +64,7 @@ event CaptureLoss::take_measurement(last_ts: time, last_acks: count, last_gaps:
|
|||
$ts_delta=now-last_ts,
|
||||
$peer=peer_description,
|
||||
$acks=acks, $gaps=gaps,
|
||||
$percent_lost=fmt("%.3f%%", pct_lost)];
|
||||
$percent_lost=pct_lost];
|
||||
|
||||
if ( pct_lost >= too_much_loss*100 )
|
||||
NOTICE([$note=Too_Much_Loss,
|
||||
|
|
1
scripts/policy/misc/detect-traceroute/README
Normal file
1
scripts/policy/misc/detect-traceroute/README
Normal file
|
@ -0,0 +1 @@
|
|||
Detect hosts that are running traceroute.
|
|
@ -1,7 +1,8 @@
|
|||
##! This script detects a large number of ICMP Time Exceeded messages heading toward
|
||||
##! hosts that have sent low TTL packets. It generates a notice when the number of
|
||||
##! ICMP Time Exceeded messages for a source-destination pair exceeds a
|
||||
##! threshold.
|
||||
##! This script detects a large number of ICMP Time Exceeded messages heading
|
||||
##! toward hosts that have sent low TTL packets. It generates a notice when the
|
||||
##! number of ICMP Time Exceeded messages for a source-destination pair exceeds
|
||||
##! a threshold.
|
||||
|
||||
@load base/frameworks/sumstats
|
||||
@load base/frameworks/signatures
|
||||
@load-sigs ./detect-low-ttls.sig
|
||||
|
@ -20,15 +21,16 @@ export {
|
|||
Detected
|
||||
};
|
||||
|
||||
## By default this script requires that any host detected running traceroutes
|
||||
## first send low TTL packets (TTL < 10) to the traceroute destination host.
|
||||
## Changing this this setting to `F` will relax the detection a bit by
|
||||
## solely relying on ICMP time-exceeded messages to detect traceroute.
|
||||
## By default this script requires that any host detected running
|
||||
## traceroutes first send low TTL packets (TTL < 10) to the traceroute
|
||||
## destination host. Changing this setting to F will relax the
|
||||
## detection a bit by solely relying on ICMP time-exceeded messages to
|
||||
## detect traceroute.
|
||||
const require_low_ttl_packets = T &redef;
|
||||
|
||||
## Defines the threshold for ICMP Time Exceeded messages for a src-dst pair.
|
||||
## This threshold only comes into play after a host is found to be
|
||||
## sending low ttl packets.
|
||||
## Defines the threshold for ICMP Time Exceeded messages for a src-dst
|
||||
## pair. This threshold only comes into play after a host is found to
|
||||
## be sending low TTL packets.
|
||||
const icmp_time_exceeded_threshold: double = 3 &redef;
|
||||
|
||||
## Interval at which to watch for the
|
||||
|
@ -40,7 +42,7 @@ export {
|
|||
type Info: record {
|
||||
## Timestamp
|
||||
ts: time &log;
|
||||
## Address initiaing the traceroute.
|
||||
## Address initiating the traceroute.
|
||||
src: addr &log;
|
||||
## Destination address of the traceroute.
|
||||
dst: addr &log;
|
||||
|
|
40
scripts/policy/misc/dump-events.bro
Normal file
40
scripts/policy/misc/dump-events.bro
Normal file
|
@ -0,0 +1,40 @@
|
|||
##! This script dumps the events that Bro raises out to standard output in a
|
||||
##! readable form. This is for debugging only and allows to understand events and
|
||||
##! their parameters as Bro processes input. Note that it will show only events
|
||||
##! for which a handler is defined.
|
||||
|
||||
module DumpEvents;
|
||||
|
||||
export {
|
||||
## If true, include event arguments in output.
|
||||
const include_args = T &redef;
|
||||
|
||||
## Only include events matching the given pattern into output. By default, the
|
||||
## pattern matches all events.
|
||||
const include = /.*/ &redef;
|
||||
}
|
||||
|
||||
event new_event(name: string, args: call_argument_vector)
|
||||
{
|
||||
if ( include !in name )
|
||||
return;
|
||||
|
||||
print fmt("%17.6f %s", network_time(), name);
|
||||
|
||||
if ( ! include_args || |args| == 0 )
|
||||
return;
|
||||
|
||||
for ( i in args )
|
||||
{
|
||||
local a = args[i];
|
||||
|
||||
local proto = fmt("%s: %s", a$name, a$type_name);
|
||||
|
||||
if ( a?$value )
|
||||
print fmt(" [%d] %-18s = %s", i, proto, a$value);
|
||||
else
|
||||
print fmt(" | %-18s = %s [default]", proto, a$value);
|
||||
}
|
||||
|
||||
print "";
|
||||
}
|
|
@ -1,13 +1,13 @@
|
|||
##! This script provides infrastructure for logging devices for which Bro has been
|
||||
##! able to determine the MAC address, and it logs them once per day (by default).
|
||||
##! The log that is output provides an easy way to determine a count of the devices
|
||||
##! in use on a network per day.
|
||||
##! This script provides infrastructure for logging devices for which Bro has
|
||||
##! been able to determine the MAC address, and it logs them once per day (by
|
||||
##! default). The log that is output provides an easy way to determine a count
|
||||
##! of the devices in use on a network per day.
|
||||
##!
|
||||
##! ..note::
|
||||
##! .. note::
|
||||
##!
|
||||
##! This script will not generate any logs on its own, it needs to be
|
||||
##! supplied with information from elsewhere, such as
|
||||
##! :doc:`policy/protocols/dhcp/known-devices-and-hostnames/scripts/.
|
||||
##! :doc:`/scripts/policy/protocols/dhcp/known-devices-and-hostnames.bro`.
|
||||
|
||||
module Known;
|
||||
|
||||
|
@ -15,7 +15,8 @@ export {
|
|||
## The known-hosts logging stream identifier.
|
||||
redef enum Log::ID += { DEVICES_LOG };
|
||||
|
||||
## The record type which contains the column fields of the known-devices log.
|
||||
## The record type which contains the column fields of the known-devices
|
||||
## log.
|
||||
type DevicesInfo: record {
|
||||
## The timestamp at which the host was detected.
|
||||
ts: time &log;
|
||||
|
@ -24,10 +25,10 @@ export {
|
|||
};
|
||||
|
||||
## The set of all known MAC addresses. It can accessed from other
|
||||
## to add, and check for, addresses seen in use.
|
||||
##
|
||||
## We maintain each entry for 24 hours by default so that the existence of
|
||||
## individual addressed is logged each day.
|
||||
## scripts to add, and check for, addresses seen in use.
|
||||
##
|
||||
## We maintain each entry for 24 hours by default so that the existence
|
||||
## of individual addresses is logged each day.
|
||||
global known_devices: set[string] &create_expire=1day &synchronized &redef;
|
||||
|
||||
## An event that can be handled to access the :bro:type:`Known::DevicesInfo`
|
||||
|
|
|
@ -29,9 +29,10 @@ export {
|
|||
#global confirm_filter_installation: event(success: bool);
|
||||
|
||||
redef record Cluster::Node += {
|
||||
## A BPF filter for load balancing traffic sniffed on a single interface
|
||||
## across a number of processes. In normal uses, this will be assigned
|
||||
## dynamically by the manager and installed by the workers.
|
||||
## A BPF filter for load balancing traffic sniffed on a single
|
||||
## interface across a number of processes. In normal uses, this
|
||||
## will be assigned dynamically by the manager and installed by
|
||||
## the workers.
|
||||
lb_filter: string &optional;
|
||||
};
|
||||
}
|
||||
|
@ -81,7 +82,7 @@ event bro_init() &priority=5
|
|||
++lb_proc_track[that_node$ip, that_node$interface];
|
||||
if ( total_lb_procs > 1 )
|
||||
{
|
||||
that_node$lb_filter = PacketFilter::sample_filter(total_lb_procs, this_lb_proc);
|
||||
that_node$lb_filter = PacketFilter::sampling_filter(total_lb_procs, this_lb_proc);
|
||||
Communication::nodes[no]$capture_filter = that_node$lb_filter;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,9 +7,9 @@ export {
|
|||
redef enum Log::ID += { LOG };
|
||||
|
||||
type Info: record {
|
||||
## Name of the script loaded potentially with spaces included before
|
||||
## the file name to indicate load depth. The convention is two spaces
|
||||
## per level of depth.
|
||||
## Name of the script loaded potentially with spaces included
|
||||
## before the file name to indicate load depth. The convention
|
||||
## is two spaces per level of depth.
|
||||
name: string &log;
|
||||
};
|
||||
}
|
||||
|
@ -36,4 +36,4 @@ event bro_init() &priority=5
|
|||
event bro_script_loaded(path: string, level: count)
|
||||
{
|
||||
Log::write(LoadedScripts::LOG, [$name=cat(depth[level], compress_path(path))]);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,7 +8,8 @@ redef profiling_file = open_log_file("prof");
|
|||
## Set the cheap profiling interval.
|
||||
redef profiling_interval = 15 secs;
|
||||
|
||||
## Set the expensive profiling interval.
|
||||
## Set the expensive profiling interval (multiple of
|
||||
## :bro:id:`profiling_interval`).
|
||||
redef expensive_profiling_multiple = 20;
|
||||
|
||||
event bro_init()
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
##! TCP Scan detection
|
||||
##!
|
||||
##! ..Authors: Sheharbano Khattak
|
||||
##! Seth Hall
|
||||
##! All the authors of the old scan.bro
|
||||
##! TCP Scan detection.
|
||||
|
||||
# ..Authors: Sheharbano Khattak
|
||||
# Seth Hall
|
||||
# All the authors of the old scan.bro
|
||||
|
||||
@load base/frameworks/notice
|
||||
@load base/frameworks/sumstats
|
||||
|
@ -13,37 +13,38 @@ module Scan;
|
|||
|
||||
export {
|
||||
redef enum Notice::Type += {
|
||||
## Address scans detect that a host appears to be scanning some number
|
||||
## of destinations on a single port. This notice is generated when more
|
||||
## than :bro:id:`Scan::addr_scan_threshold` unique hosts are seen over
|
||||
## the previous :bro:id:`Scan::addr_scan_interval` time range.
|
||||
## Address scans detect that a host appears to be scanning some
|
||||
## number of destinations on a single port. This notice is
|
||||
## generated when more than :bro:id:`Scan::addr_scan_threshold`
|
||||
## unique hosts are seen over the previous
|
||||
## :bro:id:`Scan::addr_scan_interval` time range.
|
||||
Address_Scan,
|
||||
|
||||
## Port scans detect that an attacking host appears to be scanning a
|
||||
## single victim host on several ports. This notice is generated when
|
||||
## an attacking host attempts to connect to
|
||||
## Port scans detect that an attacking host appears to be
|
||||
## scanning a single victim host on several ports. This notice
|
||||
## is generated when an attacking host attempts to connect to
|
||||
## :bro:id:`Scan::port_scan_threshold`
|
||||
## unique ports on a single host over the previous
|
||||
## :bro:id:`Scan::port_scan_interval` time range.
|
||||
Port_Scan,
|
||||
};
|
||||
|
||||
## Failed connection attempts are tracked over this time interval for the address
|
||||
## scan detection. A higher interval will detect slower scanners, but may also
|
||||
## yield more false positives.
|
||||
## Failed connection attempts are tracked over this time interval for
|
||||
## the address scan detection. A higher interval will detect slower
|
||||
## scanners, but may also yield more false positives.
|
||||
const addr_scan_interval = 5min &redef;
|
||||
|
||||
## Failed connection attempts are tracked over this time interval for the port scan
|
||||
## detection. A higher interval will detect slower scanners, but may also yield
|
||||
## more false positives.
|
||||
## Failed connection attempts are tracked over this time interval for
|
||||
## the port scan detection. A higher interval will detect slower
|
||||
## scanners, but may also yield more false positives.
|
||||
const port_scan_interval = 5min &redef;
|
||||
|
||||
## The threshold of a unique number of hosts a scanning host has to have failed
|
||||
## connections with on a single port.
|
||||
## The threshold of the unique number of hosts a scanning host has to
|
||||
## have failed connections with on a single port.
|
||||
const addr_scan_threshold = 25.0 &redef;
|
||||
|
||||
## The threshold of a number of unique ports a scanning host has to have failed
|
||||
## connections with on a single victim host.
|
||||
## The threshold of the number of unique ports a scanning host has to
|
||||
## have failed connections with on a single victim host.
|
||||
const port_scan_threshold = 15.0 &redef;
|
||||
|
||||
global Scan::addr_scan_policy: hook(scanner: addr, victim: addr, scanned_port: port);
|
||||
|
@ -52,7 +53,7 @@ export {
|
|||
|
||||
event bro_init() &priority=5
|
||||
{
|
||||
local r1: SumStats::Reducer = [$stream="scan.addr.fail", $apply=set(SumStats::UNIQUE)];
|
||||
local r1: SumStats::Reducer = [$stream="scan.addr.fail", $apply=set(SumStats::UNIQUE), $unique_max=double_to_count(addr_scan_threshold+2)];
|
||||
SumStats::create([$name="addr-scan",
|
||||
$epoch=addr_scan_interval,
|
||||
$reducers=set(r1),
|
||||
|
@ -77,7 +78,7 @@ event bro_init() &priority=5
|
|||
}]);
|
||||
|
||||
# Note: port scans are tracked similar to: table[src_ip, dst_ip] of set(port);
|
||||
local r2: SumStats::Reducer = [$stream="scan.port.fail", $apply=set(SumStats::UNIQUE)];
|
||||
local r2: SumStats::Reducer = [$stream="scan.port.fail", $apply=set(SumStats::UNIQUE), $unique_max=double_to_count(port_scan_threshold+2)];
|
||||
SumStats::create([$name="port-scan",
|
||||
$epoch=port_scan_interval,
|
||||
$reducers=set(r2),
|
||||
|
@ -146,11 +147,6 @@ function is_reverse_failed_conn(c: connection): bool
|
|||
return F;
|
||||
}
|
||||
|
||||
## Generated for an unsuccessful connection attempt. This
|
||||
## event is raised when an originator unsuccessfully attempted
|
||||
## to establish a connection. “Unsuccessful” is defined as at least
|
||||
## tcp_attempt_delay seconds having elapsed since the originator first sent a
|
||||
## connection establishment packet to the destination without seeing a reply.
|
||||
event connection_attempt(c: connection)
|
||||
{
|
||||
local is_reverse_scan = F;
|
||||
|
@ -160,9 +156,6 @@ event connection_attempt(c: connection)
|
|||
add_sumstats(c$id, is_reverse_scan);
|
||||
}
|
||||
|
||||
## Generated for a rejected TCP connection. This event is raised when an originator
|
||||
## attempted to setup a TCP connection but the responder replied with a RST packet
|
||||
## denying it.
|
||||
event connection_rejected(c: connection)
|
||||
{
|
||||
local is_reverse_scan = F;
|
||||
|
@ -172,8 +165,6 @@ event connection_rejected(c: connection)
|
|||
add_sumstats(c$id, is_reverse_scan);
|
||||
}
|
||||
|
||||
## Generated when an endpoint aborted a TCP connection. The event is raised when
|
||||
## one endpoint of an *established* TCP connection aborted by sending a RST packet.
|
||||
event connection_reset(c: connection)
|
||||
{
|
||||
if ( is_failed_conn(c) )
|
||||
|
@ -182,7 +173,6 @@ event connection_reset(c: connection)
|
|||
add_sumstats(c$id, T);
|
||||
}
|
||||
|
||||
## Generated for each still-open connection when Bro terminates.
|
||||
event connection_pending(c: connection)
|
||||
{
|
||||
if ( is_failed_conn(c) )
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
##! Log memory/packet/lag statistics. Differs from profiling.bro in that this
|
||||
##! Log memory/packet/lag statistics. Differs from
|
||||
##! :doc:`/scripts/policy/misc/profiling.bro` in that this
|
||||
##! is lighter-weight (much less info, and less load to generate).
|
||||
|
||||
@load base/frameworks/notice
|
||||
|
@ -20,21 +21,23 @@ export {
|
|||
mem: count &log;
|
||||
## Number of packets processed since the last stats interval.
|
||||
pkts_proc: count &log;
|
||||
## Number of events that been processed since the last stats interval.
|
||||
## Number of events processed since the last stats interval.
|
||||
events_proc: count &log;
|
||||
## Number of events that have been queued since the last stats interval.
|
||||
## Number of events that have been queued since the last stats
|
||||
## interval.
|
||||
events_queued: count &log;
|
||||
|
||||
## Lag between the wall clock and packet timestamps if reading live traffic.
|
||||
## Lag between the wall clock and packet timestamps if reading
|
||||
## live traffic.
|
||||
lag: interval &log &optional;
|
||||
## Number of packets received since the last stats interval if reading
|
||||
## live traffic.
|
||||
## Number of packets received since the last stats interval if
|
||||
## reading live traffic.
|
||||
pkts_recv: count &log &optional;
|
||||
## Number of packets dropped since the last stats interval if reading
|
||||
## live traffic.
|
||||
## Number of packets dropped since the last stats interval if
|
||||
## reading live traffic.
|
||||
pkts_dropped: count &log &optional;
|
||||
## Number of packets seen on the link since the last stats interval
|
||||
## if reading live traffic.
|
||||
## Number of packets seen on the link since the last stats
|
||||
## interval if reading live traffic.
|
||||
pkts_link: count &log &optional;
|
||||
};
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
##! Deletes the -w tracefile at regular intervals and starts a new file
|
||||
##! Deletes the ``-w`` tracefile at regular intervals and starts a new file
|
||||
##! from scratch.
|
||||
|
||||
module TrimTraceFile;
|
||||
|
@ -8,9 +8,9 @@ export {
|
|||
const trim_interval = 10 mins &redef;
|
||||
|
||||
## This event can be generated externally to this script if on-demand
|
||||
## tracefile rotation is required with the caveat that the script doesn't
|
||||
## currently attempt to get back on schedule automatically and the next
|
||||
## trim will likely won't happen on the
|
||||
## tracefile rotation is required with the caveat that the script
|
||||
## doesn't currently attempt to get back on schedule automatically and
|
||||
## the next trim likely won't happen on the
|
||||
## :bro:id:`TrimTraceFile::trim_interval`.
|
||||
global go: event(first_trim: bool);
|
||||
}
|
||||
|
|
|
@ -15,8 +15,8 @@ export {
|
|||
type HostsInfo: record {
|
||||
## The timestamp at which the host was detected.
|
||||
ts: time &log;
|
||||
## The address that was detected originating or responding to a TCP
|
||||
## connection.
|
||||
## The address that was detected originating or responding to a
|
||||
## TCP connection.
|
||||
host: addr &log;
|
||||
};
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@ module Known;
|
|||
|
||||
export {
|
||||
redef record DevicesInfo += {
|
||||
## The value of the DHCP host name option, if seen
|
||||
## The value of the DHCP host name option, if seen.
|
||||
dhcp_host_name: string &log &optional;
|
||||
};
|
||||
}
|
||||
|
|
|
@ -19,13 +19,17 @@ export {
|
|||
};
|
||||
}
|
||||
|
||||
event DNS::do_reply(c: connection, msg: dns_msg, ans: dns_answer, reply: string) &priority=4
|
||||
hook DNS::do_reply(c: connection, msg: dns_msg, ans: dns_answer, reply: string) &priority=5
|
||||
{
|
||||
# The "ready" flag will be set here. This causes the setting from the
|
||||
# base script to be overridden since the base script will log immediately
|
||||
# after all of the ANS replies have been seen.
|
||||
c$dns$ready=F;
|
||||
|
||||
if ( msg$opcode != 0 )
|
||||
# Currently only standard queries are tracked.
|
||||
return;
|
||||
|
||||
if ( ! msg$QR )
|
||||
# This is weird: the inquirer must also be providing answers in
|
||||
# the request, which is not what we want to track.
|
||||
return;
|
||||
|
||||
if ( ans$answer_type == DNS_AUTH )
|
||||
{
|
||||
if ( ! c$dns?$auth )
|
||||
|
@ -38,11 +42,4 @@ event DNS::do_reply(c: connection, msg: dns_msg, ans: dns_answer, reply: string)
|
|||
c$dns$addl = set();
|
||||
add c$dns$addl[reply];
|
||||
}
|
||||
|
||||
if ( c$dns?$answers && c$dns?$auth && c$dns?$addl &&
|
||||
c$dns$total_replies == |c$dns$answers| + |c$dns$auth| + |c$dns$addl| )
|
||||
{
|
||||
# *Now* all replies desired have been seen.
|
||||
c$dns$ready = T;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,9 +10,9 @@ module DNS;
|
|||
|
||||
export {
|
||||
redef enum Notice::Type += {
|
||||
## Raised when a non-local name is found to be pointing at a local host.
|
||||
## :bro:id:`Site::local_zones` variable **must** be set appropriately
|
||||
## for this detection.
|
||||
## Raised when a non-local name is found to be pointing at a
|
||||
## local host. The :bro:id:`Site::local_zones` variable
|
||||
## **must** be set appropriately for this detection.
|
||||
External_Name,
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
##! FTP brute-forcing detector, triggering when too many rejected usernames or
|
||||
##! failed passwords have occured from a single address.
|
||||
##! FTP brute-forcing detector, triggering when too many rejected usernames or
|
||||
##! failed passwords have occurred from a single address.
|
||||
|
||||
@load base/protocols/ftp
|
||||
@load base/frameworks/sumstats
|
||||
|
@ -10,8 +10,8 @@ module FTP;
|
|||
|
||||
export {
|
||||
redef enum Notice::Type += {
|
||||
## Indicates a host bruteforcing FTP logins by watching for too many
|
||||
## rejected usernames or failed passwords.
|
||||
## Indicates a host bruteforcing FTP logins by watching for too
|
||||
## many rejected usernames or failed passwords.
|
||||
Bruteforcing
|
||||
};
|
||||
|
||||
|
@ -27,7 +27,7 @@ export {
|
|||
|
||||
event bro_init()
|
||||
{
|
||||
local r1: SumStats::Reducer = [$stream="ftp.failed_auth", $apply=set(SumStats::UNIQUE)];
|
||||
local r1: SumStats::Reducer = [$stream="ftp.failed_auth", $apply=set(SumStats::UNIQUE), $unique_max=double_to_count(bruteforce_threshold+2)];
|
||||
SumStats::create([$name="ftp-detect-bruteforcing",
|
||||
$epoch=bruteforce_measurement_interval,
|
||||
$reducers=set(r1),
|
||||
|
|
|
@ -8,10 +8,12 @@ module HTTP;
|
|||
|
||||
export {
|
||||
redef enum Notice::Type += {
|
||||
## Indicates that a host performing SQL injection attacks was detected.
|
||||
## Indicates that a host performing SQL injection attacks was
|
||||
## detected.
|
||||
SQL_Injection_Attacker,
|
||||
## Indicates that a host was seen to have SQL injection attacks against
|
||||
## it. This is tracked by IP address as opposed to hostname.
|
||||
## Indicates that a host was seen to have SQL injection attacks
|
||||
## against it. This is tracked by IP address as opposed to
|
||||
## hostname.
|
||||
SQL_Injection_Victim,
|
||||
};
|
||||
|
||||
|
@ -19,9 +21,11 @@ export {
|
|||
## Indicator of a URI based SQL injection attack.
|
||||
URI_SQLI,
|
||||
## Indicator of client body based SQL injection attack. This is
|
||||
## typically the body content of a POST request. Not implemented yet.
|
||||
## typically the body content of a POST request. Not implemented
|
||||
## yet.
|
||||
POST_SQLI,
|
||||
## Indicator of a cookie based SQL injection attack. Not implemented yet.
|
||||
## Indicator of a cookie based SQL injection attack. Not
|
||||
## implemented yet.
|
||||
COOKIE_SQLI,
|
||||
};
|
||||
|
||||
|
|
|
@ -8,12 +8,12 @@ module HTTP;
|
|||
|
||||
export {
|
||||
redef record Info += {
|
||||
## The vector of HTTP header names sent by the client. No header
|
||||
## values are included here, just the header names.
|
||||
## The vector of HTTP header names sent by the client. No
|
||||
## header values are included here, just the header names.
|
||||
client_header_names: vector of string &log &optional;
|
||||
|
||||
## The vector of HTTP header names sent by the server. No header
|
||||
## values are included here, just the header names.
|
||||
## The vector of HTTP header names sent by the server. No
|
||||
## header values are included here, just the header names.
|
||||
server_header_names: vector of string &log &optional;
|
||||
};
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
##! Extracts and logs variables names from cookies sent by clients.
|
||||
##! Extracts and logs variable names from cookies sent by clients.
|
||||
|
||||
@load base/protocols/http/main
|
||||
@load base/protocols/http/utils
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
##! Extracts and log variables from the requested URI in the default HTTP
|
||||
##! Extracts and logs variables from the requested URI in the default HTTP
|
||||
##! logging stream.
|
||||
|
||||
@load base/protocols/http
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
##! Script for tracking known Modbus masters and slaves.
|
||||
##!
|
||||
##! .. todo: This script needs a lot of work. What might be more interesting is to track
|
||||
##! master/slave relationships based on commands sent and successful (non-exception)
|
||||
##! responses.
|
||||
##! .. todo:: This script needs a lot of work. What might be more interesting
|
||||
##! is to track master/slave relationships based on commands sent and
|
||||
##! successful (non-exception) responses.
|
||||
|
||||
@load base/protocols/modbus
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
##! This script tracks the memory map of holding (read/write) registers and logs
|
||||
##! changes as they are discovered.
|
||||
##!
|
||||
##! .. todo: Not all register reads and write functions are being supported yet.
|
||||
##! .. todo:: Not all register read and write functions are supported yet.
|
||||
|
||||
@load base/protocols/modbus
|
||||
@load base/utils/directions-and-hosts
|
||||
|
@ -15,9 +15,9 @@ export {
|
|||
const track_memmap: Host = ALL_HOSTS &redef;
|
||||
|
||||
type MemmapInfo: record {
|
||||
## Timestamp for the detected register change
|
||||
## Timestamp for the detected register change.
|
||||
ts: time &log;
|
||||
## Unique ID for the connection
|
||||
## Unique ID for the connection.
|
||||
uid: string &log;
|
||||
## Connection ID.
|
||||
id: conn_id &log;
|
||||
|
@ -27,7 +27,8 @@ export {
|
|||
old_val: count &log;
|
||||
## The new value stored in the register.
|
||||
new_val: count &log;
|
||||
## The time delta between when the 'old_val' and 'new_val' were seen.
|
||||
## The time delta between when the *old_val* and *new_val* were
|
||||
## seen.
|
||||
delta: interval &log;
|
||||
};
|
||||
|
||||
|
@ -42,8 +43,8 @@ export {
|
|||
## The memory map of slaves is tracked with this variable.
|
||||
global device_registers: table[addr] of Registers;
|
||||
|
||||
## This event is generated every time a register is seen to be different than
|
||||
## it was previously seen to be.
|
||||
## This event is generated every time a register is seen to be different
|
||||
## than it was previously seen to be.
|
||||
global changed_register: event(c: connection, register: count, old_val: count, new_val: count, delta: interval);
|
||||
}
|
||||
|
||||
|
|
20
scripts/policy/protocols/mysql/software.bro
Normal file
20
scripts/policy/protocols/mysql/software.bro
Normal file
|
@ -0,0 +1,20 @@
|
|||
##! Software identification and extraction for MySQL traffic.
|
||||
|
||||
@load base/frameworks/software
|
||||
|
||||
module MySQL;
|
||||
|
||||
export {
|
||||
redef enum Software::Type += {
|
||||
## Identifier for MySQL servers in the software framework.
|
||||
SERVER,
|
||||
};
|
||||
}
|
||||
|
||||
event mysql_server_version(c: connection, ver: string)
|
||||
{
|
||||
if ( ver == "" )
|
||||
return;
|
||||
|
||||
Software::found(c$id, [$unparsed_version=ver, $host=c$id$resp_h, $software_type=SERVER]);
|
||||
}
|
|
@ -8,8 +8,8 @@ export {
|
|||
Suspicious_Origination
|
||||
};
|
||||
|
||||
## Places where it's suspicious for mail to originate from represented as
|
||||
## all-capital, two character country codes (e.x. US). It requires
|
||||
## Places where it's suspicious for mail to originate from represented
|
||||
## as all-capital, two character country codes (e.g., US). It requires
|
||||
## libGeoIP support built in.
|
||||
const suspicious_origination_countries: set[string] = {} &redef;
|
||||
const suspicious_origination_networks: set[subnet] = {} &redef;
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
##! TODO:
|
||||
##!
|
||||
##! * Find some heuristic to determine if email was sent through
|
||||
##! a MS Exhange webmail interface as opposed to a desktop client.
|
||||
##! a MS Exchange webmail interface as opposed to a desktop client.
|
||||
|
||||
@load base/frameworks/software/main
|
||||
@load base/protocols/smtp/main
|
||||
|
@ -20,19 +20,19 @@ export {
|
|||
};
|
||||
|
||||
redef record Info += {
|
||||
## Boolean indicator of if the message was sent through a webmail
|
||||
## interface.
|
||||
## Boolean indicator of if the message was sent through a
|
||||
## webmail interface.
|
||||
is_webmail: bool &log &default=F;
|
||||
};
|
||||
|
||||
## Assuming that local mail servers are more trustworthy with the headers
|
||||
## they insert into messages envelopes, this default makes Bro not attempt
|
||||
## to detect software in inbound message bodies. If mail coming in from
|
||||
## external addresses gives incorrect data in the Received headers, it
|
||||
## could populate your SOFTWARE logging stream with incorrect data.
|
||||
## If you would like to detect mail clients for incoming messages
|
||||
## (network traffic originating from a non-local address), set this
|
||||
## variable to EXTERNAL_HOSTS or ALL_HOSTS.
|
||||
## Assuming that local mail servers are more trustworthy with the
|
||||
## headers they insert into message envelopes, this default makes Bro
|
||||
## not attempt to detect software in inbound message bodies. If mail
|
||||
## coming in from external addresses gives incorrect data in
|
||||
## the Received headers, it could populate your SOFTWARE logging stream
|
||||
## with incorrect data. If you would like to detect mail clients for
|
||||
## incoming messages (network traffic originating from a non-local
|
||||
## address), set this variable to EXTERNAL_HOSTS or ALL_HOSTS.
|
||||
const detect_clients_in_messages_from = LOCAL_HOSTS &redef;
|
||||
|
||||
## A regular expression to match USER-AGENT-like headers to find if a
|
||||
|
|
|
@ -11,12 +11,12 @@ module SSH;
|
|||
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.
|
||||
## :bro:id:`SSH::password_guesses_limit` threshold with
|
||||
## heuristically determined 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.
|
||||
## Indicates that a host previously identified as a "password
|
||||
## guesser" has now had a heuristically successful login
|
||||
## attempt. This is not currently implemented.
|
||||
Login_By_Password_Guesser,
|
||||
};
|
||||
|
||||
|
@ -29,8 +29,8 @@ export {
|
|||
## guessing passwords.
|
||||
const password_guesses_limit: double = 30 &redef;
|
||||
|
||||
## The amount of time to remember presumed non-successful logins to build
|
||||
## model of a password guesser.
|
||||
## The amount of time to remember presumed non-successful logins to
|
||||
## build a model of a password guesser.
|
||||
const guessing_timeout = 30 mins &redef;
|
||||
|
||||
## This value can be used to exclude hosts or entire networks from being
|
||||
|
|
|
@ -7,14 +7,15 @@ module SSH;
|
|||
|
||||
export {
|
||||
redef enum Notice::Type += {
|
||||
## If an SSH login is seen to or from a "watched" country based on the
|
||||
## :bro:id:`SSH::watched_countries` variable then this notice will
|
||||
## be generated.
|
||||
## If an SSH login is seen to or from a "watched" country based
|
||||
## on the :bro:id:`SSH::watched_countries` variable then this
|
||||
## notice will be generated.
|
||||
Watched_Country_Login,
|
||||
};
|
||||
|
||||
redef record Info += {
|
||||
## Add geographic data related to the "remote" host of the connection.
|
||||
## Add geographic data related to the "remote" host of the
|
||||
## connection.
|
||||
remote_location: geo_location &log &optional;
|
||||
};
|
||||
|
||||
|
@ -23,21 +24,29 @@ export {
|
|||
const watched_countries: set[string] = {"RO"} &redef;
|
||||
}
|
||||
|
||||
function get_location(c: connection): geo_location
|
||||
{
|
||||
local lookup_ip = (c$ssh$direction == OUTBOUND) ? c$id$resp_h : c$id$orig_h;
|
||||
return lookup_location(lookup_ip);
|
||||
}
|
||||
|
||||
event SSH::heuristic_successful_login(c: connection) &priority=5
|
||||
{
|
||||
local location: geo_location;
|
||||
location = (c$ssh$direction == OUTBOUND) ?
|
||||
lookup_location(c$id$resp_h) : lookup_location(c$id$orig_h);
|
||||
|
||||
# Add the location data to the SSH record.
|
||||
c$ssh$remote_location = location;
|
||||
c$ssh$remote_location = get_location(c);
|
||||
|
||||
if ( location?$country_code && location$country_code in watched_countries )
|
||||
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",
|
||||
location$country_code)]);
|
||||
c$ssh$remote_location$country_code)]);
|
||||
}
|
||||
}
|
||||
|
||||
event SSH::heuristic_failed_login(c: connection) &priority=5
|
||||
{
|
||||
# Add the location data to the SSH record.
|
||||
c$ssh$remote_location = get_location(c);
|
||||
}
|
||||
|
|
|
@ -10,8 +10,8 @@ module SSH;
|
|||
|
||||
export {
|
||||
redef enum Notice::Type += {
|
||||
## Generated if a login originates or responds with a host where the
|
||||
## reverse hostname lookup resolves to a name matched by the
|
||||
## Generated if a login originates or responds with a host where
|
||||
## the reverse hostname lookup resolves to a name matched by the
|
||||
## :bro:id:`SSH::interesting_hostnames` regular expression.
|
||||
Interesting_Hostname_Login,
|
||||
};
|
||||
|
|
|
@ -1,22 +0,0 @@
|
|||
##! Calculate MD5 sums for server DER formatted certificates.
|
||||
|
||||
@load base/protocols/ssl
|
||||
|
||||
module SSL;
|
||||
|
||||
export {
|
||||
redef record Info += {
|
||||
## MD5 sum of the raw server certificate.
|
||||
cert_hash: string &log &optional;
|
||||
};
|
||||
}
|
||||
|
||||
event x509_certificate(c: connection, is_orig: bool, cert: X509, chain_idx: count, chain_len: count, der_cert: string) &priority=4
|
||||
{
|
||||
# We aren't tracking client certificates yet and we are also only tracking
|
||||
# the primary cert. Watch that this came from an SSL analyzed session too.
|
||||
if ( is_orig || chain_idx != 0 || ! c?$ssl )
|
||||
return;
|
||||
|
||||
c$ssl$cert_hash = md5_hash(der_cert);
|
||||
}
|
|
@ -3,22 +3,22 @@
|
|||
##! certificate.
|
||||
|
||||
@load base/protocols/ssl
|
||||
@load base/files/x509
|
||||
@load base/frameworks/notice
|
||||
@load base/utils/directions-and-hosts
|
||||
|
||||
@load protocols/ssl/cert-hash
|
||||
|
||||
module SSL;
|
||||
|
||||
export {
|
||||
redef enum Notice::Type += {
|
||||
## Indicates that a certificate's NotValidAfter date has lapsed and
|
||||
## the certificate is now invalid.
|
||||
## Indicates that a certificate's NotValidAfter date has lapsed
|
||||
## and the certificate is now invalid.
|
||||
Certificate_Expired,
|
||||
## Indicates that a certificate is going to expire within
|
||||
## :bro:id:`SSL::notify_when_cert_expiring_in`.
|
||||
Certificate_Expires_Soon,
|
||||
## Indicates that a certificate's NotValidBefore date is future dated.
|
||||
## Indicates that a certificate's NotValidBefore date is future
|
||||
## dated.
|
||||
Certificate_Not_Valid_Yet,
|
||||
};
|
||||
|
||||
|
@ -29,35 +29,41 @@ export {
|
|||
## Choices are: LOCAL_HOSTS, REMOTE_HOSTS, ALL_HOSTS, NO_HOSTS
|
||||
const notify_certs_expiration = LOCAL_HOSTS &redef;
|
||||
|
||||
## The time before a certificate is going to expire that you would like to
|
||||
## start receiving :bro:enum:`SSL::Certificate_Expires_Soon` notices.
|
||||
## The time before a certificate is going to expire that you would like
|
||||
## to start receiving :bro:enum:`SSL::Certificate_Expires_Soon` notices.
|
||||
const notify_when_cert_expiring_in = 30days &redef;
|
||||
}
|
||||
|
||||
event x509_certificate(c: connection, is_orig: bool, cert: X509, chain_idx: count, chain_len: count, der_cert: string) &priority=3
|
||||
event ssl_established(c: connection) &priority=3
|
||||
{
|
||||
# If this isn't the host cert or we aren't interested in the server, just return.
|
||||
if ( is_orig ||
|
||||
chain_idx != 0 ||
|
||||
! c$ssl?$cert_hash ||
|
||||
! addr_matches_host(c$id$resp_h, notify_certs_expiration) )
|
||||
# If there are no certificates or we are not interested in the server, just return.
|
||||
if ( ! c$ssl?$cert_chain || |c$ssl$cert_chain| == 0 ||
|
||||
! addr_matches_host(c$id$resp_h, notify_certs_expiration) ||
|
||||
! c$ssl$cert_chain[0]?$x509 || ! c$ssl$cert_chain[0]?$sha1 )
|
||||
return;
|
||||
|
||||
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$not_valid_before > network_time() )
|
||||
NOTICE([$note=Certificate_Not_Valid_Yet,
|
||||
$conn=c, $suppress_for=1day,
|
||||
$msg=fmt("Certificate %s isn't valid until %T", cert$subject, cert$not_valid_before),
|
||||
$identifier=cat(c$id$resp_h, c$id$resp_p, c$ssl$cert_hash)]);
|
||||
$identifier=cat(c$id$resp_h, c$id$resp_p, hash),
|
||||
$fuid=fuid]);
|
||||
|
||||
else if ( cert$not_valid_after < network_time() )
|
||||
NOTICE([$note=Certificate_Expired,
|
||||
$conn=c, $suppress_for=1day,
|
||||
$msg=fmt("Certificate %s expired at %T", cert$subject, cert$not_valid_after),
|
||||
$identifier=cat(c$id$resp_h, c$id$resp_p, c$ssl$cert_hash)]);
|
||||
$identifier=cat(c$id$resp_h, c$id$resp_p, hash),
|
||||
$fuid=fuid]);
|
||||
|
||||
else if ( cert$not_valid_after - notify_when_cert_expiring_in < network_time() )
|
||||
NOTICE([$note=Certificate_Expires_Soon,
|
||||
$msg=fmt("Certificate %s is going to expire at %T", cert$subject, cert$not_valid_after),
|
||||
$conn=c, $suppress_for=1day,
|
||||
$identifier=cat(c$id$resp_h, c$id$resp_p, c$ssl$cert_hash)]);
|
||||
$identifier=cat(c$id$resp_h, c$id$resp_p, hash),
|
||||
$fuid=fuid]);
|
||||
}
|
||||
|
|
|
@ -2,62 +2,53 @@
|
|||
##! after being converted to PEM files. The certificates will be stored in
|
||||
##! a single file, one for local certificates and one for remote certificates.
|
||||
##!
|
||||
##! ..note::
|
||||
##! .. note::
|
||||
##!
|
||||
##! - It doesn't work well on a cluster because each worker will write its
|
||||
##! own certificate files and no duplicate checking is done across
|
||||
##! clusters so each node would log each certificate.
|
||||
##! own certificate files and no duplicate checking is done across the
|
||||
##! cluster so each node would log each certificate.
|
||||
##!
|
||||
|
||||
@load base/protocols/ssl
|
||||
@load base/files/x509
|
||||
@load base/utils/directions-and-hosts
|
||||
@load protocols/ssl/cert-hash
|
||||
|
||||
module SSL;
|
||||
|
||||
export {
|
||||
## Control if host certificates offered by the defined hosts
|
||||
## will be written to the PEM certificates file.
|
||||
## Choices are: LOCAL_HOSTS, REMOTE_HOSTS, ALL_HOSTS, NO_HOSTS
|
||||
## Choices are: LOCAL_HOSTS, REMOTE_HOSTS, ALL_HOSTS, NO_HOSTS.
|
||||
const extract_certs_pem = LOCAL_HOSTS &redef;
|
||||
}
|
||||
|
||||
# This is an internally maintained variable to prevent relogging of
|
||||
# certificates that have already been seen. It is indexed on an md5 sum of
|
||||
# certificates that have already been seen. It is indexed on an sha1 sum of
|
||||
# the certificate.
|
||||
global extracted_certs: set[string] = set() &read_expire=1hr &redef;
|
||||
|
||||
event ssl_established(c: connection) &priority=5
|
||||
{
|
||||
if ( ! c$ssl?$cert )
|
||||
if ( ! c$ssl?$cert_chain || |c$ssl$cert_chain| == 0 ||
|
||||
! c$ssl$cert_chain[0]?$x509 )
|
||||
return;
|
||||
|
||||
if ( ! addr_matches_host(c$id$resp_h, extract_certs_pem) )
|
||||
return;
|
||||
|
||||
if ( c$ssl$cert_hash in extracted_certs )
|
||||
local hash = c$ssl$cert_chain[0]$sha1;
|
||||
local cert = c$ssl$cert_chain[0]$x509$handle;
|
||||
|
||||
if ( hash in extracted_certs )
|
||||
# If we already extracted this cert, don't do it again.
|
||||
return;
|
||||
|
||||
add extracted_certs[c$ssl$cert_hash];
|
||||
add extracted_certs[hash];
|
||||
local filename = Site::is_local_addr(c$id$resp_h) ? "certs-local.pem" : "certs-remote.pem";
|
||||
local outfile = open_for_append(filename);
|
||||
enable_raw_output(outfile);
|
||||
|
||||
print outfile, "-----BEGIN CERTIFICATE-----";
|
||||
print outfile, x509_get_certificate_string(cert, T);
|
||||
|
||||
# Encode to base64 and format to fit 50 lines. Otherwise openssl won't like it later.
|
||||
local lines = split_all(encode_base64(c$ssl$cert), /.{50}/);
|
||||
local i = 1;
|
||||
for ( line in lines )
|
||||
{
|
||||
if ( |lines[i]| > 0 )
|
||||
{
|
||||
print outfile, lines[i];
|
||||
}
|
||||
i+=1;
|
||||
}
|
||||
|
||||
print outfile, "-----END CERTIFICATE-----";
|
||||
print outfile, "";
|
||||
close(outfile);
|
||||
}
|
||||
|
|
238
scripts/policy/protocols/ssl/heartbleed.bro
Normal file
238
scripts/policy/protocols/ssl/heartbleed.bro
Normal file
|
@ -0,0 +1,238 @@
|
|||
##! Detect the TLS heartbleed attack. See http://heartbleed.com for more.
|
||||
|
||||
@load base/protocols/ssl
|
||||
@load base/frameworks/notice
|
||||
|
||||
module Heartbleed;
|
||||
|
||||
export {
|
||||
redef enum Notice::Type += {
|
||||
## Indicates that a host performed a heartbleed attack or scan.
|
||||
SSL_Heartbeat_Attack,
|
||||
## Indicates that a host performing a heartbleed attack was probably successful.
|
||||
SSL_Heartbeat_Attack_Success,
|
||||
## Indicates we saw heartbeat requests with odd length. Probably an attack or scan.
|
||||
SSL_Heartbeat_Odd_Length,
|
||||
## Indicates we saw many heartbeat requests without an reply. Might be an attack.
|
||||
SSL_Heartbeat_Many_Requests
|
||||
};
|
||||
}
|
||||
|
||||
# Do not disable analyzers after detection - otherwhise we will not notice
|
||||
# encrypted attacks.
|
||||
redef SSL::disable_analyzer_after_detection=F;
|
||||
|
||||
redef record SSL::Info += {
|
||||
last_originator_heartbeat_request_size: count &optional;
|
||||
last_responder_heartbeat_request_size: count &optional;
|
||||
|
||||
originator_heartbeats: count &default=0;
|
||||
responder_heartbeats: count &default=0;
|
||||
|
||||
# Unencrypted connections - was an exploit attempt detected yet.
|
||||
heartbleed_detected: bool &default=F;
|
||||
|
||||
# Count number of appdata packages and bytes exchanged so far.
|
||||
enc_appdata_packages: count &default=0;
|
||||
enc_appdata_bytes: count &default=0;
|
||||
};
|
||||
|
||||
type min_length: record {
|
||||
cipher: pattern;
|
||||
min_length: count;
|
||||
};
|
||||
|
||||
global min_lengths: vector of min_length = vector();
|
||||
global min_lengths_tls11: vector of min_length = vector();
|
||||
|
||||
event bro_init()
|
||||
{
|
||||
# Minimum length a heartbeat packet must have for different cipher suites.
|
||||
# Note - tls 1.1f and 1.0 have different lengths :(
|
||||
# This should be all cipher suites usually supported by vulnerable servers.
|
||||
min_lengths_tls11[|min_lengths_tls11|] = [$cipher=/_AES_256_GCM_SHA384$/, $min_length=43];
|
||||
min_lengths_tls11[|min_lengths_tls11|] = [$cipher=/_AES_128_GCM_SHA256$/, $min_length=43];
|
||||
min_lengths_tls11[|min_lengths_tls11|] = [$cipher=/_256_CBC_SHA384$/, $min_length=96];
|
||||
min_lengths_tls11[|min_lengths_tls11|] = [$cipher=/_256_CBC_SHA256$/, $min_length=80];
|
||||
min_lengths_tls11[|min_lengths_tls11|] = [$cipher=/_256_CBC_SHA$/, $min_length=64];
|
||||
min_lengths_tls11[|min_lengths_tls11|] = [$cipher=/_128_CBC_SHA256$/, $min_length=80];
|
||||
min_lengths_tls11[|min_lengths_tls11|] = [$cipher=/_128_CBC_SHA$/, $min_length=64];
|
||||
min_lengths_tls11[|min_lengths_tls11|] = [$cipher=/_3DES_EDE_CBC_SHA$/, $min_length=48];
|
||||
min_lengths_tls11[|min_lengths_tls11|] = [$cipher=/_SEED_CBC_SHA$/, $min_length=64];
|
||||
min_lengths_tls11[|min_lengths_tls11|] = [$cipher=/_IDEA_CBC_SHA$/, $min_length=48];
|
||||
min_lengths_tls11[|min_lengths_tls11|] = [$cipher=/_DES_CBC_SHA$/, $min_length=48];
|
||||
min_lengths_tls11[|min_lengths_tls11|] = [$cipher=/_DES40_CBC_SHA$/, $min_length=48];
|
||||
min_lengths_tls11[|min_lengths_tls11|] = [$cipher=/_RC4_128_SHA$/, $min_length=39];
|
||||
min_lengths_tls11[|min_lengths_tls11|] = [$cipher=/_RC4_128_MD5$/, $min_length=35];
|
||||
min_lengths_tls11[|min_lengths_tls11|] = [$cipher=/_RC4_40_MD5$/, $min_length=35];
|
||||
min_lengths_tls11[|min_lengths_tls11|] = [$cipher=/_RC2_CBC_40_MD5$/, $min_length=48];
|
||||
min_lengths[|min_lengths|] = [$cipher=/_256_CBC_SHA$/, $min_length=48];
|
||||
min_lengths[|min_lengths|] = [$cipher=/_128_CBC_SHA$/, $min_length=48];
|
||||
min_lengths[|min_lengths|] = [$cipher=/_3DES_EDE_CBC_SHA$/, $min_length=40];
|
||||
min_lengths[|min_lengths|] = [$cipher=/_SEED_CBC_SHA$/, $min_length=48];
|
||||
min_lengths[|min_lengths|] = [$cipher=/_IDEA_CBC_SHA$/, $min_length=40];
|
||||
min_lengths[|min_lengths|] = [$cipher=/_DES_CBC_SHA$/, $min_length=40];
|
||||
min_lengths[|min_lengths|] = [$cipher=/_DES40_CBC_SHA$/, $min_length=40];
|
||||
min_lengths[|min_lengths|] = [$cipher=/_RC4_128_SHA$/, $min_length=39];
|
||||
min_lengths[|min_lengths|] = [$cipher=/_RC4_128_MD5$/, $min_length=35];
|
||||
min_lengths[|min_lengths|] = [$cipher=/_RC4_40_MD5$/, $min_length=35];
|
||||
min_lengths[|min_lengths|] = [$cipher=/_RC2_CBC_40_MD5$/, $min_length=40];
|
||||
}
|
||||
|
||||
event ssl_heartbeat(c: connection, is_orig: bool, length: count, heartbeat_type: count, payload_length: count, payload: string)
|
||||
{
|
||||
if ( ! c?$ssl )
|
||||
return;
|
||||
|
||||
if ( heartbeat_type == 1 )
|
||||
{
|
||||
local checklength: count = (length<(3+16)) ? length : (length - 3 - 16);
|
||||
|
||||
if ( payload_length > checklength )
|
||||
{
|
||||
c$ssl$heartbleed_detected = T;
|
||||
NOTICE([$note=Heartbleed::SSL_Heartbeat_Attack,
|
||||
$msg=fmt("An TLS heartbleed attack was detected! Record length %d. Payload length %d", length, payload_length),
|
||||
$conn=c,
|
||||
$identifier=cat(c$uid, length, payload_length)
|
||||
]);
|
||||
}
|
||||
else if ( is_orig )
|
||||
{
|
||||
NOTICE([$note=Heartbleed::SSL_Heartbeat_Attack,
|
||||
$msg=fmt("Heartbeat request before encryption. Probable Scan without exploit attempt. Message length: %d. Payload length: %d", length, payload_length),
|
||||
$conn=c,
|
||||
$n=length,
|
||||
$identifier=cat(c$uid, length)
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
if ( heartbeat_type == 2 && c$ssl$heartbleed_detected )
|
||||
{
|
||||
NOTICE([$note=Heartbleed::SSL_Heartbeat_Attack_Success,
|
||||
$msg=fmt("An TLS heartbleed attack detected before was probably exploited. Message length: %d. Payload length: %d", length, payload_length),
|
||||
$conn=c,
|
||||
$identifier=c$uid
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
event ssl_encrypted_heartbeat(c: connection, is_orig: bool, length: count)
|
||||
{
|
||||
if ( is_orig )
|
||||
++c$ssl$originator_heartbeats;
|
||||
else
|
||||
++c$ssl$responder_heartbeats;
|
||||
|
||||
local duration = network_time() - c$start_time;
|
||||
|
||||
if ( c$ssl$enc_appdata_packages == 0 )
|
||||
NOTICE([$note=SSL_Heartbeat_Attack,
|
||||
$msg=fmt("Heartbeat before ciphertext. Probable attack or scan. Length: %d, is_orig: %d", length, is_orig),
|
||||
$conn=c,
|
||||
$n=length,
|
||||
$identifier=fmt("%s%s", c$uid, "early")
|
||||
]);
|
||||
else if ( duration < 1min )
|
||||
NOTICE([$note=SSL_Heartbeat_Attack,
|
||||
$msg=fmt("Heartbeat within first minute. Possible attack or scan. Length: %d, is_orig: %d, time: %s", length, is_orig, duration),
|
||||
$conn=c,
|
||||
$n=length,
|
||||
$identifier=fmt("%s%s", c$uid, "early")
|
||||
]);
|
||||
|
||||
if ( c$ssl$originator_heartbeats > c$ssl$responder_heartbeats + 3 )
|
||||
NOTICE([$note=SSL_Heartbeat_Many_Requests,
|
||||
$msg=fmt("More than 3 heartbeat requests without replies from server. Possible attack. Client count: %d, server count: %d", c$ssl$originator_heartbeats, c$ssl$responder_heartbeats),
|
||||
$conn=c,
|
||||
$n=(c$ssl$originator_heartbeats-c$ssl$responder_heartbeats),
|
||||
$identifier=fmt("%s%d", c$uid, c$ssl$responder_heartbeats/1000) # re-throw every 1000 heartbeats
|
||||
]);
|
||||
|
||||
if ( c$ssl$responder_heartbeats > c$ssl$originator_heartbeats + 3 )
|
||||
NOTICE([$note=SSL_Heartbeat_Many_Requests,
|
||||
$msg=fmt("Server sending more heartbeat responses than requests seen. Possible attack. Client count: %d, server count: %d", c$ssl$originator_heartbeats, c$ssl$responder_heartbeats),
|
||||
$conn=c,
|
||||
$n=(c$ssl$originator_heartbeats-c$ssl$responder_heartbeats),
|
||||
$identifier=fmt("%s%d", c$uid, c$ssl$responder_heartbeats/1000) # re-throw every 1000 heartbeats
|
||||
]);
|
||||
|
||||
if ( is_orig && length < 19 )
|
||||
NOTICE([$note=SSL_Heartbeat_Odd_Length,
|
||||
$msg=fmt("Heartbeat message smaller than minimum required length. Probable attack or scan. Message length: %d. Cipher: %s. Time: %f", length, c$ssl$cipher, duration),
|
||||
$conn=c,
|
||||
$n=length,
|
||||
$identifier=fmt("%s-weak-%d", c$uid, length)
|
||||
]);
|
||||
|
||||
# Examine request lengths based on used cipher...
|
||||
local min_length_choice: vector of min_length;
|
||||
if ( (c$ssl$version == "TLSv11") || (c$ssl$version == "TLSv12") ) # tls 1.1+ have different lengths for CBC
|
||||
min_length_choice = min_lengths_tls11;
|
||||
else
|
||||
min_length_choice = min_lengths;
|
||||
|
||||
for ( i in min_length_choice )
|
||||
{
|
||||
if ( min_length_choice[i]$cipher in c$ssl$cipher )
|
||||
{
|
||||
if ( length < min_length_choice[i]$min_length )
|
||||
{
|
||||
NOTICE([$note=SSL_Heartbeat_Odd_Length,
|
||||
$msg=fmt("Heartbeat message smaller than minimum required length. Probable attack. Message length: %d. Required length: %d. Cipher: %s. Cipher match: %s", length, min_length_choice[i]$min_length, c$ssl$cipher, min_length_choice[i]$cipher),
|
||||
$conn=c,
|
||||
$n=length,
|
||||
$identifier=fmt("%s-weak-%d", c$uid, length)
|
||||
]);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if ( is_orig )
|
||||
{
|
||||
if ( c$ssl?$last_responder_heartbeat_request_size )
|
||||
{
|
||||
# server originated heartbeat. Ignore & continue
|
||||
delete c$ssl$last_responder_heartbeat_request_size;
|
||||
}
|
||||
|
||||
else
|
||||
c$ssl$last_originator_heartbeat_request_size = length;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( c$ssl?$last_originator_heartbeat_request_size && c$ssl$last_originator_heartbeat_request_size < length )
|
||||
{
|
||||
NOTICE([$note=SSL_Heartbeat_Attack_Success,
|
||||
$msg=fmt("An encrypted TLS heartbleed attack was probably detected! First packet client record length %d, first packet server record length %d. Time: %f",
|
||||
c$ssl$last_originator_heartbeat_request_size, length, duration),
|
||||
$conn=c,
|
||||
$identifier=c$uid # only throw once per connection
|
||||
]);
|
||||
}
|
||||
|
||||
else if ( ! c$ssl?$last_originator_heartbeat_request_size )
|
||||
c$ssl$last_responder_heartbeat_request_size = length;
|
||||
|
||||
if ( c$ssl?$last_originator_heartbeat_request_size )
|
||||
delete c$ssl$last_originator_heartbeat_request_size;
|
||||
}
|
||||
}
|
||||
|
||||
event ssl_encrypted_data(c: connection, is_orig: bool, content_type: count, length: count)
|
||||
{
|
||||
if ( !c?$ssl )
|
||||
return;
|
||||
|
||||
if ( content_type == SSL::HEARTBEAT )
|
||||
event ssl_encrypted_heartbeat(c, is_orig, length);
|
||||
else if ( (content_type == SSL::APPLICATION_DATA) && (length > 0) )
|
||||
{
|
||||
++c$ssl$enc_appdata_packages;
|
||||
c$ssl$enc_appdata_bytes += length;
|
||||
}
|
||||
}
|
|
@ -1,8 +1,9 @@
|
|||
##! Log information about certificates while attempting to avoid duplicate logging.
|
||||
##! Log information about certificates while attempting to avoid duplicate
|
||||
##! logging.
|
||||
|
||||
@load base/utils/directions-and-hosts
|
||||
@load base/protocols/ssl
|
||||
@load protocols/ssl/cert-hash
|
||||
@load base/files/x509
|
||||
|
||||
module Known;
|
||||
|
||||
|
@ -26,16 +27,16 @@ export {
|
|||
};
|
||||
|
||||
## The certificates whose existence should be logged and tracked.
|
||||
## Choices are: LOCAL_HOSTS, REMOTE_HOSTS, ALL_HOSTS, NO_HOSTS
|
||||
## Choices are: LOCAL_HOSTS, REMOTE_HOSTS, ALL_HOSTS, NO_HOSTS.
|
||||
const cert_tracking = LOCAL_HOSTS &redef;
|
||||
|
||||
## The set of all known certificates to store for preventing duplicate
|
||||
## logging. It can also be used from other scripts to
|
||||
## logging. It can also be used from other scripts to
|
||||
## inspect if a certificate has been seen in use. The string value
|
||||
## in the set is for storing the DER formatted certificate's MD5 hash.
|
||||
## in the set is for storing the DER formatted certificate' SHA1 hash.
|
||||
global certs: set[addr, string] &create_expire=1day &synchronized &redef;
|
||||
|
||||
## Event that can be handled to access the loggable record as it is sent
|
||||
## Event that can be handled to access the loggable record as it is sent
|
||||
## on to the logging framework.
|
||||
global log_known_certs: event(rec: CertsInfo);
|
||||
}
|
||||
|
@ -45,16 +46,28 @@ event bro_init() &priority=5
|
|||
Log::create_stream(Known::CERTS_LOG, [$columns=CertsInfo, $ev=log_known_certs]);
|
||||
}
|
||||
|
||||
event x509_certificate(c: connection, is_orig: bool, cert: X509, chain_idx: count, chain_len: count, der_cert: string) &priority=3
|
||||
event ssl_established(c: connection) &priority=3
|
||||
{
|
||||
# Make sure this is the server cert and we have a hash for it.
|
||||
if ( is_orig || chain_idx != 0 || ! c$ssl?$cert_hash )
|
||||
if ( ! c$ssl?$cert_chain || |c$ssl$cert_chain| < 1 ||
|
||||
! c$ssl$cert_chain[0]?$x509 )
|
||||
return;
|
||||
|
||||
local host = c$id$resp_h;
|
||||
if ( [host, c$ssl$cert_hash] !in certs && addr_matches_host(host, cert_tracking) )
|
||||
|
||||
local fuid = c$ssl$cert_chain_fuids[0];
|
||||
|
||||
if ( ! c$ssl$cert_chain[0]?$sha1 )
|
||||
{
|
||||
add certs[host, c$ssl$cert_hash];
|
||||
Reporter::error(fmt("Certificate with fuid %s did not contain sha1 hash when checking for known certs. Aborting",
|
||||
fuid));
|
||||
return;
|
||||
}
|
||||
|
||||
local hash = c$ssl$cert_chain[0]$sha1;
|
||||
local cert = c$ssl$cert_chain[0]$x509$certificate;
|
||||
|
||||
local host = c$id$resp_h;
|
||||
if ( [host, hash] !in certs && addr_matches_host(host, cert_tracking) )
|
||||
{
|
||||
add certs[host, hash];
|
||||
Log::write(Known::CERTS_LOG, [$ts=network_time(), $host=host,
|
||||
$port_num=c$id$resp_p, $subject=cert$subject,
|
||||
$issuer_subject=cert$issuer,
|
||||
|
|
68
scripts/policy/protocols/ssl/log-hostcerts-only.bro
Normal file
68
scripts/policy/protocols/ssl/log-hostcerts-only.bro
Normal file
|
@ -0,0 +1,68 @@
|
|||
##! When this script is loaded, only the host certificates (client and server)
|
||||
##! will be logged to x509.log. Logging of all other certificates will be suppressed.
|
||||
|
||||
@load base/protocols/ssl
|
||||
@load base/files/x509
|
||||
|
||||
module X509;
|
||||
|
||||
export {
|
||||
redef record Info += {
|
||||
# Logging is suppressed if field is set to F
|
||||
logcert: bool &default=T;
|
||||
};
|
||||
}
|
||||
|
||||
# We need both the Info and the fa_file record modified.
|
||||
# The only instant when we have both, the connection and the
|
||||
# file available without having to loop is in the file_over_new_connection
|
||||
# event.
|
||||
# When that event is raised, the x509 record in f$info (which is the only
|
||||
# record the logging framework gets) is not yet available. So - we
|
||||
# have to do this two times, sorry.
|
||||
# Alternatively, we could place it info Files::Info first - but we would
|
||||
# still have to copy it.
|
||||
redef record fa_file += {
|
||||
logcert: bool &default=T;
|
||||
};
|
||||
|
||||
function host_certs_only(rec: X509::Info): bool
|
||||
{
|
||||
return rec$logcert;
|
||||
}
|
||||
|
||||
event bro_init() &priority=2
|
||||
{
|
||||
local f = Log::get_filter(X509::LOG, "default");
|
||||
Log::remove_filter(X509::LOG, "default"); # disable default logging
|
||||
f$pred=host_certs_only; # and add our predicate
|
||||
Log::add_filter(X509::LOG, f);
|
||||
}
|
||||
|
||||
event file_over_new_connection(f: fa_file, c: connection, is_orig: bool) &priority=2
|
||||
{
|
||||
if ( ! c?$ssl )
|
||||
return;
|
||||
|
||||
local chain: vector of string;
|
||||
|
||||
if ( is_orig )
|
||||
chain = c$ssl$client_cert_chain_fuids;
|
||||
else
|
||||
chain = c$ssl$cert_chain_fuids;
|
||||
|
||||
if ( |chain| == 0 )
|
||||
{
|
||||
Reporter::warning(fmt("Certificate not in chain? (fuid %s)", f$id));
|
||||
return;
|
||||
}
|
||||
|
||||
# Check if this is the host certificate
|
||||
if ( f$id != chain[0] )
|
||||
f$logcert=F;
|
||||
}
|
||||
|
||||
event x509_certificate(f: fa_file, cert_ref: opaque of x509, cert: X509::Certificate) &priority=2
|
||||
{
|
||||
f$info$x509$logcert = f$logcert; # info record available, copy information.
|
||||
}
|
|
@ -16,7 +16,6 @@ export {
|
|||
}
|
||||
|
||||
redef record SSL::Info += {
|
||||
sha1: string &log &optional;
|
||||
notary: Response &log &optional;
|
||||
};
|
||||
|
||||
|
@ -38,14 +37,13 @@ function clear_waitlist(digest: string)
|
|||
}
|
||||
}
|
||||
|
||||
event x509_certificate(c: connection, is_orig: bool, cert: X509,
|
||||
chain_idx: count, chain_len: count, der_cert: string)
|
||||
event ssl_established(c: connection) &priority=3
|
||||
{
|
||||
if ( is_orig || chain_idx != 0 || ! c?$ssl )
|
||||
if ( ! c$ssl?$cert_chain || |c$ssl$cert_chain| == 0 ||
|
||||
! c$ssl$cert_chain[0]?$sha1 )
|
||||
return;
|
||||
|
||||
local digest = sha1_hash(der_cert);
|
||||
c$ssl$sha1 = digest;
|
||||
local digest = c$ssl$cert_chain[0]$sha1;
|
||||
|
||||
if ( digest in notary_cache )
|
||||
{
|
||||
|
|
|
@ -2,14 +2,14 @@
|
|||
|
||||
@load base/frameworks/notice
|
||||
@load base/protocols/ssl
|
||||
@load protocols/ssl/cert-hash
|
||||
|
||||
module SSL;
|
||||
|
||||
export {
|
||||
redef enum Notice::Type += {
|
||||
## This notice indicates that the result of validating the certificate
|
||||
## along with it's full certificate chain was invalid.
|
||||
## This notice indicates that the result of validating the
|
||||
## certificate along with its full certificate chain was
|
||||
## invalid.
|
||||
Invalid_Server_Cert
|
||||
};
|
||||
|
||||
|
@ -18,9 +18,9 @@ export {
|
|||
validation_status: string &log &optional;
|
||||
};
|
||||
|
||||
## MD5 hash values for recently validated certs along with the validation
|
||||
## status message are kept in this table to avoid constant validation
|
||||
## everytime the same certificate is seen.
|
||||
## MD5 hash values for recently validated chains along with the
|
||||
## validation status message 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;
|
||||
}
|
||||
|
@ -28,25 +28,36 @@ export {
|
|||
event ssl_established(c: connection) &priority=3
|
||||
{
|
||||
# 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_chain || |c$ssl$cert_chain| == 0 ||
|
||||
! c$ssl$cert_chain[0]?$x509 )
|
||||
return;
|
||||
|
||||
if ( c$ssl?$cert_hash && c$ssl$cert_hash in recently_validated_certs )
|
||||
|
||||
local chain_id = join_string_vec(c$ssl$cert_chain_fuids, ".");
|
||||
|
||||
local chain: vector of opaque of x509 = vector();
|
||||
for ( i in c$ssl$cert_chain )
|
||||
{
|
||||
c$ssl$validation_status = recently_validated_certs[c$ssl$cert_hash];
|
||||
if ( c$ssl$cert_chain[i]?$x509 )
|
||||
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(c$ssl$cert, c$ssl$cert_chain, root_certs);
|
||||
c$ssl$validation_status = x509_err2str(result);
|
||||
local result = x509_verify(chain, root_certs);
|
||||
c$ssl$validation_status = result$result_string;
|
||||
recently_validated_certs[chain_id] = result$result_string;
|
||||
}
|
||||
|
||||
|
||||
if ( c$ssl$validation_status != "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,c$ssl$cert_hash)]);
|
||||
$identifier=cat(c$id$resp_h,c$id$resp_p,c$ssl$validation_status)]);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
66
scripts/policy/protocols/ssl/validate-ocsp.bro
Normal file
66
scripts/policy/protocols/ssl/validate-ocsp.bro
Normal file
|
@ -0,0 +1,66 @@
|
|||
##! Perform OCSP response validation.
|
||||
|
||||
@load base/frameworks/notice
|
||||
@load base/protocols/ssl
|
||||
|
||||
module SSL;
|
||||
|
||||
export {
|
||||
redef enum Notice::Type += {
|
||||
## This indicates that the OCSP response was not deemed
|
||||
## to be valid.
|
||||
Invalid_Ocsp_Response
|
||||
};
|
||||
|
||||
redef record Info += {
|
||||
## Result of ocsp validation for this connection.
|
||||
ocsp_status: string &log &optional;
|
||||
|
||||
## ocsp response as string.
|
||||
ocsp_response: string &optional;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
# MD5 hash values for recently validated chains along with the OCSP validation
|
||||
# status are kept in this table to avoid constant validation every time the same
|
||||
# certificate chain is seen.
|
||||
global recently_ocsp_validated: table[string] of string = table() &read_expire=5mins;
|
||||
|
||||
event ssl_stapled_ocsp(c: connection, is_orig: bool, response: string) &priority=3
|
||||
{
|
||||
c$ssl$ocsp_response = response;
|
||||
}
|
||||
|
||||
event ssl_established(c: connection) &priority=3
|
||||
{
|
||||
if ( ! c$ssl?$cert_chain || |c$ssl$cert_chain| == 0 || !c$ssl?$ocsp_response )
|
||||
return;
|
||||
|
||||
local chain: vector of opaque of x509 = vector();
|
||||
for ( i in c$ssl$cert_chain )
|
||||
{
|
||||
if ( c$ssl$cert_chain[i]?$x509 )
|
||||
chain[i] = c$ssl$cert_chain[i]$x509$handle;
|
||||
}
|
||||
|
||||
local reply_id = cat(md5_hash(c$ssl$ocsp_response), join_string_vec(c$ssl$cert_chain_fuids, "."));
|
||||
|
||||
if ( reply_id in recently_ocsp_validated )
|
||||
{
|
||||
c$ssl$ocsp_status = recently_ocsp_validated[reply_id];
|
||||
return;
|
||||
}
|
||||
|
||||
local result = x509_ocsp_verify(chain, c$ssl$ocsp_response, root_certs);
|
||||
c$ssl$ocsp_status = result$result_string;
|
||||
recently_ocsp_validated[reply_id] = result$result_string;
|
||||
|
||||
if( result$result_string != "good" )
|
||||
{
|
||||
local message = fmt("OCSP response validation failed with (%s)", result$result_string);
|
||||
NOTICE([$note=Invalid_Ocsp_Response, $msg=message,
|
||||
$sub=c$ssl$subject, $conn=c,
|
||||
$identifier=cat(c$id$resp_h,c$id$resp_p,c$ssl$ocsp_status)]);
|
||||
}
|
||||
}
|
92
scripts/policy/protocols/ssl/weak-keys.bro
Normal file
92
scripts/policy/protocols/ssl/weak-keys.bro
Normal file
|
@ -0,0 +1,92 @@
|
|||
##! Generate notices when SSL/TLS connections use certificates or DH parameters
|
||||
##! that have potentially unsafe key lengths.
|
||||
|
||||
@load base/protocols/ssl
|
||||
@load base/frameworks/notice
|
||||
@load base/utils/directions-and-hosts
|
||||
|
||||
module SSL;
|
||||
|
||||
export {
|
||||
redef enum Notice::Type += {
|
||||
## Indicates that a server is using a potentially unsafe key.
|
||||
Weak_Key,
|
||||
};
|
||||
|
||||
## 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
|
||||
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.
|
||||
const notify_minimal_key_length = 2048 &redef;
|
||||
|
||||
## Warn if the DH key length is smaller than the certificate key length. This is
|
||||
## potentially unsafe because it gives a wrong impression of safety due to the
|
||||
## 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;
|
||||
}
|
||||
|
||||
# We check key lengths only for DSA or RSA certificates. For others, we do
|
||||
# not know what is safe (e.g. EC is safe even with very short key lengths).
|
||||
event ssl_established(c: connection) &priority=3
|
||||
{
|
||||
# If there are no certificates or we are not interested in the server, just return.
|
||||
if ( ! c$ssl?$cert_chain || |c$ssl$cert_chain| == 0 ||
|
||||
! addr_matches_host(c$id$resp_h, notify_weak_keys) ||
|
||||
! c$ssl$cert_chain[0]?$x509 )
|
||||
return;
|
||||
|
||||
local fuid = c$ssl$cert_chain_fuids[0];
|
||||
local cert = c$ssl$cert_chain[0]$x509$certificate;
|
||||
|
||||
if ( !cert?$key_type || !cert?$key_length )
|
||||
return;
|
||||
|
||||
if ( cert$key_type != "dsa" && cert$key_type != "rsa" )
|
||||
return;
|
||||
|
||||
local key_length = cert$key_length;
|
||||
|
||||
if ( key_length < notify_minimal_key_length )
|
||||
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)
|
||||
]);
|
||||
}
|
||||
|
||||
event ssl_dh_server_params(c: connection, p: string, q: string, Ys: string) &priority=3
|
||||
{
|
||||
if ( ! addr_matches_host(c$id$resp_h, notify_weak_keys) )
|
||||
return;
|
||||
|
||||
local key_length = |p| * 8; # length of the used prime number in bits
|
||||
|
||||
if ( key_length < notify_minimal_key_length )
|
||||
NOTICE([$note=Weak_Key,
|
||||
$msg=fmt("Host uses weak DH parameters with %d key bits", key_length),
|
||||
$conn=c, $suppress_for=1day,
|
||||
$identifier=cat(c$id$resp_h, c$id$resp_p, key_length)
|
||||
]);
|
||||
|
||||
if ( notify_dh_length_shorter_cert_length &&
|
||||
c?$ssl && c$ssl?$cert_chain && |c$ssl$cert_chain| > 0 && c$ssl$cert_chain[0]?$x509 &&
|
||||
c$ssl$cert_chain[0]$x509?$certificate && c$ssl$cert_chain[0]$x509$certificate?$key_type &&
|
||||
(c$ssl$cert_chain[0]$x509$certificate$key_type == "rsa" ||
|
||||
c$ssl$cert_chain[0]$x509$certificate$key_type == "dsa" ))
|
||||
{
|
||||
if ( c$ssl$cert_chain[0]$x509$certificate?$key_length &&
|
||||
c$ssl$cert_chain[0]$x509$certificate$key_length > key_length )
|
||||
NOTICE([$note=Weak_Key,
|
||||
$msg=fmt("DH key length of %d bits is smaller certificate key length of %d bits",
|
||||
key_length, c$ssl$cert_chain[0]$x509$certificate$key_length),
|
||||
$conn=c, $suppress_for=1day,
|
||||
$identifier=cat(c$id$resp_h, c$id$resp_p)
|
||||
]);
|
||||
}
|
||||
}
|
1
scripts/policy/tuning/README
Normal file
1
scripts/policy/tuning/README
Normal file
|
@ -0,0 +1 @@
|
|||
Miscellaneous tuning parameters.
|
2
scripts/policy/tuning/defaults/README
Normal file
2
scripts/policy/tuning/defaults/README
Normal file
|
@ -0,0 +1,2 @@
|
|||
Sets various defaults, and prints warning messages to stdout under
|
||||
certain conditions.
|
|
@ -1,2 +1,3 @@
|
|||
@load ./packet-fragments
|
||||
@load ./warnings
|
||||
@load ./warnings
|
||||
@load ./extracted_file_limits.bro
|
||||
|
|
4
scripts/policy/tuning/defaults/extracted_file_limits.bro
Normal file
4
scripts/policy/tuning/defaults/extracted_file_limits.bro
Normal file
|
@ -0,0 +1,4 @@
|
|||
@load base/files/extract
|
||||
|
||||
# 100 MB.
|
||||
redef FileExtract::default_limit = 104857600;
|
4
scripts/policy/tuning/json-logs.bro
Normal file
4
scripts/policy/tuning/json-logs.bro
Normal file
|
@ -0,0 +1,4 @@
|
|||
##! Loading this script will cause all logs to be written
|
||||
##! out as JSON by default.
|
||||
|
||||
redef LogAscii::use_json=T;
|
|
@ -1,36 +0,0 @@
|
|||
##! Load this script to enable global log output to an ElasticSearch database.
|
||||
|
||||
module LogElasticSearch;
|
||||
|
||||
export {
|
||||
## An elasticsearch specific rotation interval.
|
||||
const rotation_interval = 3hr &redef;
|
||||
|
||||
## Optionally ignore any :bro:type:`Log::ID` from being sent to
|
||||
## ElasticSearch with this script.
|
||||
const excluded_log_ids: set[Log::ID] &redef;
|
||||
|
||||
## If you want to explicitly only send certain :bro:type:`Log::ID`
|
||||
## streams, add them to this set. If the set remains empty, all will
|
||||
## be sent. The :bro:id:`LogElasticSearch::excluded_log_ids` option will remain in
|
||||
## effect as well.
|
||||
const send_logs: set[Log::ID] &redef;
|
||||
}
|
||||
|
||||
event bro_init() &priority=-5
|
||||
{
|
||||
if ( server_host == "" )
|
||||
return;
|
||||
|
||||
for ( stream_id in Log::active_streams )
|
||||
{
|
||||
if ( stream_id in excluded_log_ids ||
|
||||
(|send_logs| > 0 && stream_id !in send_logs) )
|
||||
next;
|
||||
|
||||
local filter: Log::Filter = [$name = "default-es",
|
||||
$writer = Log::WRITER_ELASTICSEARCH,
|
||||
$interv = LogElasticSearch::rotation_interval];
|
||||
Log::add_filter(stream_id, filter);
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue