mirror of
https://github.com/zeek/zeek.git
synced 2025-10-14 12:38:20 +00:00
New default notice action for emailing network admins.
- When ACTION_EMAIL_ADMIN is applied to a notice, the email addresses associated with the address are collected from the new local_admins table and the email is sent to all discovered email addresses. - The site.bro script is now in the Site module. - Some other small cleanup.
This commit is contained in:
parent
09e242f98f
commit
71d6488637
7 changed files with 97 additions and 35 deletions
|
@ -16,7 +16,7 @@ export {
|
|||
## also used by the global function :bro:id:`NOTICE`.
|
||||
NOTICE,
|
||||
## This is the notice policy auditing log. It records what the current
|
||||
## notice policy is at Bro init time..
|
||||
## notice policy is at Bro init time.
|
||||
NOTICE_POLICY,
|
||||
};
|
||||
|
||||
|
@ -38,11 +38,15 @@ export {
|
|||
ACTION_FILE,
|
||||
## Indicates that the notice should be alarmed on.
|
||||
ACTION_ALARM,
|
||||
## Indicates that the notice should be sent to the configured notice
|
||||
## contact email address(es).
|
||||
## Indicates that the notice should be sent to the email address(es)
|
||||
## configured in the :bro:id:`mail_dest` variable.
|
||||
ACTION_EMAIL,
|
||||
## Indicates that the notice should be sent to the configured pager
|
||||
## email address.
|
||||
## Indicate that the generated email should be addressed to the
|
||||
## appropriate addresses as found in the :bro:id:`Site::addr_to_emails`
|
||||
## variable.
|
||||
ACTION_EMAIL_ADMIN,
|
||||
## Indicates that the notice should be sent to the pager email address
|
||||
## configured in the :bro:id:`mail_page_dest` variable.
|
||||
ACTION_PAGE,
|
||||
## Indicates that no more actions should be found after the policy
|
||||
## item returning this matched.
|
||||
|
@ -113,15 +117,15 @@ export {
|
|||
# This is the :bro:id:`Notice::policy` where the local notice conversion
|
||||
# policy is set.
|
||||
const policy: set[Notice::PolicyItem] = {
|
||||
[$pred(n: Notice::Info) = { return T; },
|
||||
$result = ACTION_FILE,
|
||||
$priority = 0],
|
||||
[$pred(n: Notice::Info) = { return (n$note in ignored_types); },
|
||||
$result = ACTION_STOP,
|
||||
$priority = 10],
|
||||
[$pred(n: Notice::Info) = { return (n$note in emailed_types); },
|
||||
$result = ACTION_EMAIL,
|
||||
$priority = 9],
|
||||
[$pred(n: Notice::Info) = { return T; },
|
||||
$result = ACTION_FILE,
|
||||
$priority = 0],
|
||||
} &redef;
|
||||
|
||||
## Local system mail program.
|
||||
|
|
|
@ -37,8 +37,8 @@ export {
|
|||
|
||||
## If the connection is originated locally, this value will be T. If
|
||||
## it was originated remotely it will be F. In the case that the
|
||||
## :bro:id:`local_nets` variable is undefined, this field will be left
|
||||
## empty at all times.
|
||||
## :bro:id:`Site::local_nets` variable is undefined, this field will
|
||||
## be left empty at all times.
|
||||
local_orig: bool &log &optional;
|
||||
|
||||
## Indicates the number of bytes missed in content gaps which is
|
||||
|
@ -154,8 +154,8 @@ function set_conn(c: connection, eoc: bool)
|
|||
tmp$uid=c$uid;
|
||||
tmp$id=id;
|
||||
tmp$proto=get_port_transport_proto(id$resp_p);
|
||||
if( |local_nets| > 0 )
|
||||
tmp$local_orig=is_local_addr(id$orig_h);
|
||||
if( |Site::local_nets| > 0 )
|
||||
tmp$local_orig=Site::is_local_addr(id$orig_h);
|
||||
c$conn = tmp;
|
||||
}
|
||||
|
||||
|
|
|
@ -17,21 +17,21 @@ export {
|
|||
redef enum Notice::Type += {
|
||||
## Raised when a non-local name is found to be pointing at a local host.
|
||||
## This only works appropriately when all of your authoritative DNS
|
||||
## servers are located in your :bro:id:`local_nets`.
|
||||
## servers are located in your :bro:id:`Site::local_nets`.
|
||||
DNS_ExternalName,
|
||||
};
|
||||
}
|
||||
|
||||
event dns_A_reply(c: connection, msg: dns_msg, ans: dns_answer, a: addr) &priority=-3
|
||||
{
|
||||
if ( |local_zones| == 0 )
|
||||
if ( |Site::local_zones| == 0 )
|
||||
return;
|
||||
|
||||
# Check for responses from remote hosts that point at local hosts
|
||||
# but the name is not considered to be within a "local" zone.
|
||||
if ( is_local_addr(a) && # referring to a local host
|
||||
!is_local_addr(c$id$resp_h) && # response from an external nameserver
|
||||
!is_local_name(ans$query) ) # name isn't in a local zone.
|
||||
if ( Site::is_local_addr(a) && # referring to a local host
|
||||
!Site::is_local_addr(c$id$resp_h) && # response from an external nameserver
|
||||
!Site::is_local_name(ans$query) ) # name isn't in a local zone.
|
||||
{
|
||||
NOTICE([$note=DNS_ExternalName,
|
||||
$msg=fmt("%s is pointing to a local host - %s.", ans$query, a),
|
||||
|
|
|
@ -339,7 +339,7 @@ event smtp_data(c: connection, is_orig: bool, data: string) &priority=3
|
|||
local ip = to_addr(text_ip);
|
||||
|
||||
if ( ! addr_matches_host(ip, mail_path_capture) &&
|
||||
ip !in private_address_space )
|
||||
! Site::is_private_addr(ip) )
|
||||
{
|
||||
c$smtp$process_received_from = F;
|
||||
}
|
||||
|
|
|
@ -8,8 +8,8 @@ export {
|
|||
|
||||
redef enum Notice::Type += {
|
||||
Login,
|
||||
PasswordGuessing,
|
||||
LoginByPasswordGuesser,
|
||||
Password_Guessing,
|
||||
Login_By_Password_Guesser,
|
||||
Login_From_Interesting_Hostname,
|
||||
Bytecount_Inconsistency,
|
||||
};
|
||||
|
@ -88,12 +88,6 @@ event bro_init()
|
|||
Log::create_stream(SSH, [$columns=Info, $ev=log_ssh]);
|
||||
}
|
||||
|
||||
# TODO: move this elsewhere
|
||||
function local_filter(rec: record { id: conn_id; } ): bool
|
||||
{
|
||||
return is_local_addr(rec$id$resp_h);
|
||||
}
|
||||
|
||||
function set_session(c: connection)
|
||||
{
|
||||
if ( ! c?$ssh )
|
||||
|
@ -124,7 +118,7 @@ function check_ssh_connection(c: connection, done: bool)
|
|||
return;
|
||||
|
||||
local status = "failure";
|
||||
local direction = is_local_addr(c$id$orig_h) ? "to" : "from";
|
||||
local direction = Site::is_local_addr(c$id$orig_h) ? "to" : "from";
|
||||
local location: geo_location;
|
||||
location = (direction == "to") ? lookup_location(c$id$resp_h) : lookup_location(c$id$orig_h);
|
||||
|
||||
|
@ -142,7 +136,7 @@ function check_ssh_connection(c: connection, done: bool)
|
|||
if ( default_check_threshold(password_rejections[c$id$orig_h]) )
|
||||
{
|
||||
add password_guessers[c$id$orig_h];
|
||||
NOTICE([$note=PasswordGuessing,
|
||||
NOTICE([$note=Password_Guessing,
|
||||
$conn=c,
|
||||
$msg=fmt("SSH password guessing by %s", c$id$orig_h),
|
||||
$sub=fmt("%d failed logins", password_rejections[c$id$orig_h]$n),
|
||||
|
@ -163,7 +157,7 @@ function check_ssh_connection(c: connection, done: bool)
|
|||
c$id$orig_h !in password_guessers )
|
||||
{
|
||||
add password_guessers[c$id$orig_h];
|
||||
NOTICE([$note=LoginByPasswordGuesser,
|
||||
NOTICE([$note=Login_By_Password_Guesser,
|
||||
$conn=c,
|
||||
$n=password_rejections[c$id$orig_h]$n,
|
||||
$msg=fmt("Successful SSH login by password guesser %s", c$id$orig_h),
|
||||
|
@ -190,6 +184,12 @@ function check_ssh_connection(c: connection, done: bool)
|
|||
$sub=hostname]);
|
||||
}
|
||||
}
|
||||
|
||||
if ( location$country_code in watched_countries )
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
else if ( c$resp$size >= 200000000 )
|
||||
{
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
##! and "neighbors", and servers running particular services.
|
||||
@load utils/pattern
|
||||
|
||||
module GLOBAL;
|
||||
module Site;
|
||||
|
||||
export {
|
||||
## Address space that is considered private and unrouted.
|
||||
|
@ -20,6 +20,12 @@ export {
|
|||
## Networks that are considered "neighbors".
|
||||
const neighbor_nets: set[subnet] &redef;
|
||||
|
||||
## If local network administrators are known and they have responsibility
|
||||
## for defined address space, then a mapping can be defined here between
|
||||
## networks for which they have responsibility and a set of email
|
||||
## addresses.
|
||||
const local_admins: table[subnet] of set[string] = {} &redef;
|
||||
|
||||
## DNS zones that are considered "local".
|
||||
const local_zones: set[string] &redef;
|
||||
|
||||
|
@ -34,6 +40,10 @@ export {
|
|||
## the neighbor networks, false if not.
|
||||
global is_neighbor_addr: function(a: addr): bool;
|
||||
|
||||
## Function that returns true if an address corresponds to one of
|
||||
## the private/unrouted networks, false if not.
|
||||
global is_private_addr: function(a: addr): bool;
|
||||
|
||||
## Function that returns true if a host name is within a local
|
||||
## DNS zone.
|
||||
global is_local_name: function(name: string): bool;
|
||||
|
@ -42,6 +52,10 @@ export {
|
|||
## DNS zone.
|
||||
global is_neighbor_name: function(name: string): bool;
|
||||
|
||||
## Function that returns a common separated list of email addresses
|
||||
## that are considered administrators for the IP address provided as
|
||||
## an argument.
|
||||
global get_emails: function(a: addr): string;
|
||||
}
|
||||
|
||||
# Please ignore, this is an interally used variable.
|
||||
|
@ -74,6 +88,48 @@ function is_neighbor_name(name: string): bool
|
|||
return local_dns_neighbor_suffix_regex in name;
|
||||
}
|
||||
|
||||
# This is a hack for doing a for loop.
|
||||
const one_to_32: vector of count = {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32};
|
||||
|
||||
# TODO: make this work with IPv6
|
||||
function find_all_emails(ip: addr): set[string]
|
||||
{
|
||||
if ( ip !in local_admins ) return set();
|
||||
|
||||
local output_values: set[string] = set();
|
||||
local tmp_ip: addr;
|
||||
local i: count;
|
||||
local emails: string;
|
||||
for ( i in one_to_32 )
|
||||
{
|
||||
tmp_ip = mask_addr(ip, one_to_32[i]);
|
||||
for ( email in local_admins[tmp_ip] )
|
||||
{
|
||||
if ( email != "" )
|
||||
add output_values[email];
|
||||
}
|
||||
}
|
||||
return output_values;
|
||||
}
|
||||
|
||||
function fmt_email_string(emails: set[string]): string
|
||||
{
|
||||
local output="";
|
||||
for( email in emails )
|
||||
{
|
||||
if ( output == "" )
|
||||
output = email;
|
||||
else
|
||||
output = fmt("%s, %s", output, email);
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
function get_emails(a: addr): string
|
||||
{
|
||||
return fmt_email_string(find_all_emails(a));
|
||||
}
|
||||
|
||||
event bro_init() &priority=10
|
||||
{
|
||||
# Double backslashes are needed due to string parsing.
|
||||
|
|
|
@ -1,11 +1,13 @@
|
|||
@load site
|
||||
|
||||
type Direction: enum { INBOUND, OUTBOUND, BIDIRECTIONAL, NO_DIRECTION };
|
||||
function id_matches_direction(id: conn_id, d: Direction): bool
|
||||
{
|
||||
if ( d == NO_DIRECTION ) return F;
|
||||
|
||||
return ( d == BIDIRECTIONAL ||
|
||||
(d == OUTBOUND && is_local_addr(id$orig_h)) ||
|
||||
(d == INBOUND && is_local_addr(id$resp_h)) );
|
||||
(d == OUTBOUND && Site::is_local_addr(id$orig_h)) ||
|
||||
(d == INBOUND && Site::is_local_addr(id$resp_h)) );
|
||||
}
|
||||
|
||||
type Host: enum { LOCAL_HOSTS, REMOTE_HOSTS, ALL_HOSTS, NO_HOSTS };
|
||||
|
@ -14,6 +16,6 @@ function addr_matches_host(ip: addr, h: Host): bool
|
|||
if ( h == NO_HOSTS ) return F;
|
||||
|
||||
return ( h == ALL_HOSTS ||
|
||||
(h == LOCAL_HOSTS && is_local_addr(ip)) ||
|
||||
(h == REMOTE_HOSTS && !is_local_addr(ip)) );
|
||||
(h == LOCAL_HOSTS && Site::is_local_addr(ip)) ||
|
||||
(h == REMOTE_HOSTS && !Site::is_local_addr(ip)) );
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue