mirror of
https://github.com/zeek/zeek.git
synced 2025-10-10 18:48:20 +00:00

SMB error handling improved. The analyzer isn't destroyed when a problem is encoutered anymore. The flowbuffer in the parser is now flushed and the analyzer is set to resync against an SMB command. This was needed because there is some state about open files that is kept within the parser itself which was being destroyed and that was causing analysis after content gaps or parse errors to be faulty. The new mechanism doesn't detroy the parser so parsing after gaps is improved. DCE_RPC handling in SMB is improved in the edge case where a drive mapping isn't seen. There is a new const named SMB::pipe_filenames which is used as a heuristic for identifying "files" opened on named pipe shares. If the share mapping type isn't known and a filename in this set is found, the share type will change to "PIPE" by generating an event named "smb_pipe_connect_heuristic". Reads and writes to that file will be sent to the DCE_RPC analyzer instead of to the files framework. The concept of "unknown" share types has been removed due to the new heuristic detection of share types. Some general clean up of how the SMB cmd log is written and when.
267 lines
No EOL
7.5 KiB
Text
267 lines
No EOL
7.5 KiB
Text
@load base/protocols/smb
|
|
|
|
module SMB;
|
|
|
|
export {
|
|
redef enum Log::ID += {
|
|
CMD_LOG,
|
|
AUTH_LOG,
|
|
MAPPING_LOG,
|
|
FILES_LOG
|
|
};
|
|
|
|
## Abstracted actions for SMB file actions.
|
|
type Action: enum {
|
|
FILE_READ,
|
|
FILE_WRITE,
|
|
FILE_OPEN,
|
|
FILE_CLOSE,
|
|
FILE_DELETE,
|
|
FILE_RENAME,
|
|
|
|
PIPE_READ,
|
|
PIPE_WRITE,
|
|
PIPE_OPEN,
|
|
PIPE_CLOSE,
|
|
|
|
PRINT_READ,
|
|
PRINT_WRITE,
|
|
PRINT_OPEN,
|
|
PRINT_CLOSE,
|
|
};
|
|
|
|
## The file actions which are logged.
|
|
const logged_file_actions: set[Action] = {
|
|
FILE_OPEN,
|
|
FILE_RENAME,
|
|
FILE_DELETE,
|
|
|
|
PRINT_OPEN,
|
|
PRINT_CLOSE,
|
|
} &redef;
|
|
|
|
## The server response statuses which are *not* logged.
|
|
const ignored_command_statuses: set[string] = {
|
|
"MORE_PROCESSING_REQUIRED",
|
|
} &redef;
|
|
|
|
## This record is for the smb_files.log
|
|
type FileInfo: record {
|
|
## Time when the file was first discovered.
|
|
ts : time &log;
|
|
## Unique ID of the connection the file was sent over.
|
|
uid : string &log;
|
|
## ID of the connection the file was sent over.
|
|
id : conn_id &log;
|
|
## Unique ID of the file.
|
|
fuid : string &log &optional;
|
|
|
|
## Action this log record represents.
|
|
action : Action &log &optional;
|
|
## Path pulled from the tree this file was transferred to or from.
|
|
path : string &log &optional;
|
|
## Filename if one was seen.
|
|
name : string &log &optional;
|
|
## Total size of the file.
|
|
size : count &log &default=0;
|
|
## If the rename action was seen, this will
|
|
## the file's previous name.
|
|
prev_name : string &log &optional;
|
|
## Last time this file was modified.
|
|
times : SMB::MACTimes &log &optional;
|
|
};
|
|
|
|
## This record is for the smb_mapping.log
|
|
type TreeInfo: record {
|
|
## Time when the tree was mapped.
|
|
ts : time &log &optional;
|
|
## Unique ID of the connection the tree was mapped over.
|
|
uid : string &log;
|
|
## ID of the connection the tree was mapped over.
|
|
id : conn_id &log;
|
|
|
|
## Name of the tree path.
|
|
path : string &log &optional;
|
|
## The type of resource of the tree (disk share, printer share, named pipe, etc.)
|
|
service : string &log &optional;
|
|
## File system of the tree.
|
|
native_file_system : string &log &optional;
|
|
## If this is SMB2, a share type will be included. For SMB1,
|
|
## the type of share will be deduced and included as well.
|
|
share_type : string &log &default="DISK";
|
|
};
|
|
|
|
## This record is for the smb_cmd.log
|
|
type CmdInfo: record {
|
|
## Timestamp of the command request
|
|
ts : time &log;
|
|
## Unique ID of the connection the request was sent over
|
|
uid : string &log;
|
|
## ID of the connection the request was sent over
|
|
id : conn_id &log;
|
|
|
|
## The command sent by the client
|
|
command : string &log;
|
|
## The subcommand sent by the client, if present
|
|
sub_command : string &log &optional;
|
|
## Command argument sent by the client, if any
|
|
argument : string &log &optional;
|
|
|
|
## Server reply to the client's command
|
|
status : string &log &optional;
|
|
## Round trip time from the request to the response.
|
|
rtt : interval &log &optional;
|
|
## Version of SMB for the command
|
|
version : string &log;
|
|
|
|
## Authenticated username, if available
|
|
username : string &log &optional;
|
|
|
|
## If this is related to a tree, this is the tree
|
|
## that was used for the current command.
|
|
tree : string &log &optional;
|
|
## The type of tree (disk share, printer share, named pipe, etc.)
|
|
tree_service : string &log &optional;
|
|
|
|
## If the command referenced a file, store it here.
|
|
referenced_file : FileInfo &log &optional;
|
|
## If the command referenced a tree, store it here.
|
|
referenced_tree : TreeInfo &optional;
|
|
};
|
|
|
|
## This record stores the SMB state of in-flight commands,
|
|
## the file and tree map of the connection.
|
|
type State: record {
|
|
## A reference to the current command.
|
|
current_cmd : CmdInfo &optional;
|
|
## A reference to the current file.
|
|
current_file : FileInfo &optional;
|
|
## A reference to the current tree.
|
|
current_tree : TreeInfo &optional;
|
|
|
|
## Indexed on MID to map responses to requests.
|
|
pending_cmds : table[count] of CmdInfo &optional;
|
|
## File map to retrieve file information based on the file ID.
|
|
fid_map : table[count] of FileInfo &optional;
|
|
## Tree map to retrieve tree information based on the tree ID.
|
|
tid_map : table[count] of TreeInfo &optional;
|
|
## User map to retrieve user name based on the user ID.
|
|
uid_map : table[count] of string &optional;
|
|
## Pipe map to retrieve UUID based on the file ID of a pipe.
|
|
pipe_map : table[count] of string &optional;
|
|
|
|
## A set of recent files to avoid logging the same
|
|
## files over and over in the smb files log.
|
|
## This only applies to files seen in a single connection.
|
|
recent_files : set[string] &default=string_set() &read_expire=3min;
|
|
};
|
|
|
|
## Optionally write out the SMB commands log. This is
|
|
## primarily useful for debugging so is disabled by default.
|
|
const write_cmd_log = F &redef;
|
|
|
|
## Everything below here is used internally in the SMB scripts.
|
|
|
|
redef record connection += {
|
|
smb_state : State &optional;
|
|
};
|
|
|
|
## Internal use only
|
|
## Some commands shouldn't be logged by the smb1_message event
|
|
const deferred_logging_cmds: set[string] = {
|
|
"NEGOTIATE",
|
|
"READ_ANDX",
|
|
"SESSION_SETUP_ANDX",
|
|
"TREE_CONNECT_ANDX",
|
|
};
|
|
|
|
## This is an internally used function.
|
|
const set_current_file: function(smb_state: State, file_id: count) &redef;
|
|
|
|
## This is an internally used function.
|
|
const write_file_log: function(state: State) &redef;
|
|
}
|
|
|
|
redef record FileInfo += {
|
|
## ID referencing this file.
|
|
fid : count &optional;
|
|
|
|
## UUID referencing this file if DCE/RPC
|
|
uuid : string &optional;
|
|
};
|
|
|
|
const ports = { 139/tcp, 445/tcp };
|
|
redef likely_server_ports += { ports };
|
|
|
|
event bro_init() &priority=5
|
|
{
|
|
Log::create_stream(SMB::CMD_LOG, [$columns=SMB::CmdInfo]);
|
|
Log::create_stream(SMB::FILES_LOG, [$columns=SMB::FileInfo]);
|
|
Log::create_stream(SMB::MAPPING_LOG, [$columns=SMB::TreeInfo]);
|
|
|
|
Analyzer::register_for_ports(Analyzer::ANALYZER_SMB, ports);
|
|
}
|
|
|
|
function set_current_file(smb_state: State, file_id: count)
|
|
{
|
|
if ( file_id !in smb_state$fid_map )
|
|
{
|
|
smb_state$fid_map[file_id] = smb_state$current_cmd$referenced_file;
|
|
smb_state$fid_map[file_id]$fid = file_id;
|
|
}
|
|
|
|
smb_state$current_cmd$referenced_file = smb_state$fid_map[file_id];
|
|
smb_state$current_file = smb_state$current_cmd$referenced_file;
|
|
}
|
|
|
|
function write_file_log(state: State)
|
|
{
|
|
local f = state$current_file;
|
|
if ( f?$name &&
|
|
f$action in logged_file_actions )
|
|
{
|
|
# Everything in this if statement is to avoid overlogging
|
|
# of the same data from a single connection based on recently
|
|
# seen files in the SMB::State $recent_files field.
|
|
if ( f?$times )
|
|
{
|
|
local file_ident = cat(f$action,
|
|
f?$fuid ? f$fuid : "",
|
|
f?$name ? f$name : "",
|
|
f?$path ? f$path : "",
|
|
f$size,
|
|
f$times);
|
|
if ( file_ident in state$recent_files )
|
|
{
|
|
# We've already seen this file and don't want to log it again.
|
|
return;
|
|
}
|
|
else
|
|
add state$recent_files[file_ident];
|
|
}
|
|
|
|
Log::write(FILES_LOG, f);
|
|
}
|
|
}
|
|
|
|
event smb_pipe_connect_heuristic(c: connection) &priority=5
|
|
{
|
|
c$smb_state$current_tree$path = "<unknown>";
|
|
c$smb_state$current_tree$share_type = "PIPE";
|
|
}
|
|
|
|
event file_state_remove(f: fa_file) &priority=-5
|
|
{
|
|
if ( f$source != "SMB" )
|
|
return;
|
|
|
|
for ( id in f$conns )
|
|
{
|
|
local c = f$conns[id];
|
|
if ( c?$smb_state && c$smb_state?$current_file)
|
|
{
|
|
write_file_log(c$smb_state);
|
|
}
|
|
return;
|
|
}
|
|
} |