mirror of
https://github.com/zeek/zeek.git
synced 2025-10-17 14:08:20 +00:00
Merge commit 'e105a7f948
' into topic/policy-scripts-new
* commit 'e105a7f948
':
software.bro now more complete and documented.
This commit is contained in:
commit
8044b730d6
1 changed files with 93 additions and 121 deletions
|
@ -1,58 +1,68 @@
|
|||
@load global-ext
|
||||
@load weird
|
||||
## This script provides the framework for software version detection and
|
||||
## parsing, but doesn't actually do any detection on it's own. It relys on
|
||||
## other protocol specific scripts to parse out software from the protocol(s)
|
||||
## that they analyze. The entry point for providing new software detections
|
||||
## to this framework is through the Software::found event.
|
||||
|
||||
@load functions
|
||||
@load notice
|
||||
|
||||
module Software;
|
||||
|
||||
redef enum Notice += {
|
||||
redef enum Notice::Type += {
|
||||
## For certain softwares, 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
|
||||
## Software::interesting_version_changes variable.
|
||||
Software_Version_Change,
|
||||
};
|
||||
|
||||
export {
|
||||
redef enum Log::ID += { SOFTWARE };
|
||||
type Log: record {
|
||||
ts: time;
|
||||
host: addr;
|
||||
software: string;
|
||||
version: string;
|
||||
description: string;
|
||||
|
||||
};
|
||||
|
||||
type Version: record {
|
||||
major: count; # Major version number
|
||||
minor: count; # Minor version number
|
||||
minor2: count; # Minor subversion number
|
||||
addl: string; # Additional version string (e.g. "beta42")
|
||||
};
|
||||
|
||||
type Type: enum = {
|
||||
WEB_SERVER, WEB_BROWSER,
|
||||
MAIL_SERVER, MAIL_CLIENT,
|
||||
FTP_SERVER, FTP_CLIENT,
|
||||
|
||||
major: count; ##< Major version number
|
||||
minor: count; ##< Minor version number
|
||||
minor2: count; ##< Minor subversion number
|
||||
addl: string; ##< Additional version string (e.g. "beta42")
|
||||
};
|
||||
|
||||
type Software: record {
|
||||
name: string; # Unique name of a software, e.g., "OS"
|
||||
type: Type; #
|
||||
version: Version;
|
||||
type Type: enum {
|
||||
WEB_SERVER,
|
||||
WEB_BROWSER,
|
||||
MAIL_SERVER,
|
||||
MAIL_CLIENT,
|
||||
FTP_SERVER,
|
||||
FTP_CLIENT,
|
||||
BROWSER_PLUGIN,
|
||||
WEBAPP,
|
||||
DATABASE_SERVER,
|
||||
## There are a number of ways to detect printers on the network.
|
||||
PRINTER,
|
||||
};
|
||||
|
||||
redef enum Log::ID += { SOFTWARE };
|
||||
type Info: record {
|
||||
## The time at which the software was first detected.
|
||||
ts: time;
|
||||
## The IP address detected running the software.
|
||||
host: addr;
|
||||
## The type of software detected (e.g. WEB_SERVER)
|
||||
software_type: Type;
|
||||
## Name of the software (e.g. Apache)
|
||||
name: string;
|
||||
## Version of the software
|
||||
version: Version;
|
||||
## The full unparsed version string found because the version parsing
|
||||
## work 100% reliably and this acts as a fall back in the logs.
|
||||
unparsed_version: string;
|
||||
};
|
||||
|
||||
|
||||
## The hosts whose software should be logged.
|
||||
## Choices are: LocalHosts, RemoteHosts, Enabled, Disabled
|
||||
#const logging = Enabled &redef;
|
||||
const logging = Enabled &redef;
|
||||
|
||||
## In case you are interested in more than logging just local assets
|
||||
## you can split the log file.
|
||||
#const split_log_file = F &redef;
|
||||
|
||||
# Some software can be installed twice on the same server
|
||||
# with different major numbers.
|
||||
const identify_by_major: set[string] = {
|
||||
"PHP",
|
||||
"WebSTAR",
|
||||
} &redef;
|
||||
|
||||
## Some software is more interesting when the version changes. This is
|
||||
## a set of all software that should raise a notice when a different version
|
||||
|
@ -61,21 +71,20 @@ export {
|
|||
"SSH"
|
||||
} &redef;
|
||||
|
||||
# Raise this event from other scripts when software is discovered.
|
||||
# This event is actually defined internally in Bro.
|
||||
#global software_version_found: event(c: connection, host: addr, s: software, descr: string);
|
||||
## Other scripts should call this function when they detect software.
|
||||
## @param unparsed_version: This is the full string from which the Software::Info was extracted.
|
||||
## @return: T if the software was logged, F otherwise.
|
||||
global found: function(c: connection, info: Software::Info): bool;
|
||||
|
||||
global found: event(c: connection, host: addr, s: software, full_);
|
||||
|
||||
# Index is the name of the software.
|
||||
type SoftwareSet: table[string] of software;
|
||||
## Index is the name of the software.
|
||||
type SoftwareSet: table[string] of Info;
|
||||
# The set of software associated with an address.
|
||||
global host_software: table[addr] of SoftwareSet &create_expire=1day &synchronized;
|
||||
global tracked_software: table[addr] of SoftwareSet &create_expire=1day &synchronized;
|
||||
}
|
||||
|
||||
event bro_init()
|
||||
{
|
||||
Log::create_stream("SOFTWARE", "Software::Log");
|
||||
Log::create_stream("SOFTWARE", "Software::Info");
|
||||
Log::add_default_filter("SOFTWARE");
|
||||
}
|
||||
|
||||
|
@ -83,7 +92,7 @@ event bro_init()
|
|||
# Returns -1 for v1 < v2, 0 for v1 == v2, 1 for v1 > v2.
|
||||
# If the numerical version numbers match, the addl string
|
||||
# is compared lexicographically.
|
||||
function software_cmp_version(v1: software_version, v2: software_version): int
|
||||
function software_cmp_version(v1: Version, v2: Version): int
|
||||
{
|
||||
if ( v1$major < v2$major )
|
||||
return -1;
|
||||
|
@ -102,109 +111,72 @@ function software_cmp_version(v1: software_version, v2: software_version): int
|
|||
|
||||
return strcmp(v1$addl, v2$addl);
|
||||
}
|
||||
|
||||
|
||||
function software_endpoint_name(c: connection, host: addr): string
|
||||
{
|
||||
return fmt("%s %s", host, (host == c$id$orig_h ? "client" : "server"));
|
||||
}
|
||||
|
||||
# Convert a version into a string "a.b.c-x".
|
||||
function software_fmt_version(v: software_version): string
|
||||
function software_fmt_version(v: Version): string
|
||||
{
|
||||
return fmt("%s%s%s%s",
|
||||
v$major >= 0 ? fmt("%d", v$major) : "",
|
||||
v$minor >= 0 ? fmt(".%d", v$minor) : "",
|
||||
v$minor2 >= 0 ? fmt(".%d", v$minor2) : "",
|
||||
return fmt("%d.%d.%d%s",
|
||||
v$major, v$minor, v$minor2,
|
||||
v$addl != "" ? fmt("-%s", v$addl) : "");
|
||||
}
|
||||
|
||||
# Convert a software into a string "name a.b.cx".
|
||||
function software_fmt(s: software): string
|
||||
function software_fmt(i: Info): string
|
||||
{
|
||||
return fmt("%s %s", s$name, software_fmt_version(s$version));
|
||||
}
|
||||
|
||||
event software_new(c: connection, host: addr, s: software, descr: string)
|
||||
{
|
||||
if ( addr_matches_hosts(host, logging) )
|
||||
{
|
||||
local log = LOG::get_file_by_addr("software-ext", host, F);
|
||||
print log, cat_sep("\t", "\\N",
|
||||
network_time(), host,
|
||||
s$name, software_fmt_version(s$version), descr);
|
||||
}
|
||||
return fmt("%s %s", i$name, software_fmt_version(i$version));
|
||||
}
|
||||
|
||||
# Insert a mapping into the table
|
||||
# Overides old entries for the same software and generates events if needed.
|
||||
event software_register(c: connection, host: addr, s: software, descr: string)
|
||||
event software_register(c: connection, info: Info)
|
||||
{
|
||||
# Host already known?
|
||||
if ( host !in host_software )
|
||||
host_software[host] = table();
|
||||
if ( info$host !in tracked_software )
|
||||
tracked_software[info$host] = table();
|
||||
|
||||
# If a software can be installed more than once on a host
|
||||
# (with a different major version), we identify it by "<name>-<major>"
|
||||
if ( s$name in identify_by_major && s$version$major >= 0 )
|
||||
s$name = fmt("%s-%d", s$name, s$version$major);
|
||||
|
||||
local hs = host_software[host];
|
||||
local ts = tracked_software[info$host];
|
||||
# Software already registered for this host?
|
||||
if ( s$name in hs )
|
||||
if ( info$name in ts )
|
||||
{
|
||||
local old = hs[s$name];
|
||||
local old = ts[info$name];
|
||||
|
||||
# Is it a potentially interesting version change
|
||||
# and is it a different version?
|
||||
if ( s$name in interesting_version_changes &&
|
||||
software_cmp_version(old$version, s$version) != 0 )
|
||||
if ( info$name in interesting_version_changes &&
|
||||
software_cmp_version(old$version, info$version) != 0 )
|
||||
{
|
||||
local msg = fmt("%.6f %s switched from %s to %s (%s)",
|
||||
network_time(), software_endpoint_name(c, host),
|
||||
network_time(), software_endpoint_name(c, info$host),
|
||||
software_fmt_version(old$version),
|
||||
software_fmt(s), descr);
|
||||
software_fmt(info), info$software_type);
|
||||
NOTICE([$note=Software_Version_Change, $conn=c,
|
||||
$msg=msg, $sub=software_fmt(s)]);
|
||||
$msg=msg, $sub=software_fmt(info)]);
|
||||
}
|
||||
else
|
||||
{
|
||||
# If the software is known to be on this host already and version
|
||||
# changes either aren't interesting or it's the same version as
|
||||
# already known, just return and don't log.
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Log::write("SOFTWARE", info);
|
||||
ts[info$name] = info;
|
||||
}
|
||||
|
||||
function found(c: connection, info: Info): bool
|
||||
{
|
||||
if ( addr_matches_hosts(info$host, logging) )
|
||||
{
|
||||
event software_register(c, info);
|
||||
return T;
|
||||
}
|
||||
else
|
||||
{
|
||||
event software_new(c, host, s, descr);
|
||||
}
|
||||
|
||||
hs[s$name] = s;
|
||||
return F;
|
||||
}
|
||||
|
||||
|
||||
########################################
|
||||
# Below are internally defined events. #
|
||||
########################################
|
||||
|
||||
event software_version_found(c: connection, host: addr, s: software, descr: string)
|
||||
{
|
||||
if ( addr_matches_hosts(host, logging) )
|
||||
event software_register(c, host, s, descr);
|
||||
}
|
||||
|
||||
event software_parse_error(c: connection, host: addr, descr: string)
|
||||
{
|
||||
if ( addr_matches_hosts(host, logging) )
|
||||
{
|
||||
# Here we need a little hack, since software_file is
|
||||
# not always there.
|
||||
local msg = fmt("%.6f %s: can't parse '%s'", network_time(),
|
||||
software_endpoint_name(c, host), descr);
|
||||
|
||||
print Weird::weird_file, msg;
|
||||
}
|
||||
}
|
||||
|
||||
# I'm not going to handle this at the moment. It doesn't seem terribly useful.
|
||||
#event software_unparsed_version_found(c: connection, host: addr, str: string)
|
||||
# {
|
||||
# if ( addr_matches_hosts(host, logging) )
|
||||
# {
|
||||
# print Weird::weird_file, fmt("%.6f %s: [%s]", network_time(),
|
||||
# software_endpoint_name(c, host), str);
|
||||
# }
|
||||
# }
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue