147 lines
4.9 KiB
Plaintext
147 lines
4.9 KiB
Plaintext
#
|
|
# Copyright (c) 2017 Capital One. All rights reserved.
|
|
# Author: Derek Ditch <derek.ditch@criticalstack.com>
|
|
#
|
|
|
|
##! Basic POP3 analyzer
|
|
# From here: https://github.com/albert-magyar/bro/blob/topic/pop3/scripts/base/protocols/pop3/main.zeek
|
|
|
|
@load base/utils/numbers
|
|
@load base/utils/files
|
|
|
|
module POP3;
|
|
|
|
export {
|
|
redef enum Log::ID += { LOG };
|
|
|
|
## Set to true to capture passwords from PASS command
|
|
const default_capture_password = F &redef;
|
|
|
|
type session_state: enum { AUTHORIZATION, TRANSACTION, UPDATE };
|
|
|
|
|
|
## The most significant deviation in this script from the style of
|
|
## the HTTP and SMTP analyzers is that this record type is NOT used
|
|
## as a field of the connection struct. In those analyzers, any use
|
|
## of c$http/c$smtp in an event handler was preceded by a call to
|
|
## set_state that caused that field to hold the appropriate Info
|
|
## record instance. Since the persistence of the contents of that
|
|
## field were restricted to local scope, it has been replaced with
|
|
## local variables that hold the correct element of the pending queue.
|
|
type CommandInfo: record {
|
|
ts: time;
|
|
command: string &optional;
|
|
arg: string &optional;
|
|
status: string ;
|
|
msg: string &optional;
|
|
has_client_activity: bool &default=F;
|
|
};
|
|
|
|
type Info: record {
|
|
ts: time &log;
|
|
uid: string &log;
|
|
id: conn_id &log;
|
|
current_request: count &default=0;
|
|
current_response: count &default=0;
|
|
successful_commands: count &default=0 &log;
|
|
failed_commands: count &default=0 &log;
|
|
pending: table[count] of CommandInfo;
|
|
username: string &optional &log;
|
|
password: string &optional &log;
|
|
state: session_state &default=AUTHORIZATION;
|
|
};
|
|
|
|
## Event that can be handled to access the POP3 record sent to the logging framework.
|
|
global log_pop3: event(rec: Info);
|
|
}
|
|
|
|
# Add the POP3 state tracking fields to the connection record.
|
|
redef record connection += {
|
|
pop3: Info &optional;
|
|
};
|
|
|
|
const ports = { 110/tcp };
|
|
redef likely_server_ports += { ports };
|
|
event zeek_init() &priority=5 {
|
|
Log::create_stream(POP3::LOG, [$columns=Info, $ev=log_pop3]);
|
|
Analyzer::register_for_ports(Analyzer::ANALYZER_POP3, ports);
|
|
}
|
|
|
|
|
|
function new_pop3_command(c: connection): CommandInfo {
|
|
local tmp: CommandInfo;
|
|
tmp$ts=network_time();
|
|
return tmp;
|
|
}
|
|
|
|
function new_pop3_session(c: connection): Info {
|
|
local tmp: Info;
|
|
tmp$ts=network_time();
|
|
tmp$uid=c$uid;
|
|
tmp$id=c$id;
|
|
return tmp;
|
|
}
|
|
|
|
function select_command(c: connection, is_request: bool): CommandInfo {
|
|
if (!c?$pop3) {
|
|
local s: Info;
|
|
c$pop3 = s;
|
|
}
|
|
local current_command: count;
|
|
current_command = (is_request) ? c$pop3$current_request : c$pop3$current_response;
|
|
if (current_command !in c$pop3$pending) {
|
|
c$pop3$pending[current_command] = new_pop3_command(c);
|
|
}
|
|
return c$pop3$pending[current_command];
|
|
}
|
|
|
|
event pop3_request(c: connection, is_orig: bool, command: string, arg: string) &priority=5 {
|
|
if (!c?$pop3)
|
|
c$pop3 = new_pop3_session(c);
|
|
local current_command: CommandInfo;
|
|
current_command = select_command(c, is_orig);
|
|
current_command$has_client_activity = T;
|
|
current_command$command = command;
|
|
current_command$arg = arg;
|
|
++c$pop3$current_request;
|
|
}
|
|
|
|
function process_command(c: connection, command: CommandInfo) {
|
|
if (command?$command && command$status == "OK") {
|
|
++c$pop3$successful_commands;
|
|
switch(command$command) {
|
|
case "USER":
|
|
c$pop3$username = command$arg;
|
|
break;
|
|
case "PASS":
|
|
if (default_capture_password)
|
|
c$pop3$password = command$arg;
|
|
c$pop3$state = TRANSACTION;
|
|
break;
|
|
case "QUIT":
|
|
c$pop3$state = UPDATE;
|
|
break;
|
|
}
|
|
} else if (command?$command && command$status == "ERR") {
|
|
++c$pop3$failed_commands;
|
|
}
|
|
}
|
|
|
|
event pop3_reply(c: connection, is_orig: bool, cmd: string, msg: string) &priority=5 {
|
|
if (!c?$pop3)
|
|
c$pop3 = new_pop3_session(c);
|
|
local current_command: CommandInfo;
|
|
current_command = select_command(c, is_orig);
|
|
current_command$status = cmd;
|
|
current_command$msg = msg;
|
|
process_command(c, current_command);
|
|
if (current_command$has_client_activity)
|
|
++c$pop3$current_response;
|
|
}
|
|
|
|
event connection_state_remove(c: connection) &priority=-5 {
|
|
if (c?$pop3) {
|
|
Log::write(POP3::LOG, c$pop3);
|
|
}
|
|
}
|