mirror of
https://github.com/zeek/zeek.git
synced 2025-10-02 14:48:21 +00:00
Merge remote-tracking branch 'origin/topic/vladg/smb' into topic/seth/smb
# Conflicts: # scripts/base/protocols/smb/files.bro # scripts/base/protocols/smb/main.bro # scripts/base/protocols/smb/smb1-main.bro # scripts/base/protocols/smb/smb2-main.bro
This commit is contained in:
commit
2e2fb6831f
30 changed files with 1990 additions and 550 deletions
|
@ -2689,6 +2689,34 @@ export {
|
||||||
security_blob : string &optional;
|
security_blob : string &optional;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type SMB1::Find_First2_Request_Args: record {
|
||||||
|
## File attributes to apply as a constraint to the search
|
||||||
|
search_attrs : count;
|
||||||
|
## Max search results
|
||||||
|
search_count : count;
|
||||||
|
## Misc. flags for how the server should manage the transaction
|
||||||
|
## once results are returned
|
||||||
|
flags : count;
|
||||||
|
## How detailed the information returned in the results should be
|
||||||
|
info_level : count;
|
||||||
|
## Specify whether to search for directories or files
|
||||||
|
search_storage_type : count;
|
||||||
|
## The string to serch for (note: may contain wildcards)
|
||||||
|
file_name : string;
|
||||||
|
};
|
||||||
|
|
||||||
|
type SMB1::Find_First2_Response_Args: record {
|
||||||
|
## The server generated search identifier
|
||||||
|
sid : count;
|
||||||
|
## Number of results returned by the search
|
||||||
|
search_count : count;
|
||||||
|
## Whether or not the search can be continued using
|
||||||
|
## the TRANS2_FIND_NEXT2 transaction
|
||||||
|
end_of_search : bool;
|
||||||
|
## An extended attribute name that couldn't be retrieved
|
||||||
|
ext_attr_error : string &optional;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -9,6 +9,113 @@ export {
|
||||||
const statuses: table[count] of StatusCode = {
|
const statuses: table[count] of StatusCode = {
|
||||||
[0x00000000] = [$id="SUCCESS", $desc="The operation completed successfully."],
|
[0x00000000] = [$id="SUCCESS", $desc="The operation completed successfully."],
|
||||||
} &redef &default=function(i: count):StatusCode { local unknown=fmt("unknown-%d", i); return [$id=unknown, $desc=unknown]; };
|
} &redef &default=function(i: count):StatusCode { local unknown=fmt("unknown-%d", i); return [$id=unknown, $desc=unknown]; };
|
||||||
|
|
||||||
|
## These are files names that are used for special
|
||||||
|
## cases by the file system and would not be
|
||||||
|
## considered "normal" files.
|
||||||
|
const pipe_names: set[string] = {
|
||||||
|
"\\netdfs",
|
||||||
|
"\\spoolss",
|
||||||
|
"\\NETLOGON",
|
||||||
|
"\\winreg",
|
||||||
|
"\\lsarpc",
|
||||||
|
"\\samr",
|
||||||
|
"\\srvsvc",
|
||||||
|
"srvsvc",
|
||||||
|
"MsFteWds",
|
||||||
|
"\\wkssvc",
|
||||||
|
};
|
||||||
|
|
||||||
|
## The UUIDs used by the various RPC endpoints
|
||||||
|
const rpc_uuids: table[string] of string = {
|
||||||
|
["4b324fc8-1670-01d3-1278-5a47bf6ee188"] = "Server Service",
|
||||||
|
["6bffd098-a112-3610-9833-46c3f87e345a"] = "Workstation Service",
|
||||||
|
} &redef &default=function(i: string):string { return fmt("unknown-uuid-%s", i); };
|
||||||
|
|
||||||
|
## Server service sub commands
|
||||||
|
const srv_cmds: table[count] of string = {
|
||||||
|
[8] = "NetrConnectionEnum",
|
||||||
|
[9] = "NetrFileEnum",
|
||||||
|
[10] = "NetrFileGetInfo",
|
||||||
|
[11] = "NetrFileClose",
|
||||||
|
[12] = "NetrSessionEnum",
|
||||||
|
[13] = "NetrSessionDel",
|
||||||
|
[14] = "NetrShareAdd",
|
||||||
|
[15] = "NetrShareEnum",
|
||||||
|
[16] = "NetrShareGetInfo",
|
||||||
|
[17] = "NetrShareSetInfo",
|
||||||
|
[18] = "NetrShareDel",
|
||||||
|
[19] = "NetrShareDelSticky",
|
||||||
|
[20] = "NetrShareCheck",
|
||||||
|
[21] = "NetrServerGetInfo",
|
||||||
|
[22] = "NetrServerSetInfo",
|
||||||
|
[23] = "NetrServerDiskEnum",
|
||||||
|
[24] = "NetrServerStatisticsGet",
|
||||||
|
[25] = "NetrServerTransportAdd",
|
||||||
|
[26] = "NetrServerTransportEnum",
|
||||||
|
[27] = "NetrServerTransportDel",
|
||||||
|
[28] = "NetrRemoteTOD",
|
||||||
|
[30] = "NetprPathType",
|
||||||
|
[31] = "NetprPathCanonicalize",
|
||||||
|
[32] = "NetprPathCompare",
|
||||||
|
[33] = "NetprNameValidate",
|
||||||
|
[34] = "NetprNameCanonicalize",
|
||||||
|
[35] = "NetprNameCompare",
|
||||||
|
[36] = "NetrShareEnumSticky",
|
||||||
|
[37] = "NetrShareDelStart",
|
||||||
|
[38] = "NetrShareDelCommit",
|
||||||
|
[39] = "NetrGetFileSecurity",
|
||||||
|
[40] = "NetrSetFileSecurity",
|
||||||
|
[41] = "NetrServerTransportAddEx",
|
||||||
|
[43] = "NetrDfsGetVersion",
|
||||||
|
[44] = "NetrDfsCreateLocalPartition",
|
||||||
|
[45] = "NetrDfsDeleteLocalPartition",
|
||||||
|
[46] = "NetrDfsSetLocalVolumeState",
|
||||||
|
[48] = "NetrDfsCreateExitPoint",
|
||||||
|
[49] = "NetrDfsDeleteExitPoint",
|
||||||
|
[50] = "NetrDfsModifyPrefix",
|
||||||
|
[51] = "NetrDfsFixLocalVolume",
|
||||||
|
[52] = "NetrDfsManagerReportSiteInfo",
|
||||||
|
[53] = "NetrServerTransportDelEx",
|
||||||
|
[54] = "NetrServerAliasAdd",
|
||||||
|
[55] = "NetrServerAliasEnum",
|
||||||
|
[56] = "NetrServerAliasDel",
|
||||||
|
[57] = "NetrShareDelEx",
|
||||||
|
} &redef &default=function(i: count):string { return fmt("unknown-srv-command-%d", i); };
|
||||||
|
|
||||||
|
## Workstation service sub commands
|
||||||
|
const wksta_cmds: table[count] of string = {
|
||||||
|
[0] = "NetrWkstaGetInfo",
|
||||||
|
[1] = "NetrWkstaSetInfo",
|
||||||
|
[2] = "NetrWkstaUserEnum",
|
||||||
|
[5] = "NetrWkstaTransportEnum",
|
||||||
|
[6] = "NetrWkstaTransportAdd",
|
||||||
|
[7] = "NetrWkstaTransportDel",
|
||||||
|
[8] = "NetrUseAdd",
|
||||||
|
[9] = "NetrUseGetInfo",
|
||||||
|
[10] = "NetrUseDel",
|
||||||
|
[11] = "NetrUseEnum",
|
||||||
|
[13] = "NetrWorkstationStatisticsGet",
|
||||||
|
[20] = "NetrGetJoinInformation",
|
||||||
|
[22] = "NetrJoinDomain2",
|
||||||
|
[23] = "NetrUnjoinDomain2",
|
||||||
|
[24] = "NetrRenameMachineInDomain2",
|
||||||
|
[25] = "NetrValidateName2",
|
||||||
|
[26] = "NetrGetJoinableOUs2",
|
||||||
|
[27] = "NetrAddAlternateComputerName",
|
||||||
|
[28] = "NetrRemoveAlternateComputerName",
|
||||||
|
[29] = "NetrSetPrimaryComputerName",
|
||||||
|
[30] = "NetrEnumerateComputerNames",
|
||||||
|
} &redef &default=function(i: count):string { return fmt("unknown-wksta-command-%d", i); };
|
||||||
|
|
||||||
|
type rpc_cmd_table: table[count] of string;
|
||||||
|
|
||||||
|
## The subcommands for RPC endpoints
|
||||||
|
const rpc_sub_cmds: table[string] of rpc_cmd_table = {
|
||||||
|
["4b324fc8-1670-01d3-1278-5a47bf6ee188"] = srv_cmds,
|
||||||
|
["6bffd098-a112-3610-9833-46c3f87e345a"] = wksta_cmds,
|
||||||
|
} &redef &default=function(i: string):rpc_cmd_table { return table() &default=function(j: string):string { return fmt("unknown-uuid-%s", j); }; };
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
module SMB1;
|
module SMB1;
|
||||||
|
@ -88,6 +195,40 @@ export {
|
||||||
[0xD9] = "WRITE_BULK",
|
[0xD9] = "WRITE_BULK",
|
||||||
[0xDA] = "WRITE_BULK_DATA",
|
[0xDA] = "WRITE_BULK_DATA",
|
||||||
} &default=function(i: count):string { return fmt("unknown-%d", i); };
|
} &default=function(i: count):string { return fmt("unknown-%d", i); };
|
||||||
|
|
||||||
|
const trans2_sub_commands: table[count] of string = {
|
||||||
|
[0x00] = "OPEN2",
|
||||||
|
[0x01] = "FIND_FIRST2",
|
||||||
|
[0x02] = "FIND_NEXT2",
|
||||||
|
[0x03] = "QUERY_FS_INFORMATION",
|
||||||
|
[0x04] = "SET_FS_INFORMATION",
|
||||||
|
[0x05] = "QUERY_PATH_INFORMATION",
|
||||||
|
[0x06] = "SET_PATH_INFORMATION",
|
||||||
|
[0x07] = "QUERY_FILE_INFORMATION",
|
||||||
|
[0x08] = "SET_FILE_INFORMATION",
|
||||||
|
[0x09] = "FSCTL",
|
||||||
|
[0x0A] = "IOCTL",
|
||||||
|
[0x0B] = "FIND_NOTIFY_FIRST",
|
||||||
|
[0x0C] = "FIND_NOTIFY_NEXT",
|
||||||
|
[0x0D] = "CREATE_DIRECTORY",
|
||||||
|
[0x0E] = "SESSION_SETUP",
|
||||||
|
[0x10] = "GET_DFS_REFERRAL",
|
||||||
|
[0x11] = "REPORT_DFS_INCONSISTENCY",
|
||||||
|
} &default=function(i: count):string { return fmt("unknown-trans2-sub-cmd-%d", i); };
|
||||||
|
|
||||||
|
const trans_sub_commands: table[count] of string = {
|
||||||
|
[0x01] = "SET_NMPIPE_STATE",
|
||||||
|
[0x11] = "RAW_READ_NMPIPE",
|
||||||
|
[0x21] = "QUERY_NMPIPE_STATE",
|
||||||
|
[0x22] = "QUERY_NMPIPE_INFO",
|
||||||
|
[0x23] = "PEEK_NMPIPE",
|
||||||
|
[0x26] = "TRANSACT_NMPIPE",
|
||||||
|
[0x31] = "RAW_WRITE_NMPIPE",
|
||||||
|
[0x36] = "READ_NMPIPE",
|
||||||
|
[0x37] = "WRITE_NMPIPE",
|
||||||
|
[0x53] = "WAIT_NMPIPE",
|
||||||
|
[0x54] = "CALL_NMPIPE",
|
||||||
|
} &default=function(i: count):string { return fmt("unknown-trans-sub-cmd-%d", i); };
|
||||||
}
|
}
|
||||||
|
|
||||||
module SMB2;
|
module SMB2;
|
||||||
|
|
|
@ -12,15 +12,15 @@ export {
|
||||||
|
|
||||||
function get_file_handle(c: connection, is_orig: bool): string
|
function get_file_handle(c: connection, is_orig: bool): string
|
||||||
{
|
{
|
||||||
if ( ! (c$smb?$current_file &&
|
if ( ! (c$smb_state?$current_file &&
|
||||||
((c$smb$current_file?$name && c$smb$current_file$name !in pipe_names) ||
|
((c$smb_state$current_file?$name && c$smb_state$current_file$name !in pipe_names) ||
|
||||||
c$smb$current_file?$path)) )
|
c$smb_state$current_file?$path)) )
|
||||||
{
|
{
|
||||||
# TODO: figure out what are the cases where this happens.
|
# TODO - figure out what are the cases where this happens.
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
local current_file = c$smb$current_file;
|
local current_file = c$smb_state$current_file;
|
||||||
local path_name = current_file?$path ? current_file$path : "";
|
local path_name = current_file?$path ? current_file$path : "";
|
||||||
local file_name = current_file?$name ? current_file$name : "";
|
local file_name = current_file?$name ? current_file$name : "";
|
||||||
# Include last_mod time if available because if a file has been modified it
|
# Include last_mod time if available because if a file has been modified it
|
||||||
|
@ -38,8 +38,8 @@ function describe_file(f: fa_file): string
|
||||||
for ( cid in f$conns )
|
for ( cid in f$conns )
|
||||||
{
|
{
|
||||||
local info = f$conns[cid];
|
local info = f$conns[cid];
|
||||||
if ( info?$smb && info$smb?$current_file && info$smb$current_file?$name )
|
if ( info?$smb_state && info$smb_state?$current_file && info$smb_state$current_file?$name )
|
||||||
return info$smb$current_file$name;
|
return info$smb_state$current_file$name;
|
||||||
}
|
}
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
@ -53,14 +53,14 @@ event bro_init() &priority=5
|
||||||
|
|
||||||
event file_over_new_connection(f: fa_file, c: connection, is_orig: bool) &priority=5
|
event file_over_new_connection(f: fa_file, c: connection, is_orig: bool) &priority=5
|
||||||
{
|
{
|
||||||
if ( c?$smb && c$smb?$current_file )
|
if ( c?$smb_state && c$smb_state?$current_file )
|
||||||
{
|
{
|
||||||
c$smb$current_file$fuid = f$id;
|
c$smb_state$current_file$fuid = f$id;
|
||||||
|
|
||||||
if ( c$smb$current_file$size > 0 )
|
if ( c$smb_state$current_file$size > 0 )
|
||||||
f$total_bytes = c$smb$current_file$size;
|
f$total_bytes = c$smb_state$current_file$size;
|
||||||
|
|
||||||
if ( c$smb$current_file?$name )
|
if ( c$smb_state$current_file?$name )
|
||||||
f$info$filename = c$smb$current_file$name;
|
f$info$filename = c$smb_state$current_file$name;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
@load ./consts
|
||||||
|
|
||||||
module SMB;
|
module SMB;
|
||||||
|
|
||||||
export {
|
export {
|
||||||
|
@ -16,118 +18,132 @@ export {
|
||||||
FILE_UNKNOWN,
|
FILE_UNKNOWN,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
## The file actions which are logged.
|
||||||
const logged_file_actions: set[FileAction] = {
|
const logged_file_actions: set[FileAction] = {
|
||||||
FILE_OPEN,
|
FILE_OPEN,
|
||||||
FILE_READ,
|
FILE_READ,
|
||||||
FILE_WRITE,
|
FILE_WRITE,
|
||||||
};
|
} &redef;
|
||||||
|
|
||||||
## These are files names that are used for special
|
## The server response statuses which are *not* logged.
|
||||||
## cases by the file system and would not be
|
const ignored_command_statuses: set[string] = {
|
||||||
## considered "normal" files.
|
"MORE_PROCESSING_REQUIRED",
|
||||||
const pipe_names: set[string] = {
|
} &redef;
|
||||||
"\\netdfs",
|
|
||||||
"\\spoolss",
|
|
||||||
"\\NETLOGON",
|
|
||||||
"\\winreg",
|
|
||||||
"\\lsarpc",
|
|
||||||
"\\samr",
|
|
||||||
"\\srvsvc",
|
|
||||||
"srvsvc",
|
|
||||||
"MsFteWds",
|
|
||||||
"\\wkssvc",
|
|
||||||
};
|
|
||||||
|
|
||||||
|
## This record is for the smb_files.log
|
||||||
type FileInfo: record {
|
type FileInfo: record {
|
||||||
## Time when the file was first discovered.
|
## Time when the file was first discovered.
|
||||||
ts : time &log;
|
ts : time &log;
|
||||||
uid : string &log;
|
## Unique ID of the connection the file was sent over.
|
||||||
id : conn_id &log;
|
uid : string &log;
|
||||||
fuid : 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 this log record represents.
|
||||||
action : FileAction &log &default=FILE_UNKNOWN;
|
action : FileAction &log &default=FILE_UNKNOWN;
|
||||||
|
|
||||||
## Path pulled from the tree this file was transferred to or from.
|
## Path pulled from the tree this file was transferred to or from.
|
||||||
path : string &log &optional;
|
path : string &log &optional;
|
||||||
## Filename if one was seen.
|
## Filename if one was seen.
|
||||||
name : string &log &optional;
|
name : string &log &optional;
|
||||||
|
|
||||||
## Total size of the file.
|
## Total size of the file.
|
||||||
size : count &log &default=0;
|
size : count &log &default=0;
|
||||||
## Last time this file was modified.
|
## Last time this file was modified.
|
||||||
times : SMB::MACTimes &log &optional;
|
times : SMB::MACTimes &log &optional;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
## This record is for the smb_mapping.log
|
||||||
type TreeInfo: record {
|
type TreeInfo: record {
|
||||||
## Time when the tree was mapped.
|
## Time when the tree was mapped.
|
||||||
ts : time &log &optional;
|
ts : time &log &optional;
|
||||||
|
## Unique ID of the connection the tree was mapped over.
|
||||||
uid : string &log;
|
uid : string &log;
|
||||||
id : conn_id &log;
|
## ID of the connection the tree was mapped over.
|
||||||
|
id : conn_id &log;
|
||||||
|
|
||||||
## Name of the tree path.
|
## Name of the tree path.
|
||||||
path : string &log &optional;
|
path : string &log &optional;
|
||||||
service : string &log &optional;
|
## The type of resource of the tree (disk share, printer share, named pipe, etc.)
|
||||||
native_file_system : string &log &optional;
|
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.
|
## If this is SMB2, a share type will be included.
|
||||||
share_type : string &log &optional;
|
share_type : string &log &optional;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
## This record is for the smb_cmd.log
|
||||||
type CmdInfo: record {
|
type CmdInfo: record {
|
||||||
## The command.
|
## Timestamp of the command request
|
||||||
command : string &optional;
|
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;
|
||||||
|
|
||||||
## If the command referenced a file, store it here.
|
## The command sent by the client
|
||||||
referenced_file : FileInfo &optional;
|
command : string &log;
|
||||||
## If the command referenced a tree, store it here.
|
## The subcommand sent by the client, if present
|
||||||
referenced_tree : TreeInfo &optional;
|
sub_command : string &log &optional;
|
||||||
};
|
## Command argument sent by the client, if any
|
||||||
|
argument : string &log &optional;
|
||||||
type Info: record {
|
|
||||||
ts: time &log;
|
|
||||||
uid: string &log;
|
|
||||||
id: conn_id &log;
|
|
||||||
|
|
||||||
## Version of SMB for the command.
|
|
||||||
version: string &log;
|
|
||||||
|
|
||||||
## Command sent by the client.
|
|
||||||
command: string &log &optional;
|
|
||||||
|
|
||||||
## Server reply to the client's command
|
## Server reply to the client's command
|
||||||
status: string &log &optional;
|
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
|
## If this is related to a tree, this is the tree
|
||||||
## that was used for the current command.
|
## that was used for the current command.
|
||||||
tree: string &log &optional;
|
tree : string &log &optional;
|
||||||
|
## The type of tree (disk share, printer share, named pipe, etc.)
|
||||||
|
tree_service : string &log &optional;
|
||||||
|
|
||||||
## The negotiated dialect for the connection.
|
## If the command referenced a file, store it here.
|
||||||
dialect: string &log &optional;
|
referenced_file : FileInfo &optional;
|
||||||
|
## If the command referenced a tree, store it here.
|
||||||
## Round trip time from the request to the response.
|
referenced_tree : TreeInfo &optional;
|
||||||
rtt: interval &log &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.
|
## A reference to the current command.
|
||||||
current_cmd : CmdInfo &optional;
|
current_cmd : CmdInfo &optional;
|
||||||
|
|
||||||
## A reference to the current file.
|
## A reference to the current file.
|
||||||
current_file : FileInfo &optional;
|
current_file : FileInfo &optional;
|
||||||
|
|
||||||
## A reference to the current tree.
|
## A reference to the current tree.
|
||||||
current_tree : TreeInfo &optional;
|
current_tree : TreeInfo &optional;
|
||||||
|
|
||||||
## Indexed on MID to map responses to requests.
|
## Indexed on MID to map responses to requests.
|
||||||
pending_cmds : table[count] of CmdInfo &optional;
|
pending_cmds: table[count] of CmdInfo &optional;
|
||||||
## File map to retrieve file information based on the file ID.
|
## File map to retrieve file information based on the file ID.
|
||||||
fid_map : table[count] of FileInfo &optional;
|
fid_map : table[count] of FileInfo &optional;
|
||||||
## Tree map to retrieve tree information based on the tree ID.
|
## Tree map to retrieve tree information based on the tree ID.
|
||||||
tid_map : table[count] of TreeInfo &optional;
|
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;
|
||||||
};
|
};
|
||||||
|
|
||||||
redef record connection += {
|
redef record connection += {
|
||||||
smb : Info &optional;
|
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",
|
||||||
};
|
};
|
||||||
|
|
||||||
## Optionally write out the SMB commands log. This is
|
## Optionally write out the SMB commands log. This is
|
||||||
|
@ -135,22 +151,21 @@ export {
|
||||||
const write_cmd_log = F &redef;
|
const write_cmd_log = F &redef;
|
||||||
|
|
||||||
## This is an internally used function.
|
## This is an internally used function.
|
||||||
const set_current_file: function(smb: Info, file_id: count) &redef;
|
const set_current_file: function(smb_state: State, file_id: count) &redef;
|
||||||
|
|
||||||
## This is an internally used function.
|
## This is an internally used function.
|
||||||
const write_file_log: function(f: FileInfo) &redef;
|
const write_file_log: function(f: FileInfo) &redef;
|
||||||
}
|
}
|
||||||
|
|
||||||
redef record connection += {
|
|
||||||
smb_pending_cmds : table[count, count] of Info &default=table();
|
|
||||||
};
|
|
||||||
|
|
||||||
redef record FileInfo += {
|
redef record FileInfo += {
|
||||||
## ID referencing this file.
|
## ID referencing this file.
|
||||||
fid : count &optional;
|
fid : count &optional;
|
||||||
|
|
||||||
## Maintain a reference to the file record.
|
## Maintain a reference to the file record.
|
||||||
f : fa_file &optional;
|
f : fa_file &optional;
|
||||||
|
|
||||||
|
## UUID referencing this file if DCE/RPC
|
||||||
|
uuid: string &optional;
|
||||||
};
|
};
|
||||||
|
|
||||||
const ports = { 139/tcp, 445/tcp };
|
const ports = { 139/tcp, 445/tcp };
|
||||||
|
@ -158,22 +173,22 @@ redef likely_server_ports += { ports };
|
||||||
|
|
||||||
event bro_init() &priority=5
|
event bro_init() &priority=5
|
||||||
{
|
{
|
||||||
Log::create_stream(CMD_LOG, [$columns=SMB::Info]);
|
Log::create_stream(CMD_LOG, [$columns=SMB::CmdInfo]);
|
||||||
Log::create_stream(FILES_LOG, [$columns=SMB::FileInfo]);
|
Log::create_stream(FILES_LOG, [$columns=SMB::FileInfo]);
|
||||||
Log::create_stream(MAPPING_LOG, [$columns=SMB::TreeInfo]);
|
Log::create_stream(MAPPING_LOG, [$columns=SMB::TreeInfo]);
|
||||||
|
|
||||||
Analyzer::register_for_ports(Analyzer::ANALYZER_SMB, ports);
|
Analyzer::register_for_ports(Analyzer::ANALYZER_SMB, ports);
|
||||||
}
|
}
|
||||||
|
|
||||||
function set_current_file(smb: Info, file_id: count)
|
function set_current_file(smb_state: State, file_id: count)
|
||||||
{
|
{
|
||||||
if ( file_id !in smb$fid_map )
|
if ( file_id !in smb_state$fid_map )
|
||||||
{
|
{
|
||||||
smb$fid_map[file_id] = smb$current_cmd$referenced_file;
|
smb_state$fid_map[file_id] = smb_state$current_cmd$referenced_file;
|
||||||
smb$fid_map[file_id]$fid = file_id;
|
smb_state$fid_map[file_id]$fid = file_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
smb$current_file = smb$fid_map[file_id];
|
smb_state$current_file = smb_state$fid_map[file_id];
|
||||||
}
|
}
|
||||||
|
|
||||||
function write_file_log(f: FileInfo)
|
function write_file_log(f: FileInfo)
|
||||||
|
@ -194,9 +209,9 @@ event file_state_remove(f: fa_file) &priority=-5
|
||||||
for ( id in f$conns )
|
for ( id in f$conns )
|
||||||
{
|
{
|
||||||
local c = f$conns[id];
|
local c = f$conns[id];
|
||||||
if ( c?$smb && c$smb?$current_file)
|
if ( c?$smb_state && c$smb_state?$current_file)
|
||||||
{
|
{
|
||||||
write_file_log(c$smb$current_file);
|
write_file_log(c$smb_state$current_file);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,197 +1,355 @@
|
||||||
module SMB1;
|
module SMB1;
|
||||||
|
|
||||||
redef record SMB::Info += {
|
redef record SMB::CmdInfo += {
|
||||||
|
## Dialects offered by the client
|
||||||
smb1_offered_dialects: string_vec &optional;
|
smb1_offered_dialects: string_vec &optional;
|
||||||
};
|
};
|
||||||
|
|
||||||
event smb1_message(c: connection, hdr: SMB1::Header, is_orig: bool) &priority=5
|
event smb1_message(c: connection, hdr: SMB1::Header, is_orig: bool) &priority=5
|
||||||
{
|
{
|
||||||
if ( ! c?$smb )
|
if ( ! c?$smb_state )
|
||||||
{
|
{
|
||||||
local info: SMB::Info = [$ts=network_time(), $uid=c$uid, $id=c$id, $version="SMB1"];
|
local state: SMB::State;
|
||||||
info$fid_map = table();
|
state$fid_map = table();
|
||||||
info$tid_map = table();
|
state$tid_map = table();
|
||||||
info$pending_cmds = table();
|
state$uid_map = table();
|
||||||
c$smb = info;
|
state$pipe_map = table();
|
||||||
|
state$pending_cmds = table();
|
||||||
|
c$smb_state = state;
|
||||||
}
|
}
|
||||||
|
|
||||||
local smb = c$smb;
|
local smb_state = c$smb_state;
|
||||||
local tid = hdr$tid;
|
local tid = hdr$tid;
|
||||||
local pid = hdr$pid;
|
|
||||||
local uid = hdr$uid;
|
local uid = hdr$uid;
|
||||||
|
local pid = hdr$pid;
|
||||||
local mid = hdr$mid;
|
local mid = hdr$mid;
|
||||||
|
|
||||||
if ( tid !in smb$tid_map )
|
if ( uid in smb_state$uid_map )
|
||||||
|
{
|
||||||
|
smb_state$current_cmd$username = smb_state$uid_map[uid];
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( tid !in smb_state$tid_map )
|
||||||
{
|
{
|
||||||
local tmp_tree: SMB::TreeInfo = [$uid=c$uid, $id=c$id];
|
local tmp_tree: SMB::TreeInfo = [$uid=c$uid, $id=c$id];
|
||||||
smb$tid_map[tid] = tmp_tree;
|
smb_state$tid_map[tid] = tmp_tree;
|
||||||
}
|
}
|
||||||
smb$current_tree = smb$tid_map[tid];
|
smb_state$current_tree = smb_state$tid_map[tid];
|
||||||
|
if ( smb_state$current_tree?$path )
|
||||||
if ( mid !in smb$pending_cmds )
|
|
||||||
{
|
{
|
||||||
local tmp_cmd: SMB::CmdInfo;
|
smb_state$current_cmd$tree = smb_state$current_tree$path;
|
||||||
tmp_cmd$command = SMB1::commands[hdr$command];
|
}
|
||||||
|
|
||||||
local tmp_file: SMB::FileInfo;
|
if ( smb_state$current_tree?$service )
|
||||||
tmp_file$ts = network_time();
|
{
|
||||||
tmp_file$id = c$id;
|
smb_state$current_cmd$tree_service = smb_state$current_tree$service;
|
||||||
tmp_file$uid = c$uid;
|
}
|
||||||
|
|
||||||
|
if ( mid !in smb_state$pending_cmds )
|
||||||
|
{
|
||||||
|
local tmp_cmd: SMB::CmdInfo = [$ts=network_time(), $uid=c$uid, $id=c$id, $version="SMB1", $command = SMB1::commands[hdr$command]];
|
||||||
|
|
||||||
|
local tmp_file: SMB::FileInfo = [$ts=network_time(), $uid=c$uid, $id=c$id];
|
||||||
tmp_cmd$referenced_file = tmp_file;
|
tmp_cmd$referenced_file = tmp_file;
|
||||||
tmp_cmd$referenced_tree = smb$current_tree;
|
tmp_cmd$referenced_tree = smb_state$current_tree;
|
||||||
|
|
||||||
smb$pending_cmds[mid] = tmp_cmd;
|
smb_state$pending_cmds[mid] = tmp_cmd;
|
||||||
}
|
}
|
||||||
|
|
||||||
smb$current_cmd = smb$pending_cmds[mid];
|
smb_state$current_cmd = smb_state$pending_cmds[mid];
|
||||||
smb$command = smb$current_cmd$command;
|
|
||||||
|
|
||||||
if ( is_orig )
|
if ( !is_orig )
|
||||||
{
|
{
|
||||||
smb$ts = network_time();
|
smb_state$current_cmd$rtt = network_time() - smb_state$current_cmd$ts;
|
||||||
}
|
smb_state$current_cmd$status = SMB::statuses[hdr$status]$id;
|
||||||
else
|
|
||||||
{
|
|
||||||
smb$rtt = network_time() - smb$ts;
|
|
||||||
smb$status = SMB::statuses[hdr$status]$id;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
event smb1_message(c: connection, hdr: SMB1::Header, is_orig: bool) &priority=-5
|
event smb1_message(c: connection, hdr: SMB1::Header, is_orig: bool) &priority=-5
|
||||||
{
|
{
|
||||||
|
# Is this a response?
|
||||||
if ( !is_orig )
|
if ( !is_orig )
|
||||||
# This is a response and the command is no longer pending
|
{
|
||||||
# so let's get rid of it.
|
if ( SMB::write_cmd_log &&
|
||||||
delete c$smb$pending_cmds[hdr$mid];
|
c$smb_state$current_cmd$status !in SMB::ignored_command_statuses &&
|
||||||
|
c$smb_state$current_cmd$command !in SMB::deferred_logging_cmds )
|
||||||
|
{
|
||||||
|
Log::write(SMB::CMD_LOG, c$smb_state$current_cmd);
|
||||||
|
}
|
||||||
|
delete c$smb_state$pending_cmds[hdr$mid];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if ( SMB::write_cmd_log && c?$smb )
|
|
||||||
Log::write(SMB::CMD_LOG, c$smb);
|
event smb1_transaction2_request(c: connection, hdr: SMB1::Header, sub_cmd: count)
|
||||||
|
{
|
||||||
|
c$smb_state$current_cmd$sub_command = SMB1::trans2_sub_commands[sub_cmd];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
event smb1_negotiate_request(c: connection, hdr: SMB1::Header, dialects: string_vec) &priority=5
|
event smb1_negotiate_request(c: connection, hdr: SMB1::Header, dialects: string_vec) &priority=5
|
||||||
{
|
{
|
||||||
c$smb$smb1_offered_dialects = dialects;
|
c$smb_state$current_cmd$smb1_offered_dialects = dialects;
|
||||||
}
|
}
|
||||||
|
|
||||||
event smb1_negotiate_response(c: connection, hdr: SMB1::Header, response: SMB1::NegotiateResponse) &priority=5
|
event smb1_negotiate_response(c: connection, hdr: SMB1::Header, response: SMB1::NegotiateResponse) &priority=5
|
||||||
{
|
{
|
||||||
if ( c$smb?$smb1_offered_dialects )
|
if ( c$smb_state$current_cmd?$smb1_offered_dialects )
|
||||||
{
|
{
|
||||||
if ( response?$ntlm )
|
if ( response?$ntlm )
|
||||||
c$smb$dialect = c$smb$smb1_offered_dialects[response$ntlm$dialect_index];
|
{
|
||||||
delete c$smb$smb1_offered_dialects;
|
c$smb_state$current_cmd$argument = c$smb_state$current_cmd$smb1_offered_dialects[response$ntlm$dialect_index];
|
||||||
|
}
|
||||||
|
|
||||||
|
delete c$smb_state$current_cmd$smb1_offered_dialects;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
event smb1_negotiate_response(c: connection, hdr: SMB1::Header, response: SMB1::NegotiateResponse) &priority=-5
|
||||||
|
{
|
||||||
|
if ( c$smb_state$current_cmd$status !in SMB::ignored_command_statuses )
|
||||||
|
{
|
||||||
|
Log::write(SMB::CMD_LOG, c$smb_state$current_cmd);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
event smb1_tree_connect_andx_request(c: connection, hdr: SMB1::Header, path: string, service: string) &priority=5
|
event smb1_tree_connect_andx_request(c: connection, hdr: SMB1::Header, path: string, service: string) &priority=5
|
||||||
{
|
{
|
||||||
c$smb$current_cmd$referenced_tree$path = path;
|
local tmp_tree: SMB::TreeInfo = [$ts=network_time(), $uid=c$uid, $id=c$id, $path=path, $service=service];
|
||||||
c$smb$current_cmd$referenced_tree$service = service;
|
|
||||||
c$smb$current_tree$ts=network_time();
|
c$smb_state$current_cmd$referenced_tree = tmp_tree;
|
||||||
|
c$smb_state$current_cmd$argument = path;
|
||||||
}
|
}
|
||||||
|
|
||||||
event smb1_tree_connect_andx_response(c: connection, hdr: SMB1::Header, service: string, native_file_system: string) &priority=5
|
event smb1_tree_connect_andx_response(c: connection, hdr: SMB1::Header, service: string, native_file_system: string) &priority=5
|
||||||
{
|
{
|
||||||
c$smb$current_cmd$referenced_tree$native_file_system = native_file_system;
|
c$smb_state$current_cmd$referenced_tree$service = service;
|
||||||
c$smb$current_tree = c$smb$current_cmd$referenced_tree;
|
c$smb_state$current_cmd$tree_service = service;
|
||||||
c$smb$tid_map[hdr$tid] = c$smb$current_tree;
|
|
||||||
|
c$smb_state$current_cmd$referenced_tree$native_file_system = native_file_system;
|
||||||
|
|
||||||
|
c$smb_state$current_tree = c$smb_state$current_cmd$referenced_tree;
|
||||||
|
c$smb_state$tid_map[hdr$tid] = c$smb_state$current_tree;
|
||||||
}
|
}
|
||||||
|
|
||||||
event smb1_tree_connect_andx_response(c: connection, hdr: SMB1::Header, service: string, native_file_system: string) &priority=-5
|
event smb1_tree_connect_andx_response(c: connection, hdr: SMB1::Header, service: string, native_file_system: string) &priority=-5
|
||||||
{
|
{
|
||||||
Log::write(SMB::MAPPING_LOG, c$smb$current_tree);
|
Log::write(SMB::MAPPING_LOG, c$smb_state$current_tree);
|
||||||
|
|
||||||
|
if ( c$smb_state$current_cmd$status !in SMB::ignored_command_statuses )
|
||||||
|
{
|
||||||
|
Log::write(SMB::CMD_LOG, c$smb_state$current_cmd);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
event smb1_nt_create_andx_request(c: connection, hdr: SMB1::Header, name: string) &priority=5
|
event smb1_nt_create_andx_request(c: connection, hdr: SMB1::Header, name: string) &priority=5
|
||||||
{
|
{
|
||||||
c$smb$current_cmd$referenced_file$name = name;
|
local tmp_file: SMB::FileInfo = [$ts=network_time(), $uid=c$uid, $id=c$id];
|
||||||
c$smb$current_file = c$smb$current_cmd$referenced_file;
|
c$smb_state$current_cmd$referenced_file = tmp_file;
|
||||||
c$smb$current_file$action = SMB::FILE_OPEN;
|
c$smb_state$current_cmd$referenced_file$name = name;
|
||||||
|
c$smb_state$current_cmd$referenced_file$action = SMB::FILE_OPEN;
|
||||||
|
c$smb_state$current_file = c$smb_state$current_cmd$referenced_file;
|
||||||
|
c$smb_state$current_cmd$argument = name;
|
||||||
}
|
}
|
||||||
|
|
||||||
event smb1_nt_create_andx_response(c: connection, hdr: SMB1::Header, file_id: count, file_size: count, times: SMB::MACTimes) &priority=5
|
event smb1_nt_create_andx_response(c: connection, hdr: SMB1::Header, file_id: count, file_size: count, times: SMB::MACTimes) &priority=5
|
||||||
{
|
{
|
||||||
if ( ! c$smb?$current_file )
|
c$smb_state$current_cmd$referenced_file$action = SMB::FILE_OPEN;
|
||||||
{
|
c$smb_state$current_cmd$referenced_file$fid = file_id;
|
||||||
c$smb$current_file = c$smb$current_cmd$referenced_file;
|
c$smb_state$current_cmd$referenced_file$size = file_size;
|
||||||
c$smb$current_file$action = SMB::FILE_OPEN;
|
|
||||||
}
|
|
||||||
c$smb$current_file$fid = file_id;
|
|
||||||
c$smb$current_file$size = file_size;
|
|
||||||
|
|
||||||
# I'm seeing negative data from IPC tree transfers
|
# I'm seeing negative data from IPC tree transfers
|
||||||
if ( time_to_double(times$modified) > 0.0 )
|
if ( time_to_double(times$modified) > 0.0 )
|
||||||
c$smb$current_file$times = times;
|
c$smb_state$current_cmd$referenced_file$times = times;
|
||||||
|
|
||||||
# We can identify the file by its file id now so let's stick it
|
# We can identify the file by its file id now so let's stick it
|
||||||
# in the file map.
|
# in the file map.
|
||||||
c$smb$fid_map[file_id] = c$smb$current_file;
|
c$smb_state$fid_map[file_id] = c$smb_state$current_cmd$referenced_file;
|
||||||
}
|
|
||||||
|
|
||||||
event smb1_nt_create_andx_response(c: connection, hdr: SMB1::Header, file_id: count, file_size: count, times: SMB::MACTimes) &priority=-5
|
c$smb_state$current_file = c$smb_state$fid_map[file_id];
|
||||||
{
|
|
||||||
SMB::write_file_log(c$smb$current_file);
|
|
||||||
|
SMB::write_file_log(c$smb_state$current_file);
|
||||||
}
|
}
|
||||||
|
|
||||||
event smb1_read_andx_request(c: connection, hdr: SMB1::Header, file_id: count, offset: count, length: count) &priority=5
|
event smb1_read_andx_request(c: connection, hdr: SMB1::Header, file_id: count, offset: count, length: count) &priority=5
|
||||||
{
|
{
|
||||||
SMB::set_current_file(c$smb, file_id);
|
SMB::set_current_file(c$smb_state, file_id);
|
||||||
c$smb$current_file$action = SMB::FILE_READ;
|
c$smb_state$current_file$action = SMB::FILE_READ;
|
||||||
|
c$smb_state$current_cmd$argument = c$smb_state$current_file$name;
|
||||||
|
}
|
||||||
|
|
||||||
if ( c$smb$current_tree?$path && !c$smb$current_file?$path )
|
event smb1_read_andx_request(c: connection, hdr: SMB1::Header, file_id: count, offset: count, length: count) &priority=-5
|
||||||
c$smb$current_file$path = c$smb$current_tree$path;
|
{
|
||||||
|
if ( c$smb_state$current_tree?$path && !c$smb_state$current_file?$path )
|
||||||
|
c$smb_state$current_file$path = c$smb_state$current_tree$path;
|
||||||
|
|
||||||
#write_file_log(c$smb$current_file);
|
# TODO - Why is this commented out?
|
||||||
|
#write_file_log(c$smb_state$current_file);
|
||||||
}
|
}
|
||||||
|
|
||||||
event smb1_read_andx_response(c: connection, hdr: SMB1::Header, data_len: count) &priority=5
|
event smb1_read_andx_response(c: connection, hdr: SMB1::Header, data_len: count) &priority=5
|
||||||
{
|
{
|
||||||
#print "read andx response!";
|
if ( c$smb_state$current_cmd$status !in SMB::ignored_command_statuses )
|
||||||
|
{
|
||||||
|
Log::write(SMB::CMD_LOG, c$smb_state$current_cmd);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
event smb1_write_andx_request(c: connection, hdr: SMB1::Header, file_id: count, offset: count, data_len: count) &priority=5
|
event smb1_write_andx_request(c: connection, hdr: SMB1::Header, file_id: count, offset: count, data_len: count) &priority=5
|
||||||
{
|
{
|
||||||
SMB::set_current_file(c$smb, file_id);
|
SMB::set_current_file(c$smb_state, file_id);
|
||||||
c$smb$current_file$action = SMB::FILE_WRITE;
|
c$smb_state$current_file$action = SMB::FILE_WRITE;
|
||||||
|
if ( !c$smb_state$current_cmd?$argument )
|
||||||
|
c$smb_state$current_cmd$argument = c$smb_state$current_file$name;
|
||||||
}
|
}
|
||||||
|
|
||||||
event smb1_write_andx_request(c: connection, hdr: SMB1::Header, file_id: count, offset: count, data_len: count) &priority=-5
|
event smb1_write_andx_request(c: connection, hdr: SMB1::Header, file_id: count, offset: count, data_len: count) &priority=-5
|
||||||
{
|
{
|
||||||
if ( c$smb$current_tree?$path && !c$smb$current_file?$path )
|
if ( c$smb_state$current_tree?$path && !c$smb_state$current_file?$path )
|
||||||
c$smb$current_file$path = c$smb$current_tree$path;
|
c$smb_state$current_file$path = c$smb_state$current_tree$path;
|
||||||
|
|
||||||
#write_file_log(c$smb$current_file);
|
# TODO - Why is this commented out?
|
||||||
|
#write_file_log(c$smb_state$current_file);
|
||||||
}
|
}
|
||||||
|
|
||||||
#event smb1_write_andx_response(c: connection, hdr: SMB1::Header, written_bytes: count) &priority=5
|
#event smb1_write_andx_response(c: connection, hdr: SMB1::Header, written_bytes: count) &priority=5
|
||||||
# {
|
# {
|
||||||
# # Do i really need to do anything here? Maybe do a weird if the number of bytes written is odd?
|
# # TODO - determine what to do here
|
||||||
# }
|
# }
|
||||||
|
|
||||||
event smb1_close_request(c: connection, hdr: SMB1::Header, file_id: count) &priority=5
|
event smb1_close_request(c: connection, hdr: SMB1::Header, file_id: count) &priority=5
|
||||||
{
|
{
|
||||||
SMB::set_current_file(c$smb, file_id);
|
SMB::set_current_file(c$smb_state, file_id);
|
||||||
c$smb$current_file$action = SMB::FILE_CLOSE;
|
c$smb_state$current_file$action = SMB::FILE_CLOSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
event smb1_close_request(c: connection, hdr: SMB1::Header, file_id: count) &priority=-5
|
event smb1_close_request(c: connection, hdr: SMB1::Header, file_id: count) &priority=-5
|
||||||
{
|
{
|
||||||
if ( file_id in c$smb$fid_map )
|
if ( file_id in c$smb_state$fid_map )
|
||||||
{
|
{
|
||||||
local fl = c$smb$fid_map[file_id];
|
local fl = c$smb_state$fid_map[file_id];
|
||||||
fl$uid = c$uid;
|
|
||||||
fl$id = c$id;
|
|
||||||
# Need to check for existence of path in case tree connect message wasn't seen.
|
# Need to check for existence of path in case tree connect message wasn't seen.
|
||||||
if ( c$smb$current_tree?$path )
|
if ( c$smb_state$current_tree?$path )
|
||||||
fl$path = c$smb$current_tree$path;
|
fl$path = c$smb_state$current_tree$path;
|
||||||
delete c$smb$fid_map[file_id];
|
|
||||||
|
c$smb_state$current_cmd$argument = fl$name;
|
||||||
|
|
||||||
|
delete c$smb_state$fid_map[file_id];
|
||||||
|
|
||||||
SMB::write_file_log(fl);
|
SMB::write_file_log(fl);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
# TODO - Determine correct action
|
||||||
# A reporter message is not right...
|
# A reporter message is not right...
|
||||||
#Reporter::warning("attempting to close an unknown file!");
|
#Reporter::warning("attempting to close an unknown file!");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
event smb1_trans2_get_dfs_referral_request(c: connection, hdr: SMB1::Header, file_name: string, max_referral_level: count)
|
||||||
|
{
|
||||||
|
c$smb_state$current_cmd$argument = file_name;
|
||||||
|
}
|
||||||
|
|
||||||
|
event smb1_trans2_query_path_info_request(c: connection, hdr: SMB1::Header, file_name: string, level_of_interets: count)
|
||||||
|
{
|
||||||
|
c$smb_state$current_cmd$argument = file_name;
|
||||||
|
}
|
||||||
|
|
||||||
|
event smb1_trans2_find_first2_request(c: connection, hdr: SMB1::Header, args: SMB1::Find_First2_Request_Args)
|
||||||
|
{
|
||||||
|
c$smb_state$current_cmd$argument = args$file_name;
|
||||||
|
}
|
||||||
|
|
||||||
|
event smb1_session_setup_andx_response(c: connection, hdr: SMB1::Header, response: SMB1::SessionSetupAndXResponse) &priority=-5
|
||||||
|
{
|
||||||
|
if ( c$smb_state$current_cmd$status !in SMB::ignored_command_statuses )
|
||||||
|
{
|
||||||
|
Log::write(SMB::CMD_LOG, c$smb_state$current_cmd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
event smb_ntlm_negotiate(c: connection, hdr: SMB1::Header, request: SMB::NTLMNegotiate)
|
||||||
|
{
|
||||||
|
c$smb_state$current_cmd$sub_command = "NTLMSSP_NEGOTIATE";
|
||||||
|
}
|
||||||
|
|
||||||
|
event smb1_error(c: connection, hdr: SMB1::Header, is_orig: bool)
|
||||||
|
{
|
||||||
|
if ( ! is_orig )
|
||||||
|
{
|
||||||
|
# This is for deferred commands only.
|
||||||
|
# The more specific messages won't fire for errors
|
||||||
|
if ( ( c$smb_state$current_cmd$status !in SMB::ignored_command_statuses ) &&
|
||||||
|
( c$smb_state$current_cmd$command in SMB::deferred_logging_cmds ) )
|
||||||
|
{
|
||||||
|
Log::write(SMB::CMD_LOG, c$smb_state$current_cmd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
event smb_ntlm_authenticate(c: connection, hdr: SMB1::Header, request: SMB::NTLMAuthenticate)
|
||||||
|
{
|
||||||
|
c$smb_state$current_cmd$sub_command = "NTLMSSP_AUTHENTICATE";
|
||||||
|
|
||||||
|
local user: string = "";
|
||||||
|
if ( ( request?$domain_name && request$domain_name != "" ) && ( request?$user_name && request$user_name != "" ) )
|
||||||
|
user = fmt("%s\\%s", request$domain_name, request$user_name);
|
||||||
|
else if ( ( request?$workstation && request$workstation != "" ) && ( request?$user_name && request$user_name != "" ) )
|
||||||
|
user = fmt("%s\\%s", request$workstation, request$user_name);
|
||||||
|
else if ( request?$user_name && request$user_name != "" )
|
||||||
|
user = request$user_name;
|
||||||
|
else if ( request?$domain_name && request$domain_name != "" )
|
||||||
|
user = fmt("%s\\", request$domain_name);
|
||||||
|
else if ( request?$workstation && request$workstation != "" )
|
||||||
|
user = fmt("%s", request$workstation);
|
||||||
|
|
||||||
|
if ( user != "" )
|
||||||
|
{
|
||||||
|
c$smb_state$current_cmd$argument = user;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( hdr$uid !in c$smb_state$uid_map )
|
||||||
|
{
|
||||||
|
c$smb_state$uid_map[hdr$uid] = user;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
event smb1_transaction_request(c: connection, hdr: SMB1::Header, name: string, sub_cmd: count)
|
||||||
|
{
|
||||||
|
c$smb_state$current_cmd$sub_command = SMB1::trans_sub_commands[sub_cmd];
|
||||||
|
}
|
||||||
|
|
||||||
|
event smb1_write_andx_request(c: connection, hdr: SMB1::Header, file_id: count, offset: count, data_len: count)
|
||||||
|
{
|
||||||
|
c$smb_state$pipe_map[file_id] = c$smb_state$current_file$uuid;
|
||||||
|
}
|
||||||
|
|
||||||
|
event smb_pipe_bind_ack_response(c: connection, hdr: SMB1::Header)
|
||||||
|
{
|
||||||
|
c$smb_state$current_cmd$sub_command = "RPC_BIND_ACK";
|
||||||
|
c$smb_state$current_cmd$argument = SMB::rpc_uuids[c$smb_state$current_file$uuid];
|
||||||
|
}
|
||||||
|
|
||||||
|
event smb_pipe_bind_request(c: connection, hdr: SMB1::Header, uuid: string, version: string)
|
||||||
|
{
|
||||||
|
c$smb_state$current_cmd$sub_command = "RPC_BIND";
|
||||||
|
c$smb_state$current_file$uuid = uuid;
|
||||||
|
c$smb_state$current_cmd$argument = fmt("%s v%s", SMB::rpc_uuids[uuid], version);
|
||||||
|
}
|
||||||
|
|
||||||
|
event smb_pipe_request(c: connection, hdr: SMB1::Header, op_num: count)
|
||||||
|
{
|
||||||
|
c$smb_state$current_cmd$argument = fmt("%s: %s", SMB::rpc_uuids[c$smb_state$current_file$uuid],
|
||||||
|
SMB::rpc_sub_cmds[c$smb_state$current_file$uuid][op_num]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#event smb1_transaction_setup(c: connection, hdr: SMB1::Header, op_code: count, file_id: count)
|
||||||
|
# {
|
||||||
|
# local uuid = SMB::rpc_uuids[c$smb_state$pipe_map[file_id]];
|
||||||
|
# if ( uuid in SMB::rpc_uuids )
|
||||||
|
# {
|
||||||
|
# print fmt("smb1_transaction_setup %s", SMB::rap_cmds[op_code]);
|
||||||
|
# }
|
||||||
|
# }
|
|
@ -1,141 +1,143 @@
|
||||||
module SMB2;
|
module SMB2;
|
||||||
|
|
||||||
redef record SMB::Info += {
|
redef record SMB::CmdInfo += {
|
||||||
|
## Dialects offered by the client
|
||||||
smb2_offered_dialects: index_vec &optional;
|
smb2_offered_dialects: index_vec &optional;
|
||||||
};
|
};
|
||||||
|
|
||||||
event smb2_message(c: connection, hdr: SMB2::Header, is_orig: bool) &priority=5
|
event smb2_message(c: connection, hdr: SMB2::Header, is_orig: bool) &priority=5
|
||||||
{
|
{
|
||||||
if ( ! c?$smb )
|
if ( ! c?$smb_state )
|
||||||
{
|
{
|
||||||
local info: SMB::Info = [$ts=network_time(), $uid=c$uid, $id=c$id, $version="SMB2"];
|
local state: SMB::State;
|
||||||
info$fid_map = table();
|
state$fid_map = table();
|
||||||
info$tid_map = table();
|
state$tid_map = table();
|
||||||
info$pending_cmds = table();
|
state$pending_cmds = table();
|
||||||
c$smb = info;
|
c$smb_state = state;
|
||||||
}
|
}
|
||||||
|
|
||||||
local smb = c$smb;
|
local smb_state = c$smb_state;
|
||||||
local tid = hdr$tree_id;
|
local tid = hdr$tree_id;
|
||||||
local pid = hdr$process_id;
|
local pid = hdr$process_id;
|
||||||
local mid = hdr$message_id;
|
local mid = hdr$message_id;
|
||||||
local sid = hdr$session_id;
|
local sid = hdr$session_id;
|
||||||
|
|
||||||
if ( tid !in smb$tid_map )
|
if ( tid !in smb_state$tid_map )
|
||||||
{
|
{
|
||||||
local tmp_tree: SMB::TreeInfo = [$uid=c$uid, $id=c$id];
|
local tmp_tree: SMB::TreeInfo = [$uid=c$uid, $id=c$id];
|
||||||
smb$tid_map[tid] = tmp_tree;
|
smb_state$tid_map[tid] = tmp_tree;
|
||||||
}
|
}
|
||||||
smb$current_tree = smb$tid_map[tid];
|
smb_state$current_tree = smb_state$tid_map[tid];
|
||||||
|
|
||||||
if ( mid !in smb$pending_cmds )
|
if ( mid !in smb_state$pending_cmds )
|
||||||
{
|
{
|
||||||
local tmp_cmd: SMB::CmdInfo;
|
local tmp_cmd: SMB::CmdInfo = [$ts=network_time(), $uid=c$uid, $id=c$id, $version="SMB2", $command = SMB2::commands[hdr$command]];
|
||||||
tmp_cmd$command = SMB2::commands[hdr$command];
|
|
||||||
|
|
||||||
local tmp_file: SMB::FileInfo;
|
local tmp_file: SMB::FileInfo = [$ts=network_time(), $uid=c$uid, $id=c$id];
|
||||||
tmp_file$ts = network_time();
|
|
||||||
tmp_file$id = c$id;
|
|
||||||
tmp_file$uid = c$uid;
|
|
||||||
tmp_cmd$referenced_file = tmp_file;
|
tmp_cmd$referenced_file = tmp_file;
|
||||||
tmp_cmd$referenced_tree = smb$current_tree;
|
tmp_cmd$referenced_tree = smb_state$current_tree;
|
||||||
|
|
||||||
smb$pending_cmds[mid] = tmp_cmd;
|
smb_state$pending_cmds[mid] = tmp_cmd;
|
||||||
}
|
}
|
||||||
|
|
||||||
smb$current_cmd = smb$pending_cmds[mid];
|
smb_state$current_cmd = smb_state$pending_cmds[mid];
|
||||||
smb$command = smb$current_cmd$command;
|
|
||||||
|
|
||||||
if ( is_orig )
|
if ( !is_orig )
|
||||||
{
|
{
|
||||||
smb$ts = network_time();
|
smb_state$current_cmd$rtt = network_time() - smb_state$current_cmd$ts;
|
||||||
}
|
smb_state$current_cmd$status = SMB::statuses[hdr$status]$id;
|
||||||
else
|
|
||||||
{
|
|
||||||
smb$rtt = network_time() - smb$ts;
|
|
||||||
smb$status = SMB::statuses[hdr$status]$id;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
event smb2_message(c: connection, hdr: SMB2::Header, is_orig: bool) &priority=-5
|
event smb2_message(c: connection, hdr: SMB2::Header, is_orig: bool) &priority=-5
|
||||||
{
|
{
|
||||||
|
# Is this a response?
|
||||||
if ( !is_orig )
|
if ( !is_orig )
|
||||||
# This is a response and the command is no longer pending
|
{
|
||||||
# so let's get rid of it.
|
if ( SMB::write_cmd_log &&
|
||||||
delete c$smb$pending_cmds[hdr$message_id];
|
c$smb_state$current_cmd$status !in SMB::ignored_command_statuses &&
|
||||||
|
c$smb_state$current_cmd$command !in SMB::deferred_logging_cmds )
|
||||||
if ( SMB::write_cmd_log && c?$smb )
|
{
|
||||||
Log::write(SMB::CMD_LOG, c$smb);
|
Log::write(SMB::CMD_LOG, c$smb_state$current_cmd);
|
||||||
|
}
|
||||||
|
delete c$smb_state$pending_cmds[hdr$message_id];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
event smb2_negotiate_request(c: connection, hdr: SMB2::Header, dialects: index_vec) &priority=5
|
event smb2_negotiate_request(c: connection, hdr: SMB2::Header, dialects: index_vec) &priority=5
|
||||||
{
|
{
|
||||||
c$smb$smb2_offered_dialects = dialects;
|
c$smb_state$current_cmd$smb2_offered_dialects = dialects;
|
||||||
}
|
}
|
||||||
|
|
||||||
event smb2_negotiate_response(c: connection, hdr: SMB2::Header, response: SMB2::NegotiateResponse)
|
event smb2_negotiate_response(c: connection, hdr: SMB2::Header, response: SMB2::NegotiateResponse) &priority=5
|
||||||
{
|
{
|
||||||
if ( c$smb?$smb2_offered_dialects )
|
if ( c$smb_state$current_cmd?$smb2_offered_dialects )
|
||||||
{
|
{
|
||||||
for ( i in c$smb$smb2_offered_dialects )
|
for ( i in c$smb_state$current_cmd$smb2_offered_dialects )
|
||||||
{
|
{
|
||||||
if ( response$dialect_revision == c$smb$smb2_offered_dialects[i] )
|
if ( response$dialect_revision == c$smb_state$current_cmd$smb2_offered_dialects[i] )
|
||||||
{
|
{
|
||||||
c$smb$dialect = SMB2::dialects[response$dialect_revision];
|
c$smb_state$current_cmd$argument = SMB2::dialects[response$dialect_revision];
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
delete c$smb$smb2_offered_dialects;
|
delete c$smb_state$current_cmd$smb2_offered_dialects;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
event smb2_negotiate_response(c: connection, hdr: SMB2::Header, response: SMB2::NegotiateResponse) &priority=5
|
||||||
|
{
|
||||||
|
if ( c$smb_state$current_cmd$status !in SMB::ignored_command_statuses )
|
||||||
|
{
|
||||||
|
Log::write(SMB::CMD_LOG, c$smb_state$current_cmd);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
event smb2_tree_connect_request(c: connection, hdr: SMB2::Header, path: string) &priority=5
|
event smb2_tree_connect_request(c: connection, hdr: SMB2::Header, path: string) &priority=5
|
||||||
{
|
{
|
||||||
c$smb$current_cmd$referenced_tree$path = path;
|
local tmp_tree: SMB::TreeInfo = [$ts=network_time(), $uid=c$uid, $id=c$id, $path=path];
|
||||||
c$smb$current_tree$ts=network_time();
|
|
||||||
|
c$smb_state$current_cmd$referenced_tree = tmp_tree;
|
||||||
}
|
}
|
||||||
|
|
||||||
event smb2_tree_connect_response(c: connection, hdr: SMB2::Header, response: SMB2::TreeConnectResponse) &priority=5
|
event smb2_tree_connect_response(c: connection, hdr: SMB2::Header, response: SMB2::TreeConnectResponse) &priority=5
|
||||||
{
|
{
|
||||||
c$smb$current_tree = c$smb$current_cmd$referenced_tree;
|
c$smb_state$current_cmd$referenced_tree$share_type = SMB2::share_types[response$share_type];
|
||||||
c$smb$current_tree$share_type = SMB2::share_types[response$share_type];
|
c$smb_state$current_tree = c$smb_state$current_cmd$referenced_tree;
|
||||||
c$smb$tid_map[hdr$tree_id] = c$smb$current_tree;
|
c$smb_state$tid_map[hdr$tree_id] = c$smb_state$current_tree;
|
||||||
}
|
}
|
||||||
|
|
||||||
event smb2_tree_connect_response(c: connection, hdr: SMB2::Header, response: SMB2::TreeConnectResponse) &priority=-5
|
event smb2_tree_connect_response(c: connection, hdr: SMB2::Header, response: SMB2::TreeConnectResponse) &priority=-5
|
||||||
{
|
{
|
||||||
Log::write(SMB::MAPPING_LOG, c$smb$current_tree);
|
Log::write(SMB::MAPPING_LOG, c$smb_state$current_tree);
|
||||||
}
|
}
|
||||||
|
|
||||||
event smb2_create_request(c: connection, hdr: SMB2::Header, name: string) &priority=5
|
event smb2_create_request(c: connection, hdr: SMB2::Header, name: string) &priority=5
|
||||||
{
|
{
|
||||||
c$smb$current_cmd$referenced_file$name = name;
|
local tmp_file: SMB::FileInfo = [$ts=network_time(), $uid=c$uid, $id=c$id];
|
||||||
c$smb$current_file = c$smb$current_cmd$referenced_file;
|
c$smb_state$current_cmd$referenced_file = tmp_file;
|
||||||
c$smb$current_file$action = SMB::FILE_OPEN;
|
c$smb_state$current_cmd$referenced_file$name = name;
|
||||||
|
c$smb_state$current_cmd$referenced_file$action = SMB::FILE_OPEN;
|
||||||
|
c$smb_state$current_file = c$smb_state$current_cmd$referenced_file;
|
||||||
}
|
}
|
||||||
|
|
||||||
event smb2_create_response(c: connection, hdr: SMB2::Header, file_id: SMB2::GUID, file_size: count, times: SMB::MACTimes, attrs: SMB2::FileAttrs) &priority=5
|
event smb2_create_response(c: connection, hdr: SMB2::Header, file_id: SMB2::GUID, file_size: count, times: SMB::MACTimes, attrs: SMB2::FileAttrs) &priority=5
|
||||||
{
|
{
|
||||||
if ( ! c$smb?$current_file )
|
c$smb_state$current_cmd$referenced_file$action = SMB::FILE_OPEN;
|
||||||
{
|
c$smb_state$current_cmd$referenced_file$fid = file_id$persistent+file_id$volatile;
|
||||||
c$smb$current_file = c$smb$current_cmd$referenced_file;
|
c$smb_state$current_cmd$referenced_file$size = file_size;
|
||||||
c$smb$current_file$action = SMB::FILE_OPEN;
|
|
||||||
}
|
|
||||||
c$smb$current_file$fid = file_id$persistent+file_id$volatile;
|
|
||||||
c$smb$current_file$size = file_size;
|
|
||||||
|
|
||||||
# I'm seeing negative data from IPC tree transfers
|
# I'm seeing negative data from IPC tree transfers
|
||||||
if ( time_to_double(times$modified) > 0.0 )
|
if ( time_to_double(times$modified) > 0.0 )
|
||||||
c$smb$current_file$times = times;
|
c$smb_state$current_cmd$referenced_file$times = times;
|
||||||
|
|
||||||
# We can identify the file by its file id now so let's stick it
|
# We can identify the file by its file id now so let's stick it
|
||||||
# in the file map.
|
# in the file map.
|
||||||
c$smb$fid_map[file_id$persistent+file_id$volatile] = c$smb$current_file;
|
c$smb_state$fid_map[file_id$persistent+file_id$volatile] = c$smb_state$current_cmd$referenced_file;
|
||||||
}
|
|
||||||
|
|
||||||
event smb2_create_response(c: connection, hdr: SMB2::Header, file_id: SMB2::GUID, file_size: count, times: SMB::MACTimes, attrs: SMB2::FileAttrs) &priority=-5
|
c$smb_state$current_file = c$smb_state$fid_map[file_id$persistent+file_id$volatile];
|
||||||
{
|
|
||||||
SMB::write_file_log(c$smb$current_file);
|
SMB::write_file_log(c$smb_state$current_file);
|
||||||
}
|
}
|
||||||
|
|
||||||
event smb2_set_info_request(c: connection, hdr: SMB2::Header, request: SMB2::SetInfoRequest) &priority=5
|
event smb2_set_info_request(c: connection, hdr: SMB2::Header, request: SMB2::SetInfoRequest) &priority=5
|
||||||
|
@ -145,46 +147,55 @@ event smb2_set_info_request(c: connection, hdr: SMB2::Header, request: SMB2::Set
|
||||||
|
|
||||||
event smb2_read_request(c: connection, hdr: SMB2::Header, file_id: SMB2::GUID, offset: count, length: count) &priority=5
|
event smb2_read_request(c: connection, hdr: SMB2::Header, file_id: SMB2::GUID, offset: count, length: count) &priority=5
|
||||||
{
|
{
|
||||||
SMB::set_current_file(c$smb, file_id$persistent+file_id$volatile);
|
SMB::set_current_file(c$smb_state, file_id$persistent+file_id$volatile);
|
||||||
c$smb$current_file$action = SMB::FILE_READ;
|
c$smb_state$current_file$action = SMB::FILE_READ;
|
||||||
|
}
|
||||||
|
|
||||||
if ( c$smb$current_tree?$path && !c$smb$current_file?$path )
|
event smb2_read_request(c: connection, hdr: SMB2::Header, file_id: SMB2::GUID, offset: count, length: count) &priority=-5
|
||||||
c$smb$current_file$path = c$smb$current_tree$path;
|
{
|
||||||
|
if ( c$smb_state$current_tree?$path && !c$smb_state$current_file?$path )
|
||||||
|
c$smb_state$current_file$path = c$smb_state$current_tree$path;
|
||||||
|
|
||||||
#write_file_log(c$smb$current_file);
|
# TODO - Why is this commented out?
|
||||||
|
#write_file_log(c$smb_state$current_file);
|
||||||
}
|
}
|
||||||
|
|
||||||
event smb2_write_request(c: connection, hdr: SMB2::Header, file_id: SMB2::GUID, offset: count, length: count) &priority=5
|
event smb2_write_request(c: connection, hdr: SMB2::Header, file_id: SMB2::GUID, offset: count, length: count) &priority=5
|
||||||
{
|
{
|
||||||
SMB::set_current_file(c$smb, file_id$persistent+file_id$volatile);
|
SMB::set_current_file(c$smb_state, file_id$persistent+file_id$volatile);
|
||||||
c$smb$current_file$action = SMB::FILE_WRITE;
|
c$smb_state$current_file$action = SMB::FILE_WRITE;
|
||||||
|
}
|
||||||
|
|
||||||
if ( c$smb$current_tree?$path && ! c$smb$current_file?$path )
|
event smb2_write_request(c: connection, hdr: SMB2::Header, file_id: SMB2::GUID, offset: count, length: count) &priority=-5
|
||||||
c$smb$current_file$path = c$smb$current_tree$path;
|
{
|
||||||
|
if ( c$smb_state$current_tree?$path && ! c$smb_state$current_file?$path )
|
||||||
|
c$smb_state$current_file$path = c$smb_state$current_tree$path;
|
||||||
|
|
||||||
|
# TODO - Why is this commented out?
|
||||||
|
#write_file_log(c$smb_state$current_file);
|
||||||
}
|
}
|
||||||
|
|
||||||
event smb2_close_request(c: connection, hdr: SMB2::Header, file_id: SMB2::GUID) &priority=5
|
event smb2_close_request(c: connection, hdr: SMB2::Header, file_id: SMB2::GUID) &priority=5
|
||||||
{
|
{
|
||||||
SMB::set_current_file(c$smb, file_id$persistent+file_id$volatile);
|
SMB::set_current_file(c$smb_state, file_id$persistent+file_id$volatile);
|
||||||
c$smb$current_file$action = SMB::FILE_CLOSE;
|
c$smb_state$current_file$action = SMB::FILE_CLOSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
event smb2_close_request(c: connection, hdr: SMB2::Header, file_id: SMB2::GUID) &priority=-5
|
event smb2_close_request(c: connection, hdr: SMB2::Header, file_id: SMB2::GUID) &priority=-5
|
||||||
{
|
{
|
||||||
if ( file_id$persistent+file_id$volatile in c$smb$fid_map )
|
if ( file_id$persistent+file_id$volatile in c$smb_state$fid_map )
|
||||||
{
|
{
|
||||||
local fl = c$smb$fid_map[file_id$persistent+file_id$volatile];
|
local fl = c$smb_state$fid_map[file_id$persistent+file_id$volatile];
|
||||||
fl$uid = c$uid;
|
|
||||||
fl$id = c$id;
|
|
||||||
# Need to check for existence of path in case tree connect message wasn't seen.
|
# Need to check for existence of path in case tree connect message wasn't seen.
|
||||||
if ( c$smb$current_tree?$path )
|
if ( c$smb_state$current_tree?$path )
|
||||||
fl$path = c$smb$current_tree$path;
|
fl$path = c$smb_state$current_tree$path;
|
||||||
delete c$smb$fid_map[file_id$persistent+file_id$volatile];
|
delete c$smb_state$fid_map[file_id$persistent+file_id$volatile];
|
||||||
|
|
||||||
SMB::write_file_log(fl);
|
SMB::write_file_log(fl);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
# TODO - Determine correct action
|
||||||
# A reporter message is not right...
|
# A reporter message is not right...
|
||||||
#Reporter::warning("attempting to close an unknown file!");
|
#Reporter::warning("attempting to close an unknown file!");
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,6 +20,8 @@ bro_plugin_bif(
|
||||||
smb1_com_query_information.bif
|
smb1_com_query_information.bif
|
||||||
smb1_com_read_andx.bif
|
smb1_com_read_andx.bif
|
||||||
smb1_com_session_setup_andx.bif
|
smb1_com_session_setup_andx.bif
|
||||||
|
smb1_com_transaction.bif
|
||||||
|
smb1_com_transaction2.bif
|
||||||
smb1_com_tree_connect_andx.bif
|
smb1_com_tree_connect_andx.bif
|
||||||
smb1_com_tree_disconnect.bif
|
smb1_com_tree_disconnect.bif
|
||||||
smb1_com_write_andx.bif
|
smb1_com_write_andx.bif
|
||||||
|
|
588
src/analyzer/protocol/smb/DCE_RPC.cc
Normal file
588
src/analyzer/protocol/smb/DCE_RPC.cc
Normal file
|
@ -0,0 +1,588 @@
|
||||||
|
// See the file "COPYING" in the main distribution directory for copyright.
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string>
|
||||||
|
#include <map>
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
#include "DCE_RPC.h"
|
||||||
|
#include "Sessions.h"
|
||||||
|
|
||||||
|
#include "analyzer/Manager.h"
|
||||||
|
|
||||||
|
#include "events.bif.h"
|
||||||
|
|
||||||
|
using namespace analyzer::dce_rpc;
|
||||||
|
|
||||||
|
#define xbyte(b, n) (((const u_char*) (b))[n])
|
||||||
|
|
||||||
|
#define extract_uint16(little_endian, bytes) \
|
||||||
|
((little_endian) ? \
|
||||||
|
uint16(xbyte(bytes, 0)) | ((uint16(xbyte(bytes, 1))) << 8) : \
|
||||||
|
uint16(xbyte(bytes, 1)) | ((uint16(xbyte(bytes, 0))) << 8))
|
||||||
|
|
||||||
|
static int uuid_index[] = {
|
||||||
|
3, 2, 1, 0,
|
||||||
|
5, 4, 7, 6,
|
||||||
|
8, 9, 10, 11,
|
||||||
|
12, 13, 14, 15
|
||||||
|
};
|
||||||
|
|
||||||
|
const char* analyzer::dce_rpc::uuid_to_string(const u_char* uuid_data)
|
||||||
|
{
|
||||||
|
static char s[1024];
|
||||||
|
char* sp = s;
|
||||||
|
|
||||||
|
for ( int i = 0; i < 16; ++i )
|
||||||
|
{
|
||||||
|
if ( i == 4 || i == 6 || i == 8 || i == 10 )
|
||||||
|
sp += snprintf(sp, s + sizeof(s) - sp, "-");
|
||||||
|
|
||||||
|
int j = uuid_index[i];
|
||||||
|
sp += snprintf(sp, s + sizeof(s) - sp, "%02x", uuid_data[j]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
UUID::UUID()
|
||||||
|
{
|
||||||
|
memset(data, 0, 16);
|
||||||
|
s = uuid_to_string(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
UUID::UUID(const u_char d[16])
|
||||||
|
{
|
||||||
|
memcpy(data, d, 16);
|
||||||
|
s = uuid_to_string(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
UUID::UUID(const binpac::bytestring& uuid)
|
||||||
|
{
|
||||||
|
if ( uuid.length() != 16 )
|
||||||
|
reporter->InternalError("UUID length error");
|
||||||
|
memcpy(data, uuid.begin(), 16);
|
||||||
|
s = uuid_to_string(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
UUID::UUID(const char* str)
|
||||||
|
{
|
||||||
|
s = string(str);
|
||||||
|
const char* sp = str;
|
||||||
|
int i;
|
||||||
|
for ( i = 0; i < 16; ++i )
|
||||||
|
{
|
||||||
|
if ( *sp == '-' )
|
||||||
|
++sp;
|
||||||
|
if ( ! *sp || ! *(sp+1) )
|
||||||
|
break;
|
||||||
|
|
||||||
|
data[uuid_index[i]] =
|
||||||
|
(u_char) (decode_hex(*sp) * 16 + decode_hex(*(sp+1)));
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( i != 16 )
|
||||||
|
reporter->InternalError("invalid UUID string: %s", str);
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef map<UUID, BifEnum::dce_rpc_if_id> uuid_map_t;
|
||||||
|
|
||||||
|
static uuid_map_t& well_known_uuid_map()
|
||||||
|
{
|
||||||
|
static uuid_map_t the_map;
|
||||||
|
static bool initialized = false;
|
||||||
|
|
||||||
|
if ( initialized )
|
||||||
|
return the_map;
|
||||||
|
|
||||||
|
using namespace BifEnum;
|
||||||
|
|
||||||
|
the_map[UUID("e1af8308-5d1f-11c9-91a4-08002b14a0fa")] = DCE_RPC_epmapper;
|
||||||
|
|
||||||
|
the_map[UUID("afa8bd80-7d8a-11c9-bef4-08002b102989")] = DCE_RPC_mgmt;
|
||||||
|
|
||||||
|
// It's said that the following interfaces are merely aliases.
|
||||||
|
the_map[UUID("12345778-1234-abcd-ef00-0123456789ab")] = DCE_RPC_lsarpc;
|
||||||
|
the_map[UUID("12345678-1234-abcd-ef00-01234567cffb")] = DCE_RPC_netlogon;
|
||||||
|
the_map[UUID("12345778-1234-abcd-ef00-0123456789ac")] = DCE_RPC_samr;
|
||||||
|
|
||||||
|
// The next group of aliases.
|
||||||
|
the_map[UUID("4b324fc8-1670-01d3-1278-5a47bf6ee188")] = DCE_RPC_srvsvc;
|
||||||
|
the_map[UUID("12345678-1234-abcd-ef00-0123456789ab")] = DCE_RPC_spoolss;
|
||||||
|
the_map[UUID("45f52c28-7f9f-101a-b52b-08002b2efabe")] = DCE_RPC_winspipe;
|
||||||
|
the_map[UUID("6bffd098-a112-3610-9833-46c3f87e345a")] = DCE_RPC_wkssvc;
|
||||||
|
|
||||||
|
// DRS - NT directory replication service.
|
||||||
|
the_map[UUID("e3514235-4b06-11d1-ab04-00c04fc2dcd2")] = DCE_RPC_drs;
|
||||||
|
|
||||||
|
// "The IOXIDResolver RPC interface (formerly known as
|
||||||
|
// IObjectExporter) is remotely used to reach the local object
|
||||||
|
// resolver (OR)."
|
||||||
|
the_map[UUID("99fcfec4-5260-101b-bbcb-00aa0021347a")] = DCE_RPC_oxid;
|
||||||
|
|
||||||
|
the_map[UUID("3919286a-b10c-11d0-9ba8-00c04fd92ef5")] = DCE_RPC_lsa_ds;
|
||||||
|
|
||||||
|
the_map[UUID("000001a0-0000-0000-c000-000000000046")] = DCE_RPC_ISCMActivator;
|
||||||
|
|
||||||
|
initialized = true;
|
||||||
|
return the_map;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Used to remember mapped DCE/RPC endpoints and parse the follow-up
|
||||||
|
// connections as DCE/RPC sessions.
|
||||||
|
map<dce_rpc_endpoint_addr, UUID> dce_rpc_endpoints;
|
||||||
|
|
||||||
|
static bool is_mapped_dce_rpc_endpoint(const dce_rpc_endpoint_addr& addr)
|
||||||
|
{
|
||||||
|
return dce_rpc_endpoints.find(addr) != dce_rpc_endpoints.end();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool is_mapped_dce_rpc_endpoint(const ConnID* id, TransportProto proto)
|
||||||
|
{
|
||||||
|
if ( id->dst_addr.GetFamily() == IPv6 )
|
||||||
|
// TODO: Does the protocol support v6 addresses? #773
|
||||||
|
return false;
|
||||||
|
|
||||||
|
dce_rpc_endpoint_addr addr;
|
||||||
|
addr.addr = id->dst_addr;
|
||||||
|
addr.port = ntohs(id->dst_port);
|
||||||
|
addr.proto = proto;
|
||||||
|
|
||||||
|
return is_mapped_dce_rpc_endpoint(addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void add_dce_rpc_endpoint(const dce_rpc_endpoint_addr& addr,
|
||||||
|
const UUID& uuid)
|
||||||
|
{
|
||||||
|
DEBUG_MSG("Adding endpoint %s @ %s\n",
|
||||||
|
uuid.to_string(), addr.to_string().c_str());
|
||||||
|
dce_rpc_endpoints[addr] = uuid;
|
||||||
|
|
||||||
|
// FIXME: Once we can pass the cookie to the analyzer, we can get rid
|
||||||
|
// of the dce_rpc_endpoints table.
|
||||||
|
// FIXME: Don't hard-code the timeout.
|
||||||
|
|
||||||
|
analyzer_mgr->ScheduleAnalyzer(IPAddr(), addr.addr, addr.port, addr.proto,
|
||||||
|
"DCE_RPC", 5 * 60);
|
||||||
|
}
|
||||||
|
|
||||||
|
DCE_RPC_Header::DCE_RPC_Header(analyzer::Analyzer* a, const u_char* b)
|
||||||
|
{
|
||||||
|
analyzer = a;
|
||||||
|
bytes = b;
|
||||||
|
|
||||||
|
// This checks whether it's both the first fragment *and*
|
||||||
|
// the last fragment.
|
||||||
|
if ( (bytes[3] & 0x3) != 0x3 )
|
||||||
|
{
|
||||||
|
fragmented = 1;
|
||||||
|
Weird("Fragmented DCE/RPC message");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
fragmented = 0;
|
||||||
|
|
||||||
|
ptype = (BifEnum::dce_rpc_ptype) bytes[2];
|
||||||
|
frag_len = extract_uint16(LittleEndian(), bytes + 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
DCE_RPC_Session::DCE_RPC_Session(analyzer::Analyzer* a)
|
||||||
|
: analyzer(a),
|
||||||
|
if_uuid("00000000-0000-0000-0000-000000000000"),
|
||||||
|
if_id(BifEnum::DCE_RPC_unknown_if)
|
||||||
|
{
|
||||||
|
opnum = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DCE_RPC_Session::LooksLikeRPC(int len, const u_char* msg)
|
||||||
|
{
|
||||||
|
// if ( ! is_IPC )
|
||||||
|
// return false;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
binpac::DCE_RPC_Simple::DCE_RPC_Header h;
|
||||||
|
h.Parse(msg, msg + len);
|
||||||
|
if ( h.rpc_vers() == 5 && h.rpc_vers_minor() == 0 )
|
||||||
|
{
|
||||||
|
if ( h.frag_length() == len )
|
||||||
|
return true;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
DEBUG_MSG("length mismatch: %d != %d\n",
|
||||||
|
h.frag_length(), len);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch ( const binpac::Exception& )
|
||||||
|
{
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DCE_RPC_Session::DeliverPDU(int is_orig, int len, const u_char* data)
|
||||||
|
{
|
||||||
|
if ( dce_rpc_message )
|
||||||
|
{
|
||||||
|
val_list* vl = new val_list;
|
||||||
|
vl->append(analyzer->BuildConnVal());
|
||||||
|
vl->append(new Val(is_orig, TYPE_BOOL));
|
||||||
|
vl->append(new EnumVal(data[2], BifType::Enum::dce_rpc_ptype));
|
||||||
|
vl->append(new StringVal(len, (const char*) data));
|
||||||
|
|
||||||
|
analyzer->ConnectionEvent(dce_rpc_message, vl);
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// TODO: handle incremental input
|
||||||
|
binpac::DCE_RPC_Simple::DCE_RPC_PDU pdu;
|
||||||
|
pdu.Parse(data, data + len);
|
||||||
|
|
||||||
|
switch ( pdu.header()->PTYPE() ) {
|
||||||
|
case binpac::DCE_RPC_Simple::DCE_RPC_BIND:
|
||||||
|
case binpac::DCE_RPC_Simple::DCE_RPC_ALTER_CONTEXT:
|
||||||
|
DeliverBind(&pdu);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case binpac::DCE_RPC_Simple::DCE_RPC_REQUEST:
|
||||||
|
DeliverRequest(&pdu);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case binpac::DCE_RPC_Simple::DCE_RPC_RESPONSE:
|
||||||
|
DeliverResponse(&pdu);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch ( const binpac::Exception& e )
|
||||||
|
{
|
||||||
|
analyzer->Weird(e.msg().c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DCE_RPC_Session::DeliverBind(const binpac::DCE_RPC_Simple::DCE_RPC_PDU* pdu)
|
||||||
|
{
|
||||||
|
binpac::DCE_RPC_Simple::DCE_RPC_Bind* bind = pdu->body()->bind();
|
||||||
|
|
||||||
|
for ( int i = 0; i < bind->p_context_elem()->n_context_elem(); ++i )
|
||||||
|
{
|
||||||
|
binpac::DCE_RPC_Simple::p_cont_elem_t* elem =
|
||||||
|
(*bind->p_context_elem()->p_cont_elem())[i];
|
||||||
|
|
||||||
|
if_uuid = UUID(elem->abstract_syntax()->if_uuid().begin());
|
||||||
|
uuid_map_t::const_iterator uuid_it =
|
||||||
|
well_known_uuid_map().find(if_uuid);
|
||||||
|
|
||||||
|
if ( uuid_it == well_known_uuid_map().end() )
|
||||||
|
{
|
||||||
|
#ifdef DEBUG
|
||||||
|
// conn->Weird(fmt("Unknown DCE_RPC interface %s",
|
||||||
|
// if_uuid.to_string()));
|
||||||
|
#endif
|
||||||
|
if_id = BifEnum::DCE_RPC_unknown_if;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
if_id = uuid_it->second;
|
||||||
|
|
||||||
|
if ( dce_rpc_bind )
|
||||||
|
{
|
||||||
|
val_list* vl = new val_list;
|
||||||
|
vl->append(analyzer->BuildConnVal());
|
||||||
|
vl->append(new StringVal(if_uuid.to_string()));
|
||||||
|
// vl->append(new EnumVal(if_id, BifType::Enum::dce_rpc_if_id));
|
||||||
|
|
||||||
|
analyzer->ConnectionEvent(dce_rpc_bind, vl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DCE_RPC_Session::DeliverRequest(const binpac::DCE_RPC_Simple::DCE_RPC_PDU* pdu)
|
||||||
|
{
|
||||||
|
binpac::DCE_RPC_Simple::DCE_RPC_Request* req = pdu->body()->request();
|
||||||
|
|
||||||
|
opnum = req->opnum();
|
||||||
|
|
||||||
|
if ( dce_rpc_request )
|
||||||
|
{
|
||||||
|
val_list* vl = new val_list;
|
||||||
|
vl->append(analyzer->BuildConnVal());
|
||||||
|
vl->append(new Val(opnum, TYPE_COUNT));
|
||||||
|
vl->append(new StringVal(req->stub().length(),
|
||||||
|
(const char*) req->stub().begin()));
|
||||||
|
|
||||||
|
analyzer->ConnectionEvent(dce_rpc_request, vl);
|
||||||
|
}
|
||||||
|
|
||||||
|
switch ( if_id ) {
|
||||||
|
case BifEnum::DCE_RPC_epmapper:
|
||||||
|
DeliverEpmapperRequest(pdu, req);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DCE_RPC_Session::DeliverResponse(const binpac::DCE_RPC_Simple::DCE_RPC_PDU* pdu)
|
||||||
|
{
|
||||||
|
binpac::DCE_RPC_Simple::DCE_RPC_Response* resp = pdu->body()->response();
|
||||||
|
|
||||||
|
if ( dce_rpc_response )
|
||||||
|
{
|
||||||
|
val_list* vl = new val_list;
|
||||||
|
vl->append(analyzer->BuildConnVal());
|
||||||
|
vl->append(new Val(opnum, TYPE_COUNT));
|
||||||
|
vl->append(new StringVal(resp->stub().length(),
|
||||||
|
(const char*) resp->stub().begin()));
|
||||||
|
analyzer->ConnectionEvent(dce_rpc_response, vl);
|
||||||
|
}
|
||||||
|
|
||||||
|
switch ( if_id ) {
|
||||||
|
case BifEnum::DCE_RPC_epmapper:
|
||||||
|
DeliverEpmapperResponse(pdu, resp);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DCE_RPC_Session::DeliverEpmapperRequest(
|
||||||
|
const binpac::DCE_RPC_Simple::DCE_RPC_PDU* /* pdu */,
|
||||||
|
const binpac::DCE_RPC_Simple::DCE_RPC_Request* /* req */)
|
||||||
|
{
|
||||||
|
// DEBUG_MSG("Epmapper request opnum = %d\n", req->opnum());
|
||||||
|
// ### TODO(rpang): generate an event on epmapper request
|
||||||
|
}
|
||||||
|
|
||||||
|
void DCE_RPC_Session::DeliverEpmapperResponse(
|
||||||
|
const binpac::DCE_RPC_Simple::DCE_RPC_PDU* pdu,
|
||||||
|
const binpac::DCE_RPC_Simple::DCE_RPC_Response* resp)
|
||||||
|
{
|
||||||
|
// DEBUG_MSG("Epmapper request opnum = %d\n", req->opnum());
|
||||||
|
switch ( opnum ) {
|
||||||
|
case 3: // Map
|
||||||
|
DeliverEpmapperMapResponse(pdu, resp);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void DCE_RPC_Session::DeliverEpmapperMapResponse(
|
||||||
|
const binpac::DCE_RPC_Simple::DCE_RPC_PDU* pdu,
|
||||||
|
const binpac::DCE_RPC_Simple::DCE_RPC_Response* resp)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
binpac::DCE_RPC_Simple::epmapper_map_resp epm_resp;
|
||||||
|
|
||||||
|
epm_resp.Parse(resp->stub().begin(), resp->stub().end(),
|
||||||
|
pdu->byteorder());
|
||||||
|
|
||||||
|
for ( unsigned int twr_i = 0;
|
||||||
|
twr_i < epm_resp.towers()->actual_count(); ++twr_i )
|
||||||
|
{
|
||||||
|
binpac::DCE_RPC_Simple::epm_tower* twr =
|
||||||
|
(*epm_resp.towers()->towers())[twr_i]->tower();
|
||||||
|
|
||||||
|
mapped.addr = dce_rpc_endpoint_addr();
|
||||||
|
mapped.uuid = UUID();
|
||||||
|
|
||||||
|
for ( int floor_i = 0; floor_i < twr->num_floors();
|
||||||
|
++floor_i )
|
||||||
|
{
|
||||||
|
binpac::DCE_RPC_Simple::epm_floor* floor =
|
||||||
|
(*twr->floors())[floor_i];
|
||||||
|
|
||||||
|
switch ( floor->protocol() ) {
|
||||||
|
case binpac::DCE_RPC_Simple::EPM_PROTOCOL_UUID:
|
||||||
|
if ( floor_i == 0 )
|
||||||
|
mapped.uuid = UUID(floor->lhs()->data()->uuid()->if_uuid());
|
||||||
|
break;
|
||||||
|
|
||||||
|
case binpac::DCE_RPC_Simple::EPM_PROTOCOL_TCP:
|
||||||
|
mapped.addr.port =
|
||||||
|
floor->rhs()->data()->tcp();
|
||||||
|
mapped.addr.proto = TRANSPORT_TCP;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case binpac::DCE_RPC_Simple::EPM_PROTOCOL_UDP:
|
||||||
|
mapped.addr.port =
|
||||||
|
floor->rhs()->data()->udp();
|
||||||
|
mapped.addr.proto = TRANSPORT_UDP;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case binpac::DCE_RPC_Simple::EPM_PROTOCOL_IP:
|
||||||
|
uint32 hostip = floor->rhs()->data()->ip();
|
||||||
|
mapped.addr.addr = IPAddr(IPv4, &hostip, IPAddr::Host);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( mapped.addr.is_valid_addr() )
|
||||||
|
add_dce_rpc_endpoint(mapped.addr, mapped.uuid);
|
||||||
|
|
||||||
|
if ( epm_map_response )
|
||||||
|
{
|
||||||
|
val_list* vl = new val_list;
|
||||||
|
vl->append(analyzer->BuildConnVal());
|
||||||
|
vl->append(new StringVal(mapped.uuid.to_string()));
|
||||||
|
vl->append(new PortVal(mapped.addr.port, mapped.addr.proto));
|
||||||
|
vl->append(new AddrVal(mapped.addr.addr));
|
||||||
|
|
||||||
|
analyzer->ConnectionEvent(epm_map_response, vl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch ( const binpac::Exception& e )
|
||||||
|
{
|
||||||
|
analyzer->Weird(e.msg().c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Contents_DCE_RPC_Analyzer::Contents_DCE_RPC_Analyzer(Connection* conn,
|
||||||
|
bool orig, DCE_RPC_Session* arg_session, bool speculative)
|
||||||
|
: tcp::TCP_SupportAnalyzer("CONTENTS_DCE_RPC", conn, orig)
|
||||||
|
{
|
||||||
|
session = arg_session;
|
||||||
|
msg_buf = 0;
|
||||||
|
buf_len = 0;
|
||||||
|
speculation = speculative ? 0 : 1;
|
||||||
|
|
||||||
|
InitState();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Contents_DCE_RPC_Analyzer::InitState()
|
||||||
|
{
|
||||||
|
// Allocate space for header.
|
||||||
|
if ( ! msg_buf )
|
||||||
|
{
|
||||||
|
buf_len = DCE_RPC_HEADER_LENGTH;
|
||||||
|
msg_buf = new u_char[buf_len];
|
||||||
|
}
|
||||||
|
|
||||||
|
buf_n = 0;
|
||||||
|
msg_len = 0;
|
||||||
|
hdr = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
Contents_DCE_RPC_Analyzer::~Contents_DCE_RPC_Analyzer()
|
||||||
|
{
|
||||||
|
delete [] msg_buf;
|
||||||
|
delete hdr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Contents_DCE_RPC_Analyzer::DeliverStream(int len, const u_char* data, bool orig)
|
||||||
|
{
|
||||||
|
tcp::TCP_SupportAnalyzer::DeliverStream(len, data, orig);
|
||||||
|
|
||||||
|
tcp::TCP_Analyzer* tcp =
|
||||||
|
static_cast<tcp::TCP_ApplicationAnalyzer*>(Parent())->TCP();
|
||||||
|
|
||||||
|
if ( tcp->HadGap(orig) || tcp->IsPartial() )
|
||||||
|
return;
|
||||||
|
|
||||||
|
if ( speculation == 0 ) // undecided
|
||||||
|
{
|
||||||
|
if ( ! DCE_RPC_Session::LooksLikeRPC(len, data) )
|
||||||
|
speculation = -1;
|
||||||
|
else
|
||||||
|
speculation = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( speculation < 0 )
|
||||||
|
return;
|
||||||
|
|
||||||
|
ASSERT(buf_len >= DCE_RPC_HEADER_LENGTH);
|
||||||
|
while ( len > 0 )
|
||||||
|
{
|
||||||
|
if ( buf_n < DCE_RPC_HEADER_LENGTH )
|
||||||
|
{
|
||||||
|
while ( buf_n < DCE_RPC_HEADER_LENGTH && len > 0 )
|
||||||
|
{
|
||||||
|
msg_buf[buf_n] = *data;
|
||||||
|
++buf_n; ++data; --len;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( buf_n < DCE_RPC_HEADER_LENGTH )
|
||||||
|
break;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if ( ! ParseHeader() )
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
while ( buf_n < msg_len && len > 0 )
|
||||||
|
{
|
||||||
|
msg_buf[buf_n] = *data;
|
||||||
|
++buf_n; ++data; --len;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( buf_n < msg_len )
|
||||||
|
break;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if ( msg_len > 0 )
|
||||||
|
DeliverPDU(msg_len, msg_buf);
|
||||||
|
// Reset for next message
|
||||||
|
InitState();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Contents_DCE_RPC_Analyzer::DeliverPDU(int len, const u_char* data)
|
||||||
|
{
|
||||||
|
session->DeliverPDU(IsOrig(), len, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Contents_DCE_RPC_Analyzer::ParseHeader()
|
||||||
|
{
|
||||||
|
delete hdr;
|
||||||
|
hdr = 0;
|
||||||
|
|
||||||
|
if ( msg_buf[0] != 5 ) // DCE/RPC version
|
||||||
|
{
|
||||||
|
Conn()->Weird("DCE/RPC_version_error (non-DCE/RPC?)");
|
||||||
|
Conn()->SetSkip(1);
|
||||||
|
msg_len = 0;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
hdr = new DCE_RPC_Header(this, msg_buf);
|
||||||
|
|
||||||
|
msg_len = hdr->FragLen();
|
||||||
|
if ( msg_len > buf_len )
|
||||||
|
{
|
||||||
|
u_char* new_msg_buf = new u_char[msg_len];
|
||||||
|
memcpy(new_msg_buf, msg_buf, buf_n);
|
||||||
|
delete [] msg_buf;
|
||||||
|
buf_len = msg_len;
|
||||||
|
msg_buf = new_msg_buf;
|
||||||
|
hdr->SetBytes(new_msg_buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
DCE_RPC_Analyzer::DCE_RPC_Analyzer(Connection* conn, bool arg_speculative)
|
||||||
|
: tcp::TCP_ApplicationAnalyzer("DCE_RPC", conn)
|
||||||
|
{
|
||||||
|
session = new DCE_RPC_Session(this);
|
||||||
|
speculative = arg_speculative;
|
||||||
|
|
||||||
|
AddSupportAnalyzer(new Contents_DCE_RPC_Analyzer(conn, true, session,
|
||||||
|
speculative));
|
||||||
|
AddSupportAnalyzer(new Contents_DCE_RPC_Analyzer(conn, false, session,
|
||||||
|
speculative));
|
||||||
|
}
|
||||||
|
|
||||||
|
DCE_RPC_Analyzer::~DCE_RPC_Analyzer()
|
||||||
|
{
|
||||||
|
delete session;
|
||||||
|
}
|
191
src/analyzer/protocol/smb/DCE_RPC.h
Normal file
191
src/analyzer/protocol/smb/DCE_RPC.h
Normal file
|
@ -0,0 +1,191 @@
|
||||||
|
// See the file "COPYING" in the main distribution directory for copyright.
|
||||||
|
|
||||||
|
#ifndef ANALYZER_PROTOCOL_DCE_RPC_DCE_RPC_H
|
||||||
|
#define ANALYZER_PROTOCOL_DCE_RPC_DCE_RPC_H
|
||||||
|
|
||||||
|
// NOTE: This is a somewhat crude analyzer for DCE/RPC (used on Microsoft
|
||||||
|
// Windows systems) and shouldn't be considered as stable.
|
||||||
|
|
||||||
|
#include "NetVar.h"
|
||||||
|
#include "analyzer/protocol/tcp/TCP.h"
|
||||||
|
#include "analyzer/protocol/dce-rpc/events.bif.h"
|
||||||
|
#include "IPAddr.h"
|
||||||
|
|
||||||
|
#include "dce_rpc_simple_pac.h"
|
||||||
|
|
||||||
|
|
||||||
|
namespace analyzer { namespace dce_rpc {
|
||||||
|
|
||||||
|
class UUID {
|
||||||
|
public:
|
||||||
|
UUID();
|
||||||
|
UUID(const u_char data[16]);
|
||||||
|
UUID(const binpac::bytestring &uuid);
|
||||||
|
UUID(const char* s);
|
||||||
|
|
||||||
|
const char* to_string() const { return s.c_str(); }
|
||||||
|
const string& str() const { return s; }
|
||||||
|
bool operator==(const UUID& u) const
|
||||||
|
{ return s == u.str(); }
|
||||||
|
bool operator<(const UUID& u) const
|
||||||
|
{ return s < u.str(); }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
u_char data[16];
|
||||||
|
string s;
|
||||||
|
};
|
||||||
|
|
||||||
|
const char* uuid_to_string(const u_char* uuid_data);
|
||||||
|
|
||||||
|
struct dce_rpc_endpoint_addr {
|
||||||
|
// All fields are in host byteorder.
|
||||||
|
IPAddr addr;
|
||||||
|
u_short port;
|
||||||
|
TransportProto proto;
|
||||||
|
|
||||||
|
dce_rpc_endpoint_addr()
|
||||||
|
{
|
||||||
|
addr = IPAddr();
|
||||||
|
port = 0;
|
||||||
|
proto = TRANSPORT_UNKNOWN;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool is_valid_addr() const
|
||||||
|
{ return addr != IPAddr() && port != 0 && proto != TRANSPORT_UNKNOWN; }
|
||||||
|
|
||||||
|
bool operator<(dce_rpc_endpoint_addr const &e) const
|
||||||
|
{
|
||||||
|
if ( addr != e.addr )
|
||||||
|
return addr < e.addr;
|
||||||
|
if ( proto != e.proto )
|
||||||
|
return proto < e.proto;
|
||||||
|
if ( port != e.port )
|
||||||
|
return port < e.port;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
string to_string() const
|
||||||
|
{
|
||||||
|
static char buf[128];
|
||||||
|
snprintf(buf, sizeof(buf), "%s/%d/%s",
|
||||||
|
addr.AsString().c_str(), port,
|
||||||
|
proto == TRANSPORT_TCP ? "tcp" :
|
||||||
|
(proto == TRANSPORT_UDP ? "udp" : "?"));
|
||||||
|
|
||||||
|
return string(buf);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
enum DCE_RPC_PTYPE {
|
||||||
|
DCE_RPC_REQUEST, DCE_RPC_PING, DCE_RPC_RESPONSE, DCE_RPC_FAULT,
|
||||||
|
DCE_RPC_WORKING, DCE_RPC_NOCALL, DCE_RPC_REJECT, DCE_RPC_ACK,
|
||||||
|
DCE_RPC_CL_CANCEL, DCE_RPC_FACK, DCE_RPC_CANCEL_ACK, DCE_RPC_BIND,
|
||||||
|
DCE_RPC_BIND_ACK, DCE_RPC_BIND_NAK, DCE_RPC_ALTER_CONTEXT,
|
||||||
|
DCE_RPC_ALTER_CONTEXT_RESP, DCE_RPC_SHUTDOWN, DCE_RPC_CO_CANCEL,
|
||||||
|
DCE_RPC_ORPHANED,
|
||||||
|
};
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define DCE_RPC_HEADER_LENGTH 16
|
||||||
|
|
||||||
|
class DCE_RPC_Header {
|
||||||
|
public:
|
||||||
|
DCE_RPC_Header(analyzer::Analyzer* a, const u_char* bytes);
|
||||||
|
|
||||||
|
BifEnum::dce_rpc_ptype PTYPE() const { return ptype; }
|
||||||
|
int FragLen() const { return frag_len; }
|
||||||
|
int LittleEndian() const { return bytes[4] >> 4; }
|
||||||
|
bool Fragmented() const { return fragmented; }
|
||||||
|
|
||||||
|
void Weird(const char* msg) { analyzer->Weird(msg); }
|
||||||
|
void SetBytes(const u_char* b) { bytes = b; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
analyzer::Analyzer* analyzer;
|
||||||
|
const u_char* bytes;
|
||||||
|
BifEnum::dce_rpc_ptype ptype;
|
||||||
|
int frag_len;
|
||||||
|
bool fragmented;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Create a general DCE_RPC_Session class so that it can be used in
|
||||||
|
// case the RPC conversation is tunneled through other connections,
|
||||||
|
// e.g. through an SMB session.
|
||||||
|
|
||||||
|
class DCE_RPC_Session {
|
||||||
|
public:
|
||||||
|
DCE_RPC_Session(analyzer::Analyzer* a);
|
||||||
|
virtual ~DCE_RPC_Session() {}
|
||||||
|
virtual void DeliverPDU(int is_orig, int len, const u_char* data);
|
||||||
|
|
||||||
|
static bool LooksLikeRPC(int len, const u_char* msg);
|
||||||
|
static bool any_dce_rpc_event()
|
||||||
|
{ return dce_rpc_message || dce_rpc_bind || dce_rpc_request; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void DeliverBind(const binpac::DCE_RPC_Simple::DCE_RPC_PDU* pdu);
|
||||||
|
void DeliverRequest(const binpac::DCE_RPC_Simple::DCE_RPC_PDU* pdu);
|
||||||
|
void DeliverResponse(const binpac::DCE_RPC_Simple::DCE_RPC_PDU* pdu);
|
||||||
|
|
||||||
|
void DeliverEpmapperRequest(
|
||||||
|
const binpac::DCE_RPC_Simple::DCE_RPC_PDU* pdu,
|
||||||
|
const binpac::DCE_RPC_Simple::DCE_RPC_Request* req);
|
||||||
|
void DeliverEpmapperResponse(
|
||||||
|
const binpac::DCE_RPC_Simple::DCE_RPC_PDU* pdu,
|
||||||
|
const binpac::DCE_RPC_Simple::DCE_RPC_Response* resp);
|
||||||
|
void DeliverEpmapperMapResponse(
|
||||||
|
const binpac::DCE_RPC_Simple::DCE_RPC_PDU* pdu,
|
||||||
|
const binpac::DCE_RPC_Simple::DCE_RPC_Response* resp);
|
||||||
|
|
||||||
|
analyzer::Analyzer* analyzer;
|
||||||
|
UUID if_uuid;
|
||||||
|
BifEnum::dce_rpc_if_id if_id;
|
||||||
|
int opnum;
|
||||||
|
struct {
|
||||||
|
dce_rpc_endpoint_addr addr;
|
||||||
|
UUID uuid;
|
||||||
|
} mapped;
|
||||||
|
};
|
||||||
|
|
||||||
|
class Contents_DCE_RPC_Analyzer : public tcp::TCP_SupportAnalyzer {
|
||||||
|
public:
|
||||||
|
Contents_DCE_RPC_Analyzer(Connection* conn, bool orig, DCE_RPC_Session* session,
|
||||||
|
bool speculative);
|
||||||
|
~Contents_DCE_RPC_Analyzer();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual void DeliverStream(int len, const u_char* data, bool orig);
|
||||||
|
virtual void DeliverPDU(int len, const u_char* data);
|
||||||
|
|
||||||
|
void InitState();
|
||||||
|
|
||||||
|
int speculation;
|
||||||
|
u_char* msg_buf;
|
||||||
|
int msg_len;
|
||||||
|
int buf_n; // number of bytes in msg_buf
|
||||||
|
int buf_len; // size off msg_buf
|
||||||
|
DCE_RPC_Header* hdr;
|
||||||
|
|
||||||
|
bool ParseHeader();
|
||||||
|
|
||||||
|
DCE_RPC_Session* session;
|
||||||
|
};
|
||||||
|
|
||||||
|
class DCE_RPC_Analyzer : public tcp::TCP_ApplicationAnalyzer {
|
||||||
|
public:
|
||||||
|
DCE_RPC_Analyzer(Connection* conn, bool speculative = false);
|
||||||
|
~DCE_RPC_Analyzer();
|
||||||
|
|
||||||
|
static analyzer::Analyzer* Instantiate(Connection* conn)
|
||||||
|
{ return new DCE_RPC_Analyzer(conn); }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
DCE_RPC_Session* session;
|
||||||
|
bool speculative;
|
||||||
|
};
|
||||||
|
|
||||||
|
} } // namespace analyzer::*
|
||||||
|
|
||||||
|
#endif /* dce_rpc_h */
|
|
@ -88,6 +88,16 @@ type DCE_RPC_Bind = record {
|
||||||
p_context_elem : p_cont_list_t;
|
p_context_elem : p_cont_list_t;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type DCE_RPC_Bind_Ack = record {
|
||||||
|
max_xmit_frag : uint16;
|
||||||
|
max_recv_frag : uint16;
|
||||||
|
assoc_group_id : uint32;
|
||||||
|
sec_addr_length : uint16;
|
||||||
|
sec_addr : bytestring &length=sec_addr_length;
|
||||||
|
pad : padding align 4;
|
||||||
|
p_context_elem : p_cont_list_t;
|
||||||
|
};
|
||||||
|
|
||||||
type DCE_RPC_AlterContext = record {
|
type DCE_RPC_AlterContext = record {
|
||||||
max_xmit_frag : uint16;
|
max_xmit_frag : uint16;
|
||||||
max_recv_frag : uint16;
|
max_recv_frag : uint16;
|
||||||
|
@ -115,6 +125,7 @@ type DCE_RPC_Response = record {
|
||||||
|
|
||||||
type DCE_RPC_Body(header: DCE_RPC_Header) = case header.PTYPE of {
|
type DCE_RPC_Body(header: DCE_RPC_Header) = case header.PTYPE of {
|
||||||
DCE_RPC_BIND -> bind : DCE_RPC_Bind;
|
DCE_RPC_BIND -> bind : DCE_RPC_Bind;
|
||||||
|
DCE_RPC_BIND_ACK -> bind_ack: DCE_RPC_Bind_Ack;
|
||||||
DCE_RPC_REQUEST -> request : DCE_RPC_Request;
|
DCE_RPC_REQUEST -> request : DCE_RPC_Request;
|
||||||
DCE_RPC_RESPONSE -> response : DCE_RPC_Response;
|
DCE_RPC_RESPONSE -> response : DCE_RPC_Response;
|
||||||
default -> other : bytestring &restofdata;
|
default -> other : bytestring &restofdata;
|
||||||
|
|
57
src/analyzer/protocol/smb/smb-ntlmssp-asn1.pac
Normal file
57
src/analyzer/protocol/smb/smb-ntlmssp-asn1.pac
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
# Supporting types for ASN.1
|
||||||
|
#
|
||||||
|
# From the Kerberos analyzer
|
||||||
|
#
|
||||||
|
# TODO: Figure out a way to include this code only once.
|
||||||
|
|
||||||
|
type ASN1Encoding = record {
|
||||||
|
meta: ASN1EncodingMeta;
|
||||||
|
content: bytestring &length = meta.length;
|
||||||
|
};
|
||||||
|
|
||||||
|
type ASN1EncodingMeta = record {
|
||||||
|
tag: uint8;
|
||||||
|
len: uint8;
|
||||||
|
more_len: bytestring &length = long_len ? len & 0x7f : 0;
|
||||||
|
} &let {
|
||||||
|
long_len: bool = len & 0x80;
|
||||||
|
length: uint64 = long_len ? binary_to_int64(more_len) : len & 0x7f;
|
||||||
|
index: uint8 = tag - 160;
|
||||||
|
};
|
||||||
|
|
||||||
|
type ASN1Integer = record {
|
||||||
|
encoding: ASN1Encoding;
|
||||||
|
};
|
||||||
|
|
||||||
|
type ASN1OctetString = record {
|
||||||
|
encoding: ASN1Encoding;
|
||||||
|
};
|
||||||
|
|
||||||
|
type SequenceElement(grab_content: bool) = record {
|
||||||
|
index_meta: ASN1EncodingMeta;
|
||||||
|
have_content: case grab_content of {
|
||||||
|
true -> data: ASN1Encoding;
|
||||||
|
false -> meta: ASN1EncodingMeta;
|
||||||
|
};
|
||||||
|
} &let {
|
||||||
|
index: uint8 = index_meta.index;
|
||||||
|
length: uint64 = index_meta.length;
|
||||||
|
};
|
||||||
|
|
||||||
|
type Array = record {
|
||||||
|
array_meta: ASN1EncodingMeta;
|
||||||
|
data: ASN1Encoding[];
|
||||||
|
};
|
||||||
|
|
||||||
|
function binary_to_int64(bs: bytestring): int64
|
||||||
|
%{
|
||||||
|
int64 rval = 0;
|
||||||
|
|
||||||
|
for ( int i = 0; i < bs.length(); ++i )
|
||||||
|
{
|
||||||
|
uint64 byte = bs[i];
|
||||||
|
rval |= byte << (8 * (bs.length() - (i + 1)));
|
||||||
|
}
|
||||||
|
|
||||||
|
return rval;
|
||||||
|
%}
|
|
@ -16,32 +16,32 @@ refine connection SMB_Conn += {
|
||||||
function build_negotiate_flag_record(val: SMB_NTLM_Negotiate_Flags): BroVal
|
function build_negotiate_flag_record(val: SMB_NTLM_Negotiate_Flags): BroVal
|
||||||
%{
|
%{
|
||||||
RecordVal* flags = new RecordVal(BifType::Record::SMB::NTLMNegotiateFlags);
|
RecordVal* flags = new RecordVal(BifType::Record::SMB::NTLMNegotiateFlags);
|
||||||
flags->Assign(0, new Val(${val.negotiate_56}, TYPE_BOOL));
|
flags->Assign(0, new Val(${val.negotiate_56}, TYPE_BOOL));
|
||||||
flags->Assign(1, new Val(${val.negotiate_key_exch}, TYPE_BOOL));
|
flags->Assign(1, new Val(${val.negotiate_key_exch}, TYPE_BOOL));
|
||||||
flags->Assign(2, new Val(${val.negotiate_128}, TYPE_BOOL));
|
flags->Assign(2, new Val(${val.negotiate_128}, TYPE_BOOL));
|
||||||
flags->Assign(3, new Val(${val.negotiate_version}, TYPE_BOOL));
|
flags->Assign(3, new Val(${val.negotiate_version}, TYPE_BOOL));
|
||||||
flags->Assign(4, new Val(${val.negotiate_target_info}, TYPE_BOOL));
|
flags->Assign(4, new Val(${val.negotiate_target_info}, TYPE_BOOL));
|
||||||
|
|
||||||
flags->Assign(5, new Val(${val.request_non_nt_session_key}, TYPE_BOOL));
|
flags->Assign(5, new Val(${val.request_non_nt_session_key}, TYPE_BOOL));
|
||||||
flags->Assign(6, new Val(${val.negotiate_identify}, TYPE_BOOL));
|
flags->Assign(6, new Val(${val.negotiate_identify}, TYPE_BOOL));
|
||||||
flags->Assign(7, new Val(${val.negotiate_extended_sessionsecurity}, TYPE_BOOL));
|
flags->Assign(7, new Val(${val.negotiate_extended_sessionsecurity}, TYPE_BOOL));
|
||||||
flags->Assign(8, new Val(${val.target_type_server}, TYPE_BOOL));
|
flags->Assign(8, new Val(${val.target_type_server}, TYPE_BOOL));
|
||||||
flags->Assign(9, new Val(${val.target_type_domain}, TYPE_BOOL));
|
flags->Assign(9, new Val(${val.target_type_domain}, TYPE_BOOL));
|
||||||
|
|
||||||
flags->Assign(10, new Val(${val.negotiate_always_sign}, TYPE_BOOL));
|
flags->Assign(10, new Val(${val.negotiate_always_sign}, TYPE_BOOL));
|
||||||
flags->Assign(11, new Val(${val.negotiate_oem_workstation_supplied}, TYPE_BOOL));
|
flags->Assign(11, new Val(${val.negotiate_oem_workstation_supplied}, TYPE_BOOL));
|
||||||
flags->Assign(12, new Val(${val.negotiate_oem_domain_supplied}, TYPE_BOOL));
|
flags->Assign(12, new Val(${val.negotiate_oem_domain_supplied}, TYPE_BOOL));
|
||||||
flags->Assign(13, new Val(${val.negotiate_anonymous_connection}, TYPE_BOOL));
|
flags->Assign(13, new Val(${val.negotiate_anonymous_connection}, TYPE_BOOL));
|
||||||
flags->Assign(14, new Val(${val.negotiate_ntlm}, TYPE_BOOL));
|
flags->Assign(14, new Val(${val.negotiate_ntlm}, TYPE_BOOL));
|
||||||
|
|
||||||
flags->Assign(15, new Val(${val.negotiate_lm_key}, TYPE_BOOL));
|
flags->Assign(15, new Val(${val.negotiate_lm_key}, TYPE_BOOL));
|
||||||
flags->Assign(16, new Val(${val.negotiate_datagram}, TYPE_BOOL));
|
flags->Assign(16, new Val(${val.negotiate_datagram}, TYPE_BOOL));
|
||||||
flags->Assign(17, new Val(${val.negotiate_seal}, TYPE_BOOL));
|
flags->Assign(17, new Val(${val.negotiate_seal}, TYPE_BOOL));
|
||||||
flags->Assign(18, new Val(${val.negotiate_sign}, TYPE_BOOL));
|
flags->Assign(18, new Val(${val.negotiate_sign}, TYPE_BOOL));
|
||||||
flags->Assign(19, new Val(${val.request_target}, TYPE_BOOL));
|
flags->Assign(19, new Val(${val.request_target}, TYPE_BOOL));
|
||||||
|
|
||||||
flags->Assign(20, new Val(${val.negotiate_oem}, TYPE_BOOL));
|
flags->Assign(20, new Val(${val.negotiate_oem}, TYPE_BOOL));
|
||||||
flags->Assign(21, new Val(${val.negotiate_unicode}, TYPE_BOOL));
|
flags->Assign(21, new Val(${val.negotiate_unicode}, TYPE_BOOL));
|
||||||
|
|
||||||
return flags;
|
return flags;
|
||||||
%}
|
%}
|
||||||
|
@ -51,7 +51,7 @@ refine connection SMB_Conn += {
|
||||||
RecordVal* result = new RecordVal(BifType::Record::SMB::NTLMVersion);
|
RecordVal* result = new RecordVal(BifType::Record::SMB::NTLMVersion);
|
||||||
result->Assign(0, new Val(${val.major_version}, TYPE_COUNT));
|
result->Assign(0, new Val(${val.major_version}, TYPE_COUNT));
|
||||||
result->Assign(1, new Val(${val.minor_version}, TYPE_COUNT));
|
result->Assign(1, new Val(${val.minor_version}, TYPE_COUNT));
|
||||||
result->Assign(2, new Val(${val.build_number}, TYPE_COUNT));
|
result->Assign(2, new Val(${val.build_number}, TYPE_COUNT));
|
||||||
result->Assign(3, new Val(${val.ntlm_revision}, TYPE_COUNT));
|
result->Assign(3, new Val(${val.ntlm_revision}, TYPE_COUNT));
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
@ -94,9 +94,22 @@ refine connection SMB_Conn += {
|
||||||
return result;
|
return result;
|
||||||
%}
|
%}
|
||||||
|
|
||||||
function proc_smb_ntlm_accept(header: SMB_Header): bool
|
function proc_smb_ntlm_ssp(header: SMB_Header, val:SMB_NTLM_SSP): bool
|
||||||
%{
|
%{
|
||||||
BifEvent::generate_smb_ntlm_accepted(bro_analyzer(), bro_analyzer()->Conn(), BuildHeaderVal(header));
|
if ( ${val.gssapi.is_init} )
|
||||||
|
return true;
|
||||||
|
for ( uint i = 0; i < ${val.gssapi.resp.args}->size(); ++i )
|
||||||
|
{
|
||||||
|
switch ( ${val.gssapi.resp.args[i].seq_meta.index} )
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
if ( ${val.gssapi.resp.args[i].args.neg_state} == 0 )
|
||||||
|
BifEvent::generate_smb_ntlm_accepted(bro_analyzer(), bro_analyzer()->Conn(), BuildHeaderVal(header));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
%}
|
%}
|
||||||
|
|
||||||
|
@ -104,9 +117,15 @@ refine connection SMB_Conn += {
|
||||||
%{
|
%{
|
||||||
RecordVal* result = new RecordVal(BifType::Record::SMB::NTLMNegotiate);
|
RecordVal* result = new RecordVal(BifType::Record::SMB::NTLMNegotiate);
|
||||||
result->Assign(0, build_negotiate_flag_record(${val.flags}));
|
result->Assign(0, build_negotiate_flag_record(${val.flags}));
|
||||||
if ( ${val.flags.negotiate_oem_domain_supplied} ) result->Assign(1, bytestring_to_val(${val.domain_name.string.data}));
|
|
||||||
if ( ${val.flags.negotiate_oem_workstation_supplied} ) result->Assign(2, bytestring_to_val(${val.workstation.string.data}));
|
if ( ${val.flags.negotiate_oem_domain_supplied} )
|
||||||
if ( ${val.flags.negotiate_version} ) result->Assign(3, build_version_record(${val.version}));
|
result->Assign(1, bytestring_to_val(${val.domain_name.string.data}));
|
||||||
|
|
||||||
|
if ( ${val.flags.negotiate_oem_workstation_supplied} )
|
||||||
|
result->Assign(2, bytestring_to_val(${val.workstation.string.data}));
|
||||||
|
|
||||||
|
if ( ${val.flags.negotiate_version} )
|
||||||
|
result->Assign(3, build_version_record(${val.version}));
|
||||||
|
|
||||||
BifEvent::generate_smb_ntlm_negotiate(bro_analyzer(), bro_analyzer()->Conn(), BuildHeaderVal(header), result);
|
BifEvent::generate_smb_ntlm_negotiate(bro_analyzer(), bro_analyzer()->Conn(), BuildHeaderVal(header), result);
|
||||||
|
|
||||||
|
@ -117,9 +136,15 @@ refine connection SMB_Conn += {
|
||||||
%{
|
%{
|
||||||
RecordVal* result = new RecordVal(BifType::Record::SMB::NTLMChallenge);
|
RecordVal* result = new RecordVal(BifType::Record::SMB::NTLMChallenge);
|
||||||
result->Assign(0, build_negotiate_flag_record(${val.flags}));
|
result->Assign(0, build_negotiate_flag_record(${val.flags}));
|
||||||
if ( ${val.flags.request_target} ) result->Assign(1, bytestring_to_val(${val.target_name.string.data}));
|
|
||||||
if ( ${val.flags.negotiate_version} ) result->Assign(2, build_version_record(${val.version}));
|
if ( ${val.flags.request_target} )
|
||||||
if ( ${val.flags.negotiate_target_info} ) result->Assign(3, build_av_record(${val.target_info}));
|
result->Assign(1, bytestring_to_val(${val.target_name.string.data}));
|
||||||
|
|
||||||
|
if ( ${val.flags.negotiate_version} )
|
||||||
|
result->Assign(2, build_version_record(${val.version}));
|
||||||
|
|
||||||
|
if ( ${val.flags.negotiate_target_info} )
|
||||||
|
result->Assign(3, build_av_record(${val.target_info}));
|
||||||
|
|
||||||
BifEvent::generate_smb_ntlm_challenge(bro_analyzer(), bro_analyzer()->Conn(), BuildHeaderVal(header), result);
|
BifEvent::generate_smb_ntlm_challenge(bro_analyzer(), bro_analyzer()->Conn(), BuildHeaderVal(header), result);
|
||||||
|
|
||||||
|
@ -130,154 +155,161 @@ refine connection SMB_Conn += {
|
||||||
%{
|
%{
|
||||||
RecordVal* result = new RecordVal(BifType::Record::SMB::NTLMAuthenticate);
|
RecordVal* result = new RecordVal(BifType::Record::SMB::NTLMAuthenticate);
|
||||||
result->Assign(0, build_negotiate_flag_record(${val.flags}));
|
result->Assign(0, build_negotiate_flag_record(${val.flags}));
|
||||||
result->Assign(1, bytestring_to_val(${val.domain_name.string.data}));
|
|
||||||
result->Assign(2, bytestring_to_val(${val.user_name.string.data}));
|
if ( ${val.domain_name_fields.length} > 0 )
|
||||||
result->Assign(3, bytestring_to_val(${val.workstation.string.data}));
|
result->Assign(1, bytestring_to_val(${val.domain_name.string.data}));
|
||||||
if ( ${val.flags.negotiate_version} ) result->Assign(4, build_version_record(${val.version}));
|
|
||||||
|
if ( ${val.user_name_fields.length} > 0 )
|
||||||
|
result->Assign(2, bytestring_to_val(${val.user_name.string.data}));
|
||||||
|
|
||||||
|
if ( ${val.workstation_fields.length} > 0 )
|
||||||
|
result->Assign(3, bytestring_to_val(${val.workstation.string.data}));
|
||||||
|
|
||||||
|
if ( ${val.flags.negotiate_version} )
|
||||||
|
result->Assign(4, build_version_record(${val.version}));
|
||||||
|
|
||||||
BifEvent::generate_smb_ntlm_authenticate(bro_analyzer(), bro_analyzer()->Conn(), BuildHeaderVal(header), result);
|
BifEvent::generate_smb_ntlm_authenticate(bro_analyzer(), bro_analyzer()->Conn(), BuildHeaderVal(header), result);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
%}
|
%}
|
||||||
|
|
||||||
function convert_der_num(is_long: bool, first: uint8, fields: bytestring): uint64
|
|
||||||
%{
|
|
||||||
if (!is_long) return first;
|
|
||||||
|
|
||||||
int result;
|
|
||||||
result = 0;
|
|
||||||
|
|
||||||
for (uint i = 0; i < fields.length(); i++)
|
|
||||||
{
|
|
||||||
result << 8;
|
|
||||||
result += ${fields[i]};
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
%}
|
|
||||||
|
|
||||||
function test_accepted(m: SMB_NTLM_SSP): bool
|
|
||||||
%{
|
|
||||||
if ( ${m.gssapi}->size() < 4 )
|
|
||||||
return false;
|
|
||||||
return ( ${m.gssapi[3].tag} == 10 && ${m.gssapi[3].value} == "\x00" );
|
|
||||||
%}
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
type DER_Length = record {
|
type GSSAPI_NEG_TOKEN(header: SMB_Header) = record {
|
||||||
first : uint8;
|
wrapper : ASN1EncodingMeta;
|
||||||
rest : bytestring &length=size;
|
have_oid : case is_init of {
|
||||||
|
true -> oid: ASN1Encoding;
|
||||||
|
false -> no_oid: empty;
|
||||||
|
};
|
||||||
|
have_init_wrapper: case is_init of {
|
||||||
|
true -> init_wrapper: ASN1EncodingMeta;
|
||||||
|
false -> no_init_wrapper: empty;
|
||||||
|
};
|
||||||
|
msg_type : case is_init of {
|
||||||
|
true -> init: GSSAPI_NEG_TOKEN_INIT(header);
|
||||||
|
false -> resp: GSSAPI_NEG_TOKEN_RESP(header);
|
||||||
|
};
|
||||||
} &let {
|
} &let {
|
||||||
is_long_form : bool = (first >= 128);
|
is_init: bool = wrapper.tag == 0x60;
|
||||||
size : uint8 = is_long_form ? first % 128 : 0;
|
|
||||||
value : uint64 = $context.connection.convert_der_num(is_long_form, first, rest);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
type DER_ASN = record {
|
type GSSAPI_NEG_TOKEN_INIT(header: SMB_Header) = record {
|
||||||
tag: uint8;
|
seq_meta: ASN1EncodingMeta;
|
||||||
length: DER_Length;
|
args : GSSAPI_NEG_TOKEN_INIT_Arg(header)[];
|
||||||
skip_constructed: case (is_primitive && (tag != 0x04) ) of {
|
};
|
||||||
true -> value: bytestring &length=length.value;
|
|
||||||
false -> nothing: empty;
|
type GSSAPI_NEG_TOKEN_INIT_Arg(header: SMB_Header) = record {
|
||||||
} &requires(is_primitive);
|
seq_meta: ASN1EncodingMeta;
|
||||||
} &let {
|
args : GSSAPI_NEG_TOKEN_INIT_Arg_Data(header, seq_meta.index) &length=seq_meta.length;
|
||||||
is_primitive: bool = (tag & 0x20) == 0;
|
};
|
||||||
last: bool = tag == 0x04;
|
|
||||||
|
type GSSAPI_NEG_TOKEN_INIT_Arg_Data(header: SMB_Header, index: uint8) = case index of {
|
||||||
|
0 -> mech_type_list : ASN1Encoding;
|
||||||
|
1 -> req_flags : ASN1Encoding;
|
||||||
|
2 -> mech_token : SMB_NTLM_SSP_Token(header);
|
||||||
|
3 -> mech_list_mic : ASN1OctetString;
|
||||||
|
};
|
||||||
|
|
||||||
|
type GSSAPI_NEG_TOKEN_RESP(header: SMB_Header) = record {
|
||||||
|
seq_meta: ASN1EncodingMeta;
|
||||||
|
args : GSSAPI_NEG_TOKEN_RESP_Arg(header)[];
|
||||||
|
};
|
||||||
|
|
||||||
|
type GSSAPI_NEG_TOKEN_RESP_Arg(header: SMB_Header) = record {
|
||||||
|
seq_meta: ASN1EncodingMeta;
|
||||||
|
args : GSSAPI_NEG_TOKEN_RESP_Arg_Data(header, seq_meta.index) &length=seq_meta.length;
|
||||||
|
};
|
||||||
|
|
||||||
|
type GSSAPI_NEG_TOKEN_RESP_Arg_Data(header: SMB_Header, index: uint8) = case index of {
|
||||||
|
0 -> neg_state : ASN1Integer;
|
||||||
|
1 -> supported_mech : ASN1Encoding;
|
||||||
|
2 -> response_token : SMB_NTLM_SSP_Token(header);
|
||||||
|
3 -> mech_list_mic : ASN1OctetString;
|
||||||
};
|
};
|
||||||
|
|
||||||
type SMB_NTLM_SSP(header: SMB_Header) = record {
|
type SMB_NTLM_SSP(header: SMB_Header) = record {
|
||||||
gssapi : DER_ASN[] &until ($element.last);
|
gssapi: GSSAPI_NEG_TOKEN(header);
|
||||||
skip_accepted: case ( is_accepted ) of {
|
|
||||||
true -> nothing: empty;
|
|
||||||
false -> token: SMB_NTLM_Neg_Token(header);
|
|
||||||
} &requires(is_accepted);
|
|
||||||
} &let {
|
} &let {
|
||||||
is_accepted: bool = $context.connection.test_accepted(this);
|
proc: bool = $context.connection.proc_smb_ntlm_ssp(header, this);
|
||||||
proc: bool = $context.connection.proc_smb_ntlm_accept(header) &if(is_accepted);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
type SMB_NTLM_Neg_Token(header: SMB_Header) = record {
|
type SMB_NTLM_SSP_Token(header: SMB_Header) = record {
|
||||||
identifier : bytestring &length=8;
|
meta : ASN1EncodingMeta;
|
||||||
msg_type : uint32;
|
signature : bytestring &length=8;
|
||||||
msg : case msg_type of {
|
msg_type : uint32;
|
||||||
0 -> accept : empty;
|
msg : case msg_type of {
|
||||||
1 -> negotiate : SMB_NTLM_Negotiate(header, 12);
|
1 -> negotiate : SMB_NTLM_Negotiate(header, offsetof(msg) - offsetof(signature));
|
||||||
2 -> challenge : SMB_NTLM_Challenge(header, 12);
|
2 -> challenge : SMB_NTLM_Challenge(header, offsetof(msg) - offsetof(signature));
|
||||||
3 -> authenticate : SMB_NTLM_Authenticate(header, 12);
|
3 -> authenticate : SMB_NTLM_Authenticate(header, offsetof(msg) - offsetof(signature));
|
||||||
};
|
};
|
||||||
} &let {
|
|
||||||
is_accept : bool = (msg_type == 0);
|
|
||||||
is_negotiate : bool = (msg_type == 1);
|
|
||||||
is_challenge : bool = (msg_type == 2);
|
|
||||||
is_authenticate : bool = (msg_type == 3);
|
|
||||||
proc : bool = $context.connection.proc_smb_ntlm_accept(header) &if is_accept;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
type SMB_NTLM_Negotiate(header: SMB_Header, offset: uint16) = record {
|
type SMB_NTLM_Negotiate(header: SMB_Header, offset: uint16) = record {
|
||||||
flags : SMB_NTLM_Negotiate_Flags;
|
flags : SMB_NTLM_Negotiate_Flags;
|
||||||
domain_name_fields : SMB_NTLM_StringData;
|
domain_name_fields : SMB_NTLM_StringData;
|
||||||
workstation_fields : SMB_NTLM_StringData;
|
workstation_fields : SMB_NTLM_StringData;
|
||||||
version_present : case flags.negotiate_version of {
|
version_present : case flags.negotiate_version of {
|
||||||
true -> version: SMB_NTLM_Version;
|
true -> version : SMB_NTLM_Version;
|
||||||
false -> no_version: empty;
|
false -> no_version : empty;
|
||||||
};
|
};
|
||||||
payload : bytestring &restofdata;
|
payload : bytestring &restofdata;
|
||||||
} &let {
|
} &let {
|
||||||
absolute_offset : uint16 = offsetof(payload) + offset;
|
absolute_offset : uint16 = offsetof(payload) + offset;
|
||||||
domain_name : SMB_NTLM_String(domain_name_fields, absolute_offset, flags.negotiate_unicode) withinput payload &if(flags.negotiate_oem_domain_supplied);
|
domain_name : SMB_NTLM_String(domain_name_fields, absolute_offset, flags.negotiate_unicode) withinput payload &if(flags.negotiate_oem_domain_supplied);
|
||||||
workstation : SMB_NTLM_String(workstation_fields, absolute_offset, flags.negotiate_unicode) withinput payload &if(flags.negotiate_oem_workstation_supplied);
|
workstation : SMB_NTLM_String(workstation_fields, absolute_offset, flags.negotiate_unicode) withinput payload &if(flags.negotiate_oem_workstation_supplied);
|
||||||
proc : bool = $context.connection.proc_smb_ntlm_negotiate(header, this);
|
proc : bool = $context.connection.proc_smb_ntlm_negotiate(header, this);
|
||||||
};
|
};
|
||||||
|
|
||||||
type SMB_NTLM_Challenge(header: SMB_Header, offset: uint16) = record {
|
type SMB_NTLM_Challenge(header: SMB_Header, offset: uint16) = record {
|
||||||
target_name_fields : SMB_NTLM_StringData;
|
target_name_fields : SMB_NTLM_StringData;
|
||||||
flags : SMB_NTLM_Negotiate_Flags;
|
flags : SMB_NTLM_Negotiate_Flags;
|
||||||
challenge : uint64;
|
challenge : uint64;
|
||||||
reserved : padding[8];
|
reserved : padding[8];
|
||||||
target_info_fields : SMB_NTLM_StringData;
|
target_info_fields : SMB_NTLM_StringData;
|
||||||
version_present : case flags.negotiate_version of {
|
version_present : case flags.negotiate_version of {
|
||||||
true -> version: SMB_NTLM_Version;
|
true -> version : SMB_NTLM_Version;
|
||||||
false -> no_version: empty;
|
false -> no_version : empty;
|
||||||
};
|
};
|
||||||
payload : bytestring &restofdata;
|
payload : bytestring &restofdata;
|
||||||
} &let {
|
} &let {
|
||||||
absolute_offset : uint16 = offsetof(payload) + offset;
|
absolute_offset : uint16 = offsetof(payload) + offset;
|
||||||
target_name : SMB_NTLM_String(target_name_fields, absolute_offset, flags.negotiate_unicode) withinput payload &if(flags.request_target);
|
target_name : SMB_NTLM_String(target_name_fields, absolute_offset, flags.negotiate_unicode) withinput payload &if(flags.request_target);
|
||||||
target_info : SMB_NTLM_AV_Pair_Sequence(target_info_fields.offset - absolute_offset) withinput payload &if(flags.negotiate_target_info);
|
target_info : SMB_NTLM_AV_Pair_Sequence(target_info_fields.offset - absolute_offset) withinput payload &if(flags.negotiate_target_info);
|
||||||
proc : bool = $context.connection.proc_smb_ntlm_challenge(header, this);
|
proc : bool = $context.connection.proc_smb_ntlm_challenge(header, this);
|
||||||
};
|
};
|
||||||
|
|
||||||
type SMB_NTLM_Authenticate(header: SMB_Header, offset: uint16) = record {
|
type SMB_NTLM_Authenticate(header: SMB_Header, offset: uint16) = record {
|
||||||
lm_challenge_response_fields : SMB_NTLM_StringData;
|
lm_challenge_response_fields: SMB_NTLM_StringData;
|
||||||
nt_challenge_response_fields : SMB_NTLM_StringData;
|
nt_challenge_response_fields: SMB_NTLM_StringData;
|
||||||
domain_name_fields : SMB_NTLM_StringData;
|
domain_name_fields : SMB_NTLM_StringData;
|
||||||
user_name_fields : SMB_NTLM_StringData;
|
user_name_fields : SMB_NTLM_StringData;
|
||||||
workstation_fields : SMB_NTLM_StringData;
|
workstation_fields : SMB_NTLM_StringData;
|
||||||
encrypted_session_key_fields : SMB_NTLM_StringData;
|
encrypted_session_key_fields: SMB_NTLM_StringData;
|
||||||
flags : SMB_NTLM_Negotiate_Flags;
|
flags : SMB_NTLM_Negotiate_Flags;
|
||||||
version_present : case flags.negotiate_version of {
|
version_present : case flags.negotiate_version of {
|
||||||
true -> version: SMB_NTLM_Version;
|
true -> version : SMB_NTLM_Version;
|
||||||
false -> no_version: empty;
|
false -> no_version : empty;
|
||||||
};
|
};
|
||||||
mic : bytestring &length=16;
|
|
||||||
payload : bytestring &restofdata;
|
# Windows NT, 2000, XP, and 2003 don't have the MIC field
|
||||||
|
# TODO - figure out how to parse this for those that do have it
|
||||||
|
# mic : bytestring &length=16;
|
||||||
|
|
||||||
|
payload : bytestring &restofdata;
|
||||||
} &let {
|
} &let {
|
||||||
absolute_offset : uint16 = offsetof(payload) + offset;
|
absolute_offset : uint16 = offsetof(payload) + offset;
|
||||||
domain_name : SMB_NTLM_String(domain_name_fields, absolute_offset, flags.negotiate_unicode) withinput payload;
|
domain_name : SMB_NTLM_String(domain_name_fields, absolute_offset, flags.negotiate_unicode) withinput payload &if(domain_name_fields.length > 0);
|
||||||
user_name : SMB_NTLM_String(user_name_fields, absolute_offset, flags.negotiate_unicode) withinput payload;
|
user_name : SMB_NTLM_String(user_name_fields, absolute_offset, flags.negotiate_unicode) withinput payload &if(user_name_fields.length > 0);
|
||||||
workstation : SMB_NTLM_String(workstation_fields, absolute_offset , flags.negotiate_unicode) withinput payload;
|
workstation : SMB_NTLM_String(workstation_fields, absolute_offset , flags.negotiate_unicode) withinput payload &if(workstation_fields.length > 0);
|
||||||
encrypted_session_key : SMB_NTLM_String(workstation_fields, absolute_offset, flags.negotiate_unicode) withinput payload &if(flags.negotiate_key_exch);
|
encrypted_session_key : SMB_NTLM_String(encrypted_session_key_fields, absolute_offset, flags.negotiate_unicode) withinput payload &if(flags.negotiate_key_exch);
|
||||||
# lm_response : SMB_LM_Response(lm_challenge_response_fields.offset - absolute_offset) withinput payload &if(lm_challenge_response_fields.length > 0);
|
proc : bool = $context.connection.proc_smb_ntlm_authenticate(header, this);
|
||||||
# ntlm_response : SMB_NTLM_Response(nt_challenge_response_fields.offset - absolute_offset) withinput payload &if(nt_challenge_response_fields.length == 24);
|
|
||||||
# ntlmv2_response : SMB_NTLM_Response(nt_challenge_response_fields.offset - absolute_offset) withinput payload &if(nt_challenge_response_fields.length > 24);
|
|
||||||
proc : bool = $context.connection.proc_smb_ntlm_authenticate(header, this);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
type SMB_NTLM_Version = record {
|
type SMB_NTLM_Version = record {
|
||||||
major_version : uint8;
|
major_version : uint8;
|
||||||
minor_version : uint8;
|
minor_version : uint8;
|
||||||
build_number : uint16;
|
build_number : uint16;
|
||||||
reserved : padding[3];
|
reserved : padding[3];
|
||||||
ntlm_revision : uint8;
|
ntlm_revision : uint8;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -320,80 +352,88 @@ type SMB_NTLM_AV_Pair = record {
|
||||||
0x000a -> channel_bindings : uint16;
|
0x000a -> channel_bindings : uint16;
|
||||||
};
|
};
|
||||||
} &let {
|
} &let {
|
||||||
last : bool = ( id == 0x0000);
|
last : bool = ( id == 0x0000);
|
||||||
# av_flags refinement
|
# av_flags refinement
|
||||||
constrained_auth : bool = (av_flags & 0x00000001) > 0 &if ( id == 0x0006);
|
constrained_auth: bool = (av_flags & 0x00000001) > 0 &if ( id == 0x0006);
|
||||||
mic_present : bool = (av_flags & 0x00000002) > 0 &if ( id == 0x0006);
|
mic_present : bool = (av_flags & 0x00000002) > 0 &if ( id == 0x0006);
|
||||||
untrusted_source : bool = (av_flags & 0x00000004) > 0 &if ( id == 0x0006);
|
untrusted_source: bool = (av_flags & 0x00000004) > 0 &if ( id == 0x0006);
|
||||||
};
|
};
|
||||||
|
|
||||||
type SMB_NTLM_Single_Host = record {
|
type SMB_NTLM_Single_Host = record {
|
||||||
size : uint32;
|
size : uint32;
|
||||||
padpad : padding[4];
|
padpad : padding[4];
|
||||||
data_present : uint32;
|
data_present: uint32;
|
||||||
optional : case custom_data_present of {
|
optional : case custom_data_present of {
|
||||||
true -> custom_data : bytestring &length=4;
|
true -> custom_data : bytestring &length=4;
|
||||||
false -> nothing : empty;
|
false -> nothing : empty;
|
||||||
};
|
};
|
||||||
machine_id : uint32;
|
machine_id : uint32;
|
||||||
} &let {
|
} &let {
|
||||||
custom_data_present : bool = (data_present & 0x00000001) > 0;
|
custom_data_present: bool = (data_present & 0x00000001) > 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
type SMB_LM_Response(offset: uint16) = record {
|
type SMB_LM_Response(offset: uint16) = record {
|
||||||
# This can be either LM (24 byte response) or
|
# This can be either LM (24 byte response) or
|
||||||
# LMv2 (16 byte response + 8 byte client challenge. No way to
|
# LMv2 (16 byte response + 8 byte client challenge. No way to
|
||||||
# know for sure.
|
# know for sure.
|
||||||
padpad : padding to offset;
|
padpad : padding to offset;
|
||||||
response : bytestring &length=24;
|
response: bytestring &length=24;
|
||||||
};
|
};
|
||||||
|
|
||||||
type SMB_NTLM_Response(offset: uint16) = record {
|
type SMB_NTLM_Response(offset: uint16) = record {
|
||||||
padpad : padding to offset;
|
padpad : padding to offset;
|
||||||
response : bytestring &length=24;
|
response: bytestring &length=24;
|
||||||
};
|
};
|
||||||
|
|
||||||
type SMB_NTLMv2_Response(flags: SMB_NTLM_Negotiate_Flags, offset: uint16) = record {
|
type SMB_NTLMv2_Response(flags: SMB_NTLM_Negotiate_Flags, offset: uint16) = record {
|
||||||
padpad : padding to offset;
|
padpad : padding to offset;
|
||||||
response : bytestring &length=16;
|
response : bytestring &length=16;
|
||||||
client_challenge : SMB_NTLMv2_Client_Challenge(flags);
|
client_challenge: SMB_NTLMv2_Client_Challenge(flags);
|
||||||
};
|
};
|
||||||
|
|
||||||
type SMB_NTLMv2_Client_Challenge(flags: SMB_NTLM_Negotiate_Flags) = record {
|
type SMB_NTLMv2_Client_Challenge(flags: SMB_NTLM_Negotiate_Flags) = record {
|
||||||
resp_type : uint8;
|
resp_type : uint8;
|
||||||
max_resp_type : uint8;
|
max_resp_type : uint8;
|
||||||
reserved : padding[6];
|
reserved : padding[6];
|
||||||
timestamp : uint64;
|
timestamp : uint64;
|
||||||
client_challenge : bytestring &length=8;
|
client_challenge: bytestring &length=8;
|
||||||
reserved2 : padding[4];
|
reserved2 : padding[4];
|
||||||
av_pairs : SMB_NTLM_AV_Pair_Sequence(0);
|
av_pairs : SMB_NTLM_AV_Pair_Sequence(0);
|
||||||
};
|
};
|
||||||
|
|
||||||
type SMB_NTLM_Negotiate_Flags = record {
|
type SMB_NTLM_Negotiate_Flags = record {
|
||||||
flags : uint32;
|
flags: uint32;
|
||||||
} &let {
|
} &let {
|
||||||
negotiate_56 : bool = (flags & 0x80000000) > 0;
|
negotiate_56 : bool = (flags & 0x80000000) > 0;
|
||||||
negotiate_key_exch : bool = (flags & 0x40000000) > 0;
|
negotiate_key_exch : bool = (flags & 0x40000000) > 0;
|
||||||
negotiate_128 : bool = (flags & 0x20000000) > 0;
|
negotiate_128 : bool = (flags & 0x20000000) > 0;
|
||||||
negotiate_version : bool = (flags & 0x02000000) > 0;
|
|
||||||
negotiate_target_info : bool = (flags & 0x00800000) > 0;
|
negotiate_version : bool = (flags & 0x02000000) > 0;
|
||||||
request_non_nt_session_key : bool = (flags & 0x00400000) > 0;
|
|
||||||
negotiate_identify : bool = (flags & 0x00100000) > 0;
|
negotiate_target_info : bool = (flags & 0x00800000) > 0;
|
||||||
negotiate_extended_sessionsecurity : bool = (flags & 0x00040000) > 0;
|
request_non_nt_session_key : bool = (flags & 0x00400000) > 0;
|
||||||
target_type_server : bool = (flags & 0x00020000) > 0;
|
negotiate_identify : bool = (flags & 0x00100000) > 0;
|
||||||
target_type_domain : bool = (flags & 0x00010000) > 0;
|
|
||||||
negotiate_always_sign : bool = (flags & 0x00008000) > 0;
|
negotiate_extended_sessionsecurity : bool = (flags & 0x00040000) > 0;
|
||||||
negotiate_oem_workstation_supplied : bool = (flags & 0x00002000) > 0;
|
target_type_server : bool = (flags & 0x00020000) > 0;
|
||||||
negotiate_oem_domain_supplied : bool = (flags & 0x00001000) > 0;
|
target_type_domain : bool = (flags & 0x00010000) > 0;
|
||||||
negotiate_anonymous_connection : bool = (flags & 0x00000400) > 0;
|
|
||||||
negotiate_ntlm : bool = (flags & 0x00000100) > 0;
|
negotiate_always_sign : bool = (flags & 0x00008000) > 0;
|
||||||
negotiate_lm_key : bool = (flags & 0x00000080) > 0;
|
negotiate_oem_workstation_supplied : bool = (flags & 0x00002000) > 0;
|
||||||
negotiate_datagram : bool = (flags & 0x00000040) > 0;
|
negotiate_oem_domain_supplied : bool = (flags & 0x00001000) > 0;
|
||||||
negotiate_seal : bool = (flags & 0x00000020) > 0;
|
|
||||||
negotiate_sign : bool = (flags & 0x00000008) > 0;
|
negotiate_anonymous_connection : bool = (flags & 0x00000400) > 0;
|
||||||
request_target : bool = (flags & 0x00000004) > 0;
|
negotiate_ntlm : bool = (flags & 0x00000100) > 0;
|
||||||
negotiate_oem : bool = (flags & 0x00000002) > 0;
|
|
||||||
negotiate_unicode : bool = (flags & 0x00000001) > 0;
|
negotiate_lm_key : bool = (flags & 0x00000080) > 0;
|
||||||
is_oem : bool = !negotiate_unicode && negotiate_oem;
|
negotiate_datagram : bool = (flags & 0x00000040) > 0;
|
||||||
is_invalid : bool = !negotiate_unicode && !negotiate_oem;
|
negotiate_seal : bool = (flags & 0x00000020) > 0;
|
||||||
|
|
||||||
|
negotiate_sign : bool = (flags & 0x00000008) > 0;
|
||||||
|
request_target : bool = (flags & 0x00000004) > 0;
|
||||||
|
negotiate_oem : bool = (flags & 0x00000002) > 0;
|
||||||
|
negotiate_unicode : bool = (flags & 0x00000001) > 0;
|
||||||
|
|
||||||
|
is_oem : bool = !negotiate_unicode && negotiate_oem;
|
||||||
|
is_invalid : bool = !negotiate_unicode && !negotiate_oem;
|
||||||
};
|
};
|
||||||
|
|
|
@ -3,44 +3,67 @@
|
||||||
|
|
||||||
%include dce_rpc-protocol.pac
|
%include dce_rpc-protocol.pac
|
||||||
|
|
||||||
|
%extern{
|
||||||
|
#include "DCE_RPC.h"
|
||||||
|
%}
|
||||||
|
|
||||||
refine connection SMB_Conn += {
|
refine connection SMB_Conn += {
|
||||||
function proc_smb_atsvc_job_add(val: AT_SVC_NetrJobAdd): bool
|
|
||||||
%{
|
|
||||||
if ( smb_atsvc_job_add )
|
|
||||||
{
|
|
||||||
BifEvent::generate_smb_atsvc_job_add(bro_analyzer(), bro_analyzer()->Conn(), smb_string2stringval(${val.server.string}), smb_string2stringval(${val.command.string}));
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
%}
|
|
||||||
|
|
||||||
function proc_smb_atsvc_job_id(val: AT_SVC_JobID): bool
|
function get_tree_is_pipe(tree_id: uint16): bool
|
||||||
%{
|
|
||||||
if ( smb_atsvc_job_id )
|
|
||||||
{
|
|
||||||
BifEvent::generate_smb_atsvc_job_id(bro_analyzer(), bro_analyzer()->Conn(), ${val.id}, ${val.status});
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
%}
|
|
||||||
|
|
||||||
function determine_pipe_msg_type(hdr: DCE_RPC_Header, opnum: uint8): uint8
|
|
||||||
%{
|
%{
|
||||||
if ( !is_atsvc ) return 0;
|
if ( tree_is_pipe_map.count(tree_id) == 0 )
|
||||||
if ( ${hdr.PTYPE} == 0 && ${opnum} == 0 ) return 1;
|
return false;
|
||||||
if ( ${hdr.PTYPE} == 2 && ${opnum} == 0 ) return 2;
|
return tree_is_pipe_map[tree_id];
|
||||||
return 0;
|
|
||||||
%}
|
%}
|
||||||
|
|
||||||
|
function set_tree_is_pipe(tree_id: uint16, is_pipe: bool): bool
|
||||||
|
%{
|
||||||
|
tree_is_pipe_map[tree_id] = is_pipe;
|
||||||
|
return true;
|
||||||
|
%}
|
||||||
|
|
||||||
|
%member{
|
||||||
|
map<uint16,bool> tree_is_pipe_map;
|
||||||
|
%}
|
||||||
|
|
||||||
|
function proc_smb_pipe_message(val: SMB_Pipe_message, header: SMB_Header): bool
|
||||||
|
%{
|
||||||
|
switch ( ${val.rpc_header.PTYPE} ) {
|
||||||
|
case DCE_RPC_REQUEST:
|
||||||
|
if ( smb_pipe_request )
|
||||||
|
BifEvent::generate_smb_pipe_request(bro_analyzer(), bro_analyzer()->Conn(), BuildHeaderVal(header), \
|
||||||
|
${val.rpc_body.request.opnum});
|
||||||
|
break;
|
||||||
|
case DCE_RPC_RESPONSE:
|
||||||
|
if ( smb_pipe_response )
|
||||||
|
BifEvent::generate_smb_pipe_response(bro_analyzer(), bro_analyzer()->Conn(), BuildHeaderVal(header));
|
||||||
|
break;
|
||||||
|
case DCE_RPC_BIND_ACK:
|
||||||
|
if ( smb_pipe_bind_ack_response )
|
||||||
|
BifEvent::generate_smb_pipe_bind_ack_response(bro_analyzer(), bro_analyzer()->Conn(), BuildHeaderVal(header));
|
||||||
|
break;
|
||||||
|
case DCE_RPC_BIND:
|
||||||
|
if ( smb_pipe_bind_request )
|
||||||
|
// TODO - the version number needs to be calculated properly
|
||||||
|
BifEvent::generate_smb_pipe_bind_request(bro_analyzer(), bro_analyzer()->Conn(), BuildHeaderVal(header), \
|
||||||
|
new StringVal(analyzer::dce_rpc::uuid_to_string(bytestring_to_val(${val.rpc_body.bind.p_context_elem.p_cont_elem[0].abstract_syntax.if_uuid})->Bytes())), new StringVal(fmt("%d.0", ${val.rpc_body.bind.p_context_elem.p_cont_elem[0].abstract_syntax.if_version})));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
%}
|
||||||
};
|
};
|
||||||
|
|
||||||
type SMB_Pipe_message( unicode: bool, byte_count: uint16, sub_cmd: uint16 ) = record {
|
type SMB_Pipe_message(header: SMB_Header, byte_count: uint16) = record {
|
||||||
rpc : DCE_RPC_Header;
|
rpc_header : DCE_RPC_Header;
|
||||||
todo : padding[6]; # These fields are currently missing from DCE/RPC for some reason.
|
rpc_body : DCE_RPC_Body(rpc_header);
|
||||||
opnum : uint8;
|
# pipe_type: case $context.connection.determine_pipe_msg_type(rpc, opnum) of {
|
||||||
pipe_type: case $context.connection.determine_pipe_msg_type(rpc, opnum) of {
|
# 1 -> atsvc_request : AT_SVC_Request(unicode, opnum);
|
||||||
1 -> atsvc_request : AT_SVC_Request(unicode, opnum);
|
# 2 -> atsvc_reply : AT_SVC_Reply(unicode, opnum);
|
||||||
2 -> atsvc_reply : AT_SVC_Reply(unicode, opnum);
|
# default -> unknown : bytestring &restofdata;
|
||||||
default -> unknown : bytestring &restofdata;
|
# };
|
||||||
};
|
} &let {
|
||||||
|
proc: bool = $context.connection.proc_smb_pipe_message(this, header);
|
||||||
} &byteorder = littleendian;
|
} &byteorder = littleendian;
|
||||||
|
|
||||||
type SMB_RAP_message( unicode: bool, byte_count: uint16 ) = record {
|
type SMB_RAP_message( unicode: bool, byte_count: uint16 ) = record {
|
||||||
|
@ -77,8 +100,6 @@ type AT_SVC_NetrJobAdd(unicode: bool) = record {
|
||||||
flags : uint8;
|
flags : uint8;
|
||||||
unknown2 : padding[2];
|
unknown2 : padding[2];
|
||||||
command : AT_SVC_String_Pointer(unicode);
|
command : AT_SVC_String_Pointer(unicode);
|
||||||
} &let {
|
|
||||||
proc: bool = $context.connection.proc_smb_atsvc_job_add(this);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
type AT_SVC_Reply(unicode: bool, opnum: uint16) = record {
|
type AT_SVC_Reply(unicode: bool, opnum: uint16) = record {
|
||||||
|
@ -91,6 +112,4 @@ type AT_SVC_Reply(unicode: bool, opnum: uint16) = record {
|
||||||
type AT_SVC_JobID(unicode: bool) = record {
|
type AT_SVC_JobID(unicode: bool) = record {
|
||||||
id: uint32;
|
id: uint32;
|
||||||
status: uint32;
|
status: uint32;
|
||||||
} &let {
|
|
||||||
proc: bool = $context.connection.proc_smb_atsvc_job_id(this);
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -379,8 +379,8 @@ type SMB_transaction_data(unicode: bool, count: uint16, sub_cmd: uint16,
|
||||||
|
|
||||||
SMB_MAILSLOT_BROWSE -> mailslot : SMB_MailSlot_message(unicode, count);
|
SMB_MAILSLOT_BROWSE -> mailslot : SMB_MailSlot_message(unicode, count);
|
||||||
SMB_MAILSLOT_LANMAN -> lanman : SMB_MailSlot_message(unicode, count);
|
SMB_MAILSLOT_LANMAN -> lanman : SMB_MailSlot_message(unicode, count);
|
||||||
SMB_RAP -> rap : SMB_Pipe_message(unicode, count, sub_cmd);
|
SMB_RAP -> rap : SMB_Pipe_message(unicode, count);
|
||||||
SMB_PIPE -> pipe : SMB_Pipe_message(unicode, count, sub_cmd);
|
SMB_PIPE -> pipe : SMB_Pipe_message(unicode, count);
|
||||||
SMB_UNKNOWN -> unknown : bytestring &restofdata;
|
SMB_UNKNOWN -> unknown : bytestring &restofdata;
|
||||||
default -> data : bytestring &restofdata;
|
default -> data : bytestring &restofdata;
|
||||||
|
|
||||||
|
@ -416,7 +416,7 @@ type SMB_transaction(trans_type: int, unicode: bool) = record {
|
||||||
determine_transaction_type( setup_count, name_string( this )));
|
determine_transaction_type( setup_count, name_string( this )));
|
||||||
} &let {
|
} &let {
|
||||||
# does this work?
|
# does this work?
|
||||||
sub_cmd : uint16 = setup_count ? setup[0] : 0;
|
sub_cmd : uint16 = setup_count ? setup.op_code : 0;
|
||||||
|
|
||||||
} &byteorder = littleendian;
|
} &byteorder = littleendian;
|
||||||
|
|
||||||
|
|
|
@ -47,7 +47,6 @@ function uint8s_to_stringval(s: uint8[]): StringVal
|
||||||
// If the last 2 bytes are nulls, cut them with the length.
|
// If the last 2 bytes are nulls, cut them with the length.
|
||||||
length = length-2;
|
length = length-2;
|
||||||
}
|
}
|
||||||
|
|
||||||
StringVal *output = new StringVal(length, buf);
|
StringVal *output = new StringVal(length, buf);
|
||||||
delete [] buf;
|
delete [] buf;
|
||||||
return output;
|
return output;
|
||||||
|
@ -71,7 +70,7 @@ function extract_string(s: SMB_string) : StringVal
|
||||||
buf[i] = t;
|
buf[i] = t;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( length > 0 && buf[length] == 0x00 )
|
if ( length > 0 && buf[length-1] == 0x00 )
|
||||||
length--;
|
length--;
|
||||||
|
|
||||||
StringVal *ret = new StringVal(length, buf);
|
StringVal *ret = new StringVal(length, buf);
|
||||||
|
|
|
@ -21,6 +21,8 @@
|
||||||
#include "smb1_com_query_information.bif.h"
|
#include "smb1_com_query_information.bif.h"
|
||||||
#include "smb1_com_read_andx.bif.h"
|
#include "smb1_com_read_andx.bif.h"
|
||||||
#include "smb1_com_session_setup_andx.bif.h"
|
#include "smb1_com_session_setup_andx.bif.h"
|
||||||
|
#include "smb1_com_transaction.bif.h"
|
||||||
|
#include "smb1_com_transaction2.bif.h"
|
||||||
#include "smb1_com_tree_connect_andx.bif.h"
|
#include "smb1_com_tree_connect_andx.bif.h"
|
||||||
#include "smb1_com_tree_disconnect.bif.h"
|
#include "smb1_com_tree_disconnect.bif.h"
|
||||||
#include "smb1_com_write_andx.bif.h"
|
#include "smb1_com_write_andx.bif.h"
|
||||||
|
@ -50,6 +52,7 @@ connection SMB_Conn(bro_analyzer: BroAnalyzer) {
|
||||||
%include smb-common.pac
|
%include smb-common.pac
|
||||||
%include smb-time.pac
|
%include smb-time.pac
|
||||||
|
|
||||||
|
%include smb-ntlmssp-asn1.pac
|
||||||
%include smb-ntlmssp.pac
|
%include smb-ntlmssp.pac
|
||||||
|
|
||||||
# SMB1 Commands
|
# SMB1 Commands
|
||||||
|
|
|
@ -2,8 +2,6 @@ refine connection SMB_Conn += {
|
||||||
|
|
||||||
function proc_smb1_close_request(h: SMB_Header, val: SMB1_close_request): bool
|
function proc_smb1_close_request(h: SMB_Header, val: SMB1_close_request): bool
|
||||||
%{
|
%{
|
||||||
is_atsvc = false;
|
|
||||||
|
|
||||||
if ( smb1_close_request )
|
if ( smb1_close_request )
|
||||||
BifEvent::generate_smb1_close_request(bro_analyzer(),
|
BifEvent::generate_smb1_close_request(bro_analyzer(),
|
||||||
bro_analyzer()->Conn(),
|
bro_analyzer()->Conn(),
|
||||||
|
|
|
@ -1,23 +1,6 @@
|
||||||
refine connection SMB_Conn += {
|
refine connection SMB_Conn += {
|
||||||
|
|
||||||
%member{
|
|
||||||
bool is_atsvc;
|
|
||||||
%}
|
|
||||||
|
|
||||||
%init{
|
|
||||||
is_atsvc = false;
|
|
||||||
%}
|
|
||||||
|
|
||||||
function isATSVC(): bool
|
|
||||||
%{
|
|
||||||
return is_atsvc;
|
|
||||||
%}
|
|
||||||
|
|
||||||
function proc_smb1_nt_create_andx_request(header: SMB_Header, val: SMB1_nt_create_andx_request): bool
|
function proc_smb1_nt_create_andx_request(header: SMB_Header, val: SMB1_nt_create_andx_request): bool
|
||||||
%{
|
%{
|
||||||
if ( ${val.filename.u.s}->size() == 14 && ${val.filename.u.s[0]} == '\\' && ${val.filename.u.s[2]} == 'a' && ${val.filename.u.s[4]} == 't' && ${val.filename.u.s[6]} == 's' && ${val.filename.u.s[8]} == 'v' && ${val.filename.u.s[10]} == 'c' )
|
|
||||||
is_atsvc = true;
|
|
||||||
|
|
||||||
if ( smb1_nt_create_andx_request )
|
if ( smb1_nt_create_andx_request )
|
||||||
{
|
{
|
||||||
// name_length : uint16;
|
// name_length : uint16;
|
||||||
|
|
79
src/analyzer/protocol/smb/smb1-com-open-andx.pcap
Normal file
79
src/analyzer/protocol/smb/smb1-com-open-andx.pcap
Normal file
|
@ -0,0 +1,79 @@
|
||||||
|
# Copyright (c) Broala LLC. All Rights Reserved. No use or distribution without permission.
|
||||||
|
|
||||||
|
refine connection SMB_Conn += {
|
||||||
|
|
||||||
|
function proc_smb1_open_andx_request(h: SMB_Header, val: SMB1_open_andx_request): bool
|
||||||
|
%{
|
||||||
|
if ( smb1_open_andx_request )
|
||||||
|
BifEvent::generate_smb1_open_andx_request(bro_analyzer(),
|
||||||
|
bro_analyzer()->Conn(),
|
||||||
|
BuildHeaderVal(h),
|
||||||
|
${val.flags},
|
||||||
|
${val.access_mode},
|
||||||
|
${val.search_attrs},
|
||||||
|
${val.file_attrs},
|
||||||
|
${val.creation_time},
|
||||||
|
${val.open_mode},
|
||||||
|
${val.allocation_size},
|
||||||
|
${val.timeout},
|
||||||
|
smb_string2stringval(${val.filename}));
|
||||||
|
|
||||||
|
return true;
|
||||||
|
%}
|
||||||
|
|
||||||
|
function proc_smb1_open_andx_response(h: SMB_Header, val: SMB1_open_andx_response): bool
|
||||||
|
%{
|
||||||
|
if ( smb1_open_andx_response )
|
||||||
|
BifEvent::generate_smb1_open_andx_response(bro_analyzer(),
|
||||||
|
bro_analyzer()->Conn(),
|
||||||
|
BuildHeaderVal(h),
|
||||||
|
${val.fid},
|
||||||
|
${val.file_attrs},
|
||||||
|
${val.last_write_time},
|
||||||
|
${val.file_data_size},
|
||||||
|
${val.access_rights},
|
||||||
|
${val.resource_type},
|
||||||
|
${val.nm_pipe_status},
|
||||||
|
${val.open_results});
|
||||||
|
|
||||||
|
return true;
|
||||||
|
%}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
type SMB1_open_andx_request(header: SMB_Header) = record {
|
||||||
|
word_count : uint8;
|
||||||
|
andx : SMB_andx;
|
||||||
|
flags : uint16;
|
||||||
|
access_mode : uint16;
|
||||||
|
search_attrs : uint16;
|
||||||
|
file_attrs : uint16;
|
||||||
|
creation_time : uint32;
|
||||||
|
open_mode : uint16;
|
||||||
|
allocation_size : uint32;
|
||||||
|
timeout : uint32;
|
||||||
|
reserved : padding[2];
|
||||||
|
byte_count : uint16;
|
||||||
|
filename : SMB_string(header.unicode, offsetof(filename);
|
||||||
|
} &let {
|
||||||
|
proc : bool = $context.connection.proc_smb1_open_andx_request(header, this);
|
||||||
|
} &byteorder=littleendian;
|
||||||
|
|
||||||
|
type SMB1_open_andx_response(header: SMB_Header) = record {
|
||||||
|
word_count : uint8;
|
||||||
|
andx : SMB_andx;
|
||||||
|
fid : uint16;
|
||||||
|
file_attrs : uint16;
|
||||||
|
last_write_time : uint32;
|
||||||
|
file_data_size : uint32;
|
||||||
|
access_rights : uint16;
|
||||||
|
resource_type : uint16;
|
||||||
|
nm_pipe_status : uint16;
|
||||||
|
open_results : uint16;
|
||||||
|
reserved : padding[3];
|
||||||
|
byte_count : uint16;
|
||||||
|
} &let {
|
||||||
|
proc : bool = $context.connection.proc_smb1_open_andx_response(header, this);
|
||||||
|
} &byteorder=littleendian;
|
|
@ -28,7 +28,7 @@ refine connection SMB_Conn += {
|
||||||
BuildHeaderVal(h),
|
BuildHeaderVal(h),
|
||||||
${val.data_len});
|
${val.data_len});
|
||||||
|
|
||||||
if ( ${val.data_len} > 0 )
|
if ( !get_tree_is_pipe(${h.tid}) && ( ${val.data_len} > 0 ) )
|
||||||
{
|
{
|
||||||
uint64 offset = read_offsets[${h.mid}];
|
uint64 offset = read_offsets[${h.mid}];
|
||||||
read_offsets.erase(${h.mid});
|
read_offsets.erase(${h.mid});
|
||||||
|
@ -80,7 +80,10 @@ type SMB1_read_andx_response(header: SMB_Header) = record {
|
||||||
|
|
||||||
byte_count : uint16;
|
byte_count : uint16;
|
||||||
pad : padding to data_offset - SMB_Header_length;
|
pad : padding to data_offset - SMB_Header_length;
|
||||||
data : bytestring &length=data_len;
|
is_pipe : case $context.connection.get_tree_is_pipe(header.tid) of {
|
||||||
|
true -> pipe_data : SMB_Pipe_message(header, byte_count) &length=data_len;
|
||||||
|
default -> data : bytestring &length=data_len;
|
||||||
|
} &requires(data_len);
|
||||||
} &let {
|
} &let {
|
||||||
padding_len : uint8 = (header.unicode == 1) ? 1 : 0;
|
padding_len : uint8 = (header.unicode == 1) ? 1 : 0;
|
||||||
data_len : uint32 = (data_len_high << 16) + data_len_low;
|
data_len : uint32 = (data_len_high << 16) + data_len_low;
|
||||||
|
|
|
@ -103,7 +103,7 @@ refine connection SMB_Conn += {
|
||||||
response->Assign(4, smb_string2stringval(${val.ntlm.primary_domain}));
|
response->Assign(4, smb_string2stringval(${val.ntlm.primary_domain}));
|
||||||
//response->Assign(5, bytestring_to_val(${val.ntlm.security_blob}));
|
//response->Assign(5, bytestring_to_val(${val.ntlm.security_blob}));
|
||||||
break;
|
break;
|
||||||
case 0: // Error!
|
default: // Error!
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -9,15 +9,26 @@ refine connection SMB_Conn += {
|
||||||
|
|
||||||
function proc_smb1_transaction_request(header: SMB_Header, val: SMB1_transaction_request): bool
|
function proc_smb1_transaction_request(header: SMB_Header, val: SMB1_transaction_request): bool
|
||||||
%{
|
%{
|
||||||
//printf("transaction_request\n");
|
if ( smb1_transaction_request )
|
||||||
|
BifEvent::generate_smb1_transaction_request(bro_analyzer(), bro_analyzer()->Conn(), BuildHeaderVal(header), \
|
||||||
|
smb_string2stringval(${val.name}), ${val.sub_cmd});
|
||||||
return true;
|
return true;
|
||||||
%}
|
%}
|
||||||
|
|
||||||
function proc_smb1_transaction_response(header: SMB_Header, val: SMB1_transaction_response): bool
|
function proc_smb1_transaction_response(header: SMB_Header, val: SMB1_transaction_response): bool
|
||||||
%{
|
%{
|
||||||
//printf("transaction_response\n");
|
//printf("transaction_response\n");
|
||||||
return true;
|
return true;
|
||||||
%}
|
%}
|
||||||
|
|
||||||
|
function proc_smb1_transaction_setup(header: SMB_Header, val: SMB1_transaction_setup): bool
|
||||||
|
%{
|
||||||
|
if ( smb1_transaction_setup )
|
||||||
|
BifEvent::generate_smb1_transaction_setup(bro_analyzer(), bro_analyzer()->Conn(), BuildHeaderVal(header), \
|
||||||
|
${val.op_code}, ${val.file_id});
|
||||||
|
return true;
|
||||||
|
%}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -25,12 +36,19 @@ type SMB1_transaction_data(header: SMB_Header, count: uint16, sub_cmd: uint16,
|
||||||
trans_type: TransactionType ) = case trans_type of {
|
trans_type: TransactionType ) = case trans_type of {
|
||||||
# SMB_MAILSLOT_BROWSE -> mailslot : SMB_MailSlot_message(header.unicode, count);
|
# SMB_MAILSLOT_BROWSE -> mailslot : SMB_MailSlot_message(header.unicode, count);
|
||||||
# SMB_MAILSLOT_LANMAN -> lanman : SMB_MailSlot_message(header.unicode, count);
|
# SMB_MAILSLOT_LANMAN -> lanman : SMB_MailSlot_message(header.unicode, count);
|
||||||
# SMB_RAP -> rap : SMB_Pipe_message(header.unicode, count, sub_cmd);
|
# SMB_RAP -> rap : SMB_Pipe_message(header.unicode, count);
|
||||||
SMB_PIPE -> pipe : SMB_Pipe_message(header.unicode, count, sub_cmd);
|
SMB_PIPE -> pipe : SMB_Pipe_message(header, count);
|
||||||
# SMB_UNKNOWN -> unknown : bytestring &restofdata;
|
SMB_UNKNOWN -> unknown : bytestring &restofdata;
|
||||||
# default -> data : bytestring &restofdata;
|
# default -> data : bytestring &restofdata;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type SMB1_transaction_setup(header: SMB_Header) = record {
|
||||||
|
op_code : uint16;
|
||||||
|
file_id : uint16;
|
||||||
|
} &let {
|
||||||
|
proc: bool = $context.connection.proc_smb1_transaction_setup(header, this);
|
||||||
|
}
|
||||||
|
|
||||||
type SMB1_transaction_request(header: SMB_Header) = record {
|
type SMB1_transaction_request(header: SMB_Header) = record {
|
||||||
word_count : uint8;
|
word_count : uint8;
|
||||||
total_param_count : uint16;
|
total_param_count : uint16;
|
||||||
|
@ -48,7 +66,7 @@ type SMB1_transaction_request(header: SMB_Header) = record {
|
||||||
data_offset : uint16;
|
data_offset : uint16;
|
||||||
setup_count : uint8;
|
setup_count : uint8;
|
||||||
reserved3 : uint8;
|
reserved3 : uint8;
|
||||||
setup : uint16[setup_count];
|
setup : SMB1_transaction_setup(header);
|
||||||
|
|
||||||
byte_count : uint16;
|
byte_count : uint16;
|
||||||
name : SMB_string(header.unicode, offsetof(name));
|
name : SMB_string(header.unicode, offsetof(name));
|
||||||
|
@ -57,7 +75,7 @@ type SMB1_transaction_request(header: SMB_Header) = record {
|
||||||
pad2 : padding to data_offset - SMB_Header_length;
|
pad2 : padding to data_offset - SMB_Header_length;
|
||||||
data : SMB1_transaction_data(header, data_count, sub_cmd, determine_transaction_type(setup_count, name));
|
data : SMB1_transaction_data(header, data_count, sub_cmd, determine_transaction_type(setup_count, name));
|
||||||
} &let {
|
} &let {
|
||||||
sub_cmd : uint16 = setup_count ? setup[0] : 0;
|
sub_cmd : uint16 = setup_count ? setup.op_code : 0;
|
||||||
proc : bool = $context.connection.proc_smb1_transaction_request(header, this);
|
proc : bool = $context.connection.proc_smb1_transaction_request(header, this);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -81,9 +99,9 @@ type SMB1_transaction_response(header: SMB_Header) = record {
|
||||||
pad0 : padding to param_offset - SMB_Header_length;
|
pad0 : padding to param_offset - SMB_Header_length;
|
||||||
parameters : bytestring &length = param_count;
|
parameters : bytestring &length = param_count;
|
||||||
pad1 : padding to data_offset - SMB_Header_length;
|
pad1 : padding to data_offset - SMB_Header_length;
|
||||||
handle_response : case $context.connection.isATSVC() of {
|
handle_response : case $context.connection.get_tree_is_pipe(header.tid) of {
|
||||||
true -> pipe_data : SMB1_transaction_data(header, data_count, 0, SMB_PIPE);
|
true -> pipe_data : SMB1_transaction_data(header, data_count, 0, SMB_PIPE);
|
||||||
# false -> unk_data : SMB1_transaction_data(header, data_count, 0, SMB_UNKNOWN);
|
false -> unk_data : SMB1_transaction_data(header, data_count, 0, SMB_UNKNOWN);
|
||||||
};
|
};
|
||||||
} &let {
|
} &let {
|
||||||
proc : bool = $context.connection.proc_smb1_transaction_response(header, this);
|
proc : bool = $context.connection.proc_smb1_transaction_response(header, this);
|
||||||
|
|
|
@ -22,13 +22,16 @@ refine connection SMB_Conn += {
|
||||||
|
|
||||||
function proc_smb1_transaction2_request(header: SMB_Header, val: SMB1_transaction2_request): bool
|
function proc_smb1_transaction2_request(header: SMB_Header, val: SMB1_transaction2_request): bool
|
||||||
%{
|
%{
|
||||||
//printf("transaction2_request sub command: %d\n", ${val.sub_cmd});
|
if ( smb1_transaction2_request )
|
||||||
|
BifEvent::generate_smb1_transaction2_request(bro_analyzer(), bro_analyzer()->Conn(), BuildHeaderVal(header), ${val.sub_cmd});
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
%}
|
%}
|
||||||
|
|
||||||
function proc_smb1_transaction2_response(header: SMB_Header, val: SMB1_transaction2_response): bool
|
function proc_smb1_transaction2_response(header: SMB_Header, val: SMB1_transaction2_response): bool
|
||||||
%{
|
%{
|
||||||
//printf("transaction2_response sub command: %d\n", ${val.sub_cmd});
|
// if ( smb1_transaction2_response )
|
||||||
|
// BifEvent::generate_smb1_transaction2_response(bro_analyzer(), bro_analyzer()->Conn(), BuildHeaderVal(header), new Val(${val.sub_cmd}, TYPE_COUNT));
|
||||||
return true;
|
return true;
|
||||||
%}
|
%}
|
||||||
|
|
||||||
|
@ -106,8 +109,19 @@ refine connection SMB_Conn += {
|
||||||
|
|
||||||
function proc_trans2_find_first2_request(header: SMB_Header, val: trans2_find_first2_request): bool
|
function proc_trans2_find_first2_request(header: SMB_Header, val: trans2_find_first2_request): bool
|
||||||
%{
|
%{
|
||||||
// TODO: implement this.
|
if ( smb1_trans2_find_first2_request )
|
||||||
//printf("trans2_find_first2 request!\n");
|
{
|
||||||
|
RecordVal* result = new RecordVal(BifType::Record::SMB1::Find_First2_Request_Args);
|
||||||
|
result->Assign(0, new Val(${val.search_attrs}, TYPE_COUNT));
|
||||||
|
result->Assign(1, new Val(${val.search_count}, TYPE_COUNT));
|
||||||
|
result->Assign(2, new Val(${val.flags}, TYPE_COUNT));
|
||||||
|
result->Assign(3, new Val(${val.info_level}, TYPE_COUNT));
|
||||||
|
result->Assign(4, new Val(${val.search_storage_type}, TYPE_COUNT));
|
||||||
|
result->Assign(5, smb_string2stringval(${val.file_name}));
|
||||||
|
BifEvent::generate_smb1_trans2_find_first2_request(bro_analyzer(), bro_analyzer()->Conn(), \
|
||||||
|
BuildHeaderVal(header), result);
|
||||||
|
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
%}
|
%}
|
||||||
|
|
||||||
|
@ -179,8 +193,14 @@ refine connection SMB_Conn += {
|
||||||
|
|
||||||
function proc_trans2_query_path_info_request(header: SMB_Header, val: trans2_query_path_info_request): bool
|
function proc_trans2_query_path_info_request(header: SMB_Header, val: trans2_query_path_info_request): bool
|
||||||
%{
|
%{
|
||||||
// TODO: implement this.
|
if ( smb1_trans2_query_path_info_request )
|
||||||
//printf("trans2_query_path_info request!\n");
|
{
|
||||||
|
BifEvent::generate_smb1_trans2_query_path_info_request(bro_analyzer(), bro_analyzer()->Conn(), \
|
||||||
|
BuildHeaderVal(header), \
|
||||||
|
smb_string2stringval(${val.file_name}),\
|
||||||
|
${val.level_of_interest});
|
||||||
|
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
%}
|
%}
|
||||||
|
|
||||||
|
@ -278,8 +298,13 @@ refine connection SMB_Conn += {
|
||||||
|
|
||||||
function proc_trans2_get_dfs_referral_request(header: SMB_Header, val: trans2_get_dfs_referral_request): bool
|
function proc_trans2_get_dfs_referral_request(header: SMB_Header, val: trans2_get_dfs_referral_request): bool
|
||||||
%{
|
%{
|
||||||
// TODO: implement this.
|
if ( smb1_trans2_get_dfs_referral_request )
|
||||||
//printf("trans2_get_dfs_referral request!\n");
|
{
|
||||||
|
BifEvent::generate_smb1_trans2_get_dfs_referral_request(bro_analyzer(), bro_analyzer()->Conn(), \
|
||||||
|
BuildHeaderVal(header), \
|
||||||
|
smb_string2stringval(${val.file_name}),\
|
||||||
|
${val.max_referral_level});
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
%}
|
%}
|
||||||
|
|
||||||
|
|
|
@ -13,6 +13,7 @@ refine connection SMB_Conn += {
|
||||||
|
|
||||||
function proc_smb1_tree_connect_andx_response(header: SMB_Header, val: SMB1_tree_connect_andx_response): bool
|
function proc_smb1_tree_connect_andx_response(header: SMB_Header, val: SMB1_tree_connect_andx_response): bool
|
||||||
%{
|
%{
|
||||||
|
set_tree_is_pipe(${header.tid}, strcmp((const char*) smb_string2stringval(${val.service})->Bytes(), "IPC") == 0);
|
||||||
if ( smb1_tree_connect_andx_response )
|
if ( smb1_tree_connect_andx_response )
|
||||||
BifEvent::generate_smb1_tree_connect_andx_response(bro_analyzer(),
|
BifEvent::generate_smb1_tree_connect_andx_response(bro_analyzer(),
|
||||||
bro_analyzer()->Conn(),
|
bro_analyzer()->Conn(),
|
||||||
|
|
|
@ -52,7 +52,10 @@ type SMB1_write_andx_request(header: SMB_Header) = record {
|
||||||
|
|
||||||
byte_count : uint16;
|
byte_count : uint16;
|
||||||
pad : padding to data_offset - SMB_Header_length;
|
pad : padding to data_offset - SMB_Header_length;
|
||||||
data : bytestring &length=data_len;
|
is_pipe : case $context.connection.get_tree_is_pipe(header.tid) of {
|
||||||
|
true -> pipe_data : SMB_Pipe_message(header, byte_count) &length=data_len;
|
||||||
|
default -> data : bytestring &length=data_len;
|
||||||
|
} &requires(data_len);
|
||||||
} &let {
|
} &let {
|
||||||
data_len : uint32 = (data_len_high << 16) + data_len_low;
|
data_len : uint32 = (data_len_high << 16) + data_len_low;
|
||||||
offset_high : uint32 = (word_count == 0x0E) ? offset_high_tmp : 0;
|
offset_high : uint32 = (word_count == 0x0E) ? offset_high_tmp : 0;
|
||||||
|
|
|
@ -68,7 +68,7 @@ refine connection SMB_Conn += {
|
||||||
{
|
{
|
||||||
BifEvent::generate_smb1_error(bro_analyzer(),
|
BifEvent::generate_smb1_error(bro_analyzer(),
|
||||||
bro_analyzer()->Conn(),
|
bro_analyzer()->Conn(),
|
||||||
BuildHeaderVal(h));
|
BuildHeaderVal(h), is_orig);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
%}
|
%}
|
||||||
|
|
5
src/analyzer/protocol/smb/smb1_com_transaction.bif
Normal file
5
src/analyzer/protocol/smb/smb1_com_transaction.bif
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
# TODO - Description
|
||||||
|
event smb1_transaction_request%(c: connection, hdr: SMB1::Header, name: string, sub_cmd: count%);
|
||||||
|
|
||||||
|
# TODO - Description
|
||||||
|
event smb1_transaction_setup%(c: connection, hdr: SMB1::Header, op_code: count, file_id: count%);
|
53
src/analyzer/protocol/smb/smb1_com_transaction2.bif
Normal file
53
src/analyzer/protocol/smb/smb1_com_transaction2.bif
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
### Requests
|
||||||
|
|
||||||
|
|
||||||
|
# TODO - Description
|
||||||
|
event smb1_transaction2_request%(c: connection, hdr: SMB1::Header, sub_cmd: count%);
|
||||||
|
|
||||||
|
# TODO - Description
|
||||||
|
event smb1_trans2_find_first2_request%(c: connection, hdr: SMB1::Header, args: SMB1::Find_First2_Request_Args%);
|
||||||
|
|
||||||
|
# TODO - Implementation
|
||||||
|
# event smb1_trans2_fs_info_request%(c: connection, hdr: SMB1::Header, ??? %);
|
||||||
|
|
||||||
|
# TODO - Description
|
||||||
|
event smb1_trans2_query_path_info_request%(c: connection, hdr: SMB1::Header, file_name: string, level_of_interest: count%);
|
||||||
|
|
||||||
|
# TODO - Description
|
||||||
|
event smb1_trans2_query_file_info_request%(c: connection, hdr: SMB1::Header, file_id: count, level_of_interest: count%);
|
||||||
|
|
||||||
|
# TODO - Implementation
|
||||||
|
# event smb1_trans2_set_file_info_request(c: connection, hdr: SMB1::Header, ??? %);
|
||||||
|
|
||||||
|
# TODO - Description
|
||||||
|
event smb1_trans2_get_dfs_referral_request%(c: connection, hdr: SMB1::Header, file_name: string, max_referral_level: count%);
|
||||||
|
|
||||||
|
### Responses
|
||||||
|
|
||||||
|
|
||||||
|
# TODO - Description
|
||||||
|
event smb1_transaction2_response%(c: connection, hdr: SMB1::Header, sub_cmd: count%);
|
||||||
|
|
||||||
|
# TODO - Description
|
||||||
|
event smb1_trans2_find_first2_response%(c: connection, hdr: SMB1::Header, args: SMB1::Find_First2_Response_Args%);
|
||||||
|
|
||||||
|
# TODO - Implementation
|
||||||
|
# event smb1_trans2_fs_info_response%(c: connection, hdr: SMB1::Header, ??? %);
|
||||||
|
|
||||||
|
# TODO - Implementation
|
||||||
|
# event smb1_trans2_query_path_info_response%(c: connection, hdr: SMB1::Header, ??? %);
|
||||||
|
|
||||||
|
# TODO - Implementation
|
||||||
|
# event smb1_trans2_query_file_info_response%(c: connection, hdr: SMB1::Header, ??? %);
|
||||||
|
|
||||||
|
# TODO - Implementation
|
||||||
|
# event smb1_trans2_set_file_info_response%(c: connection, hdr: SMB1::Header, ??? %);
|
||||||
|
|
||||||
|
# TODO - Implementation
|
||||||
|
# event smb1_trans2_get_dfs_referral_response%(c: connection, hdr: SMB1::Header, ??? %);
|
||||||
|
|
||||||
|
|
||||||
|
### Types
|
||||||
|
|
||||||
|
type SMB1::Find_First2_Request_Args: record;
|
||||||
|
type SMB1::Find_First2_Response_Args: record;
|
|
@ -25,5 +25,8 @@ event smb1_empty_response%(c: connection, hdr: SMB1::Header%);
|
||||||
## c: The connection.
|
## c: The connection.
|
||||||
##
|
##
|
||||||
## hdr: The parsed header of the SMB message.
|
## hdr: The parsed header of the SMB message.
|
||||||
event smb1_error%(c: connection, hdr: SMB1::Header%);
|
##
|
||||||
|
## is_orig: True if the message was sent by the originator of the underlying
|
||||||
|
## transport-level connection.
|
||||||
|
event smb1_error%(c: connection, hdr: SMB1::Header, is_orig: bool%);
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,12 @@
|
||||||
## TODO
|
## TODO - Description
|
||||||
event smb_atsvc_job_add%(c: connection, server: string, job: string%);
|
event smb_pipe_bind_request%(c: connection, hdr: SMB1::Header, uuid: string, version: string%);
|
||||||
|
|
||||||
## TODO
|
## TODO - Description
|
||||||
event smb_atsvc_job_id%(c: connection, id: count, status: count%);
|
event smb_pipe_bind_ack_response%(c: connection, hdr: SMB1::Header%);
|
||||||
|
|
||||||
|
## TODO - Description
|
||||||
|
event smb_pipe_request%(c: connection, hdr: SMB1::Header, op_num: count%);
|
||||||
|
|
||||||
|
## TODO - Description
|
||||||
|
event smb_pipe_response%(c: connection, hdr: SMB1::Header%);
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue