mirror of
https://github.com/zeek/zeek.git
synced 2025-10-02 06:38:20 +00:00
97 lines
2.5 KiB
Text
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);
|
|
}
|