254 lines
8.7 KiB
Plaintext
254 lines
8.7 KiB
Plaintext
module Login;
|
|
|
|
# log telnet, rlogin, and rsh events to login.log
|
|
|
|
export {
|
|
|
|
redef enum Log::ID += {
|
|
## The logging stream identifier
|
|
Log_LOGIN
|
|
};
|
|
|
|
type Info : record {
|
|
## Time the event occurred
|
|
ts : time &log;
|
|
## Unique ID for the connection
|
|
uid : string &log;
|
|
## The connection's 4-tuple of endpoint addresses/port
|
|
id : conn_id &log;
|
|
|
|
## proto (telnet, rlogin, or rsh)
|
|
proto : string &log &optional;
|
|
## login_success event was seen (successful login)
|
|
success : bool &log &default = F;
|
|
## login_confused event was seen (successful login)
|
|
confused : bool &log &default = F;
|
|
## username given for login attempt
|
|
user : string &log &optional;
|
|
## client_user given for login attempt (empty for telnet, set for rlogin)
|
|
client_user : string &log &optional;
|
|
## password given for login attempt
|
|
password : string &log &optional;
|
|
|
|
## whether or not a line has been written to login.log
|
|
logged : bool &default = F;
|
|
};
|
|
|
|
## Event that can be handled to access the :zeek:type:`Login::Info`
|
|
## record as it is sent on to the logging framework.
|
|
global log_login : event(rec : Info);
|
|
}
|
|
|
|
# Add the state tracking information variable to the connection record
|
|
redef record connection += {
|
|
login : Info &optional;
|
|
};
|
|
|
|
###############################################
|
|
# constants borrowed from the old Bro 1.5 login.bro required to make some of the telnet/rlogin/rsh events work correctly
|
|
# see https://github.com/zeek/zeek/blob/release/1.5/policy/login.bro#L178
|
|
# https://github.com/reservoirlabs/brorefguide/blob/master/analysis.texi#L3850
|
|
|
|
redef skip_authentication = { "WELCOME TO THE BERKELEY PUBLIC LIBRARY", };
|
|
|
|
redef direct_login_prompts = { "TERMINAL?", };
|
|
|
|
redef login_prompts = {
|
|
"Login:",
|
|
"login:",
|
|
"Name:",
|
|
"Username:",
|
|
"User:",
|
|
"Member Name",
|
|
"User Access Verification",
|
|
"Cisco Systems Console",
|
|
direct_login_prompts
|
|
};
|
|
|
|
redef login_non_failure_msgs = {
|
|
"Failures",
|
|
"failures", # probably is "<n> failures since last login"
|
|
"failure since last successful login",
|
|
"failures since last successful login",
|
|
};
|
|
|
|
redef login_non_failure_msgs = {
|
|
"Failures",
|
|
"failures", # probably is "<n> failures since last login"
|
|
"failure since last successful login",
|
|
"failures since last successful login",
|
|
} &redef;
|
|
|
|
redef login_failure_msgs = {
|
|
"invalid",
|
|
"Invalid",
|
|
"incorrect",
|
|
"Incorrect",
|
|
"failure",
|
|
"Failure",
|
|
# "Unable to authenticate",
|
|
# "unable to authenticate",
|
|
"User authorization failure",
|
|
"Login failed",
|
|
"INVALID",
|
|
"Sorry.",
|
|
"Sorry,",
|
|
};
|
|
|
|
const router_prompts: set[string] &redef;
|
|
|
|
redef login_success_msgs = {
|
|
"Last login",
|
|
"Last successful login",
|
|
"Last successful login",
|
|
"checking for disk quotas",
|
|
"unsuccessful login attempts",
|
|
"failure since last successful login",
|
|
"failures since last successful login",
|
|
router_prompts,
|
|
};
|
|
|
|
redef login_timeouts = {
|
|
"timeout",
|
|
"timed out",
|
|
"Timeout",
|
|
"Timed out",
|
|
"Error reading command input", # VMS
|
|
};
|
|
# end borrowed constants from Bro 1.5 login.bro
|
|
###############################################
|
|
|
|
# telnet, rlogin, rsh
|
|
const telnet_port = 23/tcp;
|
|
const telnet_ports = { telnet_port };
|
|
const rlogin_port = 513/tcp;
|
|
const rlogin_ports = { rlogin_port };
|
|
const rsh_port = 514/tcp;
|
|
const rsh_ports = { rsh_port };
|
|
redef likely_server_ports += { telnet_ports, rlogin_ports, rsh_ports };
|
|
|
|
# set_login_session - if has not yet been registered in the connection, instantiate
|
|
# the Info record and assign in c$login
|
|
function set_login_session(c : connection) {
|
|
if ( ! c?$login ) {
|
|
local s : Info = [$ts = network_time(), $uid = c$uid, $id = c$id];
|
|
switch c$id$resp_p {
|
|
case telnet_port:
|
|
s$proto = "telnet";
|
|
add c$service["telnet"];
|
|
break;
|
|
case rlogin_port:
|
|
s$proto = "rlogin";
|
|
add c$service["rlogin"];
|
|
break;
|
|
case rsh_port:
|
|
s$proto = "rsh";
|
|
add c$service["rsh"];
|
|
break;
|
|
}
|
|
c$login = s;
|
|
}
|
|
}
|
|
|
|
# login_message - log to login.log
|
|
function login_message(s : Info) {
|
|
|
|
# strip some values that can happen in a "confused" state that aren't really valid values
|
|
if (( s?$user ) && (( s$user == "" ) || ( s$user == "<none>" ) || ( s$user == "<timeout>" )))
|
|
delete s$user;
|
|
if (( s?$client_user ) && (( s$client_user == "" ) || ( s$client_user == "<none>" ) || ( s$client_user == "<timeout>" )))
|
|
delete s$client_user;
|
|
if (( s?$password ) && (( s$password == "" ) || ( s$password == "<none>" ) || ( s$password == "<timeout>" )))
|
|
delete s$password;
|
|
if (( s?$proto ) && ( s$proto == "" ))
|
|
delete s$proto;
|
|
|
|
s$ts = network_time();
|
|
Log::write(Login::Log_LOGIN, s);
|
|
s$logged = T;
|
|
}
|
|
|
|
# create log stream for login.log and register telnet, rlogin, and rsh analyzers
|
|
event zeek_init() &priority = 5 {
|
|
Log::create_stream(Login::Log_LOGIN, [$columns = Info, $ev = log_login, $path = "login"]);
|
|
Analyzer::register_for_ports(Analyzer::ANALYZER_TELNET, telnet_ports);
|
|
Analyzer::register_for_ports(Analyzer::ANALYZER_RLOGIN, rlogin_ports);
|
|
Analyzer::register_for_ports(Analyzer::ANALYZER_RSH, rsh_ports);
|
|
}
|
|
|
|
# login_confused - Generated when tracking of Telnet/Rlogin authentication failed
|
|
# https://docs.zeek.org/en/current/scripts/base/bif/plugins/Zeek_Login.events.bif.zeek.html#id-login_confused
|
|
event login_confused(c : connection, msg : string, line : string) &priority = 5 {
|
|
# print "login_confused", msg, line;
|
|
|
|
set_login_session(c);
|
|
|
|
c$login$confused = T;
|
|
}
|
|
|
|
# login_failure - Generated when tracking of Telnet/Rlogin authentication failed
|
|
# https://docs.zeek.org/en/current/scripts/base/bif/plugins/Zeek_Login.events.bif.zeek.html#id-login_failure
|
|
event login_failure(c : connection, user : string, client_user : string, password : string, line : string) &priority = 5 {
|
|
# print "login_failure", user, client_user, password, line;
|
|
|
|
set_login_session(c);
|
|
|
|
if ((!c$login?$user) || (c$login$user == ""))
|
|
c$login$user = user;
|
|
if ((!c$login?$client_user) || (c$login$client_user == ""))
|
|
c$login$client_user = client_user;
|
|
if ((!c$login?$password) || (c$login$password == ""))
|
|
c$login$password = password;
|
|
|
|
login_message(c$login);
|
|
}
|
|
|
|
# login_success - Generated for successful Telnet/Rlogin logins
|
|
# https://docs.zeek.org/en/current/scripts/base/bif/plugins/Zeek_Login.events.bif.zeek.html#id-login_success
|
|
event login_success(c : connection, user : string, client_user : string, password : string, line : string) &priority = 5 {
|
|
# print "login_success", user, client_user, password, line;
|
|
|
|
set_login_session(c);
|
|
|
|
c$login$success = T;
|
|
c$login$user = user;
|
|
c$login$client_user = client_user;
|
|
|
|
# it appears for a successful login with rsh where client_user was checked, what we're getting in
|
|
# the "password" field is actually not the password, but the first line of data
|
|
if ((c$login$proto != "rsh") || (c$login$client_user == ""))
|
|
c$login$password = password;
|
|
|
|
login_message(c$login);
|
|
}
|
|
|
|
event connection_state_remove(c : connection) &priority = -5 {
|
|
if (c?$login) {
|
|
|
|
if ( c$login$logged == F) {
|
|
login_message(c$login);
|
|
}
|
|
|
|
delete c$login;
|
|
}
|
|
}
|
|
|
|
# for testing:
|
|
# for file in /host/telnet/*; do cd /tmp; mkdir -p /host/logs/"$(basename "$file")"; /bin/rm -f /host/logs/"$(basename "$file")"/*; cd /host/logs/"$(basename "$file")"; zeek -r "$file" local > debug_output.txt; cd /tmp; done
|
|
|
|
# event activating_encryption(c: connection) { print "activating_encryption"; }
|
|
# event authentication_accepted(name: string, c: connection) { print "authentication_accepted", name; }
|
|
# event authentication_rejected(name: string, c: connection) { print "authentication_rejected", name; }
|
|
# event authentication_skipped(c: connection) { print "authentication_skipped"; }
|
|
# event bad_option(c: connection) { print "bad_option"; }
|
|
# event bad_option_termination(c: connection) { print "bad_option_termination"; }
|
|
# event inconsistent_option(c: connection) { print "inconsistent_option"; }
|
|
# event login_confused_text(c: connection, line: string) { print "login_confused_text", line; }
|
|
# event login_display(c: connection, display: string) { print "login_display", display; }
|
|
# event login_input_line(c: connection, line: string) { print "login_input_line", line; }
|
|
# event login_output_line(c: connection, line: string) { print "login_output_line", line; }
|
|
# event login_terminal(c: connection, terminal: string) { print "login_terminal", terminal; }
|
|
# event rsh_reply(c: connection, client_user: string, server_user: string, line: string) { print "rsh_reply", client_user, server_user, line; }
|
|
# event rsh_request(c: connection, client_user: string, server_user: string, line: string; new_session: bool) { print "rsh_request", client_user, server_user, line, new_session; }
|
|
|