zeek/policy/service-probe.bro

97 lines
2.5 KiB
Text

# $Id: service-probe.bro 5892 2008-07-01 02:37:03Z vern $
#
# Detects hosts that continually bang away at a particular service
# of a local host, for example for brute-forcing passwords.
#
# Written by Jim Mellander, LBNL.
# Updated by Robin Sommer, ICSI.
@load conn
module ServiceProbe;
export {
redef enum Notice += { ServiceProbe };
# No work gets done unless this is set.
global detect_probes = F &redef;
# By default, look for service probes targeting MySQL and SSH.
global probe_ports = { 1433/tcp, 22/tcp, } &redef;
# They have to connect to this many to be flagged.
global connect_threshold: table[port] of count &default=100 &redef;
# How many bytes the connection must have to be considered potentially
# a probe. If missing, then there's no lower/upper bound.
#
# Note, the attack that motivated including these was SSH password
# guessing, where it was empirically determined that connections
# with > 1KB and < 2KB bytes transferred appear to be unsuccessful
# password guesses.
#
global min_bytes: table[port] of int &default=-1 &redef;
global max_bytes: table[port] of int &default=-1 &redef;
# How many tries a given originator host has made against a given
# port on a given responder host.
global tries: table[addr, addr, port] of count
&default=0 &read_expire = 10 min;
}
global reported_hosts: set[addr] &read_expire = 1 day;
function service_probe_check(c: connection)
{
if ( ! detect_probes )
return;
local id = c$id;
local orig = id$orig_h;
local resp = id$resp_h;
local service = (port_names[20/tcp] in c$service) ? 20/tcp : id$resp_p;
if ( orig in reported_hosts )
# We've already blocked them.
return;
if ( is_local_addr(orig) )
# We only analyze probes of local servers.
return;
if ( service !in probe_ports )
# Not a port we care about.
return;
local enough_bytes = T;
local bytes_xferred = c$orig$size + c$resp$size;
if ( service in min_bytes && bytes_xferred < min_bytes[service] )
enough_bytes = F;
if ( service in max_bytes && bytes_xferred > max_bytes[service] )
enough_bytes = F;
if ( ! enough_bytes )
return;
local cnt = ++tries[orig, resp, service];
if ( cnt == connect_threshold[service] )
{
local svc = service_name(c);
NOTICE([$note=ServiceProbe, $src=orig,
$msg=fmt("service probing %s -> %s %s",
orig, resp, svc)]);
# Since we've dropped this host, we can now release the space.
delete tries[orig, resp, service];
add reported_hosts[orig];
}
}
event connection_state_remove(c: connection)
{
service_probe_check(c);
}