123 lines
4.5 KiB
Plaintext
123 lines
4.5 KiB
Plaintext
module Best_Guess;
|
|
|
|
# given an input map file with the following format:
|
|
# proto dport sport name category
|
|
# (see https://docs.zeek.org/en/master/frameworks/input.html#reading-data-into-tables
|
|
# for details on how the table is loaded),
|
|
# load up the table on zeek_init and for each connection_state_remove
|
|
# make a "best guess" of protocols based on proto+dport+sport.
|
|
# Best guesses are written to bestguess according to Best_Guess::Info
|
|
|
|
# Table key is transport protocol + destination port + source port
|
|
# Zeek will segfault if there is an unset value ('-') in the key,
|
|
# so use unknown_transport and 0 for protocol and ports, respectively,
|
|
# if they are not defined in the lookup.
|
|
type Best_Guess_Key: record {
|
|
proto: transport_proto &optional;
|
|
dport: count &optional;
|
|
sport: count &optional;
|
|
};
|
|
|
|
|
|
# Other table values include name, category.
|
|
type Best_Guess_Value: record {
|
|
name: string &optional;
|
|
category: string &optional;
|
|
};
|
|
|
|
export {
|
|
redef enum Log::ID += { BEST_GUESS_LOG };
|
|
|
|
#############################################################################
|
|
# This is the format of bestguess.log
|
|
|
|
type Info: record {
|
|
|
|
# Timestamp for when the event happened.
|
|
ts: time &log;
|
|
|
|
# Unique ID for the connection.
|
|
uid: string &log;
|
|
|
|
# The connection's 4-tuple of endpoint addresses/ports.
|
|
id: conn_id &log;
|
|
|
|
# transport protocol
|
|
proto: transport_proto &log &optional;
|
|
|
|
# protocol guess values for log
|
|
name: string &log &optional;
|
|
category: string &log &optional;
|
|
|
|
# originating structure containing guess info
|
|
guess_info: Best_Guess_Value &optional;
|
|
};
|
|
|
|
# Event that can be handled to access the record as it is sent on to the logging framework.
|
|
global log_best_guess: event(rec: Best_Guess::Info);
|
|
}
|
|
|
|
# lookup table of Best_Guess_Key -> Best_Guess_Value to be loaded in zeek_init
|
|
global proto_guesses: table[transport_proto, count, count] of Best_Guess_Value = table();
|
|
# filespec containing best guess mappings
|
|
global guest_map_filespec : string = @DIR + "/guess_ics_map.txt";
|
|
|
|
#############################################################################
|
|
event zeek_init() &priority=5 {
|
|
# populate the lookup table from guest_map_filespec and then clean up the intermediate source
|
|
Input::add_table([$source=guest_map_filespec, $name="guess_ics_map",
|
|
$idx=Best_Guess_Key, $val=Best_Guess_Value,
|
|
$destination=proto_guesses, $want_record=T]);
|
|
Input::remove("guess_ics_map");
|
|
|
|
# initialize bestguess.log
|
|
Log::create_stream(Best_Guess::BEST_GUESS_LOG, [$columns=Best_Guess::Info, $ev=log_best_guess, $path="bestguess"]);
|
|
}
|
|
|
|
#############################################################################
|
|
event connection_state_remove(c: connection) {
|
|
local p = get_port_transport_proto(c$id$resp_p);
|
|
local dp = port_to_count(c$id$resp_p);
|
|
local sp = port_to_count(c$id$orig_p);
|
|
local guess = Best_Guess_Value($name="");
|
|
local category: string = "";
|
|
|
|
# 1. only check connections for which we don't already know "service"
|
|
# 2. skip ICMP, since dp and sp don't mean the same thing for ICMP
|
|
if (((!c?$service) || (|c$service| == 0)) && (p != icmp)) {
|
|
|
|
# Look up permutations of transport protocol + destination port + source port
|
|
# from more-specific to less-specific.
|
|
if ([p, dp, sp] in proto_guesses)
|
|
guess = proto_guesses[p, dp, sp];
|
|
else if ([p, dp, 0] in proto_guesses)
|
|
guess = proto_guesses[p, dp, 0];
|
|
else if ([p, 0, sp] in proto_guesses)
|
|
guess = proto_guesses[p, 0, sp];
|
|
else if ([unknown_transport, dp, sp] in proto_guesses)
|
|
guess = proto_guesses[unknown_transport, dp, sp];
|
|
else if ([unknown_transport, dp, 0] in proto_guesses)
|
|
guess = proto_guesses[unknown_transport, dp, 0];
|
|
else if ([unknown_transport, 0, sp] in proto_guesses)
|
|
guess = proto_guesses[unknown_transport, 0, sp];
|
|
|
|
# if a best guess was made based on protocol and ports, log it
|
|
if ((guess?$name) && (guess$name != "")) {
|
|
|
|
# as category may be undefined, check before accessing
|
|
if (guess?$category)
|
|
category = guess$category;
|
|
|
|
# log entry into bestguess.log
|
|
local info = Best_Guess::Info($ts=network_time(),
|
|
$uid=c$uid,
|
|
$id=c$id,
|
|
$proto=p,
|
|
$name=guess$name,
|
|
$category=category,
|
|
$guess_info=guess);
|
|
Log::write(Best_Guess::BEST_GUESS_LOG, info);
|
|
|
|
} # found guess
|
|
} # if (p != icmp)
|
|
} # connection_state_remove |