Merge remote-tracking branch 'origin/topic/seth/smb' into topic/robin/smb-merge

This commit is contained in:
Robin Sommer 2016-07-07 10:31:15 -07:00
commit 781fdfa5ba
159 changed files with 12388 additions and 3840 deletions

View file

@ -93,7 +93,7 @@ signature file-ini {
# Microsoft LNK files
signature file-lnk {
file-mime "application/x-ms-shortcut", 49
file-magic /^\x4C\x00\x00\x00\x01\x14\x02\x00\x00\x00\x00\x00\xC0\x00\x00\x00\x00\x10\x00\x00\x00\x46/
file-magic /^\x4c\x00\x00\x00\x01\x14\x02\x00\x00\x00\x00\x00\xc0\x00\x00\x00\x00\x00\x00\x46/
}
# Microsoft Registry policies

View file

@ -2380,83 +2380,508 @@ type ntp_msg: record {
};
## Maps SMB command numbers to descriptive names.
global samba_cmds: table[count] of string &redef
&default = function(c: count): string
{ return fmt("samba-unknown-%d", c); };
module NTLM;
## An SMB command header.
##
## .. bro:see:: smb_com_close smb_com_generic_andx smb_com_logoff_andx
## smb_com_negotiate smb_com_negotiate_response smb_com_nt_create_andx
## smb_com_read_andx smb_com_setup_andx smb_com_trans_mailslot
## smb_com_trans_pipe smb_com_trans_rap smb_com_transaction
## smb_com_transaction2 smb_com_tree_connect_andx smb_com_tree_disconnect
## smb_com_write_andx smb_error smb_get_dfs_referral smb_message
type smb_hdr : record {
command: count; ##< The command number (see :bro:see:`samba_cmds`).
status: count; ##< The status code.
flags: count; ##< Flag set 1.
flags2: count; ##< Flag set 2.
tid: count; ##< TODO.
pid: count; ##< Process ID.
uid: count; ##< User ID.
mid: count; ##< TODO.
};
export {
type NTLM::Version: record {
## The major version of the Windows operating system in use
major : count;
## The minor version of the Windows operating system in use
minor : count;
## The build number of the Windows operating system in use
build : count;
## The current revision of NTLMSSP in use
ntlmssp : count;
};
## An SMB transaction.
##
## .. bro:see:: smb_com_trans_mailslot smb_com_trans_pipe smb_com_trans_rap
## smb_com_transaction smb_com_transaction2
type smb_trans : record {
word_count: count; ##< TODO.
total_param_count: count; ##< TODO.
total_data_count: count; ##< TODO.
max_param_count: count; ##< TODO.
max_data_count: count; ##< TODO.
max_setup_count: count; ##< TODO.
# flags: count;
# timeout: count;
param_count: count; ##< TODO.
param_offset: count; ##< TODO.
data_count: count; ##< TODO.
data_offset: count; ##< TODO.
setup_count: count; ##< TODO.
setup0: count; ##< TODO.
setup1: count; ##< TODO.
setup2: count; ##< TODO.
setup3: count; ##< TODO.
byte_count: count; ##< TODO.
parameters: string; ##< TODO.
};
type NTLM::NegotiateFlags: record {
## If set, requires 56-bit encryption
negotiate_56 : bool;
## If set, requests an explicit key exchange
negotiate_key_exch : bool;
## If set, requests 128-bit session key negotiation
negotiate_128 : bool;
## If set, requests the protocol version number
negotiate_version : bool;
## If set, indicates that the TargetInfo fields in the
## CHALLENGE_MESSAGE are populated
negotiate_target_info : bool;
## If set, requests the usage of the LMOWF function
request_non_nt_session_key : bool;
## If set, requests and identify level token
negotiate_identify : bool;
## If set, requests usage of NTLM v2 session security
## Note: NTML v2 session security is actually NTLM v1
negotiate_extended_sessionsecurity : bool;
## If set, TargetName must be a server name
target_type_server : bool;
## If set, TargetName must be a domain name
target_type_domain : bool;
## If set, requests the presence of a signature block
## on all messages
negotiate_always_sign : bool;
## If set, the workstation name is provided
negotiate_oem_workstation_supplied : bool;
## If set, the domain name is provided
negotiate_oem_domain_supplied : bool;
## If set, the connection should be anonymous
negotiate_anonymous_connection : bool;
## If set, requests usage of NTLM v1
negotiate_ntlm : bool;
## If set, requests LAN Manager session key computation
negotiate_lm_key : bool;
## If set, requests connectionless authentication
negotiate_datagram : bool;
## If set, requests session key negotiation for message
## confidentiality
negotiate_seal : bool;
## If set, requests session key negotiation for message
## signatures
negotiate_sign : bool;
## If set, the TargetName field is present
request_target : bool;
## If set, requests OEM character set encoding
negotiate_oem : bool;
## If set, requests Unicode character set encoding
negotiate_unicode : bool;
};
type NTLM::Negotiate: record {
## The negotiate flags
flags : NTLM::NegotiateFlags;
## The domain name of the client, if known
domain_name : string &optional;
## The machine name of the client, if known
workstation : string &optional;
## The Windows version information, if supplied
version : NTLM::Version &optional;
};
type NTLM::AVs: record {
## The server's NetBIOS computer name
nb_computer_name : string;
## The server's NetBIOS domain name
nb_domain_name : string;
## The FQDN of the computer
dns_computer_name : string &optional;
## The FQDN of the domain
dns_domain_name : string &optional;
## The FQDN of the forest
dns_tree_name : string &optional;
## Indicates to the client that the account
## authentication is constrained
constrained_auth : bool &optional;
## The associated timestamp, if present
timestamp : time &optional;
## Indicates that the client is providing
## a machine ID created at computer startup to
## identify the calling machine
single_host_id : count &optional;
## The SPN of the target server
target_name : string &optional;
};
type NTLM::Challenge: record {
## The negotiate flags
flags : NTLM::NegotiateFlags;
## The server authentication realm. If the server is
## domain-joined, the name of the domain. Otherwise
## the server name. See flags.target_type_domain
## and flags.target_type_server
target_name : string &optional;
## The Windows version information, if supplied
version : NTLM::Version &optional;
## Attribute-value pairs specified by the server
target_info : NTLM::AVs &optional;
};
type NTLM::Authenticate: record {
## The negotiate flags
flags : NTLM::NegotiateFlags;
## The domain or computer name hosting the account
domain_name : string;
## The name of the user to be authenticated.
user_name : string;
## The name of the computer to which the user was logged on.
workstation : string;
## The Windows version information, if supplied
version : NTLM::Version &optional;
};
}
module SMB;
export {
## MAC times for a file.
type SMB::MACTimes: record {
modified : time &log;
accessed : time &log;
created : time &log;
changed : time &log;
} &log;
}
module SMB1;
export {
## An SMB1 header.
##
## .. bro:see:: smb_com_close smb_com_generic_andx smb_com_logoff_andx
## smb_com_negotiate smb_com_negotiate_response smb_com_nt_create_andx
## smb_com_read_andx smb_com_setup_andx smb_com_trans_mailslot
## smb_com_trans_pipe smb_com_trans_rap smb_com_transaction
## smb_com_transaction2 smb_com_tree_connect_andx smb_com_tree_disconnect
## smb_com_write_andx smb_error smb_get_dfs_referral smb_message
type SMB1::Header : record {
command: count; ##< The command number
status: count; ##< The status code.
flags: count; ##< Flag set 1.
flags2: count; ##< Flag set 2.
tid: count; ##< Tree ID.
pid: count; ##< Process ID.
uid: count; ##< User ID.
mid: count; ##< Multiplex ID.
};
type SMB1::NegotiateRawMode: record {
## Read raw supported
read_raw : bool;
## Write raw supported
write_raw : bool;
};
type SMB1::NegotiateCapabilities: record {
## The server supports SMB_COM_READ_RAW and SMB_COM_WRITE_RAW
raw_mode : bool;
## The server supports SMB_COM_READ_MPX and SMB_COM_WRITE_MPX
mpx_mode : bool;
## The server supports unicode strings
unicode : bool;
## The server supports large files with 64 bit offsets
large_files : bool;
## The server supports the SMBs particilar to the NT LM 0.12 dialect. Implies nt_find.
nt_smbs : bool;
## The server supports remote admin API requests via DCE-RPC
rpc_remote_apis : bool;
## The server can respond with 32 bit status codes in Status.Status
status32 : bool;
## The server supports level 2 oplocks
level_2_oplocks : bool;
## The server supports SMB_COM_LOCK_AND_READ
lock_and_read : bool;
## Reserved
nt_find : bool;
## The server is DFS aware
dfs : bool;
## The server supports NT information level requests passing through
infolevel_passthru : bool;
## The server supports large SMB_COM_READ_ANDX (up to 64k)
large_readx : bool;
## The server supports large SMB_COM_WRITE_ANDX (up to 64k)
large_writex : bool;
## The server supports CIFS Extensions for UNIX
unix : bool;
## The server supports SMB_BULK_READ, SMB_BULK_WRITE
## Note: No known implementations support this
bulk_transfer : bool;
## The server supports compressed data transfer. Requires bulk_transfer.
## Note: No known implementations support this
compressed_data : bool;
## The server supports extended security exchanges
extended_security : bool;
};
type SMB1::NegotiateResponseSecurity: record {
## This indicates whether the server, as a whole, is operating under
## Share Level or User Level security.
user_level : bool;
## This indicates whether or not the server supports Challenge/Response
## authentication. If the bit is false, then plaintext passwords must
## be used.
challenge_response: bool;
## This indicates if the server is capable of performing MAC message
## signing. Note: Requires NT LM 0.12 or later.
signatures_enabled: bool &optional;
## This indicates if the server is requiring the use of a MAC in each
## packet. If false, message signing is optional. Note: Requires NT LM 0.12
## or later.
signatures_required: bool &optional;
};
type SMB1::NegotiateResponseCore: record {
## Index of selected dialect
dialect_index : count;
};
type SMB1::NegotiateResponseLANMAN: record {
## Count of parameter words (should be 13)
word_count : count;
## Index of selected dialect
dialect_index : count;
## Security mode
security_mode : SMB1::NegotiateResponseSecurity;
## Max transmit buffer size (>= 1024)
max_buffer_size : count;
## Max pending multiplexed requests
max_mpx_count : count;
## Max number of virtual circuits (VCs - transport-layer connections)
## between client and server
max_number_vcs : count;
## Raw mode
raw_mode : SMB1::NegotiateRawMode;
## Unique token identifying this session
session_key : count;
## Current date and time at server
server_time : time;
## The challenge encryption key
encryption_key : string;
## The server's primary domain
primary_domain : string;
};
type SMB1::NegotiateResponseNTLM: record {
## Count of parameter words (should be 17)
word_count : count;
## Index of selected dialect
dialect_index : count;
## Security mode
security_mode : SMB1::NegotiateResponseSecurity;
## Max transmit buffer size
max_buffer_size : count;
## Max pending multiplexed requests
max_mpx_count : count;
## Max number of virtual circuits (VCs - transport-layer connections)
## between client and server
max_number_vcs : count;
## Max raw buffer size
max_raw_size : count;
## Unique token identifying this session
session_key : count;
## Server capabilities
capabilities : SMB1::NegotiateCapabilities;
## Current date and time at server
server_time : time;
## The challenge encryption key.
## Present only for non-extended security (i.e. capabilities$extended_security = F)
encryption_key : string &optional;
## The name of the domain.
## Present only for non-extended security (i.e. capabilities$extended_security = F)
domain_name : string &optional;
## A globally unique identifier assigned to the server.
## Present only for extended security (i.e. capabilities$extended_security = T)
guid : string &optional;
## Opaque security blob associated with the security package if capabilities$extended_security = T
## Otherwise, the challenge for challenge/response authentication.
security_blob : string;
};
type SMB1::NegotiateResponse: record {
## If the server does not understand any of the dialect strings, or if
## PC NETWORK PROGRAM 1.0 is the chosen dialect.
core : SMB1::NegotiateResponseCore &optional;
## If the chosen dialect is greater than core up to and including
## LANMAN 2.1.
lanman : SMB1::NegotiateResponseLANMAN &optional;
## If the chosen dialect is NT LM 0.12.
ntlm : SMB1::NegotiateResponseNTLM &optional;
};
type SMB1::SessionSetupAndXCapabilities: record {
## The client can use unicode strings
unicode : bool;
## The client can deal with files having 64 bit offsets
large_files : bool;
## The client understands the SMBs introduced with NT LM 0.12
## Implies nt_find
nt_smbs : bool;
## The client can receive 32 bit errors encoded in Status.Status
status32 : bool;
## The client understands Level II oplocks
level_2_oplocks : bool;
## Reserved. Implied by nt_smbs.
nt_find : bool;
};
type SMB1::SessionSetupAndXRequest: record {
## Count of parameter words
## - 10 for pre NT LM 0.12
## - 12 for NT LM 0.12 with extended security
## - 13 for NT LM 0.12 without extended security
word_count : count;
## Client maximum buffer size
max_buffer_size : count;
## Actual maximum multiplexed pending request
max_mpx_count : count;
## Virtual circuit number. First VC == 0
vc_number : count;
## Session key (valid iff vc_number > 0)
session_key : count;
## Client's native operating system
native_os : string;
## Client's native LAN Manager type
native_lanman : string;
## Account name
## Note: not set for NT LM 0.12 with extended security
account_name : string &optional;
## If challenge/response auth is not being used, this is the password.
## Otherwise, it's the response to the server's challenge.
## Note: Only set for pre NT LM 0.12
account_password : string &optional;
## Client's primary domain, if known
## Note: not set for NT LM 0.12 with extended security
primary_domain : string &optional;
## Case insensitive password
## Note: only set for NT LM 0.12 without extended security
case_insensitive_password : string &optional;
## Case sensitive password
## Note: only set for NT LM 0.12 without extended security
case_sensitive_password : string &optional;
## Security blob
## Note: only set for NT LM 0.12 with extended security
security_blob : string &optional;
## Client capabilities
## Note: only set for NT LM 0.12
capabilities : SMB1::SessionSetupAndXCapabilities &optional;
};
type SMB1::SessionSetupAndXResponse: record {
## Count of parameter words (should be 3 for pre NT LM 0.12 and 4 for NT LM 0.12)
word_count : count;
## Were we logged in as a guest user?
is_guest : bool &optional;
## Server's native operating system
native_os : string &optional;
## Server's native LAN Manager type
native_lanman : string &optional;
## Server's primary domain
primary_domain : string &optional;
## Security blob if NTLM
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;
};
## SMB transaction data.
##
## .. bro:see:: smb_com_trans_mailslot smb_com_trans_pipe smb_com_trans_rap
## smb_com_transaction smb_com_transaction2
##
## .. todo:: Should this really be a record type?
type smb_trans_data : record {
data : string; ##< The transaction's data.
};
}
## Deprecated.
##
## .. todo:: Remove. It's still declared internally but doesn't seem used anywhere
## else.
type smb_tree_connect : record {
flags: count;
password: string;
path: string;
service: string;
};
module SMB2;
## Deprecated.
##
## .. todo:: Remove. It's still declared internally but doesn't seem used anywhere
## else.
type smb_negotiate : table[count] of string;
export {
type SMB2::Header: record {
credit_charge: count;
status: count;
command: count;
credits: count;
flags: count;
message_id: count;
process_id: count;
tree_id: count;
session_id: count;
signature: string;
};
type SMB2::GUID: record {
persistent: count;
volatile: count;
};
type SMB2::FileAttrs: record {
read_only: bool;
hidden: bool;
system: bool;
directory: bool;
archive: bool;
normal: bool;
temporary: bool;
sparse_file: bool;
reparse_point: bool;
compressed: bool;
offline: bool;
not_content_indexed: bool;
encrypted: bool;
integrity_stream: bool;
no_scrub_data: bool;
};
type SMB2::CloseResponse: record {
alloc_size : count;
eof : count;
times : SMB::MACTimes;
attrs : SMB2::FileAttrs;
};
type SMB2::NegotiateResponse: record {
dialect_revision : count;
security_mode : count;
server_guid : string;
system_time : time;
server_start_time : time;
};
type SMB2::SessionSetupRequest: record {
security_mode: count;
};
type SMB2::SessionSetupFlags: record {
guest: bool;
anonymous: bool;
encrypt: bool;
};
type SMB2::SessionSetupResponse: record {
flags: SMB2::SessionSetupFlags;
};
type SMB2::SetInfoRequest: record {
eof: count;
};
type SMB2::TreeConnectResponse: record {
share_type: count;
};
}
module GLOBAL;
## A list of router addresses offered by a DHCP server.
##

View file

@ -43,6 +43,7 @@
@load base/frameworks/netcontrol
@load base/protocols/conn
@load base/protocols/dce-rpc
@load base/protocols/dhcp
@load base/protocols/dnp3
@load base/protocols/dns
@ -53,12 +54,16 @@
@load base/protocols/krb
@load base/protocols/modbus
@load base/protocols/mysql
@load base/protocols/ntlm
@load base/protocols/pop3
@load base/protocols/radius
@load base/protocols/rdp
@load base/protocols/rfb
@load base/protocols/sip
@load base/protocols/snmp
# This DOES NOT enable the SMB analyzer. It's just some base support
# for other protocols.
@load base/protocols/smb
@load base/protocols/smtp
@load base/protocols/socks
@load base/protocols/ssh

View file

@ -0,0 +1,4 @@
@load ./consts
@load ./main
@load-sigs ./dpd.sig

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,6 @@
signature dpd_dce_rpc {
ip-proto == tcp
payload /^\x05[\x00\x01][\x00-\x13]\x03/
enable "DCE_RPC"
}

View file

@ -0,0 +1,204 @@
@load ./consts
module DCE_RPC;
export {
redef enum Log::ID += { LOG };
type Info: record {
## Timestamp for when the event happened.
ts : time &log;
## Unique ID for the connection.
uid : string &log;
## The connection's 4-tuple of endpoint addresses/ports.
id : conn_id &log;
## Round trip time from the request to the response.
## If either the request or response wasn't seen,
## this will be null.
rtt : interval &log &optional;
## Remote pipe name.
named_pipe : string &log &optional;
## Endpoint name looked up from the uuid.
endpoint : string &log &optional;
## Operation seen in the call.
operation : string &log &optional;
};
## These are DCE-RPC operations that are ignored, typically due
## the operations being noisy and low valueon most networks.
const ignored_operations: table[string] of set[string] = {
["winreg"] = set("BaseRegCloseKey", "BaseRegGetVersion", "BaseRegOpenKey", "BaseRegQueryValue", "BaseRegDeleteKeyEx", "OpenLocalMachine", "BaseRegEnumKey", "OpenClassesRoot"),
["spoolss"] = set("RpcSplOpenPrinter", "RpcClosePrinter"),
["wkssvc"] = set("NetrWkstaGetInfo"),
} &redef;
}
type State: record {
uuid : string &optional;
named_pipe : string &optional;
};
# This is to store the log and state information
# for multiple DCE/RPC bindings over a single TCP connection (named pipes).
type BackingState: record {
info: Info;
state: State;
};
redef record connection += {
dce_rpc: Info &optional;
dce_rpc_state: State &optional;
dce_rpc_backing: table[count] of BackingState &optional;
};
const ports = { 135/tcp };
redef likely_server_ports += { ports };
event bro_init() &priority=5
{
Log::create_stream(DCE_RPC::LOG, [$columns=Info, $path="dce_rpc"]);
Analyzer::register_for_ports(Analyzer::ANALYZER_DCE_RPC, ports);
}
function normalize_named_pipe_name(pn: string): string
{
local parts = split_string(pn, /\\[pP][iI][pP][eE]\\/);
if ( 1 in parts )
return to_lower(parts[1]);
else
return to_lower(pn);
}
function set_state(c: connection, state_x: BackingState)
{
c$dce_rpc = state_x$info;
c$dce_rpc_state = state_x$state;
if ( c$dce_rpc_state?$uuid )
c$dce_rpc$endpoint = uuid_endpoint_map[c$dce_rpc_state$uuid];
if ( c$dce_rpc_state?$named_pipe )
c$dce_rpc$named_pipe = c$dce_rpc_state$named_pipe;
}
function set_session(c: connection, fid: count)
{
if ( ! c?$dce_rpc_backing )
{
c$dce_rpc_backing = table();
}
if ( fid !in c$dce_rpc_backing )
{
local info = Info($ts=network_time(),$id=c$id,$uid=c$uid);
c$dce_rpc_backing[fid] = BackingState($info=info, $state=State());
}
local state_x = c$dce_rpc_backing[fid];
set_state(c, state_x);
}
event dce_rpc_bind(c: connection, fid: count, uuid: string, ver_major: count, ver_minor: count) &priority=5
{
set_session(c, fid);
local uuid_str = uuid_to_string(uuid);
c$dce_rpc_state$uuid = uuid_str;
c$dce_rpc$endpoint = uuid_endpoint_map[uuid_str];
}
event dce_rpc_bind_ack(c: connection, fid: count, sec_addr: string) &priority=5
{
set_session(c, fid);
if ( sec_addr != "" )
{
c$dce_rpc_state$named_pipe = sec_addr;
c$dce_rpc$named_pipe = sec_addr;
}
}
event dce_rpc_request(c: connection, fid: count, opnum: count, stub_len: count) &priority=5
{
set_session(c, fid);
if ( c?$dce_rpc )
{
c$dce_rpc$ts = network_time();
}
}
event dce_rpc_response(c: connection, fid: count, opnum: count, stub_len: count) &priority=5
{
set_session(c, fid);
# In the event that the binding wasn't seen, but the pipe
# name is known, go ahead and see if we have a pipe name to
# uuid mapping...
if ( ! c$dce_rpc?$endpoint && c$dce_rpc?$named_pipe )
{
local npn = normalize_named_pipe_name(c$dce_rpc$named_pipe);
if ( npn in pipe_name_to_common_uuid )
{
c$dce_rpc_state$uuid = pipe_name_to_common_uuid[npn];
}
}
if ( c?$dce_rpc && c$dce_rpc?$endpoint )
{
c$dce_rpc$operation = operations[c$dce_rpc_state$uuid, opnum];
if ( c$dce_rpc$ts != network_time() )
c$dce_rpc$rtt = network_time() - c$dce_rpc$ts;
}
}
event dce_rpc_response(c: connection, fid: count, opnum: count, stub_len: count) &priority=-5
{
if ( c?$dce_rpc )
{
# If there is not an endpoint, there isn't much reason to log.
# This can happen if the request isn't seen.
if ( (c$dce_rpc?$endpoint && c$dce_rpc$endpoint !in ignored_operations)
||
(c$dce_rpc?$endpoint && c$dce_rpc?$operation &&
c$dce_rpc$operation !in ignored_operations[c$dce_rpc$endpoint] &&
"*" !in ignored_operations[c$dce_rpc$endpoint]) )
{
Log::write(LOG, c$dce_rpc);
}
delete c$dce_rpc;
}
}
event connection_state_remove(c: connection)
{
if ( ! c?$dce_rpc )
return;
# TODO: Go through any remaining dce_rpc requests that haven't been processed with replies.
for ( i in c$dce_rpc_backing )
{
local x = c$dce_rpc_backing[i];
set_state(c, x);
# In the event that the binding wasn't seen, but the pipe
# name is known, go ahead and see if we have a pipe name to
# uuid mapping...
if ( ! c$dce_rpc?$endpoint && c$dce_rpc?$named_pipe )
{
local npn = normalize_named_pipe_name(c$dce_rpc$named_pipe);
if ( npn in pipe_name_to_common_uuid )
{
c$dce_rpc_state$uuid = pipe_name_to_common_uuid[npn];
}
}
if ( (c$dce_rpc?$endpoint && c$dce_rpc$endpoint !in ignored_operations)
||
(c$dce_rpc?$endpoint && c$dce_rpc?$operation &&
c$dce_rpc$operation !in ignored_operations[c$dce_rpc$endpoint] &&
"*" !in ignored_operations[c$dce_rpc$endpoint]) )
{
Log::write(LOG, c$dce_rpc);
}
}
}

View file

@ -0,0 +1 @@
@load ./main

View file

@ -0,0 +1,127 @@
@load base/protocols/smb
module NTLM;
export {
redef enum Log::ID += { LOG };
type Info: record {
## Timestamp for when the event happened.
ts : time &log;
## Unique ID for the connection.
uid : string &log;
## The connection's 4-tuple of endpoint addresses/ports.
id : conn_id &log;
## Username given by the client.
username : string &log &optional;
## Hostname given by the client.
hostname : string &log &optional;
## Domainname given by the client.
domainname : string &log &optional;
## Indicate whether or not the authentication was successful.
success : bool &log &optional;
## A string representation of the status code that was
## returned in response to the authentication attempt.
status : string &log &optional;
## Internally used field to indicate if the login attempt
## has already been logged.
done: bool &default=F;
};
## DOS and NT status codes that indicate authentication failure.
const auth_failure_statuses: set[count] = {
0x052e0001, # logonfailure
0x08c00002, # badClient
0x08c10002, # badLogonTime
0x08c20002, # passwordExpired
0xC0000022, # ACCESS_DENIED
0xC000006A, # WRONG_PASSWORD
0xC000006F, # INVALID_LOGON_HOURS
0xC0000070, # INVALID_WORKSTATION
0xC0000071, # PASSWORD_EXPIRED
0xC0000072, # ACCOUNT_DISABLED
} &redef;
}
redef record connection += {
ntlm: Info &optional;
};
event bro_init() &priority=5
{
Log::create_stream(NTLM::LOG, [$columns=Info, $path="ntlm"]);
}
event ntlm_negotiate(c: connection, request: NTLM::Negotiate) &priority=5
{
}
event ntlm_challenge(c: connection, challenge: NTLM::Challenge) &priority=5
{
}
event ntlm_authenticate(c: connection, request: NTLM::Authenticate) &priority=5
{
c$ntlm = NTLM::Info($ts=network_time(), $uid=c$uid, $id=c$id);
if ( request?$domain_name )
c$ntlm$domainname = request$domain_name;
if ( request?$workstation )
c$ntlm$hostname = request$workstation;
if ( request?$user_name )
c$ntlm$username = request$user_name;
}
event gssapi_neg_result(c: connection, state: count) &priority=3
{
if ( c?$ntlm )
c$ntlm$success = (state == 0);
}
event gssapi_neg_result(c: connection, state: count) &priority=-3
{
if ( c?$ntlm && ! c$ntlm$done )
{
if ( c$ntlm?$username || c$ntlm?$hostname )
{
Log::write(NTLM::LOG, c$ntlm);
c$ntlm$done = T;
}
}
}
event smb1_message(c: connection, hdr: SMB1::Header, is_orig: bool) &priority=3
{
if ( c?$ntlm && ! c$ntlm$done &&
( c$ntlm?$username || c$ntlm?$hostname ) )
{
c$ntlm$success = (hdr$status !in auth_failure_statuses);
c$ntlm$status = SMB::statuses[hdr$status]$id;
Log::write(NTLM::LOG, c$ntlm);
c$ntlm$done = T;
}
}
event smb2_message(c: connection, hdr: SMB2::Header, is_orig: bool) &priority=3
{
if ( c?$ntlm && ! c$ntlm$done &&
( c$ntlm?$username || c$ntlm?$hostname ) )
{
c$ntlm$success = (hdr$status !in auth_failure_statuses);
c$ntlm$status = SMB::statuses[hdr$status]$id;
Log::write(NTLM::LOG, c$ntlm);
c$ntlm$done = T;
}
}
event connection_state_remove(c: connection) &priority=-5
{
if ( c?$ntlm && ! c$ntlm$done )
{
Log::write(NTLM::LOG, c$ntlm);
}
}

View file

@ -0,0 +1,3 @@
@load ./consts
@load ./const-dos-error
@load ./const-nt-status

View file

@ -0,0 +1,132 @@
# DOS error codes.
@load ./consts
module SMB;
redef SMB::statuses += {
[0x00010001] = [$id="badfunc", $desc="Incorrect function."],
[0x00010002] = [$id="error", $desc="Incorrect function."],
[0x00020001] = [$id="badfile", $desc="The system cannot find the file specified."],
[0x00020002] = [$id="badpw", $desc="Bad password."],
[0x00030001] = [$id="badpath", $desc="The system cannot find the path specified."],
[0x00030002] = [$id="badtype", $desc="reserved"],
[0x00040001] = [$id="nofids", $desc="The system cannot open the file."],
[0x00040002] = [$id="access", $desc="The client does not have the necessary access rights to perform the requested function."],
[0x00050001] = [$id="noaccess", $desc="Access is denied."],
[0x00050002] = [$id="invnid", $desc="The TID specified was invalid."],
[0x00060001] = [$id="badfid", $desc="The handle is invalid."],
[0x00060002] = [$id="invnetname", $desc="The network name cannot be found."],
[0x00070001] = [$id="badmcb", $desc="The storage control blocks were destroyed."],
[0x00070002] = [$id="invdevice", $desc="The device specified is invalid."],
[0x00080001] = [$id="nomem", $desc="Not enough storage is available to process this command."],
[0x00090001] = [$id="badmem", $desc="The storage control block address is invalid."],
[0x000a0001] = [$id="badenv", $desc="The environment is incorrect."],
[0x000c0001] = [$id="badaccess", $desc="The access code is invalid."],
[0x000d0001] = [$id="baddata", $desc="The data is invalid."],
[0x000e0001] = [$id="res", $desc="reserved"],
[0x000f0001] = [$id="baddrive", $desc="The system cannot find the drive specified."],
[0x00100001] = [$id="remcd", $desc="The directory cannot be removed."],
[0x00110001] = [$id="diffdevice", $desc="The system cannot move the file to a different disk drive."],
[0x00120001] = [$id="nofiles", $desc="There are no more files."],
[0x00130003] = [$id="nowrite", $desc="The media is write protected."],
[0x00140003] = [$id="badunit", $desc="The system cannot find the device specified."],
[0x00150003] = [$id="notready", $desc="The device is not ready."],
[0x00160002] = [$id="unknownsmb", $desc="The device does not recognize the command."],
[0x00160003] = [$id="badcmd", $desc="The device does not recognize the command."],
[0x00170003] = [$id="data", $desc="Data error (cyclic redundancy check)."],
[0x00180003] = [$id="badreq", $desc="The program issued a command but the command length is incorrect."],
[0x00190003] = [$id="seek", $desc="The drive cannot locate a specific area or track on the disk."],
[0x001a0003] = [$id="badmedia", $desc="The specified disk or diskette cannot be accessed."],
[0x001b0003] = [$id="badsector", $desc="The drive cannot find the sector requested."],
[0x001c0003] = [$id="nopaper", $desc="The printer is out of paper."],
[0x001d0003] = [$id="write", $desc="The system cannot write to the specified device."],
[0x001e0003] = [$id="read", $desc="The system cannot read from the specified device."],
[0x001f0001] = [$id="general", $desc="A device attached to the system is not functioning."],
[0x001f0003] = [$id="general", $desc="A device attached to the system is not functioning."],
[0x00200001] = [$id="badshare", $desc="The process cannot access the file because it is being used by another process."],
[0x00200003] = [$id="badshare", $desc="The process cannot access the file because it is being used by another process."],
[0x00210001] = [$id="lock", $desc="The process cannot access the file because another process has locked a portion of the file."],
[0x00210003] = [$id="lock", $desc="The process cannot access the file because another process has locked a portion of the file."],
[0x00220003] = [$id="wrongdisk", $desc="The wrong diskette is in the drive."],
[0x00230003] = [$id="FCBunavail", $desc="No FCBs are available to process the request."],
[0x00240003] = [$id="sharebufexc", $desc="A sharing buffer has been exceeded."],
[0x00270003] = [$id="diskfull", $desc="The disk is full."],
[0x00310002] = [$id="qfull", $desc="The print queue is full."],
[0x00320001] = [$id="unsup", $desc="The network request is not supported."],
[0x00320002] = [$id="qtoobig", $desc="The queued item too big."],
[0x00340002] = [$id="invpfid", $desc="The print file FID is invalid."],
[0x00340001] = [$id="dupname", $desc="A duplicate name exists on the network."],
[0x00400001] = [$id="netnamedel", $desc="The specified network name is no longer available."],
[0x00400002] = [$id="smbcmd", $desc="The server did not recognize the command received."],
[0x00410002] = [$id="srverror", $desc="The server encountered an internal error."],
[0x00420001] = [$id="noipc", $desc="The network resource type is not correct."],
[0x00430001] = [$id="nosuchshare", $desc="The network name cannot be found."],
[0x00430002] = [$id="filespecs", $desc="The specified FID and pathname combination is invalid."],
[0x00440002] = [$id="badlink", $desc="reserved"],
[0x00450002] = [$id="badpermits", $desc="The access permissions specified for a file or directory are not a valid combination."],
[0x00460002] = [$id="badpid", $desc="reserved"],
[0x00470001] = [$id="nomoreconn", $desc="nomoreconn."],
[0x00470002] = [$id="setattrmode", $desc="The attribute mode specified is invalid."],
[0x00500001] = [$id="filexists", $desc="The file exists."],
[0x00510002] = [$id="paused", $desc="The message server is paused."],
[0x00520002] = [$id="msgoff", $desc="Not receiving messages."],
[0x00530002] = [$id="noroom", $desc="No room to buffer message."],
[0x00570001] = [$id="invalidparam", $desc="The parameter is incorrect."],
[0x00570002] = [$id="rmuns", $desc="Too many remote usernames."],
[0x00580002] = [$id="timeout", $desc="Operation timed out."],
[0x00590002] = [$id="noresource", $desc="No resources currently available for request."],
[0x005a0002] = [$id="toomanyuids", $desc="Too many Uids active on this session."],
[0x005b0002] = [$id="baduid", $desc="The Uid is not known as a valid user identifier on this session."],
[0x006d0001] = [$id="brokenpipe", $desc="The pipe has been ended."],
[0x006e0001] = [$id="cannotopen", $desc="The system cannot open the device or file specified."],
[0x007a0001] = [$id="insufficientbuffer", $desc="The data area passed to a system call is too small."],
[0x007b0001] = [$id="invalidname", $desc="The filename, directory name, or volume label syntax is incorrect."],
[0x007c0001] = [$id="unknownlevel", $desc="The system call level is not correct."],
[0x00910001] = [$id="notempty", $desc="The directory is not empty."],
[0x009e0001] = [$id="notlocked", $desc="The segment is already unlocked."],
[0x00b70001] = [$id="rename", $desc="Cannot create a file when that file already exists."],
[0x00e60001] = [$id="badpipe", $desc="The pipe state is invalid."],
[0x00e70001] = [$id="pipebusy", $desc="All pipe instances are busy."],
[0x00e80001] = [$id="pipeclosing", $desc="The pipe is being closed."],
[0x00e90001] = [$id="notconnected", $desc="No process is on the other end of the pipe."],
[0x00ea0001] = [$id="moredata", $desc="More data is available."],
[0x00fa0002] = [$id="usempx", $desc="Temporarily unable to support Raw, use Mpx mode."],
[0x00fb0002] = [$id="usestd", $desc="Temporarily unable to support Raw, use standard read/write."],
[0x00fc0002] = [$id="contmpx", $desc="Continue in MPX mode."],
[0x00fe0002] = [$id="badPassword", $desc="reserved"],
[0x01030001] = [$id="nomoreitems", $desc="No more data is available."],
[0x010b0001] = [$id="baddirectory", $desc="The directory name is invalid."],
[0x011a0001] = [$id="easnotsupported", $desc="The mounted file system does not support extended attributes."],
[0x04000002] = [$id="_NOTIFY_ENUM_DIR", $desc="Too many files have changed since the last time an NT_TRANSACT_NOTIFY_CHANGE was issued."],
[0x052e0001] = [$id="logonfailure", $desc="Logon failure: unknown user name or bad password."],
[0x07030001] = [$id="driveralreadyinstalled", $desc="The specified printer driver is already installed."],
[0x07040001] = [$id="unknownprinterport", $desc="The specified port is unknown."],
[0x07050001] = [$id="unknownprinterdriver", $desc="The printer driver is unknown."],
[0x07060001] = [$id="unknownprintprocessor", $desc="The print processor is unknown."],
[0x07070001] = [$id="invalidseparatorfile", $desc="The specified separator file is invalid."],
[0x07080001] = [$id="invalidjobpriority", $desc="The specified priority is invalid."],
[0x07090001] = [$id="invalidprintername", $desc="The printer name is invalid."],
[0x070a0001] = [$id="printeralreadyexists", $desc="The printer already exists."],
[0x070b0001] = [$id="invalidprintercommand", $desc="The printer command is invalid."],
[0x070c0001] = [$id="invaliddatatype", $desc="The specified datatype is invalid."],
[0x070d0001] = [$id="invalidenvironment", $desc="The Environment specified is invalid."],
[0x084b0001] = [$id="buftoosmall", $desc="The API return buffer is too small."],
[0x085e0001] = [$id="unknownipc", $desc="The requested API is not supported on the remote server."],
[0x08670001] = [$id="nosuchprintjob", $desc="The print job does not exist."],
[0x08bf0002] = [$id="accountExpired", $desc="This user account has expired."],
[0x08c00002] = [$id="badClient", $desc="The user is not allowed to log on from this workstation."],
[0x08c10002] = [$id="badLogonTime", $desc="The user is not allowed to log on at this time."],
[0x08c20002] = [$id="passwordExpired", $desc="The password of this user has expired."],
[0x09970001] = [$id="invgroup", $desc="invgroup"],
[0x0bb80001] = [$id="unknownprintmonitor", $desc="The specified print monitor is unknown."],
[0x0bb90001] = [$id="printerdriverinuse", $desc="The specified printer driver is currently in use."],
[0x0bba0001] = [$id="spoolfilenotfound", $desc="The spool file was not found."],
[0x0bbb0001] = [$id="nostartdoc", $desc="A StartDocPrinter call was not issued."],
[0x0bbc0001] = [$id="noaddjob", $desc="An AddJob call was not issued."],
[0x0bbd0001] = [$id="printprocessoralreadyinstalled", $desc="The specified print processor has already been installed."],
[0x0bbe0001] = [$id="printmonitoralreadyinstalled", $desc="The specified print monitor has already been installed."],
[0x0bbf0001] = [$id="invalidprintmonitor", $desc="The specified print monitor does not have the required functions."],
[0x0bc00001] = [$id="printmonitorinuse", $desc="The specified print monitor is currently in use."],
[0x0bc10001] = [$id="printerhasjobsqueued", $desc="The requested operation is not allowed when there are jobs queued to the printer."],
[0xffff0002] = [$id="nosupport", $desc="Function not supported."],
};

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,271 @@
module SMB;
export {
type StatusCode: record {
id: string;
desc: string;
};
const statuses: table[count] of StatusCode = {
[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]; };
## 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;
export {
const commands: table[count] of string = {
[0x00] = "CREATE_DIRECTORY",
[0x01] = "DELETE_DIRECTORY",
[0x02] = "OPEN",
[0x03] = "CREATE",
[0x04] = "CLOSE",
[0x05] = "FLUSH",
[0x06] = "DELETE",
[0x07] = "RENAME",
[0x08] = "QUERY_INFORMATION",
[0x09] = "SET_INFORMATION",
[0x0A] = "READ",
[0x0B] = "WRITE",
[0x0C] = "LOCK_BYTE_RANGE",
[0x0D] = "UNLOCK_BYTE_RANGE",
[0x0E] = "CREATE_TEMPORARY",
[0x0F] = "CREATE_NEW",
[0x10] = "CHECK_DIRECTORY",
[0x11] = "PROCESS_EXIT",
[0x12] = "SEEK",
[0x13] = "LOCK_AND_READ",
[0x14] = "WRITE_AND_UNLOCK",
[0x1A] = "READ_RAW",
[0x1B] = "READ_MPX",
[0x1C] = "READ_MPX_SECONDARY",
[0x1D] = "WRITE_RAW",
[0x1E] = "WRITE_MPX",
[0x1F] = "WRITE_MPX_SECONDARY",
[0x20] = "WRITE_COMPLETE",
[0x21] = "QUERY_SERVER",
[0x22] = "SET_INFORMATION2",
[0x23] = "QUERY_INFORMATION2",
[0x24] = "LOCKING_ANDX",
[0x25] = "TRANSACTION",
[0x26] = "TRANSACTION_SECONDARY",
[0x27] = "IOCTL",
[0x28] = "IOCTL_SECONDARY",
[0x29] = "COPY",
[0x2A] = "MOVE",
[0x2B] = "ECHO",
[0x2C] = "WRITE_AND_CLOSE",
[0x2D] = "OPEN_ANDX",
[0x2E] = "READ_ANDX",
[0x2F] = "WRITE_ANDX",
[0x30] = "NEW_FILE_SIZE",
[0x31] = "CLOSE_AND_TREE_DISC",
[0x32] = "TRANSACTION2",
[0x33] = "TRANSACTION2_SECONDARY",
[0x34] = "FIND_CLOSE2",
[0x35] = "FIND_NOTIFY_CLOSE",
[0x70] = "TREE_CONNECT",
[0x71] = "TREE_DISCONNECT",
[0x72] = "NEGOTIATE",
[0x73] = "SESSION_SETUP_ANDX",
[0x74] = "LOGOFF_ANDX",
[0x75] = "TREE_CONNECT_ANDX",
[0x80] = "QUERY_INFORMATION_DISK",
[0x81] = "SEARCH",
[0x82] = "FIND",
[0x83] = "FIND_UNIQUE",
[0x84] = "FIND_CLOSE",
[0xA0] = "NT_TRANSACT",
[0xA1] = "NT_TRANSACT_SECONDARY",
[0xA2] = "NT_CREATE_ANDX",
[0xA4] = "NT_CANCEL",
[0xA5] = "NT_RENAME",
[0xC0] = "OPEN_PRINT_FILE",
[0xC1] = "WRITE_PRINT_FILE",
[0xC2] = "CLOSE_PRINT_FILE",
[0xC3] = "GET_PRINT_QUEUE",
[0xD8] = "READ_BULK",
[0xD9] = "WRITE_BULK",
[0xDA] = "WRITE_BULK_DATA",
} &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;
export {
const commands: table[count] of string = {
[0] = "NEGOTIATE_PROTOCOL",
[1] = "SESSION_SETUP",
[2] = "LOGOFF",
[3] = "TREE_CONNECT",
[4] = "TREE_DISCONNECT",
[5] = "CREATE",
[6] = "CLOSE",
[7] = "FLUSH",
[8] = "READ",
[9] = "WRITE",
[10] = "LOCK",
[11] = "IOCTL",
[12] = "CANCEL",
[13] = "ECHO",
[14] = "QUERY_DIRECTORY",
[15] = "CHANGE_NOTIFY",
[16] = "QUERY_INFO",
[17] = "SET_INFO",
[18] = "OPLOCK_BREAK"
} &default=function(i: count): string { return fmt("unknown-%d", i); };
const dialects: table[count] of string = {
[0x0202] = "2.002",
[0x0210] = "2.1",
[0x0300] = "3.0",
[0x0302] = "3.02",
} &default=function(i: count): string { return fmt("unknown-%d", i); };
const share_types: table[count] of string = {
[1] = "DISK",
[2] = "PIPE",
[3] = "PRINT",
} &default=function(i: count): string { return fmt("unknown-%d", i); };
}

View file

@ -0,0 +1,8 @@
@load base/protocols/smb
@load ./main
@load ./smb1-main
@load ./smb2-main
@load ./files
@load-sigs ./dpd.sig

View file

@ -0,0 +1,5 @@
signature dpd_smb {
ip-proto == tcp
payload /^....[\xfe\xff]SMB/
enable "smb"
}

View file

@ -0,0 +1,69 @@
@load base/frameworks/files
@load ./main
module SMB;
export {
## Default file handle provider for SMB.
global get_file_handle: function(c: connection, is_orig: bool): string;
## Default file describer for SMB.
global describe_file: function(f: fa_file): string;
}
function get_file_handle(c: connection, is_orig: bool): string
{
if ( ! (c$smb_state?$current_file &&
(c$smb_state$current_file?$name ||
c$smb_state$current_file?$path)) )
{
# TODO - figure out what are the cases where this happens.
return "";
}
local current_file = c$smb_state$current_file;
local path_name = current_file?$path ? current_file$path : "";
local file_name = current_file?$name ? current_file$name : "";
# Include last_mod time if available because if a file has been modified it
# should be considered a new file.
local last_mod = cat(current_file?$times ? current_file$times$modified : double_to_time(0.0));
# TODO: This is doing hexdump to avoid problems due to file analysis handling
# using CheckString which is not immune to encapsulated null bytes.
# This needs to be fixed lower in the file analysis code later.
return hexdump(cat(Analyzer::ANALYZER_SMB, c$id$orig_h, c$id$resp_h, path_name, file_name, last_mod));
}
function describe_file(f: fa_file): string
{
# This shouldn't be needed, but just in case...
if ( f$source != "SMB" )
return "";
for ( cid in f$conns )
{
local info = f$conns[cid];
if ( info?$smb_state && info$smb_state?$current_file && info$smb_state$current_file?$name )
return info$smb_state$current_file$name;
}
return "";
}
event bro_init() &priority=5
{
Files::register_protocol(Analyzer::ANALYZER_SMB,
[$get_file_handle = SMB::get_file_handle,
$describe = SMB::describe_file]);
}
event file_over_new_connection(f: fa_file, c: connection, is_orig: bool) &priority=5
{
if ( c?$smb_state && c$smb_state?$current_file )
{
c$smb_state$current_file$fuid = f$id;
if ( c$smb_state$current_file$size > 0 )
f$total_bytes = c$smb_state$current_file$size;
if ( c$smb_state$current_file?$name )
f$info$filename = c$smb_state$current_file$name;
}
}

View file

@ -0,0 +1,268 @@
@load base/protocols/smb
module SMB;
export {
redef enum Log::ID += {
CMD_LOG,
AUTH_LOG,
MAPPING_LOG,
FILES_LOG
};
## Abstracted actions for SMB file actions.
type Action: enum {
FILE_READ,
FILE_WRITE,
FILE_OPEN,
FILE_CLOSE,
FILE_DELETE,
FILE_RENAME,
PIPE_READ,
PIPE_WRITE,
PIPE_OPEN,
PIPE_CLOSE,
PRINT_READ,
PRINT_WRITE,
PRINT_OPEN,
PRINT_CLOSE,
UNKNOWN_READ,
UNKNOWN_WRITE,
UNKNOWN_OPEN,
UNKNOWN_CLOSE,
};
## The file actions which are logged.
const logged_file_actions: set[Action] = {
FILE_OPEN,
FILE_CLOSE,
PIPE_OPEN,
PIPE_CLOSE,
PRINT_OPEN,
PRINT_CLOSE,
UNKNOWN_OPEN,
} &redef;
## The server response statuses which are *not* logged.
const ignored_command_statuses: set[string] = {
"MORE_PROCESSING_REQUIRED",
} &redef;
## This record is for the smb_files.log
type FileInfo: record {
## Time when the file was first discovered.
ts : time &log;
## Unique ID of the connection the file was sent over.
uid : string &log;
## ID of the connection the file was sent over.
id : conn_id &log;
## Unique ID of the file.
fuid : string &log &optional;
## Action this log record represents.
action : Action &log &optional;
## Path pulled from the tree this file was transferred to or from.
path : string &log &optional;
## Filename if one was seen.
name : string &log &optional;
## Total size of the file.
size : count &log &default=0;
## Last time this file was modified.
times : SMB::MACTimes &log &optional;
};
## This record is for the smb_mapping.log
type TreeInfo: record {
## Time when the tree was mapped.
ts : time &log &optional;
## Unique ID of the connection the tree was mapped over.
uid : string &log;
## ID of the connection the tree was mapped over.
id : conn_id &log;
## Name of the tree path.
path : string &log &optional;
## The type of resource of the tree (disk share, printer share, named pipe, etc.)
service : string &log &optional;
## File system of the tree.
native_file_system : string &log &optional;
## If this is SMB2, a share type will be included. For SMB1,
## the type of share will be deduced and included as well.
share_type : string &log &default="DISK";
};
## This record is for the smb_cmd.log
type CmdInfo: record {
## Timestamp of the command request
ts : time &log;
## Unique ID of the connection the request was sent over
uid : string &log;
## ID of the connection the request was sent over
id : conn_id &log;
## The command sent by the client
command : string &log;
## The subcommand sent by the client, if present
sub_command : string &log &optional;
## Command argument sent by the client, if any
argument : string &log &optional;
## Server reply to the client's command
status : string &log &optional;
## Round trip time from the request to the response.
rtt : interval &log &optional;
## Version of SMB for the command
version : string &log;
## Authenticated username, if available
username : string &log &optional;
## If this is related to a tree, this is the tree
## that was used for the current command.
tree : string &log &optional;
## The type of tree (disk share, printer share, named pipe, etc.)
tree_service : string &log &optional;
## If the command referenced a file, store it here.
referenced_file : FileInfo &log &optional;
## If the command referenced a tree, store it here.
referenced_tree : TreeInfo &optional;
};
## This record stores the SMB state of in-flight commands,
## the file and tree map of the connection.
type State: record {
## A reference to the current command.
current_cmd : CmdInfo &optional;
## A reference to the current file.
current_file : FileInfo &optional;
## A reference to the current tree.
current_tree : TreeInfo &optional;
## Indexed on MID to map responses to requests.
pending_cmds : table[count] of CmdInfo &optional;
## File map to retrieve file information based on the file ID.
fid_map : table[count] of FileInfo &optional;
## Tree map to retrieve tree information based on the tree ID.
tid_map : table[count] of TreeInfo &optional;
## User map to retrieve user name based on the user ID.
uid_map : table[count] of string &optional;
## Pipe map to retrieve UUID based on the file ID of a pipe.
pipe_map : table[count] of string &optional;
## A set of recent files to avoid logging the same
## files over and over in the smb files log.
## This only applies to files seen in a single connection.
recent_files : set[string] &default=string_set() &read_expire=3min;
};
## Optionally write out the SMB commands log. This is
## primarily useful for debugging so is disabled by default.
const write_cmd_log = F &redef;
## Everything below here is used internally in the SMB scripts.
redef record connection += {
smb_state : State &optional;
};
## Internal use only
## Some commands shouldn't be logged by the smb1_message event
const deferred_logging_cmds: set[string] = {
"NEGOTIATE",
"READ_ANDX",
"SESSION_SETUP_ANDX",
"TREE_CONNECT_ANDX",
};
## This is an internally used function.
const set_current_file: function(smb_state: State, file_id: count) &redef;
## This is an internally used function.
const write_file_log: function(state: State) &redef;
}
redef record FileInfo += {
## ID referencing this file.
fid : count &optional;
## UUID referencing this file if DCE/RPC
uuid : string &optional;
};
const ports = { 139/tcp, 445/tcp };
redef likely_server_ports += { ports };
event bro_init() &priority=5
{
Log::create_stream(SMB::CMD_LOG, [$columns=SMB::CmdInfo]);
Log::create_stream(SMB::FILES_LOG, [$columns=SMB::FileInfo]);
Log::create_stream(SMB::MAPPING_LOG, [$columns=SMB::TreeInfo]);
Analyzer::register_for_ports(Analyzer::ANALYZER_SMB, ports);
}
function set_current_file(smb_state: State, file_id: count)
{
if ( file_id !in smb_state$fid_map )
{
smb_state$fid_map[file_id] = smb_state$current_cmd$referenced_file;
smb_state$fid_map[file_id]$fid = file_id;
}
smb_state$current_cmd$referenced_file = smb_state$fid_map[file_id];
smb_state$current_file = smb_state$current_cmd$referenced_file;
}
function write_file_log(state: State)
{
local f = state$current_file;
if ( f?$name &&
f$name !in pipe_names &&
f$action in logged_file_actions )
{
# Everything in this if statement is to avoid overlogging
# of the same data from a single connection based on recently
# seen files in the SMB::State $recent_files field.
if ( f?$times )
{
local file_ident = cat(f$action,
f?$fuid ? f$fuid : "",
f?$name ? f$name : "",
f?$path ? f$path : "",
f$size,
f$times);
if ( file_ident in state$recent_files )
{
# We've already seen this file and don't want to log it again.
return;
}
else
add state$recent_files[file_ident];
}
Log::write(FILES_LOG, f);
}
}
event file_state_remove(f: fa_file) &priority=-5
{
if ( f$source != "SMB" )
return;
for ( id in f$conns )
{
local c = f$conns[id];
if ( c?$smb_state && c$smb_state?$current_file)
{
write_file_log(c$smb_state);
}
return;
}
}

View file

@ -0,0 +1,362 @@
@load ./main
module SMB1;
redef record SMB::CmdInfo += {
## Dialects offered by the client
smb1_offered_dialects: string_vec &optional;
};
event smb1_message(c: connection, hdr: SMB1::Header, is_orig: bool) &priority=5
{
if ( ! c?$smb_state )
{
local state: SMB::State;
state$fid_map = table();
state$tid_map = table();
state$uid_map = table();
state$pipe_map = table();
state$pending_cmds = table();
c$smb_state = state;
}
local smb_state = c$smb_state;
local tid = hdr$tid;
local uid = hdr$uid;
local pid = hdr$pid;
local mid = hdr$mid;
if ( uid in smb_state$uid_map )
{
smb_state$current_cmd$username = smb_state$uid_map[uid];
}
if ( tid !in smb_state$tid_map )
{
smb_state$tid_map[tid] = SMB::TreeInfo($uid=c$uid, $id=c$id);
}
smb_state$current_tree = smb_state$tid_map[tid];
if ( smb_state$current_tree?$path )
{
smb_state$current_cmd$tree = smb_state$current_tree$path;
}
if ( smb_state$current_tree?$service )
{
smb_state$current_cmd$tree_service = smb_state$current_tree$service;
}
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_tree = smb_state$current_tree;
smb_state$pending_cmds[mid] = tmp_cmd;
}
smb_state$current_cmd = smb_state$pending_cmds[mid];
if ( !is_orig )
{
smb_state$current_cmd$rtt = network_time() - smb_state$current_cmd$ts;
smb_state$current_cmd$status = SMB::statuses[hdr$status]$id;
}
}
event smb1_message(c: connection, hdr: SMB1::Header, is_orig: bool) &priority=-5
{
# Is this a response?
if ( !is_orig )
{
if ( SMB::write_cmd_log &&
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];
}
}
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
{
c$smb_state$current_cmd$smb1_offered_dialects = dialects;
}
event smb1_negotiate_response(c: connection, hdr: SMB1::Header, response: SMB1::NegotiateResponse) &priority=5
{
if ( c$smb_state$current_cmd?$smb1_offered_dialects )
{
if ( response?$ntlm )
{
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 ( SMB::write_cmd_log &&
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
{
local tmp_tree = SMB::TreeInfo($ts=network_time(), $uid=c$uid, $id=c$id, $path=path, $service=service);
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
{
c$smb_state$current_cmd$referenced_tree$service = service;
if ( service == "IPC" )
c$smb_state$current_cmd$referenced_tree$share_type = "PIPE";
c$smb_state$current_cmd$tree_service = service;
if ( native_file_system != "" )
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
{
Log::write(SMB::MAPPING_LOG, c$smb_state$current_tree);
if ( SMB::write_cmd_log &&
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
{
local tmp_file = SMB::FileInfo($ts=network_time(), $uid=c$uid, $id=c$id);
c$smb_state$current_cmd$referenced_file = tmp_file;
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
{
c$smb_state$current_cmd$referenced_file$action = SMB::FILE_OPEN;
c$smb_state$current_cmd$referenced_file$fid = file_id;
c$smb_state$current_cmd$referenced_file$size = file_size;
# I'm seeing negative data from IPC tree transfers
if ( time_to_double(times$modified) > 0.0 )
c$smb_state$current_cmd$referenced_file$times = times;
# We can identify the file by its file id now so let's stick it
# in the file map.
c$smb_state$fid_map[file_id] = c$smb_state$current_cmd$referenced_file;
c$smb_state$current_file = c$smb_state$fid_map[file_id];
SMB::write_file_log(c$smb_state);
}
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_state, file_id);
c$smb_state$current_file$action = SMB::FILE_READ;
if ( c$smb_state$current_file?$name )
c$smb_state$current_cmd$argument = c$smb_state$current_file$name;
}
event smb1_read_andx_request(c: connection, hdr: SMB1::Header, file_id: count, offset: count, length: count) &priority=-5
{
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;
# We don't even try to log reads and writes to the files log.
#write_file_log(c$smb_state);
}
event smb1_read_andx_response(c: connection, hdr: SMB1::Header, data_len: count) &priority=5
{
if ( SMB::write_cmd_log &&
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
{
SMB::set_current_file(c$smb_state, file_id);
c$smb_state$current_file$action = SMB::FILE_WRITE;
if ( !c$smb_state$current_cmd?$argument &&
# TODO: figure out why name isn't getting set sometimes.
c$smb_state$current_file?$name )
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
{
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;
# We don't even try to log reads and writes to the files log.
#write_file_log(c$smb_state);
}
#event smb1_write_andx_response(c: connection, hdr: SMB1::Header, written_bytes: count) &priority=5
# {
# # TODO - determine what to do here
# }
event smb1_close_request(c: connection, hdr: SMB1::Header, file_id: count) &priority=5
{
SMB::set_current_file(c$smb_state, file_id);
c$smb_state$current_file$action = SMB::FILE_CLOSE;
}
event smb1_close_request(c: connection, hdr: SMB1::Header, file_id: count) &priority=-5
{
if ( file_id in c$smb_state$fid_map )
{
local fl = c$smb_state$fid_map[file_id];
# Need to check for existence of path in case tree connect message wasn't seen.
if ( c$smb_state$current_tree?$path )
fl$path = c$smb_state$current_tree$path;
if ( fl?$name )
c$smb_state$current_cmd$argument = fl$name;
delete c$smb_state$fid_map[file_id];
SMB::write_file_log(c$smb_state);
}
else
{
# TODO - Determine correct action
# A reporter message is not right...
#Reporter::warning("attempting to close an unknown file!");
}
}
event smb1_trans2_get_dfs_referral_request(c: connection, hdr: SMB1::Header, file_name: string)
{
c$smb_state$current_cmd$argument = file_name;
}
event smb1_trans2_query_path_info_request(c: connection, hdr: SMB1::Header, file_name: string)
{
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 ( SMB::write_cmd_log &&
c$smb_state$current_cmd$status !in SMB::ignored_command_statuses )
{
Log::write(SMB::CMD_LOG, c$smb_state$current_cmd);
}
}
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)
{
if ( ! c$smb_state?$current_file || ! c$smb_state$current_file?$uuid )
{
# TODO: figure out why the uuid isn't getting set sometimes.
return;
}
c$smb_state$pipe_map[file_id] = c$smb_state$current_file$uuid;
}
event smb_pipe_bind_ack_response(c: connection, hdr: SMB1::Header)
{
if ( ! c$smb_state?$current_file || ! c$smb_state$current_file?$uuid )
{
# TODO: figure out why the uuid isn't getting set sometimes.
return;
}
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)
{
if ( ! c$smb_state?$current_file || ! c$smb_state$current_file?$uuid )
{
# TODO: figure out why the current_file isn't getting set sometimes.
return;
}
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)
{
if ( ! c$smb_state?$current_file )
{
# TODO: figure out why the current file isn't being set sometimes.
return;
}
local f = c$smb_state$current_file;
if ( ! f?$uuid )
{
# TODO: figure out why this is happening.
event conn_weird("smb_pipe_request_missing_uuid", c, "");
return;
}
local arg = fmt("%s: %s",
SMB::rpc_uuids[f$uuid],
SMB::rpc_sub_cmds[f$uuid][op_num]);
c$smb_state$current_cmd$argument = arg;
}
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 ( SMB::write_cmd_log &&
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);
}
}
}

View file

@ -0,0 +1,268 @@
@load ./main
module SMB2;
redef record SMB::CmdInfo += {
## Dialects offered by the client
smb2_offered_dialects: index_vec &optional;
};
event smb2_message(c: connection, hdr: SMB2::Header, is_orig: bool) &priority=5
{
if ( ! c?$smb_state )
{
local state: SMB::State;
state$fid_map = table();
state$tid_map = table();
state$pending_cmds = table();
c$smb_state = state;
}
local smb_state = c$smb_state;
local tid = hdr$tree_id;
local pid = hdr$process_id;
local mid = hdr$message_id;
local sid = hdr$session_id;
if ( mid !in smb_state$pending_cmds )
{
local tmp_file = SMB::FileInfo($ts=network_time(), $uid=c$uid, $id=c$id);
local tmp_cmd = SMB::CmdInfo($ts=network_time(), $uid=c$uid, $id=c$id, $version="SMB2", $command = SMB2::commands[hdr$command]);
tmp_cmd$referenced_file = tmp_file;
smb_state$pending_cmds[mid] = tmp_cmd;
}
smb_state$current_cmd = smb_state$pending_cmds[mid];
if ( tid > 0 )
{
if ( smb_state$current_cmd?$referenced_tree )
{
smb_state$tid_map[tid] = smb_state$current_cmd$referenced_tree;
}
else if ( tid !in smb_state$tid_map )
{
local tmp_tree = SMB::TreeInfo($ts=network_time(), $uid=c$uid, $id=c$id);
smb_state$tid_map[tid] = tmp_tree;
}
smb_state$current_cmd$referenced_tree = smb_state$tid_map[tid];
}
else
{
smb_state$current_cmd$referenced_tree = SMB::TreeInfo($ts=network_time(), $uid=c$uid, $id=c$id);
}
smb_state$current_file = smb_state$current_cmd$referenced_file;
smb_state$current_tree = smb_state$current_cmd$referenced_tree;
if ( !is_orig )
{
smb_state$current_cmd$rtt = network_time() - smb_state$current_cmd$ts;
smb_state$current_cmd$status = SMB::statuses[hdr$status]$id;
}
}
event smb2_message(c: connection, hdr: SMB2::Header, is_orig: bool) &priority=-5
{
# Is this a response?
if ( !is_orig )
{
if ( SMB::write_cmd_log &&
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$message_id];
}
}
event smb2_negotiate_request(c: connection, hdr: SMB2::Header, dialects: index_vec) &priority=5
{
c$smb_state$current_cmd$smb2_offered_dialects = dialects;
}
event smb2_negotiate_response(c: connection, hdr: SMB2::Header, response: SMB2::NegotiateResponse) &priority=5
{
if ( c$smb_state$current_cmd?$smb2_offered_dialects )
{
for ( i in c$smb_state$current_cmd$smb2_offered_dialects )
{
if ( response$dialect_revision == c$smb_state$current_cmd$smb2_offered_dialects[i] )
{
c$smb_state$current_cmd$argument = SMB2::dialects[response$dialect_revision];
break;
}
}
delete c$smb_state$current_cmd$smb2_offered_dialects;
}
}
event smb2_negotiate_response(c: connection, hdr: SMB2::Header, response: SMB2::NegotiateResponse) &priority=5
{
if ( SMB::write_cmd_log &&
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
{
c$smb_state$current_tree$path = path;
}
event smb2_tree_connect_response(c: connection, hdr: SMB2::Header, response: SMB2::TreeConnectResponse) &priority=5
{
c$smb_state$current_tree$share_type = SMB2::share_types[response$share_type];
}
event smb2_tree_connect_response(c: connection, hdr: SMB2::Header, response: SMB2::TreeConnectResponse) &priority=-5
{
Log::write(SMB::MAPPING_LOG, c$smb_state$current_tree);
}
event smb2_create_request(c: connection, hdr: SMB2::Header, name: string) &priority=5
{
if ( name == "")
name = "<share_root>";
c$smb_state$current_file$name = name;
switch ( c$smb_state$current_tree$share_type )
{
case "DISK":
c$smb_state$current_file$action = SMB::FILE_OPEN;
break;
case "PIPE":
c$smb_state$current_file$action = SMB::PIPE_OPEN;
break;
case "PRINT":
c$smb_state$current_file$action = SMB::PRINT_OPEN;
break;
default:
#c$smb_state$current_file$action = SMB::UNKNOWN_OPEN;
c$smb_state$current_file$action = SMB::FILE_OPEN;
break;
}
}
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$fid = file_id$persistent+file_id$volatile;
c$smb_state$current_file$size = file_size;
if ( c$smb_state$current_tree?$path )
c$smb_state$current_file$path = c$smb_state$current_tree$path;
# I'm seeing negative data from IPC tree transfers
if ( time_to_double(times$modified) > 0.0 )
c$smb_state$current_file$times = times;
# We can identify the file by its file id now so let's stick it
# in the file map.
c$smb_state$fid_map[file_id$persistent+file_id$volatile] = c$smb_state$current_file;
c$smb_state$current_file = c$smb_state$fid_map[file_id$persistent+file_id$volatile];
SMB::write_file_log(c$smb_state);
}
# This is commented out for now because the message type in the SMB analyzer
# is no where near complete.
#event smb2_set_info_request(c: connection, hdr: SMB2::Header, request: SMB2::SetInfoRequest) &priority=5
# {
# c$smb_state$current_file$size = request$eof;
# }
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_state, file_id$persistent+file_id$volatile);
switch ( c$smb_state$current_tree$share_type )
{
case "DISK":
c$smb_state$current_file$action = SMB::FILE_READ;
break;
case "PIPE":
c$smb_state$current_file$action = SMB::PIPE_READ;
break;
case "PRINT":
c$smb_state$current_file$action = SMB::PRINT_READ;
break;
default:
#c$smb_state$current_file$action = SMB::UNKNOWN_OPEN;
c$smb_state$current_file$action = SMB::FILE_OPEN;
break;
}
}
event smb2_read_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_state, file_id$persistent+file_id$volatile);
switch ( c$smb_state$current_tree$share_type )
{
case "DISK":
c$smb_state$current_file$action = SMB::FILE_WRITE;
break;
case "PIPE":
c$smb_state$current_file$action = SMB::PIPE_WRITE;
break;
case "PRINT":
c$smb_state$current_file$action = SMB::PRINT_WRITE;
break;
default:
#c$smb_state$current_file$action = SMB::UNKNOWN_WRITE;
c$smb_state$current_file$action = SMB::FILE_WRITE;
break;
}
}
event smb2_write_request(c: connection, hdr: SMB2::Header, file_id: SMB2::GUID, offset: count, length: count) &priority=-5
{
}
event smb2_close_request(c: connection, hdr: SMB2::Header, file_id: SMB2::GUID) &priority=5
{
SMB::set_current_file(c$smb_state, file_id$persistent+file_id$volatile);
switch ( c$smb_state$current_tree$share_type )
{
case "DISK":
c$smb_state$current_file$action = SMB::FILE_CLOSE;
break;
case "PIPE":
c$smb_state$current_file$action = SMB::PIPE_CLOSE;
break;
case "PRINT":
c$smb_state$current_file$action = SMB::PRINT_CLOSE;
break;
default:
#c$smb_state$current_file$action = SMB::UNKNOWN_CLOSE;
c$smb_state$current_file$action = SMB::FILE_CLOSE;
break;
}
}
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_state$fid_map )
{
local fl = c$smb_state$fid_map[file_id$persistent+file_id$volatile];
# Need to check for existence of path in case tree connect message wasn't seen.
if ( c$smb_state$current_tree?$path )
fl$path = c$smb_state$current_tree$path;
delete c$smb_state$fid_map[file_id$persistent+file_id$volatile];
SMB::write_file_log(c$smb_state);
}
else
{
# TODO - Determine correct action
# A reporter message is not right...
#Reporter::warning("attempting to close an unknown file!");
}
}

View file

@ -88,3 +88,7 @@
# Uncomment the following line to enable logging of link-layer addresses. Enabling
# this adds the link-layer address for each connection endpoint to the conn.log file.
# @load policy/protocols/conn/mac-logging
# Uncomment the following line to enable the SMB analyzer. The analyzer
# is currently considered a preview and therefore not loaded by default.
# @load policy/protocols/smb

View file

@ -74,6 +74,11 @@
@load protocols/modbus/track-memmap.bro
@load protocols/mysql/software.bro
@load protocols/rdp/indicate_ssl.bro
@load protocols/smb/__load__.bro
@load protocols/smb/files.bro
@load protocols/smb/main.bro
@load protocols/smb/smb1-main.bro
@load protocols/smb/smb2-main.bro
@load protocols/smtp/blocklists.bro
@load protocols/smtp/detect-suspicious-orig.bro
@load protocols/smtp/entities-excerpt.bro

View file

@ -23,6 +23,7 @@ TableType* string_set;
TableType* string_array;
TableType* count_set;
VectorType* string_vec;
VectorType* index_vec;
VectorType* mime_matches;
RecordType* mime_match;
@ -105,13 +106,6 @@ RecordType* pm_callit_request;
RecordType* ntp_msg;
TableVal* samba_cmds;
RecordType* smb_hdr;
RecordType* smb_trans;
RecordType* smb_trans_data;
RecordType* smb_tree_connect;
TableType* smb_negotiate;
RecordType* geo_location;
RecordType* entropy_test_result;
@ -331,6 +325,7 @@ void init_net_var()
string_set = internal_type("string_set")->AsTableType();
string_array = internal_type("string_array")->AsTableType();
string_vec = internal_type("string_vec")->AsVectorType();
index_vec = internal_type("index_vec")->AsVectorType();
mime_match = internal_type("mime_match")->AsRecordType();
mime_matches = internal_type("mime_matches")->AsVectorType();
@ -427,13 +422,6 @@ void init_net_var()
ntp_msg = internal_type("ntp_msg")->AsRecordType();
samba_cmds = internal_val("samba_cmds")->AsTableVal();
smb_hdr = internal_type("smb_hdr")->AsRecordType();
smb_trans = internal_type("smb_trans")->AsRecordType();
smb_trans_data = internal_type("smb_trans_data")->AsRecordType();
smb_tree_connect = internal_type("smb_tree_connect")->AsRecordType();
smb_negotiate = internal_type("smb_negotiate")->AsTableType();
geo_location = internal_type("geo_location")->AsRecordType();
entropy_test_result = internal_type("entropy_test_result")->AsRecordType();

View file

@ -26,6 +26,7 @@ extern TableType* string_set;
extern TableType* string_array;
extern TableType* count_set;
extern VectorType* string_vec;
extern VectorType* index_vec;
extern VectorType* mime_matches;
extern RecordType* mime_match;
@ -108,13 +109,6 @@ extern RecordType* pm_callit_request;
extern RecordType* ntp_msg;
extern TableVal* samba_cmds;
extern RecordType* smb_hdr;
extern RecordType* smb_trans;
extern RecordType* smb_trans_data;
extern RecordType* smb_tree_connect;
extern TableType* smb_negotiate;
extern RecordType* geo_location;
extern RecordType* entropy_test_result;

View file

@ -12,6 +12,7 @@ add_subdirectory(file)
add_subdirectory(finger)
add_subdirectory(ftp)
add_subdirectory(gnutella)
add_subdirectory(gssapi)
add_subdirectory(gtpv1)
add_subdirectory(http)
add_subdirectory(icmp)
@ -26,6 +27,7 @@ add_subdirectory(modbus)
add_subdirectory(mysql)
add_subdirectory(ncp)
add_subdirectory(netbios)
add_subdirectory(ntlm)
add_subdirectory(ntp)
add_subdirectory(pia)
add_subdirectory(pop3)

View file

@ -5,8 +5,14 @@ include_directories(BEFORE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DI
bro_plugin_begin(Bro DCE_RPC)
bro_plugin_cc(DCE_RPC.cc Plugin.cc)
bro_plugin_bif(events.bif)
bro_plugin_pac(dce_rpc.pac dce_rpc-protocol.pac dce_rpc-analyzer.pac epmapper.pac)
bro_plugin_pac(dce_rpc_simple.pac dce_rpc-protocol.pac epmapper.pac)
bro_plugin_bif(types.bif events.bif)
bro_plugin_pac(
dce_rpc.pac
dce_rpc-protocol.pac
dce_rpc-analyzer.pac
dce_rpc-auth.pac
endpoint-atsvc.pac
endpoint-epmapper.pac
)
bro_plugin_end()

View file

@ -9,580 +9,52 @@
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)
DCE_RPC_Analyzer::DCE_RPC_Analyzer(Connection *conn)
: 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));
interp = new binpac::DCE_RPC::DCE_RPC_Conn(this);
}
DCE_RPC_Analyzer::~DCE_RPC_Analyzer()
{
delete session;
delete interp;
}
void DCE_RPC_Analyzer::Done()
{
TCP_ApplicationAnalyzer::Done();
interp->FlowEOF(true);
interp->FlowEOF(false);
}
void DCE_RPC_Analyzer::EndpointEOF(bool is_orig)
{
TCP_ApplicationAnalyzer::EndpointEOF(is_orig);
interp->FlowEOF(is_orig);
}
void DCE_RPC_Analyzer::Undelivered(uint64 seq, int len, bool orig)
{
TCP_ApplicationAnalyzer::Undelivered(seq, len, orig);
interp->NewGap(orig, len);
}
void DCE_RPC_Analyzer::DeliverStream(int len, const u_char* data, bool orig)
{
TCP_ApplicationAnalyzer::DeliverStream(len, data, orig);
assert(TCP());
try
{
interp->NewData(orig, data, data + len);
}
catch ( const binpac::Exception& e )
{
ProtocolViolation(fmt("Binpac exception: %s", e.c_msg()));
}
}

View file

@ -11,12 +11,12 @@
#include "analyzer/protocol/dce-rpc/events.bif.h"
#include "IPAddr.h"
#include "dce_rpc_simple_pac.h"
#include "dce_rpc_pac.h"
namespace analyzer { namespace dce_rpc {
class UUID {
/* class UUID {
public:
UUID();
UUID(const u_char data[16]);
@ -35,7 +35,7 @@ protected:
string s;
};
const char* uuid_to_string(const u_char* uuid_data);
//const char* uuid_to_string(const u_char* uuid_data);
struct dce_rpc_endpoint_addr {
// All fields are in host byteorder.
@ -75,7 +75,7 @@ struct dce_rpc_endpoint_addr {
return string(buf);
}
};
}; */
/*
enum DCE_RPC_PTYPE {
@ -88,6 +88,7 @@ enum DCE_RPC_PTYPE {
};
*/
/*
#define DCE_RPC_HEADER_LENGTH 16
class DCE_RPC_Header {
@ -125,22 +126,22 @@ public:
{ 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 DeliverBind(const binpac::DCE_RPC::DCE_RPC_PDU* pdu);
void DeliverRequest(const binpac::DCE_RPC::DCE_RPC_PDU* pdu);
void DeliverResponse(const binpac::DCE_RPC::DCE_RPC_PDU* pdu);
void DeliverEpmapperRequest(
const binpac::DCE_RPC_Simple::DCE_RPC_PDU* pdu,
const binpac::DCE_RPC_Simple::DCE_RPC_Request* req);
const binpac::DCE_RPC::DCE_RPC_PDU* pdu,
const binpac::DCE_RPC::DCE_RPC_Request* req);
void DeliverEpmapperResponse(
const binpac::DCE_RPC_Simple::DCE_RPC_PDU* pdu,
const binpac::DCE_RPC_Simple::DCE_RPC_Response* resp);
const binpac::DCE_RPC::DCE_RPC_PDU* pdu,
const binpac::DCE_RPC::DCE_RPC_Response* resp);
void DeliverEpmapperMapResponse(
const binpac::DCE_RPC_Simple::DCE_RPC_PDU* pdu,
const binpac::DCE_RPC_Simple::DCE_RPC_Response* resp);
const binpac::DCE_RPC::DCE_RPC_PDU* pdu,
const binpac::DCE_RPC::DCE_RPC_Response* resp);
analyzer::Analyzer* analyzer;
UUID if_uuid;
UUID uuid;
BifEnum::dce_rpc_if_id if_id;
int opnum;
struct {
@ -172,18 +173,26 @@ protected:
DCE_RPC_Session* session;
};
*/
class DCE_RPC_Analyzer : public tcp::TCP_ApplicationAnalyzer {
public:
DCE_RPC_Analyzer(Connection* conn, bool speculative = false);
DCE_RPC_Analyzer(Connection* conn);
~DCE_RPC_Analyzer();
void Done() override;
void DeliverStream(int len, const u_char* data, bool orig) override;
void Undelivered(uint64 seq, int len, bool orig) override;
void EndpointEOF(bool is_orig) override;
bool SetFileID(uint64 fid_in)
{ interp->set_file_id(fid_in); return true; }
static analyzer::Analyzer* Instantiate(Connection* conn)
{ return new DCE_RPC_Analyzer(conn); }
protected:
DCE_RPC_Session* session;
bool speculative;
binpac::DCE_RPC::DCE_RPC_Conn* interp;
};
} } // namespace analyzer::*

View file

@ -13,7 +13,6 @@ public:
plugin::Configuration Configure()
{
AddComponent(new ::analyzer::Component("DCE_RPC", ::analyzer::dce_rpc::DCE_RPC_Analyzer::Instantiate));
AddComponent(new ::analyzer::Component("Contents_DCE_RPC", 0));
plugin::Configuration config;
config.name = "Bro::DCE_RPC";

View file

@ -1,26 +1,19 @@
# DCE/RPC protocol data unit.
type DCE_RPC_PDU = record {
# Set header's byteorder to little-endian (or big-endian) to
# avoid cyclic dependency.
header : DCE_RPC_Header;
frag : bytestring &length = body_length;
auth : DCE_RPC_Auth(header);
} &let {
body_length: int =
header.frag_length - sizeof(header) - header.auth_length;
frag_reassembled: bool =
$context.flow.reassemble_fragment(frag, header.lastfrag);
body: DCE_RPC_Body(header)
withinput $context.flow.reassembled_body()
&if frag_reassembled;
} &byteorder = header.byteorder,
&length = header.frag_length; # length of the PDU
refine connection DCE_RPC_Conn += {
%member{
map<uint16, uint16> cont_id_opnum_map;
uint64 fid;
%}
%init{
fid=0;
%}
connection DCE_RPC_Conn(bro_analyzer: BroAnalyzer) {
upflow = DCE_RPC_Flow(true);
downflow = DCE_RPC_Flow(false);
function set_file_id(fid_in: uint64): bool
%{
fid = fid_in;
return true;
%}
function get_cont_id_opnum_map(cont_id: uint16): uint16
%{
@ -33,102 +26,162 @@ connection DCE_RPC_Conn(bro_analyzer: BroAnalyzer) {
return true;
%}
%member{
map<uint16, uint16> cont_id_opnum_map;
%}
};
flow DCE_RPC_Flow(is_orig: bool) {
flowunit = DCE_RPC_PDU withcontext (connection, this);
%member{
FlowBuffer frag_reassembler_;
%}
# Fragment reassembly.
function reassemble_fragment(frag: bytestring, lastfrag: bool): bool
function proc_dce_rpc_pdu(pdu: DCE_RPC_PDU): bool
%{
int orig_data_length = frag_reassembler_.data_length();
frag_reassembler_.NewData(frag.begin(), frag.end());
int new_frame_length = orig_data_length + frag.length();
if ( orig_data_length == 0 )
frag_reassembler_.NewFrame(new_frame_length, false);
else
frag_reassembler_.GrowFrame(new_frame_length);
return lastfrag;
// If a whole pdu message parsed ok, let's confirm the protocol
bro_analyzer()->ProtocolConfirmation();
return true;
%}
function reassembled_body(): const_bytestring
function proc_dce_rpc_message(header: DCE_RPC_Header): bool
%{
return const_bytestring(
frag_reassembler_.begin(),
frag_reassembler_.end());
if ( dce_rpc_message )
{
BifEvent::generate_dce_rpc_message(bro_analyzer(),
bro_analyzer()->Conn(),
${header.is_orig},
fid,
${header.PTYPE},
new EnumVal(${header.PTYPE}, BifType::Enum::DCE_RPC::PType));
}
return true;
%}
# Bind.
function process_dce_rpc_bind(bind: DCE_RPC_Bind): bool
%{
$const_def{bind_elems = bind.p_context_elem};
if ( ${bind_elems.n_context_elem} > 1 ) {
${connection.bro_analyzer}->Weird(
"DCE_RPC_bind_to_multiple_interfaces");
}
if ( dce_rpc_bind ) {
if ( dce_rpc_bind )
{
// Go over the elements, each having a UUID
for ( int i = 0; i < ${bind_elems.n_context_elem}; ++i ) {
$const_def{if_uuid =
bind_elems.p_cont_elem[i].abstract_syntax.if_uuid};
$const_def{bind_elems = bind.context_list};
for ( int i = 0; i < ${bind_elems.num_contexts}; ++i )
{
if ( ${bind_elems.request_contexts[i].abstract_syntax} )
{
$const_def{uuid = bind_elems.request_contexts[i].abstract_syntax.uuid};
$const_def{ver_major = bind_elems.request_contexts[i].abstract_syntax.ver_major};
$const_def{ver_minor = bind_elems.request_contexts[i].abstract_syntax.ver_minor};
// Queue the event
BifEvent::generate_dce_rpc_bind(
${connection.bro_analyzer},
${connection.bro_analyzer}->Conn(),
bytestring_to_val(${if_uuid}));
// Set the connection's UUID
// ${connection}->set_uuid(${if_uuid});
// Queue the event
BifEvent::generate_dce_rpc_bind(bro_analyzer(),
bro_analyzer()->Conn(),
fid,
bytestring_to_val(${uuid}),
${ver_major},
${ver_minor});
}
}
}
}
return ${bind_elems.n_context_elem} > 0;
return true;
%}
function process_dce_rpc_bind_ack(bind: DCE_RPC_Bind_Ack): bool
%{
if ( dce_rpc_bind_ack )
{
StringVal *sec_addr;
// Remove the null from the end of the string if it's there.
if ( ${bind.sec_addr}.length() > 0 &&
*(${bind.sec_addr}.begin() + ${bind.sec_addr}.length()) == 0 )
{
sec_addr = new StringVal(${bind.sec_addr}.length()-1, (const char*) ${bind.sec_addr}.begin());
}
else
{
sec_addr = new StringVal(${bind.sec_addr}.length(), (const char*) ${bind.sec_addr}.begin());
}
BifEvent::generate_dce_rpc_bind_ack(bro_analyzer(),
bro_analyzer()->Conn(),
fid,
sec_addr);
}
return true;
%}
# Request.
function process_dce_rpc_request(req: DCE_RPC_Request): bool
%{
if ( dce_rpc_request )
{
BifEvent::generate_dce_rpc_request(
${connection.bro_analyzer},
${connection.bro_analyzer}->Conn(),
${req.opnum},
bytestring_to_val(${req.stub}));
BifEvent::generate_dce_rpc_request(bro_analyzer(),
bro_analyzer()->Conn(),
fid,
${req.opnum},
${req.stub}.length());
}
${connection}->set_cont_id_opnum_map(${req.p_cont_id},
${req.opnum});
set_cont_id_opnum_map(${req.context_id},
${req.opnum});
return true;
%}
# Response.
function process_dce_rpc_response(resp: DCE_RPC_Response): bool
%{
if ( dce_rpc_response )
{
BifEvent::generate_dce_rpc_response(
${connection.bro_analyzer},
${connection.bro_analyzer}->Conn(),
${connection}->get_cont_id_opnum_map(${resp.p_cont_id}),
bytestring_to_val(${resp.stub}));
BifEvent::generate_dce_rpc_response(bro_analyzer(),
bro_analyzer()->Conn(),
fid,
get_cont_id_opnum_map(${resp.context_id}),
${resp.stub}.length());
}
return true;
%}
};
refine flow DCE_RPC_Flow += {
#%member{
#FlowBuffer frag_reassembler_;
#%}
# Fragment reassembly.
#function reassemble_fragment(frag: bytestring, lastfrag: bool): bool
# %{
# int orig_data_length = frag_reassembler_.data_length();
#
# frag_reassembler_.NewData(frag.begin(), frag.end());
#
# int new_frame_length = orig_data_length + frag.length();
# if ( orig_data_length == 0 )
# frag_reassembler_.NewFrame(new_frame_length, false);
# else
# frag_reassembler_.GrowFrame(new_frame_length);
#
# return lastfrag;
# %}
#function reassembled_body(): const_bytestring
# %{
# return const_bytestring(
# frag_reassembler_.begin(),
# frag_reassembler_.end());
# %}
};
refine typeattr DCE_RPC_PDU += &let {
proc = $context.connection.proc_dce_rpc_pdu(this);
}
refine typeattr DCE_RPC_Header += &let {
proc = $context.connection.proc_dce_rpc_message(this);
};
refine typeattr DCE_RPC_Bind += &let {
proc = $context.connection.process_dce_rpc_bind(this);
};
refine typeattr DCE_RPC_Bind_Ack += &let {
proc = $context.connection.process_dce_rpc_bind_ack(this);
};
refine typeattr DCE_RPC_Request += &let {
proc = $context.connection.process_dce_rpc_request(this);
};
refine typeattr DCE_RPC_Response += &let {
proc = $context.connection.process_dce_rpc_response(this);
};

View file

@ -0,0 +1,50 @@
%extern{
#include "analyzer/Manager.h"
%}
refine connection DCE_RPC_Conn += {
%member{
analyzer::Analyzer *gssapi;
analyzer::Analyzer *ntlm;
%}
%init{
ntlm = 0;
gssapi = 0;
%}
%cleanup{
if ( gssapi )
delete gssapi;
if ( ntlm )
delete ntlm;
%}
function forward_auth(auth: DCE_RPC_Auth, is_orig: bool): bool
%{
switch ( ${auth.type} )
{
case 0x09:
if ( ! gssapi )
gssapi = analyzer_mgr->InstantiateAnalyzer("KRB", bro_analyzer()->Conn());
if ( gssapi )
gssapi->DeliverStream(${auth.blob}.length(), ${auth.blob}.begin(), is_orig);
break;
case 0x0a:
if ( ! ntlm )
ntlm = analyzer_mgr->InstantiateAnalyzer("NTLM", bro_analyzer()->Conn());
if ( ntlm )
ntlm->DeliverStream(${auth.blob}.length(), ${auth.blob}.begin(), is_orig);
break;
default:
bro_analyzer()->Weird(fmt("unknown_dce_rpc_auth_type_%d",${auth.type}));
break;
}
return true;
%}
};
refine typeattr DCE_RPC_Auth += &let {
proc = $context.connection.forward_auth(this, true);
}

View file

@ -25,101 +25,151 @@ enum dce_rpc_ptype {
type uuid = bytestring &length = 16;
type context_handle = record {
cxt_attributes: uint32;
cxt_uuid: uuid;
attrs : uint32;
uuid : bytestring &length = 16;
};
type rpc_if_id_t = record {
if_uuid : uuid;
vers_major : uint16;
vers_minor : uint16;
};
type DCE_RPC_PDU(is_orig: bool) = record {
# Set header's byteorder to little-endian (or big-endian) to
# avoid cyclic dependency.
header : DCE_RPC_Header(is_orig);
# TODO: bring back reassembly. It was having trouble.
#frag : bytestring &length = body_length;
body : DCE_RPC_Body(header);
auth : DCE_RPC_Auth_wrapper(header);
} &let {
#body_length : int = header.frag_length - sizeof(header) - header.auth_length;
#frag_reassembled : bool = $context.flow.reassemble_fragment(frag, header.lastfrag);
#body : DCE_RPC_Body(header)
# withinput $context.flow.reassembled_body()
# &if frag_reassembled;
} &byteorder = header.byteorder, &length = header.frag_length;
type NDR_Format = record {
intchar : uint8;
floatspec : uint8;
reserved : padding[2];
intchar : uint8;
floatspec : uint8;
reserved : padding[2];
} &let {
byteorder = (intchar >> 4) ? littleendian : bigendian;
};
#### There might be a endianness problem here: the frag_length
# There might be a endianness problem here: the frag_length
# causes problems despite the NDR_Format having a byteorder set.
type DCE_RPC_Header = record {
rpc_vers : uint8 &check(rpc_vers == 5);
rpc_vers_minor : uint8;
PTYPE : uint8;
pfc_flags : uint8;
packed_drep : NDR_Format;
frag_length : uint16;
auth_length : uint16;
call_id : uint32;
type DCE_RPC_Header(is_orig: bool) = record {
rpc_vers : uint8 &check(rpc_vers == 5);
rpc_vers_minor : uint8;
PTYPE : uint8;
pfc_flags : uint8;
packed_drep : NDR_Format;
frag_length : uint16;
auth_length : uint16;
call_id : uint32;
} &let {
frag = pfc_flags & 4;
lastfrag = (! frag) || (pfc_flags & 2);
} &byteorder = packed_drep.byteorder;
type p_context_id_t = uint16;
type p_syntax_id_t = record {
if_uuid : uuid;
if_version : uint32;
type Syntax = record {
uuid : bytestring &length = 16;
ver_major : uint16;
ver_minor : uint16;
};
type p_cont_elem_t = record {
p_cont_id : p_context_id_t;
n_transfer_syn : uint8;
reserved : padding[1];
abstract_syntax : p_syntax_id_t;
transfer_syntaxes : p_syntax_id_t[n_transfer_syn];
type ContextRequest = record {
id : uint16;
num_syntaxes : uint8;
reserved : padding[1];
abstract_syntax : Syntax;
transfer_syntaxes : Syntax[num_syntaxes];
};
type p_cont_list_t = record {
n_context_elem : uint8;
reserved : padding[3];
p_cont_elem : p_cont_elem_t[n_context_elem];
type ContextReply = record {
ack_result : uint16;
ack_reason : uint16;
syntax : Syntax;
};
type ContextList(is_request: bool) = record {
num_contexts : uint8;
reserved : padding[3];
req_reply : case is_request of {
true -> request_contexts : ContextRequest[num_contexts];
false -> reply_contexts : ContextReply[num_contexts];
};
};
type DCE_RPC_Bind = record {
max_xmit_frag : uint16;
max_recv_frag : uint16;
assoc_group_id : uint32;
p_context_elem : p_cont_list_t;
max_xmit_frag : uint16;
max_recv_frag : uint16;
assoc_group_id : uint32;
context_list : ContextList(1);
};
type DCE_RPC_AlterContext = record {
max_xmit_frag : uint16;
max_recv_frag : uint16;
assoc_group_id : uint32;
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;
contexts : ContextList(0);
};
type DCE_RPC_Request = record {
alloc_hint : uint32;
p_cont_id : p_context_id_t;
opnum : uint16;
# object : uuid;
# stub_pad_0 : padding align 8;
stub : bytestring &restofdata;
alloc_hint : uint32;
context_id : uint16;
opnum : uint16;
# object : uuid;
# stub_pad_0 : padding align 8;
stub : bytestring &restofdata;
};
type DCE_RPC_Response = record {
alloc_hint : uint32;
p_cont_id : p_context_id_t;
cancel_count : uint8;
reserved : uint8;
# stub_pad_0 : padding align 8;
stub : bytestring &restofdata;
alloc_hint : uint32;
context_id : uint16;
cancel_count : uint8;
reserved : uint8;
# stub_pad_0 : padding align 8;
stub : bytestring &restofdata;
};
type DCE_RPC_AlterContext = record {
max_xmit_frag : uint16;
max_recv_frag : uint16;
assoc_group_id : uint32;
contexts : ContextList(0);
};
type DCE_RPC_AlterContext_Resp = record {
max_xmit_frag : uint16;
max_recv_frag : uint16;
assoc_group_id : uint32;
sec_addr_len : uint16;
contexts : ContextList(0);
};
type DCE_RPC_Body(header: DCE_RPC_Header) = case header.PTYPE of {
DCE_RPC_BIND -> bind : DCE_RPC_Bind;
DCE_RPC_REQUEST -> request : DCE_RPC_Request;
DCE_RPC_RESPONSE -> response : DCE_RPC_Response;
default -> other : bytestring &restofdata;
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_RESPONSE -> response : DCE_RPC_Response;
# TODO: Something about the two following structures isn't being handled correctly.
#DCE_RPC_ALTER_CONTEXT -> alter_context : DCE_RPC_AlterContext;
#DCE_RPC_ALTER_CONTEXT_RESP -> alter_resp : DCE_RPC_AlterContext_Resp;
default -> other : bytestring &restofdata;
} &length=header.frag_length - 16 - header.auth_length - (header.auth_length==0 ? 0 : 8);
type DCE_RPC_Auth_wrapper(header: DCE_RPC_Header) = case header.auth_length of {
0 -> none : empty;
default -> auth : DCE_RPC_Auth(header);
};
type DCE_RPC_Auth(header: DCE_RPC_Header) = uint8[header.auth_length];
%include epmapper.pac
type DCE_RPC_Auth(header: DCE_RPC_Header) = record {
type : uint8;
level : uint8;
pad_len : uint8;
reserved : uint8;
context_id : uint32;
blob : bytestring &length=header.auth_length;
};

View file

@ -2,13 +2,27 @@
%include bro.pac
%extern{
#include "types.bif.h"
#include "events.bif.h"
%}
analyzer DCE_RPC withcontext {
connection: DCE_RPC_Conn;
flow: DCE_RPC_Flow;
connection : DCE_RPC_Conn;
flow : DCE_RPC_Flow;
};
connection DCE_RPC_Conn(bro_analyzer: BroAnalyzer) {
upflow = DCE_RPC_Flow(true);
downflow = DCE_RPC_Flow(false);
};
%include dce_rpc-protocol.pac
# Now we define the flow:
flow DCE_RPC_Flow(is_orig: bool) {
flowunit = DCE_RPC_PDU(is_orig) withcontext(connection, this);
};
%include endpoint-atsvc.pac
%include endpoint-epmapper.pac
%include dce_rpc-analyzer.pac
%include dce_rpc-auth.pac

View file

@ -1,20 +0,0 @@
%include bro.pac
%extern{
#include "events.bif.h"
%}
analyzer DCE_RPC_Simple withcontext {};
%include dce_rpc-protocol.pac
type DCE_RPC_PDU = record {
# Set header's byteorder to little-endian (or big-endian) to
# avoid cyclic dependency.
header : DCE_RPC_Header;
body : DCE_RPC_Body(header)
&length = header.frag_length - sizeof(header) -
header.auth_length;
auth : DCE_RPC_Auth(header);
} &byteorder = header.byteorder,
&length = header.frag_length;

View file

@ -0,0 +1,39 @@
type ATSVC_Request(unicode: bool, opnum: uint8) = record {
empty: padding[1];
op: case opnum of {
0 -> add : ATSVC_NetrJobAdd(unicode);
default -> unknown : bytestring &restofdata;
};
};
type ATSVC_String_Pointer(unicode: bool) = record {
referent_id : uint32;
max_count : uint32;
offset : uint32;
actual_count : uint32;
string : bytestring &length=max_count;
};
type ATSVC_NetrJobAdd(unicode: bool) = record {
server : ATSVC_String_Pointer(unicode);
unknown : padding[2];
job_time : uint32;
days_of_month : uint32;
days_of_week : uint8;
flags : uint8;
unknown2 : padding[2];
command : ATSVC_String_Pointer(unicode);
};
type ATSVC_Reply(unicode: bool, opnum: uint16) = record {
op: case opnum of {
0 -> add: ATSVC_JobID(unicode);
default -> unknown: bytestring &restofdata;
};
};
type ATSVC_JobID(unicode: bool) = record {
id : uint32;
status : uint32;
};

View file

@ -1,55 +1,74 @@
## TODO.
## Generated for every DCE-RPC message.
##
## .. bro:see:: rpc_call rpc_dialogue rpc_reply dce_rpc_bind dce_rpc_request
## dce_rpc_response rpc_timeout
## c: The connection.
##
## .. todo:: Bro's current default configuration does not activate the protocol
## analyzer that generates this event; the corresponding script has not yet
## been ported to Bro 2.x. To still enable this event, one needs to
## register a port for it or add a DPD payload signature.
event dce_rpc_message%(c: connection, is_orig: bool, ptype: dce_rpc_ptype, msg: string%);
## is_orig: True if the message was sent by the originator of the TCP connection.
##
## fid: File ID of the PIPE that carried the DCE-RPC message. Zero will be used if the
## DCE-RPC was not transported over a pipe.
##
## ptype_id: Numeric representation of the procedure type of the message.
##
## ptype: Enum representation of the prodecure type of the message.
##
## .. bro:see:: dce_rpc_bind dce_rpc_bind_ack dce_rpc_request dce_rpc_response
event dce_rpc_message%(c: connection, is_orig: bool, fid: count, ptype_id: count, ptype: DCE_RPC::PType%);
## TODO.
## Generated for every DCE-RPC bind request message. Since RPC offers the ability
## for a client to request connections to multiple endpoints, this event can occur
## multiple times for a single RPC message.
##
## .. bro:see:: rpc_call rpc_dialogue rpc_reply dce_rpc_message dce_rpc_request
## dce_rpc_response rpc_timeout
## c: The connection.
##
## .. todo:: Bro's current default configuration does not activate the protocol
## analyzer that generates this event; the corresponding script has not yet
## been ported to Bro 2.x. To still enable this event, one needs to
## register a port for it or add a DPD payload signature.
event dce_rpc_bind%(c: connection, uuid: string%);
## fid: File ID of the PIPE that carried the DCE-RPC message. Zero will be used if the
## DCE-RPC was not transported over a pipe.
##
## uuid: The string interpretted uuid of the endpoint being requested.
##
## ver_major: The major version of the endpoint being requested.
##
## ver_minor: The minor version of the endpoint being requested.
##
## .. bro:see:: dce_rpc_message dce_rpc_bind_ack dce_rpc_request dce_rpc_response
event dce_rpc_bind%(c: connection, fid: count, uuid: string, ver_major: count, ver_minor: count%);
## TODO.
## Generated for every DCE-RPC bind request ack message.
##
## .. bro:see:: rpc_call rpc_dialogue rpc_reply dce_rpc_bind dce_rpc_message
## dce_rpc_response rpc_timeout
## c: The connection.
##
## .. todo:: Bro's current default configuration does not activate the protocol
## analyzer that generates this event; the corresponding script has not yet
## been ported to Bro 2.x. To still enable this event, one needs to
## register a port for it or add a DPD payload signature.
event dce_rpc_request%(c: connection, opnum: count, stub: string%);
## fid: File ID of the PIPE that carried the DCE-RPC message. Zero will be used if the
## DCE-RPC was not transported over a pipe.
##
## sec_addr: Secondary address for the ack.
##
## .. bro:see:: dce_rpc_message dce_rpc_bind dce_rpc_request dce_rpc_response
event dce_rpc_bind_ack%(c: connection, fid: count, sec_addr: string%);
## TODO.
## Generated for every DCE-RPC request message.
##
## .. bro:see:: rpc_call rpc_dialogue rpc_reply dce_rpc_bind dce_rpc_message
## dce_rpc_request rpc_timeout
## c: The connection.
##
## .. todo:: Bro's current default configuration does not activate the protocol
## analyzer that generates this event; the corresponding script has not yet
## been ported to Bro 2.x. To still enable this event, one needs to
## register a port for it or add a DPD payload signature.
event dce_rpc_response%(c: connection, opnum: count, stub: string%);
## fid: File ID of the PIPE that carried the DCE-RPC message. Zero will be used if the
## DCE-RPC was not transported over a pipe.
##
## opnum: Number of the RPC operation.
##
## stub_len: Length of the data for the request.
##
## .. bro:see:: dce_rpc_message dce_rpc_bind dce_rpc_bind_ack dce_rpc_response
event dce_rpc_request%(c: connection, fid: count, opnum: count, stub_len: count%);
## TODO.
## Generated for every DCE-RPC response message.
##
## .. bro:see:: rpc_call rpc_dialogue rpc_reply dce_rpc_bind dce_rpc_message
## dce_rpc_request dce_rpc_response rpc_timeout
## c: The connection.
##
## .. todo:: Bro's current default configuration does not activate the protocol
## analyzer that generates this event; the corresponding script has not yet
## been ported to Bro 2.x. To still enable this event, one needs to
## register a port for it or add a DPD payload signature.
event epm_map_response%(c: connection, uuid: string, p: port, h: addr%);
## fid: File ID of the PIPE that carried the DCE-RPC message. Zero will be used if the
## DCE-RPC was not transported over a pipe.
##
## opnum: Number of the RPC operation.
##
## stub_len: Length of the data for the response.
##
## .. bro:see:: dce_rpc_message dce_rpc_bind dce_rpc_bind_ack dce_rpc_request
event dce_rpc_response%(c: connection, fid: count, opnum: count, stub_len: count%);

View file

@ -0,0 +1,41 @@
module DCE_RPC;
enum PType %{
REQUEST,
PING,
RESPONSE,
FAULT,
WORKING,
NOCALL,
REJECT,
ACK,
CL_CANCEL,
FACK,
CANCEL_ACK,
BIND,
BIND_ACK,
BIND_NAK,
ALTER_CONTEXT,
ALTER_CONTEXT_RESP,
SHUTDOWN,
CO_CANCEL,
ORPHANED,
%}
enum IfID %{
unknown_if,
epmapper,
lsarpc,
lsa_ds,
mgmt,
netlogon,
samr,
srvsvc,
spoolss,
drs,
winspipe,
wkssvc,
oxid,
ISCMActivator,
%}

View file

@ -0,0 +1,16 @@
include(BroPlugin)
include_directories(BEFORE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR})
bro_plugin_begin(Bro GSSAPI)
bro_plugin_cc(GSSAPI.cc Plugin.cc)
bro_plugin_bif(types.bif events.bif)
bro_plugin_pac(
gssapi.pac
gssapi-protocol.pac
gssapi-analyzer.pac
../asn1/asn1.pac
)
bro_plugin_end()

View file

@ -0,0 +1,56 @@
// See the file "COPYING" in the main distribution directory for copyright.
#include "GSSAPI.h"
#include "analyzer/protocol/tcp/TCP_Reassembler.h"
#include "Reporter.h"
#include "events.bif.h"
using namespace analyzer::gssapi;
GSSAPI_Analyzer::GSSAPI_Analyzer(Connection* c)
: tcp::TCP_ApplicationAnalyzer("GSSAPI", c)
{
interp = new binpac::GSSAPI::GSSAPI_Conn(this);
}
GSSAPI_Analyzer::~GSSAPI_Analyzer()
{
delete interp;
}
void GSSAPI_Analyzer::Done()
{
tcp::TCP_ApplicationAnalyzer::Done();
interp->FlowEOF(true);
interp->FlowEOF(false);
}
void GSSAPI_Analyzer::EndpointEOF(bool is_orig)
{
tcp::TCP_ApplicationAnalyzer::EndpointEOF(is_orig);
interp->FlowEOF(is_orig);
}
void GSSAPI_Analyzer::DeliverStream(int len, const u_char* data, bool orig)
{
tcp::TCP_ApplicationAnalyzer::DeliverStream(len, data, orig);
assert(TCP());
try
{
interp->NewData(orig, data, data + len);
ProtocolConfirmation();
}
catch ( const binpac::Exception& e )
{
ProtocolViolation(fmt("Binpac exception: %s", e.c_msg()));
}
}
void GSSAPI_Analyzer::Undelivered(uint64 seq, int len, bool orig)
{
tcp::TCP_ApplicationAnalyzer::Undelivered(seq, len, orig);
interp->NewGap(orig, len);
}

View file

@ -0,0 +1,39 @@
// See the file "COPYING" in the main distribution directory for copyright.
#ifndef ANALYZER_PROTOCOL_GSSAPI_GSSAPI_H
#define ANALYZER_PROTOCOL_GSSAPI_GSSAPI_H
#include "events.bif.h"
#include "analyzer/protocol/tcp/TCP.h"
#include "gssapi_pac.h"
namespace analyzer { namespace gssapi {
class GSSAPI_Analyzer
: public tcp::TCP_ApplicationAnalyzer {
public:
GSSAPI_Analyzer(Connection* conn);
virtual ~GSSAPI_Analyzer();
// Overriden from Analyzer.
void Done() override;
void DeliverStream(int len, const u_char* data, bool orig) override;
void Undelivered(uint64 seq, int len, bool orig) override;
// Overriden from tcp::TCP_ApplicationAnalyzer.
void EndpointEOF(bool is_orig) override;
static analyzer::Analyzer* Instantiate(Connection* conn)
{ return new GSSAPI_Analyzer(conn); }
protected:
binpac::GSSAPI::GSSAPI_Conn* interp;
};
} } // namespace analyzer::*
#endif

View file

@ -0,0 +1,24 @@
// See the file in the main distribution directory for copyright.
#include "plugin/Plugin.h"
#include "GSSAPI.h"
namespace plugin {
namespace Bro_GSSAPI {
class Plugin : public plugin::Plugin {
public:
plugin::Configuration Configure()
{
AddComponent(new ::analyzer::Component("GSSAPI", ::analyzer::gssapi::GSSAPI_Analyzer::Instantiate));
plugin::Configuration config;
config.name = "Bro::GSSAPI";
config.description = "GSSAPI analyzer";
return config;
}
} plugin;
}
}

View file

@ -0,0 +1,7 @@
## Generated for GSSAPI negotiation results.
##
## c: The connection.
##
## state: The resulting state of the negotiation.
##
event gssapi_neg_result%(c: connection, state: count%);

View file

@ -0,0 +1,46 @@
refine connection GSSAPI_Conn += {
%member{
analyzer::Analyzer *ntlm;
%}
%init{
ntlm = analyzer_mgr->InstantiateAnalyzer("NTLM", bro_analyzer->Conn());
%}
%cleanup{
if ( ntlm )
{
ntlm->Done();
delete ntlm;
}
%}
function forward_ntlm(data: bytestring, is_orig: bool): bool
%{
if ( ntlm )
ntlm->DeliverStream(${data}.length(), ${data}.begin(), is_orig);
return true;
%}
function proc_gssapi_neg_result(val: GSSAPI_NEG_TOKEN_RESP_Arg): bool
%{
if ( gssapi_neg_result )
{
BifEvent::generate_gssapi_neg_result(bro_analyzer(),
bro_analyzer()->Conn(),
binary_to_int64(${val.neg_state.encoding.content}));
}
return true;
%}
}
refine typeattr GSSAPI_NEG_TOKEN_MECH_TOKEN += &let {
fwd: bool = $context.connection.forward_ntlm(mech_token, is_orig);
};
refine typeattr GSSAPI_NEG_TOKEN_RESP_Arg += &let {
proc: bool = $context.connection.proc_gssapi_neg_result(this) &if(seq_meta.index==0);
};

View file

@ -0,0 +1,55 @@
type GSSAPI_NEG_TOKEN(is_orig: bool) = record {
wrapper : ASN1EncodingMeta;
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;
false -> resp : GSSAPI_NEG_TOKEN_RESP;
};
} &let {
is_init: bool = wrapper.tag == 0x60;
} &byteorder=littleendian;
type GSSAPI_NEG_TOKEN_INIT = record {
seq_meta : ASN1EncodingMeta;
args : GSSAPI_NEG_TOKEN_INIT_Arg[];
};
type GSSAPI_NEG_TOKEN_INIT_Arg = record {
seq_meta : ASN1EncodingMeta;
args : GSSAPI_NEG_TOKEN_INIT_Arg_Data(seq_meta.index) &length=seq_meta.length;
};
type GSSAPI_NEG_TOKEN_INIT_Arg_Data(index: uint8) = case index of {
0 -> mech_type_list : ASN1Encoding;
1 -> req_flags : ASN1Encoding;
2 -> mech_token : GSSAPI_NEG_TOKEN_MECH_TOKEN(true);
3 -> mech_list_mic : ASN1OctetString;
};
type GSSAPI_NEG_TOKEN_RESP = record {
seq_meta : ASN1EncodingMeta;
args : GSSAPI_NEG_TOKEN_RESP_Arg[];
};
type GSSAPI_NEG_TOKEN_RESP_Arg = record {
seq_meta : ASN1EncodingMeta;
args : case seq_meta.index of {
0 -> neg_state : ASN1Integer;
1 -> supported_mech : ASN1Encoding;
2 -> response_token : GSSAPI_NEG_TOKEN_MECH_TOKEN(false);
3 -> mech_list_mic : ASN1OctetString;
} &length=seq_meta.length;
};
type GSSAPI_NEG_TOKEN_MECH_TOKEN(is_orig: bool) = record {
meta : ASN1EncodingMeta;
mech_token : bytestring &length=meta.length;
};

View file

@ -0,0 +1,30 @@
%include binpac.pac
%include bro.pac
%extern{
#include "analyzer/Manager.h"
#include "analyzer/Analyzer.h"
#include "types.bif.h"
#include "events.bif.h"
%}
analyzer GSSAPI withcontext {
connection : GSSAPI_Conn;
flow : GSSAPI_Flow;
};
connection GSSAPI_Conn(bro_analyzer: BroAnalyzer) {
upflow = GSSAPI_Flow(true);
downflow = GSSAPI_Flow(false);
};
%include gssapi-protocol.pac
%include ../asn1/asn1.pac
# Now we define the flow:
flow GSSAPI_Flow(is_orig: bool) {
datagram = GSSAPI_NEG_TOKEN(is_orig) withcontext(connection, this);
};
%include gssapi-analyzer.pac

View file

View file

@ -47,11 +47,10 @@ NetbiosDGM_RawMsgHdr::NetbiosDGM_RawMsgHdr(const u_char*& data, int& len)
}
NetbiosSSN_Interpreter::NetbiosSSN_Interpreter(analyzer::Analyzer* arg_analyzer,
smb::SMB_Session* arg_smb_session)
NetbiosSSN_Interpreter::NetbiosSSN_Interpreter(Analyzer* arg_analyzer)
{
analyzer = arg_analyzer;
smb_session = arg_smb_session;
//smb_session = arg_smb_session;
}
int NetbiosSSN_Interpreter::ParseMessage(unsigned int type, unsigned int flags,
@ -106,11 +105,11 @@ int NetbiosSSN_Interpreter::ParseMessage(unsigned int type, unsigned int flags,
int NetbiosSSN_Interpreter::ParseDatagram(const u_char* data, int len,
int is_query)
{
if ( smb_session )
{
smb_session->Deliver(is_query, len, data);
return 0;
}
//if ( smb_session )
// {
// smb_session->Deliver(is_query, len, data);
// return 0;
// }
return 0;
}
@ -132,8 +131,8 @@ int NetbiosSSN_Interpreter::ParseBroadcast(const u_char* data, int len,
delete srcname;
delete dstname;
if ( smb_session )
smb_session->Deliver(is_query, len, data);
//if ( smb_session )
// smb_session->Deliver(is_query, len, data);
return 0;
}
@ -188,12 +187,12 @@ int NetbiosSSN_Interpreter::ParseSessionMsg(const u_char* data, int len,
return 0;
}
if ( smb_session )
{
smb_session->Deliver(is_query, len, data);
return 0;
}
else
//if ( smb_session )
// {
// smb_session->Deliver(is_query, len, data);
// return 0;
// }
//else
{
analyzer->Weird("no_smb_session_using_parsesambamsg");
data += 4;
@ -458,8 +457,8 @@ void Contents_NetbiosSSN::DeliverStream(int len, const u_char* data, bool orig)
NetbiosSSN_Analyzer::NetbiosSSN_Analyzer(Connection* conn)
: tcp::TCP_ApplicationAnalyzer("NETBIOS", conn)
{
smb_session = new smb::SMB_Session(this);
interp = new NetbiosSSN_Interpreter(this, smb_session);
//smb_session = new SMB_Session(this);
interp = new NetbiosSSN_Interpreter(this);
orig_netbios = resp_netbios = 0;
did_session_done = 0;
@ -481,7 +480,7 @@ NetbiosSSN_Analyzer::NetbiosSSN_Analyzer(Connection* conn)
NetbiosSSN_Analyzer::~NetbiosSSN_Analyzer()
{
delete interp;
delete smb_session;
//delete smb_session;
}
void NetbiosSSN_Analyzer::Done()

View file

@ -5,7 +5,7 @@
#include "analyzer/protocol/udp/UDP.h"
#include "analyzer/protocol/tcp/TCP.h"
#include "analyzer/protocol/smb/SMB.h"
//#include "analyzer/protocol/smb/SMB.h"
namespace analyzer { namespace netbios_ssn {
@ -64,7 +64,7 @@ struct NetbiosDGM_RawMsgHdr {
class NetbiosSSN_Interpreter {
public:
NetbiosSSN_Interpreter(analyzer::Analyzer* analyzer, smb::SMB_Session* smb_session);
NetbiosSSN_Interpreter(Analyzer* analyzer);
int ParseMessage(unsigned int type, unsigned int flags,
const u_char* data, int len, int is_query);
@ -99,8 +99,8 @@ protected:
u_char*& xname, int& xlen);
protected:
analyzer::Analyzer* analyzer;
smb::SMB_Session* smb_session;
Analyzer* analyzer;
//SMB_Session* smb_session;
};
@ -124,7 +124,7 @@ public:
NetbiosSSN_State State() const { return state; }
protected:
virtual void DeliverStream(int len, const u_char* data, bool orig);
void DeliverStream(int len, const u_char* data, bool orig) override;
NetbiosSSN_Interpreter* interp;
@ -144,22 +144,22 @@ public:
NetbiosSSN_Analyzer(Connection* conn);
~NetbiosSSN_Analyzer();
virtual void Done();
virtual void DeliverPacket(int len, const u_char* data, bool orig,
uint64 seq, const IP_Hdr* ip, int caplen);
void Done() override;
void DeliverPacket(int len, const u_char* data, bool orig,
uint64 seq, const IP_Hdr* ip, int caplen) override;
static analyzer::Analyzer* Instantiate(Connection* conn)
{ return new NetbiosSSN_Analyzer(conn); }
protected:
virtual void ConnectionClosed(tcp::TCP_Endpoint* endpoint,
tcp::TCP_Endpoint* peer, int gen_event);
virtual void EndpointEOF(bool is_orig);
void ConnectionClosed(tcp::TCP_Endpoint* endpoint,
tcp::TCP_Endpoint* peer, int gen_event) override;
void EndpointEOF(bool is_orig) override;
void ExpireTimer(double t);
NetbiosSSN_Interpreter* interp;
smb::SMB_Session* smb_session;
//SMB_Session* smb_session;
Contents_NetbiosSSN* orig_netbios;
Contents_NetbiosSSN* resp_netbios;
int did_session_done;

View file

@ -0,0 +1,15 @@
include(BroPlugin)
include_directories(BEFORE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR})
bro_plugin_begin(Bro NTLM)
bro_plugin_cc(NTLM.cc Plugin.cc)
bro_plugin_bif(types.bif events.bif)
bro_plugin_pac(
ntlm.pac
ntlm-protocol.pac
ntlm-analyzer.pac
)
bro_plugin_end()

View file

@ -0,0 +1,56 @@
// See the file "COPYING" in the main distribution directory for copyright.
#include "NTLM.h"
#include "analyzer/protocol/tcp/TCP_Reassembler.h"
#include "Reporter.h"
#include "events.bif.h"
using namespace analyzer::ntlm;
NTLM_Analyzer::NTLM_Analyzer(Connection* c)
: tcp::TCP_ApplicationAnalyzer("NTLM", c)
{
interp = new binpac::NTLM::NTLM_Conn(this);
}
NTLM_Analyzer::~NTLM_Analyzer()
{
delete interp;
}
void NTLM_Analyzer::Done()
{
tcp::TCP_ApplicationAnalyzer::Done();
interp->FlowEOF(true);
interp->FlowEOF(false);
}
void NTLM_Analyzer::EndpointEOF(bool is_orig)
{
tcp::TCP_ApplicationAnalyzer::EndpointEOF(is_orig);
interp->FlowEOF(is_orig);
}
void NTLM_Analyzer::DeliverStream(int len, const u_char* data, bool orig)
{
tcp::TCP_ApplicationAnalyzer::DeliverStream(len, data, orig);
assert(TCP());
try
{
interp->NewData(orig, data, data + len);
ProtocolConfirmation();
}
catch ( const binpac::Exception& e )
{
ProtocolViolation(fmt("Binpac exception: %s", e.c_msg()));
}
}
void NTLM_Analyzer::Undelivered(uint64 seq, int len, bool orig)
{
tcp::TCP_ApplicationAnalyzer::Undelivered(seq, len, orig);
interp->NewGap(orig, len);
}

View file

@ -0,0 +1,39 @@
// See the file "COPYING" in the main distribution directory for copyright.
#ifndef ANALYZER_PROTOCOL_NTLM_NTLM_H
#define ANALYZER_PROTOCOL_NTLM_NTLM_H
#include "events.bif.h"
#include "analyzer/protocol/tcp/TCP.h"
#include "ntlm_pac.h"
namespace analyzer { namespace ntlm {
class NTLM_Analyzer
: public tcp::TCP_ApplicationAnalyzer {
public:
NTLM_Analyzer(Connection* conn);
virtual ~NTLM_Analyzer();
// Overriden from Analyzer.
void Done() override;
void DeliverStream(int len, const u_char* data, bool orig) override;
void Undelivered(uint64 seq, int len, bool orig) override;
// Overriden from tcp::TCP_ApplicationAnalyzer.
void EndpointEOF(bool is_orig) override;
static analyzer::Analyzer* Instantiate(Connection* conn)
{ return new NTLM_Analyzer(conn); }
protected:
binpac::NTLM::NTLM_Conn* interp;
};
} } // namespace analyzer::*
#endif

View file

@ -0,0 +1,24 @@
// See the file in the main distribution directory for copyright.
#include "plugin/Plugin.h"
#include "NTLM.h"
namespace plugin {
namespace Bro_NTLM {
class Plugin : public plugin::Plugin {
public:
plugin::Configuration Configure()
{
AddComponent(new ::analyzer::Component("NTLM", ::analyzer::ntlm::NTLM_Analyzer::Instantiate));
plugin::Configuration config;
config.name = "Bro::NTLM";
config.description = "NTLM analyzer";
return config;
}
} plugin;
}
}

View file

@ -0,0 +1,23 @@
## Generated for NTLM messages of type *negotiate*.
##
## c: The connection.
##
## negotiate: The parsed data of the NTLM message. See init-bare for more details.
##
event ntlm_negotiate%(c: connection, negotiate: NTLM::Negotiate%);
## Generated for NTLM messages of type *challenge*.
##
## c: The connection.
##
## negotiate: The parsed data of the NTLM message. See init-bare for more details.
##
event ntlm_challenge%(c: connection, challenge: NTLM::Challenge%);
## Generated for NTLM messages of type *authenticate*.
##
## c: The connection.
##
## request: The parsed data of the NTLM message. See init-bare for more details.
##
event ntlm_authenticate%(c: connection, request: NTLM::Authenticate%);

View file

@ -0,0 +1,172 @@
refine connection NTLM_Conn += {
# This is replicated from the SMB analyzer. :(
function filetime2brotime(ts: uint64): Val
%{
double secs = (ts / 10000000.0);
// Bro can't support times back to the 1600's
// so we subtract a lot of seconds.
Val* bro_ts = new Val(secs - 11644473600.0, TYPE_TIME);
return bro_ts;
%}
function build_version_record(val: NTLM_Version): BroVal
%{
RecordVal* result = new RecordVal(BifType::Record::NTLM::Version);
result->Assign(0, new Val(${val.major_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(3, new Val(${val.ntlm_revision}, TYPE_COUNT));
return result;
%}
function build_av_record(val: NTLM_AV_Pair_Sequence): BroVal
%{
RecordVal* result = new RecordVal(BifType::Record::NTLM::AVs);
for ( uint i = 0; ${val.pairs[i].id} != 0; i++ )
{
switch ( ${val.pairs[i].id} )
{
case 1:
result->Assign(0, utf16_bytestring_to_utf8_val(${val.pairs[i].nb_computer_name.data}));
break;
case 2:
result->Assign(1, utf16_bytestring_to_utf8_val(${val.pairs[i].nb_domain_name.data}));
break;
case 3:
result->Assign(2, utf16_bytestring_to_utf8_val(${val.pairs[i].dns_computer_name.data}));
break;
case 4:
result->Assign(3, utf16_bytestring_to_utf8_val(${val.pairs[i].dns_domain_name.data}));
break;
case 5:
result->Assign(4, utf16_bytestring_to_utf8_val(${val.pairs[i].dns_tree_name.data}));
break;
case 6:
result->Assign(5, new Val(${val.pairs[i].constrained_auth}, TYPE_BOOL));
break;
case 7:
result->Assign(6, filetime2brotime(${val.pairs[i].timestamp}));
break;
case 8:
result->Assign(7, new Val(${val.pairs[i].single_host.machine_id}, TYPE_COUNT));
break;
case 9:
result->Assign(8, utf16_bytestring_to_utf8_val(${val.pairs[i].target_name.data}));
break;
}
}
return result;
%}
function build_negotiate_flag_record(val: NTLM_Negotiate_Flags): BroVal
%{
RecordVal* flags = new RecordVal(BifType::Record::NTLM::NegotiateFlags);
flags->Assign(0, new Val(${val.negotiate_56}, 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(3, new Val(${val.negotiate_version}, 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(6, new Val(${val.negotiate_identify}, 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(9, new Val(${val.target_type_domain}, 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(12, new Val(${val.negotiate_oem_domain_supplied}, 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(15, new Val(${val.negotiate_lm_key}, TYPE_BOOL));
flags->Assign(16, new Val(${val.negotiate_datagram}, TYPE_BOOL));
flags->Assign(17, new Val(${val.negotiate_seal}, TYPE_BOOL));
flags->Assign(18, new Val(${val.negotiate_sign}, TYPE_BOOL));
flags->Assign(19, new Val(${val.request_target}, TYPE_BOOL));
flags->Assign(20, new Val(${val.negotiate_oem}, TYPE_BOOL));
flags->Assign(21, new Val(${val.negotiate_unicode}, TYPE_BOOL));
return flags;
%}
function proc_ntlm_negotiate(val: NTLM_Negotiate): bool
%{
RecordVal* result = new RecordVal(BifType::Record::NTLM::Negotiate);
result->Assign(0, build_negotiate_flag_record(${val.flags}));
if ( ${val.flags.negotiate_oem_domain_supplied} )
result->Assign(1, utf16_bytestring_to_utf8_val(${val.domain_name.string.data}));
if ( ${val.flags.negotiate_oem_workstation_supplied} )
result->Assign(2, utf16_bytestring_to_utf8_val(${val.workstation.string.data}));
if ( ${val.flags.negotiate_version} )
result->Assign(3, build_version_record(${val.version}));
BifEvent::generate_ntlm_negotiate(bro_analyzer(),
bro_analyzer()->Conn(),
result);
return true;
%}
function proc_ntlm_challenge(val: NTLM_Challenge): bool
%{
RecordVal* result = new RecordVal(BifType::Record::NTLM::Challenge);
result->Assign(0, build_negotiate_flag_record(${val.flags}));
if ( ${val.flags.request_target} )
result->Assign(1, utf16_bytestring_to_utf8_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_ntlm_challenge(bro_analyzer(),
bro_analyzer()->Conn(),
result);
return true;
%}
function proc_ntlm_authenticate(val: NTLM_Authenticate): bool
%{
RecordVal* result = new RecordVal(BifType::Record::NTLM::Authenticate);
result->Assign(0, build_negotiate_flag_record(${val.flags}));
if ( ${val.domain_name_fields.length} > 0 )
result->Assign(1, utf16_bytestring_to_utf8_val(${val.domain_name.string.data}));
if ( ${val.user_name_fields.length} > 0 )
result->Assign(2, utf16_bytestring_to_utf8_val(${val.user_name.string.data}));
if ( ${val.workstation_fields.length} > 0 )
result->Assign(3, utf16_bytestring_to_utf8_val(${val.workstation.string.data}));
if ( ${val.flags.negotiate_version} )
result->Assign(4, build_version_record(${val.version}));
BifEvent::generate_ntlm_authenticate(bro_analyzer(),
bro_analyzer()->Conn(),
result);
return true;
%}
}
refine typeattr NTLM_Negotiate += &let {
proc = $context.connection.proc_ntlm_negotiate(this);
};
refine typeattr NTLM_Challenge += &let {
proc : bool = $context.connection.proc_ntlm_challenge(this);
};
refine typeattr NTLM_Authenticate += &let {
proc : bool = $context.connection.proc_ntlm_authenticate(this);
};

View file

@ -0,0 +1,200 @@
type NTLM_SSP_Token(is_orig: bool) = record {
signature : bytestring &length=8;
msg_type : uint32;
msg : case msg_type of {
1 -> negotiate : NTLM_Negotiate(offsetof(msg) - offsetof(signature));
2 -> challenge : NTLM_Challenge(offsetof(msg) - offsetof(signature));
3 -> authenticate : NTLM_Authenticate(offsetof(msg) - offsetof(signature));
default -> def : bytestring &restofdata &transient;
};
} &byteorder=littleendian;
type NTLM_Negotiate(offset: uint16) = record {
flags : NTLM_Negotiate_Flags;
domain_name_fields : NTLM_StringData;
workstation_fields : NTLM_StringData;
version_present : case flags.negotiate_version of {
true -> version : NTLM_Version;
false -> no_version : empty;
};
payload : bytestring &restofdata;
} &let {
absolute_offset : uint16 = offsetof(payload) + offset;
domain_name : NTLM_String(domain_name_fields, absolute_offset, flags.negotiate_unicode) withinput payload &if(flags.negotiate_oem_domain_supplied);
workstation : NTLM_String(workstation_fields, absolute_offset, flags.negotiate_unicode) withinput payload &if(flags.negotiate_oem_workstation_supplied);
};
type NTLM_Challenge(offset: uint16) = record {
target_name_fields : NTLM_StringData;
flags : NTLM_Negotiate_Flags;
challenge : uint64;
reserved : padding[8];
target_info_fields : NTLM_StringData;
version_present : case flags.negotiate_version of {
true -> version : NTLM_Version;
false -> no_version : empty;
};
payload : bytestring &restofdata;
} &let {
absolute_offset : uint16 = offsetof(payload) + offset;
target_name : NTLM_String(target_name_fields, absolute_offset, flags.negotiate_unicode) withinput payload &if(flags.request_target);
target_info : NTLM_AV_Pair_Sequence(target_info_fields.offset - absolute_offset) withinput payload &if(flags.negotiate_target_info);
};
type NTLM_Authenticate(offset: uint16) = record {
lm_challenge_response_fields : NTLM_StringData;
nt_challenge_response_fields : NTLM_StringData;
domain_name_fields : NTLM_StringData;
user_name_fields : NTLM_StringData;
workstation_fields : NTLM_StringData;
encrypted_session_key_fields : NTLM_StringData;
flags : NTLM_Negotiate_Flags;
version_present : case flags.negotiate_version of {
true -> version : NTLM_Version;
false -> no_version : empty;
};
# 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 {
absolute_offset : uint16 = offsetof(payload) + offset;
domain_name : NTLM_String(domain_name_fields, absolute_offset, flags.negotiate_unicode) withinput payload &if(domain_name_fields.length > 0);
user_name : NTLM_String(user_name_fields, absolute_offset, flags.negotiate_unicode) withinput payload &if(user_name_fields.length > 0);
workstation : NTLM_String(workstation_fields, absolute_offset , flags.negotiate_unicode) withinput payload &if(workstation_fields.length > 0);
encrypted_session_key : NTLM_String(encrypted_session_key_fields, absolute_offset, flags.negotiate_unicode) withinput payload &if(flags.negotiate_key_exch);
};
type NTLM_Version = record {
major_version : uint8;
minor_version : uint8;
build_number : uint16;
reserved : padding[3];
ntlm_revision : uint8;
};
type NTLM_StringData = record {
length : uint16;
max_length : uint16;
offset : uint32;
};
type Fixed_Length_String(unicode: bool) = record {
data: bytestring &restofdata;
};
type NTLM_String(fields: NTLM_StringData, offset: uint16, unicode: bool) = record {
pad1 : padding to fields.offset - offset;
string : Fixed_Length_String(unicode) &length=fields.length;
};
type NTLM_AV_Pair_Sequence(offset: uint16) = record {
pad1 : padding to offset;
pairs : NTLM_AV_Pair[] &until($element.last);
};
type NTLM_AV_Pair = record {
id : uint16;
length : uint16;
value_case : case id of {
0x0000 -> av_eol : empty;
0x0001 -> nb_computer_name : Fixed_Length_String(true) &length=length;
0x0002 -> nb_domain_name : Fixed_Length_String(true) &length=length;
0x0003 -> dns_computer_name : Fixed_Length_String(true) &length=length;
0x0004 -> dns_domain_name : Fixed_Length_String(true) &length=length;
0x0005 -> dns_tree_name : Fixed_Length_String(true) &length=length;
0x0006 -> av_flags : uint32;
0x0007 -> timestamp : uint64;
0x0008 -> single_host : NTLM_Single_Host;
0x0009 -> target_name : Fixed_Length_String(true) &length=length;
0x000a -> channel_bindings : uint16;
};
} &let {
last : bool = (id == 0x0000);
# av_flags refinement
constrained_auth : bool = (av_flags & 0x00000001) > 0 &if(id == 0x0006);
mic_present : bool = (av_flags & 0x00000002) > 0 &if(id == 0x0006);
untrusted_source : bool = (av_flags & 0x00000004) > 0 &if(id == 0x0006);
};
type NTLM_Single_Host = record {
size : uint32;
padpad : padding[4];
data_present : uint32;
optional : case custom_data_present of {
true -> custom_data : bytestring &length=4;
false -> nothing : empty;
};
machine_id : uint32;
} &let {
custom_data_present: bool = (data_present & 0x00000001) > 0;
};
type LM_Response(offset: uint16) = record {
# This can be either LM (24 byte response) or
# LMv2 (16 byte response + 8 byte client challenge. No way to
# know for sure.
padpad : padding to offset;
response : bytestring &length=24;
};
type NTLM_Response(offset: uint16) = record {
padpad : padding to offset;
response : bytestring &length=24;
};
type NTLMv2_Response(flags: NTLM_Negotiate_Flags, offset: uint16) = record {
padpad : padding to offset;
response : bytestring &length=16;
client_challenge : NTLMv2_Client_Challenge(flags);
};
type NTLMv2_Client_Challenge(flags: NTLM_Negotiate_Flags) = record {
resp_type : uint8;
max_resp_type : uint8;
reserved : padding[6];
timestamp : uint64;
client_challenge : bytestring &length=8;
reserved2 : padding[4];
av_pairs : NTLM_AV_Pair_Sequence(0);
};
type NTLM_Negotiate_Flags = record {
flags: uint32;
} &let {
negotiate_56 : bool = (flags & 0x80000000) > 0;
negotiate_key_exch : bool = (flags & 0x40000000) > 0;
negotiate_128 : bool = (flags & 0x20000000) > 0;
negotiate_version : bool = (flags & 0x02000000) > 0;
negotiate_target_info : bool = (flags & 0x00800000) > 0;
request_non_nt_session_key : bool = (flags & 0x00400000) > 0;
negotiate_identify : bool = (flags & 0x00100000) > 0;
negotiate_extended_sessionsecurity : bool = (flags & 0x00040000) > 0;
target_type_server : bool = (flags & 0x00020000) > 0;
target_type_domain : bool = (flags & 0x00010000) > 0;
negotiate_always_sign : bool = (flags & 0x00008000) > 0;
negotiate_oem_workstation_supplied : bool = (flags & 0x00002000) > 0;
negotiate_oem_domain_supplied : bool = (flags & 0x00001000) > 0;
negotiate_anonymous_connection : bool = (flags & 0x00000400) > 0;
negotiate_ntlm : bool = (flags & 0x00000100) > 0;
negotiate_lm_key : bool = (flags & 0x00000080) > 0;
negotiate_datagram : bool = (flags & 0x00000040) > 0;
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;
};

View file

@ -0,0 +1,30 @@
%include binpac.pac
%include bro.pac
%extern{
#include "analyzer/Manager.h"
#include "analyzer/Analyzer.h"
#include "types.bif.h"
#include "events.bif.h"
%}
analyzer NTLM withcontext {
connection : NTLM_Conn;
flow : NTLM_Flow;
};
connection NTLM_Conn(bro_analyzer: BroAnalyzer) {
upflow = NTLM_Flow(true);
downflow = NTLM_Flow(false);
};
%include ntlm-protocol.pac
%include ../asn1/asn1.pac
# Now we define the flow:
flow NTLM_Flow(is_orig: bool) {
datagram = NTLM_SSP_Token(is_orig) withcontext(connection, this);
};
%include ntlm-analyzer.pac

View file

@ -0,0 +1,9 @@
module NTLM;
type NTLM::Negotiate: record;
type NTLM::Challenge: record;
type NTLM::Authenticate: record;
type NTLM::NegotiateFlags: record;
type NTLM::Version: record;
type NTLM::AVs: record;

View file

@ -1,54 +1,9 @@
%extern{
#include "ConvertUTF.h"
#include "file_analysis/Manager.h"
#include "types.bif.h"
%}
refine flow RDP_Flow += {
function utf16_to_utf8_val(utf16: bytestring): StringVal
%{
std::string resultstring;
size_t utf8size = (3 * utf16.length() + 1);
if ( utf8size > resultstring.max_size() )
{
connection()->bro_analyzer()->Weird("excessive_utf16_length");
return new StringVal("");
}
resultstring.resize(utf8size, '\0');
// We can't assume that the string data is properly aligned
// here, so make a copy.
UTF16 utf16_copy[utf16.length()]; // Twice as much memory than necessary.
memcpy(utf16_copy, utf16.begin(), utf16.length());
const char* utf16_copy_end = reinterpret_cast<const char*>(utf16_copy) + utf16.length();
const UTF16* sourcestart = utf16_copy;
const UTF16* sourceend = reinterpret_cast<const UTF16*>(utf16_copy_end);
UTF8* targetstart = reinterpret_cast<UTF8*>(&resultstring[0]);
UTF8* targetend = targetstart + utf8size;
ConversionResult res = ConvertUTF16toUTF8(&sourcestart,
sourceend,
&targetstart,
targetend,
lenientConversion);
if ( res != conversionOK )
{
connection()->bro_analyzer()->Weird("Failed UTF-16 to UTF-8 conversion");
return new StringVal(utf16.length(), (const char *) utf16.begin());
}
*targetstart = 0;
// We're relying on no nulls being in the string.
return new StringVal(resultstring.c_str());
%}
function proc_rdp_connect_request(cr: Connect_Request): bool
%{
if ( rdp_connect_request )
@ -125,18 +80,18 @@ refine flow RDP_Flow += {
ccd->Assign(5, new Val(${ccore.sas_sequence}, TYPE_COUNT));
ccd->Assign(6, new Val(${ccore.keyboard_layout}, TYPE_COUNT));
ccd->Assign(7, new Val(${ccore.client_build}, TYPE_COUNT));
ccd->Assign(8, utf16_to_utf8_val(${ccore.client_name}));
ccd->Assign(8, utf16_bytestring_to_utf8_val(${ccore.client_name}));
ccd->Assign(9, new Val(${ccore.keyboard_type}, TYPE_COUNT));
ccd->Assign(10, new Val(${ccore.keyboard_sub}, TYPE_COUNT));
ccd->Assign(11, new Val(${ccore.keyboard_function_key}, TYPE_COUNT));
ccd->Assign(12, utf16_to_utf8_val(${ccore.ime_file_name}));
ccd->Assign(12, utf16_bytestring_to_utf8_val(${ccore.ime_file_name}));
ccd->Assign(13, new Val(${ccore.post_beta2_color_depth}, TYPE_COUNT));
ccd->Assign(14, new Val(${ccore.client_product_id}, TYPE_COUNT));
ccd->Assign(15, new Val(${ccore.serial_number}, TYPE_COUNT));
ccd->Assign(16, new Val(${ccore.high_color_depth}, TYPE_COUNT));
ccd->Assign(17, new Val(${ccore.supported_color_depths}, TYPE_COUNT));
ccd->Assign(18, ec_flags);
ccd->Assign(19, utf16_to_utf8_val(${ccore.dig_product_id}));
ccd->Assign(19, utf16_bytestring_to_utf8_val(${ccore.dig_product_id}));
BifEvent::generate_rdp_client_core_data(connection()->bro_analyzer(),
connection()->bro_analyzer()->Conn(),

View file

@ -1,4 +1,3 @@
include(BroPlugin)
include_directories(BEFORE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR})
@ -6,6 +5,80 @@ include_directories(AFTER ${CMAKE_CURRENT_BINARY_DIR}/../dce-rpc)
bro_plugin_begin(Bro SMB)
bro_plugin_cc(SMB.cc Plugin.cc)
bro_plugin_bif(events.bif)
bro_plugin_pac(smb.pac smb-protocol.pac smb-pipe.pac smb-mailslot.pac)
bro_plugin_bif(
smb1_com_check_directory.bif
smb1_com_close.bif
smb1_com_create_directory.bif
smb1_com_echo.bif
smb1_com_logoff_andx.bif
smb1_com_negotiate.bif
smb1_com_nt_create_andx.bif
smb1_com_nt_cancel.bif
smb1_com_query_information.bif
smb1_com_read_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_disconnect.bif
smb1_com_write_andx.bif
smb1_events.bif
smb2_com_close.bif
smb2_com_create.bif
#smb2_com_ioctl.bif
#smb2_com_lock.bif
smb2_com_negotiate.bif
smb2_com_read.bif
smb2_com_session_setup.bif
smb2_com_set_info.bif
smb2_com_tree_connect.bif
#smb2_com_tree_disconnect.bif
smb2_com_write.bif
smb2_events.bif
types.bif)
bro_plugin_pac(
smb.pac
smb-common.pac
smb-strings.pac
smb-time.pac
smb-pipe.pac
smb-gssapi.pac
smb-mailslot.pac
smb1-protocol.pac
smb1-com-check-directory.pac
smb1-com-close.pac
smb1-com-create-directory.pac
smb1-com-echo.pac
smb1-com-locking-andx.pac
smb1-com-logoff-andx.pac
smb1-com-negotiate.pac
smb1-com-nt-cancel.pac
smb1-com-nt-create-andx.pac
smb1-com-nt-transact.pac
smb1-com-query-information.pac
smb1-com-read-andx.pac
smb1-com-session-setup-andx.pac
smb1-com-transaction-secondary.pac
smb1-com-transaction.pac
smb1-com-transaction2.pac
smb1-com-tree-connect-andx.pac
smb1-com-tree-disconnect.pac
smb1-com-write-andx.pac
smb2-protocol.pac
smb2-com-close.pac
smb2-com-create.pac
smb2-com-ioctl.pac
smb2-com-lock.pac
smb2-com-negotiate.pac
smb2-com-read.pac
smb2-com-session-setup.pac
smb2-com-set-info.pac
smb2-com-tree-connect.pac
smb2-com-tree-disconnect.pac
smb2-com-write.pac
)
bro_plugin_end()

View file

@ -1,5 +1,4 @@
// See the file in the main distribution directory for copyright.
// See the file in the main distribution directory for copyright.
#include "plugin/Plugin.h"

File diff suppressed because it is too large Load diff

View file

@ -1,5 +1,3 @@
// See the file "COPYING" in the main distribution directory for copyright.
#ifndef ANALYZER_PROTOCOL_SMB_SMB_H
#define ANALYZER_PROTOCOL_SMB_SMB_H
@ -7,194 +5,68 @@
// Reference: http://www.snia.org/tech_activities/CIFS/CIFS-TR-1p00_FINAL.pdf
#include "analyzer/protocol/tcp/TCP.h"
#include "analyzer/protocol/dce-rpc/DCE_RPC.h"
#include "analyzer/protocol/rpc/RPC.h"
#include "smb_pac.h"
namespace analyzer { namespace smb {
enum IPC_named_pipe {
IPC_NONE,
IPC_LOCATOR,
IPC_EPMAPPER,
IPC_SAMR, // Security Account Manager
};
class SMB_Body : public binpac::SMB::SMB_body {
public:
SMB_Body(const u_char* data, const u_char* data_end)
: binpac::SMB::SMB_body()
{
data_ = data;
Parse(data, data_end);
data_length_ = body_length();
if ( data + data_length_ > data_end )
data_length_ = data_end - data;
}
const u_char* data() const { return data_; }
int length() const { return data_length_; }
protected:
const u_char* data_;
int data_length_;
};
class SMB_Session {
public:
SMB_Session(analyzer::Analyzer* analyzer);
~SMB_Session();
void Deliver(int is_orig, int len, const u_char* msg);
protected:
void ParseMessage(int is_orig, int cmd,
binpac::SMB::SMB_header const &hdr,
SMB_Body const &body);
int ParseNegotiate(binpac::SMB::SMB_header const &hdr,
SMB_Body const &body);
int ParseNegotiateResponse(binpac::SMB::SMB_header const &hdr,
SMB_Body const &body);
int ParseAndx(int is_orig, binpac::SMB::SMB_header const &hdr,
SMB_Body const &body);
int ParseClose(int is_orig, binpac::SMB::SMB_header const &hdr,
SMB_Body const &body);
int ParseLogoffAndx(int is_orig, binpac::SMB::SMB_header const &hdr,
SMB_Body const &body);
int ParseSetupAndx(int is_orig, binpac::SMB::SMB_header const &hdr,
SMB_Body const &body);
int ParseTreeConnectAndx(binpac::SMB::SMB_header const &hdr,
SMB_Body const &body);
int ParseTreeDisconnect(int is_orig, binpac::SMB::SMB_header const &hdr,
SMB_Body const &body);
int ParseNtCreateAndx(binpac::SMB::SMB_header const &hdr,
SMB_Body const &body);
int ParseReadAndx(binpac::SMB::SMB_header const &hdr,
SMB_Body const &body);
int ParseReadAndxResponse(binpac::SMB::SMB_header const &hdr,
SMB_Body const &body);
int ParseWriteAndx(binpac::SMB::SMB_header const &hdr,
SMB_Body const &body);
int ParseWriteAndxResponse(binpac::SMB::SMB_header const &hdr,
SMB_Body const &body);
int ParseTransaction(int is_orig, int cmd,
binpac::SMB::SMB_header const &hdr,
SMB_Body const &body);
int TransactionEvent(EventHandlerPtr f, int is_orig,
binpac::SMB::SMB_header const &hdr,
binpac::SMB::SMB_transaction const &trans,
int data_count,
binpac::SMB::SMB_transaction_data* data);
int TransactionEvent(EventHandlerPtr f, int is_orig,
binpac::SMB::SMB_header const &hdr,
binpac::SMB::SMB_transaction_secondary const &trans,
int data_count,
binpac::SMB::SMB_transaction_data* data);
int TransactionEvent(EventHandlerPtr f, int is_orig,
binpac::SMB::SMB_header const &hdr,
binpac::SMB::SMB_transaction_response const &trans,
int data_count,
binpac::SMB::SMB_transaction_data* data);
int ParseTransactionRequest(int cmd,
binpac::SMB::SMB_header const &hdr,
SMB_Body const &body);
int ParseTransactionSecondaryRequest(int cmd,
binpac::SMB::SMB_header const &hdr,
SMB_Body const &body);
int ParseTransactionResponse(int cmd,
binpac::SMB::SMB_header const &hdr,
SMB_Body const &body);
int ParseGetDFSReferral(binpac::SMB::SMB_header const &hdr,
int param_count, const u_char* param);
BroString* ExtractString(binpac::SMB::SMB_string const* s);
BroString* ExtractString(binpac::SMB::SMB_ascii_string const* s);
BroString* ExtractString(binpac::SMB::SMB_unicode_string const* s);
bool LooksLikeRPC(int len, const u_char* msg);
bool CheckRPC(int is_orig, int len, const u_char* msg);
int AndxOffset(int is_orig, int &next_command) const;
void Weird(const char* msg);
const binpac::SMB::SMB_andx* const andx(int is_orig) const
{
return is_orig ? andx_[1] : andx_[0];
}
void set_andx(int is_orig, binpac::SMB::SMB_andx* andx);
Val* BuildHeaderVal(binpac::SMB::SMB_header const &hdr);
Val* BuildTransactionVal(binpac::SMB::SMB_transaction const &trans);
Val* BuildTransactionVal(binpac::SMB::SMB_transaction_secondary const &trans);
Val* BuildTransactionVal(binpac::SMB::SMB_transaction_response const &trans);
Val* BuildTransactionDataVal(binpac::SMB::SMB_transaction_data* data);
analyzer::Analyzer* analyzer;
dce_rpc::DCE_RPC_Session* dce_rpc_session;
enum IPC_named_pipe IPC_pipe;
int is_IPC;
int req_cmd;
uint16 transaction_subcmd;
bool smb_mailslot_prot;
bool smb_pipe_prot;
StringVal* transaction_name;
binpac::SMB::SMB_andx* andx_[2];
};
class Contents_SMB : public tcp::TCP_SupportAnalyzer {
public:
Contents_SMB(Connection* conn, bool orig, SMB_Session* smb_session);
Contents_SMB(Connection* conn, bool orig);
~Contents_SMB();
virtual void DeliverStream(int len, const u_char* data, bool orig);
void DeliverStream(int len, const u_char* data, bool orig) override;
protected:
void InitMsgBuf();
typedef enum {
WAIT_FOR_HDR,
WAIT_FOR_DATA
} state_t;
typedef enum {
NEED_RESYNC,
INSYNC,
} resync_state_t;
void Init() override;
virtual bool CheckResync(int& len, const u_char*& data, bool orig);
void Undelivered(uint64 seq, int len, bool orig) override;
virtual void NeedResync() {
resync_state = NEED_RESYNC;
state = WAIT_FOR_HDR;
}
bool HasSMBHeader(int len, const u_char* data);
void DeliverSMB(int len, const u_char* data);
SMB_Session* smb_session;
u_char dshdr[4];
u_char* msg_buf;
rpc::RPC_Reasm_Buffer hdr_buf; // Reassembles the NetBIOS length and glue.
rpc::RPC_Reasm_Buffer msg_buf; // Reassembles the SMB message.
int msg_len;
int buf_n; // number of bytes in msg_buf
int buf_len; // size off msg_buf
int msg_type;
double first_time; // timestamp of first packet of current message
double last_time; // timestamp of last pakcet of current message
state_t state;
resync_state_t resync_state;
};
class SMB_Analyzer : public tcp::TCP_ApplicationAnalyzer {
public:
SMB_Analyzer(Connection* conn);
~SMB_Analyzer();
virtual ~SMB_Analyzer();
void Done() override;
void DeliverStream(int len, const u_char* data, bool orig) override;
void Undelivered(uint64 seq, int len, bool orig) override;
void EndpointEOF(bool is_orig) override;
static analyzer::Analyzer* Instantiate(Connection* conn)
{ return new SMB_Analyzer(conn); }
protected:
SMB_Session* smb_session;
Contents_SMB* o_smb;
Contents_SMB* r_smb;
binpac::SMB::SMB_Conn* interp;
// Count the number of chunks received by the analyzer
// but only used to count the first few.
uint8 chunks;
};
} } // namespace analyzer::*

View file

@ -73,3 +73,5 @@ SMB_COMMAND(SMB_COM_GET_PRINT_QUEUE, 0xC3)
SMB_COMMAND(SMB_COM_READ_BULK, 0xD8)
SMB_COMMAND(SMB_COM_WRITE_BULK, 0xD9)
SMB_COMMAND(SMB_COM_WRITE_BULK_DATA, 0xDA)
SMB_COMMAND(SMB_COM_INVALID, 0xFE)
SMB_COMMAND(SMB_COM_NO_ANDX_COMMAND, 0xFF)

View file

@ -1,495 +0,0 @@
## Generated for all SMB/CIFS messages.
##
## See `Wikipedia <http://en.wikipedia.org/wiki/Server_Message_Block>`__ for
## more information about the SMB/CIFS protocol. Bro's SMB/CIFS analyzer parses
## both SMB-over-NetBIOS on ports 138/139 and SMB-over-TCP on port 445.
##
## c: The connection.
##
## hdr: The parsed header of the SMB message.
##
## is_orig: True if the message was sent by the originator of the underlying
## transport-level connection.
##
## cmd: A string mnemonic of the SMB command code.
##
## body_length: The length of the SMB message body, i.e. the data starting after
## the SMB header.
##
## body: The raw SMB message body, i.e., the data starting after the SMB header.
##
## .. bro:see:: smb_com_close smb_com_generic_andx smb_com_logoff_andx
## smb_com_negotiate smb_com_negotiate_response smb_com_nt_create_andx
## smb_com_read_andx smb_com_setup_andx smb_com_trans_mailslot
## smb_com_trans_pipe smb_com_trans_rap smb_com_transaction smb_com_transaction2
## smb_com_tree_connect_andx smb_com_tree_disconnect smb_com_write_andx smb_error
## smb_get_dfs_referral
##
## .. todo:: Bro's current default configuration does not activate the protocol
## analyzer that generates this event; the corresponding script has not yet
## been ported to Bro 2.x. To still enable this event, one needs to
## register a port for it or add a DPD payload signature.
event smb_message%(c: connection, hdr: smb_hdr, is_orig: bool, cmd: string, body_length: count, body: string%);
## Generated for SMB/CIFS messages of type *tree connect andx*.
##
## See `Wikipedia <http://en.wikipedia.org/wiki/Server_Message_Block>`__ for
## more information about the SMB/CIFS protocol. Bro's SMB/CIFS analyzer parses
## both SMB-over-NetBIOS on ports 138/139 and SMB-over-TCP on port 445.
##
## c: The connection.
##
## hdr: The parsed header of the SMB message.
##
## path: The ``path`` attribute specified in the message.
##
## service: The ``service`` attribute specified in the message.
##
## .. bro:see:: smb_com_close smb_com_generic_andx smb_com_logoff_andx
## smb_com_negotiate smb_com_negotiate_response smb_com_nt_create_andx
## smb_com_read_andx smb_com_setup_andx smb_com_trans_mailslot
## smb_com_trans_pipe smb_com_trans_rap smb_com_transaction smb_com_transaction2
## smb_com_tree_disconnect smb_com_write_andx smb_error smb_get_dfs_referral
## smb_message
##
## .. todo:: Bro's current default configuration does not activate the protocol
## analyzer that generates this event; the corresponding script has not yet
## been ported to Bro 2.x. To still enable this event, one needs to
## register a port for it or add a DPD payload signature.
event smb_com_tree_connect_andx%(c: connection, hdr: smb_hdr, path: string, service: string%);
## Generated for SMB/CIFS messages of type *tree disconnect*.
##
## See `Wikipedia <http://en.wikipedia.org/wiki/Server_Message_Block>`__ for
## more information about the SMB/CIFS protocol. Bro's SMB/CIFS analyzer parses
## both SMB-over-NetBIOS on ports 138/139 and SMB-over-TCP on port 445.
##
## c: The connection.
##
## hdr: The parsed header of the SMB message.
##
## .. bro:see:: smb_com_close smb_com_generic_andx smb_com_logoff_andx
## smb_com_negotiate smb_com_negotiate_response smb_com_nt_create_andx
## smb_com_read_andx smb_com_setup_andx smb_com_trans_mailslot
## smb_com_trans_pipe smb_com_trans_rap smb_com_transaction smb_com_transaction2
## smb_com_tree_connect_andx smb_com_write_andx smb_error smb_get_dfs_referral
## smb_message
##
## .. todo:: Bro's current default configuration does not activate the protocol
## analyzer that generates this event; the corresponding script has not yet
## been ported to Bro 2.x. To still enable this event, one needs to
## register a port for it or add a DPD payload signature.
event smb_com_tree_disconnect%(c: connection, hdr: smb_hdr%);
## Generated for SMB/CIFS messages of type *nt create andx*.
##
## See `Wikipedia <http://en.wikipedia.org/wiki/Server_Message_Block>`__ for
## more information about the SMB/CIFS protocol. Bro's SMB/CIFS analyzer parses
## both SMB-over-NetBIOS on ports 138/139 and SMB-over-TCP on port 445.
##
## c: The connection.
##
## hdr: The parsed header of the SMB message.
##
## name: The ``name`` attribute specified in the message.
##
## .. bro:see:: smb_com_close smb_com_generic_andx smb_com_logoff_andx
## smb_com_negotiate smb_com_negotiate_response smb_com_read_andx
## smb_com_setup_andx smb_com_trans_mailslot smb_com_trans_pipe smb_com_trans_rap
## smb_com_transaction smb_com_transaction2 smb_com_tree_connect_andx
## smb_com_tree_disconnect smb_com_write_andx smb_error smb_get_dfs_referral
## smb_message
##
## .. todo:: Bro's current default configuration does not activate the protocol
## analyzer that generates this event; the corresponding script has not yet
## been ported to Bro 2.x. To still enable this event, one needs to
## register a port for it or add a DPD payload signature.
event smb_com_nt_create_andx%(c: connection, hdr: smb_hdr, name: string%);
## Generated for SMB/CIFS messages of type *nt transaction*.
##
## See `Wikipedia <http://en.wikipedia.org/wiki/Server_Message_Block>`__ for
## more information about the SMB/CIFS protocol. Bro's SMB/CIFS analyzer parses
## both SMB-over-NetBIOS on ports 138/139 and SMB-over-TCP on port 445.
##
## c: The connection.
##
## hdr: The parsed header of the SMB message.
##
## trans: The parsed transaction header.
##
## data: The raw transaction data.
##
## is_orig: True if the message was sent by the originator of the connection.
##
## .. bro:see:: smb_com_close smb_com_generic_andx smb_com_logoff_andx
## smb_com_negotiate smb_com_negotiate_response smb_com_nt_create_andx
## smb_com_read_andx smb_com_setup_andx smb_com_trans_mailslot smb_com_trans_pipe
## smb_com_trans_rap smb_com_transaction2 smb_com_tree_connect_andx
## smb_com_tree_disconnect smb_com_write_andx smb_error smb_get_dfs_referral
## smb_message
##
## .. todo:: Bro's current default configuration does not activate the protocol
## analyzer that generates this event; the corresponding script has not yet
## been ported to Bro 2.x. To still enable this event, one needs to
## register a port for it or add a DPD payload signature.
event smb_com_transaction%(c: connection, hdr: smb_hdr, trans: smb_trans, data: smb_trans_data, is_orig: bool%);
## Generated for SMB/CIFS messages of type *nt transaction 2*.
##
## See `Wikipedia <http://en.wikipedia.org/wiki/Server_Message_Block>`__ for
## more information about the SMB/CIFS protocol. Bro's SMB/CIFS analyzer parses
## both SMB-over-NetBIOS on ports 138/139 and SMB-over-TCP on port 445.
##
## c: The connection.
##
## hdr: The parsed header of the SMB message.
##
## trans: The parsed transaction header.
##
## data: The raw transaction data.
##
## is_orig: True if the message was sent by the originator of the connection.
##
## .. bro:see:: smb_com_close smb_com_generic_andx smb_com_logoff_andx
## smb_com_negotiate smb_com_negotiate_response smb_com_nt_create_andx
## smb_com_read_andx smb_com_setup_andx smb_com_trans_mailslot smb_com_trans_pipe
## smb_com_trans_rap smb_com_transaction smb_com_tree_connect_andx
## smb_com_tree_disconnect smb_com_write_andx smb_error smb_get_dfs_referral
## smb_message
##
## .. todo:: Bro's current default configuration does not activate the protocol
## analyzer that generates this event; the corresponding script has not yet
## been ported to Bro 2.x. To still enable this event, one needs to
## register a port for it or add a DPD payload signature.
event smb_com_transaction2%(c: connection, hdr: smb_hdr, trans: smb_trans, data: smb_trans_data, is_orig: bool%);
## Generated for SMB/CIFS messages of type *transaction mailslot*.
##
## See `Wikipedia <http://en.wikipedia.org/wiki/Server_Message_Block>`__ for
## more information about the SMB/CIFS protocol. Bro's SMB/CIFS analyzer parses
## both SMB-over-NetBIOS on ports 138/139 and SMB-over-TCP on port 445.
##
## c: The connection.
##
## hdr: The parsed header of the SMB message.
##
## trans: The parsed transaction header.
##
## data: The raw transaction data.
##
## is_orig: True if the message was sent by the originator of the connection.
##
## .. bro:see:: smb_com_close smb_com_generic_andx smb_com_logoff_andx
## smb_com_negotiate smb_com_negotiate_response smb_com_nt_create_andx
## smb_com_read_andx smb_com_setup_andx smb_com_trans_pipe smb_com_trans_rap
## smb_com_transaction smb_com_transaction2 smb_com_tree_connect_andx
## smb_com_tree_disconnect smb_com_write_andx smb_error smb_get_dfs_referral
## smb_message
##
## .. todo:: Bro's current default configuration does not activate the protocol
## analyzer that generates this event; the corresponding script has not yet
## been ported to Bro 2.x. To still enable this event, one needs to
## register a port for it or add a DPD payload signature.
event smb_com_trans_mailslot%(c: connection, hdr: smb_hdr, trans: smb_trans, data: smb_trans_data, is_orig: bool%);
## Generated for SMB/CIFS messages of type *transaction rap*.
##
## See `Wikipedia <http://en.wikipedia.org/wiki/Server_Message_Block>`__ for
## more information about the SMB/CIFS protocol. Bro's SMB/CIFS analyzer parses
## both SMB-over-NetBIOS on ports 138/139 and SMB-over-TCP on port 445.
##
## c: The connection.
##
## hdr: The parsed header of the SMB message.
##
## trans: The parsed transaction header.
##
## data: The raw transaction data.
##
## is_orig: True if the message was sent by the originator of the connection.
##
## .. bro:see:: smb_com_close smb_com_generic_andx smb_com_logoff_andx
## smb_com_negotiate smb_com_negotiate_response smb_com_nt_create_andx
## smb_com_read_andx smb_com_setup_andx smb_com_trans_mailslot
## smb_com_trans_pipe smb_com_transaction smb_com_transaction2
## smb_com_tree_connect_andx smb_com_tree_disconnect smb_com_write_andx smb_error
## smb_get_dfs_referral smb_message
##
## .. todo:: Bro's current default configuration does not activate the protocol
## analyzer that generates this event; the corresponding script has not yet
## been ported to Bro 2.x. To still enable this event, one needs to
## register a port for it or add a DPD payload signature.
event smb_com_trans_rap%(c: connection, hdr: smb_hdr, trans: smb_trans, data: smb_trans_data, is_orig: bool%);
## Generated for SMB/CIFS messages of type *transaction pipe*.
##
## See `Wikipedia <http://en.wikipedia.org/wiki/Server_Message_Block>`__ for
## more information about the SMB/CIFS protocol. Bro's SMB/CIFS analyzer parses
## both SMB-over-NetBIOS on ports 138/139 and SMB-over-TCP on port 445.
##
## c: The connection.
##
## hdr: The parsed header of the SMB message.
##
## trans: The parsed transaction header.
##
## data: The raw transaction data.
##
## is_orig: True if the message was sent by the originator of the connection.
##
## .. bro:see:: smb_com_close smb_com_generic_andx smb_com_logoff_andx
## smb_com_negotiate smb_com_negotiate_response smb_com_nt_create_andx
## smb_com_read_andx smb_com_setup_andx smb_com_trans_mailslot smb_com_trans_rap
## smb_com_transaction smb_com_transaction2 smb_com_tree_connect_andx
## smb_com_tree_disconnect smb_com_write_andx smb_error smb_get_dfs_referral
## smb_message
##
## .. todo:: Bro's current default configuration does not activate the protocol
## analyzer that generates this event; the corresponding script has not yet
## been ported to Bro 2.x. To still enable this event, one needs to
## register a port for it or add a DPD payload signature.
event smb_com_trans_pipe%(c: connection, hdr: smb_hdr, trans: smb_trans, data: smb_trans_data, is_orig: bool%);
## Generated for SMB/CIFS messages of type *read andx*.
##
## See `Wikipedia <http://en.wikipedia.org/wiki/Server_Message_Block>`__ for
## more information about the SMB/CIFS protocol. Bro's SMB/CIFS analyzer parses
## both SMB-over-NetBIOS on ports 138/139 and SMB-over-TCP on port 445.
##
## c: The connection.
##
## hdr: The parsed header of the SMB message.
##
## data: Always empty.
##
## .. bro:see:: smb_com_close smb_com_generic_andx smb_com_logoff_andx
## smb_com_negotiate smb_com_negotiate_response smb_com_nt_create_andx
## smb_com_setup_andx smb_com_trans_mailslot smb_com_trans_pipe smb_com_trans_rap
## smb_com_transaction smb_com_transaction2 smb_com_tree_connect_andx
## smb_com_tree_disconnect smb_com_write_andx smb_error smb_get_dfs_referral
## smb_message
##
## .. todo:: Bro's current default configuration does not activate the protocol
## analyzer that generates this event; the corresponding script has not yet
## been ported to Bro 2.x. To still enable this event, one needs to
## register a port for it or add a DPD payload signature.
event smb_com_read_andx%(c: connection, hdr: smb_hdr, data: string%);
## Generated for SMB/CIFS messages of type *read andx*.
##
## See `Wikipedia <http://en.wikipedia.org/wiki/Server_Message_Block>`__ for
## more information about the SMB/CIFS protocol. Bro's SMB/CIFS analyzer parses
## both SMB-over-NetBIOS on ports 138/139 and SMB-over-TCP on port 445.
##
## c: The connection.
##
## hdr: The parsed header of the SMB message.
##
## data: Always empty.
##
## .. bro:see:: smb_com_close smb_com_generic_andx smb_com_logoff_andx
## smb_com_negotiate smb_com_negotiate_response smb_com_nt_create_andx
## smb_com_read_andx smb_com_setup_andx smb_com_trans_mailslot
## smb_com_trans_pipe smb_com_trans_rap smb_com_transaction smb_com_transaction2
## smb_com_tree_connect_andx smb_com_tree_disconnect smb_error
## smb_get_dfs_referral smb_message
##
## .. todo:: Bro's current default configuration does not activate the protocol
## analyzer that generates this event; the corresponding script has not yet
## been ported to Bro 2.x. To still enable this event, one needs to
## register a port for it or add a DPD payload signature.
event smb_com_write_andx%(c: connection, hdr: smb_hdr, data: string%);
## Generated for SMB/CIFS messages of type *get dfs referral*.
##
## See `Wikipedia <http://en.wikipedia.org/wiki/Server_Message_Block>`__ for
## more information about the SMB/CIFS protocol. Bro's SMB/CIFS analyzer parses
## both SMB-over-NetBIOS on ports 138/139 and SMB-over-TCP on port 445.
##
## c: The connection.
##
## hdr: The parsed header of the SMB message.
##
## max_referral_level: The ``max_referral_level`` attribute specified in the
## message.
##
## file_name: The ``filene_name`` attribute specified in the message.
##
## .. bro:see:: smb_com_close smb_com_generic_andx smb_com_logoff_andx
## smb_com_negotiate smb_com_negotiate_response smb_com_nt_create_andx
## smb_com_read_andx smb_com_setup_andx smb_com_trans_mailslot
## smb_com_trans_pipe smb_com_trans_rap smb_com_transaction smb_com_transaction2
## smb_com_tree_connect_andx smb_com_tree_disconnect smb_com_write_andx smb_error
## smb_message
##
## .. todo:: Bro's current default configuration does not activate the protocol
## analyzer that generates this event; the corresponding script has not yet
## been ported to Bro 2.x. To still enable this event, one needs to
## register a port for it or add a DPD payload signature.
event smb_get_dfs_referral%(c: connection, hdr: smb_hdr, max_referral_level: count, file_name: string%);
## Generated for SMB/CIFS messages of type *negotiate*.
##
## See `Wikipedia <http://en.wikipedia.org/wiki/Server_Message_Block>`__ for
## more information about the SMB/CIFS protocol. Bro's SMB/CIFS analyzer parses
## both SMB-over-NetBIOS on ports 138/139 and SMB-over-TCP on port 445.
##
## c: The connection.
##
## hdr: The parsed header of the SMB message.
##
## .. bro:see:: smb_com_close smb_com_generic_andx smb_com_logoff_andx
## smb_com_negotiate_response smb_com_nt_create_andx smb_com_read_andx smb_com_setup_andx
## smb_com_trans_mailslot smb_com_trans_pipe smb_com_trans_rap smb_com_transaction
## smb_com_transaction2 smb_com_tree_connect_andx smb_com_tree_disconnect
## smb_com_write_andx smb_error smb_get_dfs_referral smb_message
##
## .. todo:: Bro's current default configuration does not activate the protocol
## analyzer that generates this event; the corresponding script has not yet
## been ported to Bro 2.x. To still enable this event, one needs to
## register a port for it or add a DPD payload signature.
event smb_com_negotiate%(c: connection, hdr: smb_hdr%);
## Generated for SMB/CIFS messages of type *negotiate response*.
##
## See `Wikipedia <http://en.wikipedia.org/wiki/Server_Message_Block>`__ for
## more information about the SMB/CIFS protocol. Bro's SMB/CIFS analyzer parses
## both SMB-over-NetBIOS on ports 138/139 and SMB-over-TCP on port 445.
##
## c: The connection.
##
## hdr: The parsed header of the SMB message.
##
## dialect_index: The ``dialect`` indicated in the message.
##
## .. bro:see:: smb_com_close smb_com_generic_andx smb_com_logoff_andx
## smb_com_negotiate smb_com_nt_create_andx smb_com_read_andx smb_com_setup_andx
## smb_com_trans_mailslot smb_com_trans_pipe smb_com_trans_rap smb_com_transaction
## smb_com_transaction2 smb_com_tree_connect_andx smb_com_tree_disconnect
## smb_com_write_andx smb_error smb_get_dfs_referral smb_message
##
## .. todo:: Bro's current default configuration does not activate the protocol
## analyzer that generates this event; the corresponding script has not yet
## been ported to Bro 2.x. To still enable this event, one needs to
## register a port for it or add a DPD payload signature.
event smb_com_negotiate_response%(c: connection, hdr: smb_hdr, dialect_index: count%);
## Generated for SMB/CIFS messages of type *setup andx*.
##
## See `Wikipedia <http://en.wikipedia.org/wiki/Server_Message_Block>`__ for
## more information about the SMB/CIFS protocol. Bro's SMB/CIFS analyzer parses
## both SMB-over-NetBIOS on ports 138/139 and SMB-over-TCP on port 445.
##
## c: The connection.
##
## hdr: The parsed header of the SMB message.
##
## .. bro:see:: smb_com_close smb_com_generic_andx smb_com_logoff_andx
## smb_com_negotiate smb_com_negotiate_response smb_com_nt_create_andx
## smb_com_read_andx smb_com_trans_mailslot smb_com_trans_pipe smb_com_trans_rap
## smb_com_transaction smb_com_transaction2 smb_com_tree_connect_andx
## smb_com_tree_disconnect smb_com_write_andx smb_error smb_get_dfs_referral
## smb_message
##
## .. todo:: Bro's current default configuration does not activate the protocol
## analyzer that generates this event; the corresponding script has not yet
## been ported to Bro 2.x. To still enable this event, one needs to
## register a port for it or add a DPD payload signature.
event smb_com_setup_andx%(c: connection, hdr: smb_hdr%);
## Generated for SMB/CIFS messages of type *generic andx*.
##
## See `Wikipedia <http://en.wikipedia.org/wiki/Server_Message_Block>`__ for
## more information about the SMB/CIFS protocol. Bro's SMB/CIFS analyzer parses
## both SMB-over-NetBIOS on ports 138/139 and SMB-over-TCP on port 445.
##
## c: The connection.
##
## hdr: The parsed header of the SMB message.
##
## .. bro:see:: smb_com_close smb_com_logoff_andx smb_com_negotiate
## smb_com_negotiate_response smb_com_nt_create_andx smb_com_read_andx
## smb_com_setup_andx smb_com_trans_mailslot smb_com_trans_pipe smb_com_trans_rap
## smb_com_transaction smb_com_transaction2 smb_com_tree_connect_andx
## smb_com_tree_disconnect smb_com_write_andx smb_error smb_get_dfs_referral
## smb_message
##
## .. todo:: Bro's current default configuration does not activate the protocol
## analyzer that generates this event; the corresponding script has not yet
## been ported to Bro 2.x. To still enable this event, one needs to
## register a port for it or add a DPD payload signature.
event smb_com_generic_andx%(c: connection, hdr: smb_hdr%);
## Generated for SMB/CIFS messages of type *close*.
##
## See `Wikipedia <http://en.wikipedia.org/wiki/Server_Message_Block>`__ for
## more information about the SMB/CIFS protocol. Bro's SMB/CIFS analyzer parses
## both SMB-over-NetBIOS on ports 138/139 and SMB-over-TCP on port 445.
##
## c: The connection.
##
## hdr: The parsed header of the SMB message.
##
## .. bro:see:: smb_com_generic_andx smb_com_logoff_andx smb_com_negotiate
## smb_com_negotiate_response smb_com_nt_create_andx smb_com_read_andx
## smb_com_setup_andx smb_com_trans_mailslot smb_com_trans_pipe smb_com_trans_rap
## smb_com_transaction smb_com_transaction2 smb_com_tree_connect_andx
## smb_com_tree_disconnect smb_com_write_andx smb_error smb_get_dfs_referral
## smb_message
##
## .. todo:: Bro's current default configuration does not activate the protocol
## analyzer that generates this event; the corresponding script has not yet
## been ported to Bro 2.x. To still enable this event, one needs to
## register a port for it or add a DPD payload signature.
event smb_com_close%(c: connection, hdr: smb_hdr%);
## Generated for SMB/CIFS messages of type *logoff andx*.
##
## See `Wikipedia <http://en.wikipedia.org/wiki/Server_Message_Block>`__ for
## more information about the SMB/CIFS protocol. Bro's SMB/CIFS analyzer parses
## both SMB-over-NetBIOS on ports 138/139 and SMB-over-TCP on port 445.
##
## c: The connection.
##
## hdr: The parsed header of the SMB message.
##
## .. bro:see:: smb_com_close smb_com_generic_andx smb_com_negotiate
## smb_com_negotiate_response smb_com_nt_create_andx smb_com_read_andx
## smb_com_setup_andx smb_com_trans_mailslot smb_com_trans_pipe smb_com_trans_rap
## smb_com_transaction smb_com_transaction2 smb_com_tree_connect_andx
## smb_com_tree_disconnect smb_com_write_andx smb_error smb_get_dfs_referral
## smb_message
##
## .. todo:: Bro's current default configuration does not activate the protocol
## analyzer that generates this event; the corresponding script has not yet
## been ported to Bro 2.x. To still enable this event, one needs to
## register a port for it or add a DPD payload signature.
event smb_com_logoff_andx%(c: connection, hdr: smb_hdr%);
## Generated for SMB/CIFS messages that indicate an error. This event is
## triggered by an SMB header including a status that signals an error.
##
## c: The connection.
##
## hdr: The parsed header of the SMB message.
##
## cmd: The SMB command code.
##
## cmd_str: A string mnemonic of the SMB command code.
##
## data: The raw SMB message body, i.e., the data starting after the SMB header.
##
## .. bro:see:: smb_com_close smb_com_generic_andx smb_com_logoff_andx
## smb_com_negotiate smb_com_negotiate_response smb_com_nt_create_andx
## smb_com_read_andx smb_com_setup_andx smb_com_trans_mailslot
## smb_com_trans_pipe smb_com_trans_rap smb_com_transaction smb_com_transaction2
## smb_com_tree_connect_andx smb_com_tree_disconnect smb_com_write_andx
## smb_get_dfs_referral smb_message
##
## .. todo:: Bro's current default configuration does not activate the protocol
## analyzer that generates this event; the corresponding script has not yet
## been ported to Bro 2.x. To still enable this event, one needs to
## register a port for it or add a DPD payload signature.
event smb_error%(c: connection, hdr: smb_hdr, cmd: count, cmd_str: string, data: string%);

View file

@ -0,0 +1,69 @@
# Packet Documentation
# http://msdn.microsoft.com/en-us/library/dd305039.aspx
type TDS_PDU = record {
header: TDS_Header;
message: TDS_Message(header);
} &byteorder=bigendian &length=header.len;
type TDS_Header = record {
message_type: uint8; # http://msdn.microsoft.com/en-us/library/dd304214.aspx
status: uint8; # http://msdn.microsoft.com/en-us/library/dd358342.aspx
len: uint16;
spid: uint16; # process id. server needs to send it. client can too.
packet_id: uint8;
window: uint8; # should be 0
} &let {
eom: bool = ((status>>1) & 1) == 1;
};
type TDS_Message(h: TDS_Header) = case h.message_type of {
0x01 -> sql_batch: SQL_Batch(h);
# 0x04 -> token_stream: TDS_Token[];
default -> blah : bytestring &transient &restofdata;
};
function proc_testing(a: SQL_Batch): bool
%{
printf("%.6f query: %s\n", network_time(), smb2_string2stringval(${a.query})->CheckString());
return true;
%}
type SQL_Batch(h: TDS_Header) = record {
#total_len : uint32;
#
#header_len : uint32;
#header_type : uint16;
#trans_descriptor : uint64;
#outstanding_requests : uint32;
#query: SMB2_string(total_len-header_len);
query: SMB2_string(h.len-8);
} &let {
proc: bool = proc_testing(this);
};
type TDS_Token = record {
token_type: uint8;
token: case token_type of {
0xE3 -> envchange : TDS_Token_EnvChange;
0xAB -> info : TDS_Token_Info;
default -> blah : bytestring &transient &restofdata;
};
};
type TDS_Token_EnvChange = record {
len: uint16;
envchange_type: uint8;
new_value: bytestring &length=len-2;
#tab: RE/\x09/;
#old_value: bytestring &length=
};
type TDS_Token_Info = record {
};

View file

@ -0,0 +1,280 @@
enum SMBVersion {
SMB1 = 0xff534d42, # \xffSMB
SMB2 = 0xfe534d42, # \xfeSMB
};
enum TransactionType {
SMB_MAILSLOT_BROWSE, # \MAILSLOT\BROWSE - MS Browse Protocol
SMB_MAILSLOT_LANMAN, # \MAILSLOT\LANMAN - deprecated cmds
SMB_PIPE, # \PIPE\* named pipes?
SMB_RAP, # \PIPE\LANMAN - remote administration protocol
SMB_UNKNOWN, # there are probably lots of these
};
enum SMB_Command {
SMB_COM_CREATE_DIRECTORY = 0x00,
SMB_COM_DELETE_DIRECTORY = 0x01,
SMB_COM_OPEN = 0x02,
SMB_COM_CREATE = 0x03,
SMB_COM_CLOSE = 0x04,
SMB_COM_FLUSH = 0x05,
SMB_COM_DELETE = 0x06,
SMB_COM_RENAME = 0x07,
SMB_COM_QUERY_INFORMATION = 0x08,
SMB_COM_SET_INFORMATION = 0x09,
SMB_COM_READ = 0x0A,
SMB_COM_WRITE = 0x0B,
SMB_COM_LOCK_BYTE_RANGE = 0x0C,
SMB_COM_UNLOCK_BYTE_RANGE = 0x0D,
SMB_COM_CREATE_TEMPORARY = 0x0E,
SMB_COM_CREATE_NEW = 0x0F,
SMB_COM_CHECK_DIRECTORY = 0x10,
SMB_COM_PROCESS_EXIT = 0x11,
SMB_COM_SEEK = 0x12,
SMB_COM_LOCK_AND_READ = 0x13,
SMB_COM_WRITE_AND_UNLOCK = 0x14,
SMB_COM_READ_RAW = 0x1A,
SMB_COM_READ_MPX = 0x1B,
SMB_COM_READ_MPX_SECONDARY = 0x1C,
SMB_COM_WRITE_RAW = 0x1D,
SMB_COM_WRITE_MPX = 0x1E,
SMB_COM_WRITE_MPX_SECONDARY = 0x1F,
SMB_COM_WRITE_COMPLETE = 0x20,
SMB_COM_QUERY_SERVER = 0x21,
SMB_COM_SET_INFORMATION2 = 0x22,
SMB_COM_QUERY_INFORMATION2 = 0x23,
SMB_COM_LOCKING_ANDX = 0x24,
SMB_COM_TRANSACTION = 0x25,
SMB_COM_TRANSACTION_SECONDARY = 0x26,
SMB_COM_IOCTL = 0x27,
SMB_COM_IOCTL_SECONDARY = 0x28,
SMB_COM_COPY = 0x29,
SMB_COM_MOVE = 0x2A,
SMB_COM_ECHO = 0x2B,
SMB_COM_WRITE_AND_CLOSE = 0x2C,
SMB_COM_OPEN_ANDX = 0x2D,
SMB_COM_READ_ANDX = 0x2E,
SMB_COM_WRITE_ANDX = 0x2F,
SMB_COM_NEW_FILE_SIZE = 0x30,
SMB_COM_CLOSE_AND_TREE_DISC = 0x31,
SMB_COM_TRANSACTION2 = 0x32,
SMB_COM_TRANSACTION2_SECONDARY = 0x33,
SMB_COM_FIND_CLOSE2 = 0x34,
SMB_COM_FIND_NOTIFY_CLOSE = 0x35,
SMB_COM_TREE_CONNECT = 0x70,
SMB_COM_TREE_DISCONNECT = 0x71,
SMB_COM_NEGOTIATE = 0x72,
SMB_COM_SESSION_SETUP_ANDX = 0x73,
SMB_COM_LOGOFF_ANDX = 0x74,
SMB_COM_TREE_CONNECT_ANDX = 0x75,
SMB_COM_QUERY_INFORMATION_DISK = 0x80,
SMB_COM_SEARCH = 0x81,
SMB_COM_FIND = 0x82,
SMB_COM_FIND_UNIQUE = 0x83,
SMB_COM_FIND_CLOSE = 0x84,
SMB_COM_NT_TRANSACT = 0xA0,
SMB_COM_NT_TRANSACT_SECONDARY = 0xA1,
SMB_COM_NT_CREATE_ANDX = 0xA2,
SMB_COM_NT_CANCEL = 0xA4,
SMB_COM_NT_RENAME = 0xA5,
SMB_COM_OPEN_PRINT_FILE = 0xC0,
SMB_COM_WRITE_PRINT_FILE = 0xC1,
SMB_COM_CLOSE_PRINT_FILE = 0xC2,
SMB_COM_GET_PRINT_QUEUE = 0xC3,
SMB_COM_READ_BULK = 0xD8,
SMB_COM_WRITE_BULK = 0xD9,
SMB_COM_WRITE_BULK_DATA = 0xDA,
};
enum SMB_Status {
# 0x000
STATUS_SUCCESS = 0x00000000,
STATUS_NOTIFY_ENUM_DIR = 0x0000010C,
STATUS_INVALID_SMB = 0x00010002,
STATUS_SMB_BAD_TID = 0x00050002,
STATUS_SMB_BAD_FID = 0x00060001,
STATUS_OS2_INVALID_ACCESS = 0x000C0001,
# 0x001
STATUS_SMB_BAD_COMMAND = 0x00160002,
# 0x005
STATUS_SMB_BAD_UID = 0x005B0002,
# 0x007
STATUS_OS2_NO_MORE_SIDS = 0x00710001,
STATUS_OS2_INVALID_LEVEL = 0x007C0001,
# 0x008
STATUS_OS2_NEGATIVE_SEEK = 0x00830001,
# 0x00A
STATUS_OS2_CANCEL_VIOLATION = 0x00AD0001,
STATUS_OS2_ATOMIC_LOCKS_NOT_SUPPORTED = 0x00AE0001,
# 0x00F
STATUS_SMB_USE_MPX = 0x00FA0002,
STATUS_SMB_USE_STANDARD = 0x00FB0002,
STATUS_SMB_CONTINUE_MPX = 0x00FC0002,
# 0x01
STATUS_OS2_CANNOT_COPY = 0x010A0001,
STATUS_OS2_EAS_DIDNT_FIT = 0x01130001,
# 0x03
STATUS_OS2_EA_ACCESS_DENIED = 0x03E20001,
# 0x8
STATUS_BUFFER_OVERFLOW = 0x80000005,
STATUS_NO_MORE_FILES = 0x80000006,
STATUS_DEVICE_PAPER_EMPTY = 0x8000000E,
STATUS_STOPPED_ON_SYMLINK = 0x8000002D,
# 0xC000000
STATUS_UNSUCCESSFUL = 0xC0000001,
STATUS_NOT_IMPLEMENTED = 0xC0000002,
STATUS_INVALID_INFO_CLASS = 0xC0000003,
STATUS_INVALID_HANDLE = 0xC0000008,
STATUS_INVALID_PARAMETER = 0xC000000D,
STATUS_NO_SUCH_DEVICE = 0xC000000E,
STATUS_NO_SUCH_FILE = 0xC000000F,
# 0xC000001
STATUS_INVALID_DEVICE_REQUEST = 0xC0000010,
STATUS_END_OF_FILE = 0xC0000011,
STATUS_WRONG_VOLUME = 0xC0000012,
STATUS_NONEXISTENT_SECTOR = 0xC0000015,
STATUS_NO_MEDIA_IN_DEVICE = 0xC0000013,
STATUS_MORE_PROCESSING_REQUIRED = 0xC0000016,
STATUS_INVALID_LOCK_SEQUENCE = 0xC000001E,
STATUS_INVALID_VIEW_SIZE = 0xC000001F,
# 0xC000002
STATUS_ALREADY_COMMITTED = 0xC0000021,
STATUS_ACCESS_DENIED = 0xC0000022,
STATUS_BUFFER_TOO_SMALL = 0xC0000023,
STATUS_OBJECT_TYPE_MISMATCH = 0xC0000024,
STATUS_HANDLE_NOT_CLOSABLE = 0xC0000025,
# 0xC000003
STATUS_DISK_CORRUPT_ERROR = 0xC0000032,
STATUS_OBJECT_NAME_NOT_FOUND = 0xC0000034,
STATUS_OBJECT_NAME_COLLISION = 0xC0000035,
STATUS_PORT_DISCONNECTED = 0xC0000037,
STATUS_OBJECT_PATH_INVALID = 0xC0000039,
STATUS_OBJECT_PATH_NOT_FOUND = 0xC000003A,
STATUS_OBJECT_PATH_SYNTAX_BAD = 0xC000003B,
STATUS_DATA_ERROR = 0xC000003E,
STATUS_CRC_ERROR = 0xC000003F,
# 0xC000004
STATUS_SECTION_TOO_BIG = 0xC0000040,
STATUS_PORT_CONNECTION_REFUSED = 0xC0000041,
STATUS_INVALID_PORT_HANDLE = 0xC0000042,
STATUS_SHARING_VIOLATION = 0xC0000043,
STATUS_THREAD_IS_TERMINATING = 0xC000004B,
STATUS_EAS_NOT_SUPPORTED = 0xC000004F,
# 0xC000005
STATUS_EA_TOO_LARGE = 0xC0000050,
STATUS_FILE_LOCK_CONFLICT = 0xC0000054,
STATUS_LOCK_NOT_GRANTED = 0xC0000055,
STATUS_DELETE_PENDING = 0xC0000056,
# 0xC000006
STATUS_PRIVILEGE_NOT_HELD = 0xC0000061,
STATUS_LOGON_FAILURE = 0xC000006D,
STATUS_WRONG_PASSWORD = 0xC000006A,
STATUS_INVALID_LOGON_HOURS = 0xC000006F,
# 0xC000007
STATUS_INVALID_WORKSTATION = 0xC0000070,
STATUS_PASSWORD_EXPIRED = 0xC0000071,
STATUS_ACCOUNT_DISABLED = 0xC0000072,
STATUS_RANGE_NOT_LOCKED = 0xC000007E,
STATUS_DISK_FULL = 0xC000007F,
# 0xC000009
STATUS_TOO_MANY_PAGING_FILES = 0xC0000097,
STATUS_DFS_EXIT_PATH_FOUND = 0xC000009B,
STATUS_DEVICE_DATA_ERROR = 0xC000009C,
# 0xC00000A
STATUS_MEDIA_WRITE_PROTECTED = 0xC00000A2,
STATUS_BAD_IMPERSONATION_LEVEL = 0xC00000A5,
STATUS_INSTANCE_NOT_AVAILABLE = 0xC00000AB,
STATUS_PIPE_NOT_AVAILABLE = 0xC00000AC,
STATUS_PIPE_STATE = 0xC00000AD,
STATUS_PIPE_BUSY = 0xC00000AE,
STATUS_ILLEGAL_FUNCTION = 0xC00000AF,
# 0xC00000B
STATUS_PIPE_DISCONNECTED = 0xC00000B0,
STATUS_PIPE_CLOSING = 0xC00000B1,
STATUS_INVALID_READ_MODE = 0xC00000B4,
STATUS_IO_TIMEOUT = 0xC00000B5,
STATUS_FILE_IS_A_DIRECTORY = 0xC00000BA,
STATUS_NOT_SUPPORTED = 0xC00000BB,
# 0xC00000C
STATUS_UNEXPECTED_NETWORK_ERROR = 0xC00000C4,
STATUS_PRINT_QUEUE_FULL = 0xC00000C6,
STATUS_NO_SPOOL_SPACE = 0xC00000C7,
STATUS_PRINT_CANCELLED = 0xC00000C8,
STATUS_NETWORK_NAME_DELETED = 0xC00000C9,
STATUS_NETWORK_ACCESS_DENIED = 0xC00000CA,
STATUS_BAD_DEVICE_TYPE = 0xC00000CB,
STATUS_BAD_NETWORK_NAME = 0xC00000CC,
STATUS_TOO_MANY_SESSIONS = 0xC00000CE,
# 0xC00000D
STATUS_REQUEST_NOT_ACCEPTED = 0xC00000D0,
STATUS_NOT_SAME_DEVICE = 0xC00000D4,
STATUS_FILE_RENAMED = 0xC00000D5,
STATUS_PIPE_EMPTY = 0xC00000D9,
# 0xC00000F
STATUS_REDIRECTOR_NOT_STARTED = 0xC00000FB,
# 0xC00001
STATUS_DIRECTORY_NOT_EMPTY = 0xC0000101,
STATUS_PROCESS_IS_TERMINATING = 0xC000010A,
STATUS_TOO_MANY_OPENED_FILES = 0xC000011F,
STATUS_CANNOT_DELETE = 0xC0000121,
STATUS_FILE_DELETED = 0xC0000123,
STATUS_FILE_CLOSED = 0xC0000128,
STATUS_INVALID_DEVICE_STATE = 0xC0000184,
STATUS_ACCOUNT_EXPIRED = 0xC0000193,
# 0xC00002
STATUS_USER_SESSION_DELETED = 0xC0000203,
STATUS_INSUFF_SERVER_RESOURCES = 0xC0000205,
STATUS_PASSWORD_MUST_CHANGE = 0xC0000224,
STATUS_ACCOUNT_LOCKED_OUT = 0xC0000234,
STATUS_PATH_NOT_COVERED = 0xC0000257,
# 0xC00003
STATUS_NETWORK_SESSION_EXPIRED = 0xC000035C,
# 0xC0002
STATUS_SMB_TOO_MANY_UIDS = 0xC000205A,
# 0xF
STATUS_SMB_NO_SUPPORT = 0xFFFF0002,
};
function determine_transaction_type(setup_count: int, name: SMB_string): TransactionType
%{
// This logic needs to be verified! the relationship between
// setup_count and type is very unclear.
if ( name == NULL )
{
return SMB_UNKNOWN;
}
//if ( bytestring_caseprefix( extract_string(name),
// "\\PIPE\\LANMAN" ) )
// {
// return SMB_RAP;
// }
//
//if ( bytestring_caseprefix( extract_string(name), "\\MAILSLOT\\LANMAN" ) )
// {
// return SMB_MAILSLOT_LANMAN;
// //return SMB_MAILSLOT_BROWSE;
// }
//
//if ( bytestring_caseprefix( extract_string(name), "\\MAILSLOT\\NET\\NETLOGON" ) )
// {
// /* Don't really know what to do here, its got a Mailslot
// * type but its a deprecated packet format that handles
// * old windows logon
// */
// return SMB_UNKNOWN;
// }
//
if ( ${name.u.s}->size() == 14 && ${name.u.s[0]} == '\\' && ${name.u.s[2]} == 'P' && ${name.u.s[4]} == 'I' && ${name.u.s[6]} == 'P' && ${name.u.s[8]} == 'E' && ${name.u.s[10]} == '\\')
{
return SMB_PIPE;
}
//if ( setup_count == 3 ||
// bytestring_caseprefix( extract_string(name), "\\MAILSLOT\\" ) )
// {
// return SMB_MAILSLOT_BROWSE;
// }
return SMB_UNKNOWN;
%}

View file

@ -0,0 +1,29 @@
refine connection SMB_Conn += {
%member{
analyzer::Analyzer *gssapi;
%}
%init{
gssapi = 0;
%}
%cleanup{
if ( gssapi )
{
gssapi->Done();
delete gssapi;
}
%}
function forward_gssapi(data: bytestring, is_orig: bool): bool
%{
if ( ! gssapi )
gssapi = analyzer_mgr->InstantiateAnalyzer("GSSAPI", bro_analyzer()->Conn());
if ( gssapi )
gssapi->DeliverStream(${data}.length(), ${data}.begin(), is_orig);
return true;
%}
};

View file

@ -1,4 +1,3 @@
enum SMB_MailSlot_opcode {
HOST_ANNOUNCEMENT = 1,
ANNOUCEMENT_REQUEST = 2,

View file

@ -1,19 +1,56 @@
# this won't work correctly yet, since sometimes the parameters
# field in the transaction takes up all of the data field
%extern{
#include "../dce-rpc/DCE_RPC.h"
%}
type SMB_Pipe_message( unicode: bool, byte_count: uint16, sub_cmd: uint16 ) = record {
refine connection SMB_Conn += {
%member{
map<uint16,bool> tree_is_pipe_map;
map<uint64,analyzer::dce_rpc::DCE_RPC_Analyzer*> fid_to_analyzer_map;;
%}
# there's a problem with byte_count here, not sure why ... its
# not the real length of the rest of the packet
data : bytestring &restofdata;
%cleanup{
// Iterate all of the analyzers and destroy them.
for ( auto kv : fid_to_analyzer_map )
{
if ( kv.second )
{
kv.second->Done();
delete kv.second;
}
}
%}
} &byteorder = littleendian;
type SMB_RAP_message( unicode: bool, byte_count: uint16 ) = record {
function get_tree_is_pipe(tree_id: uint16): bool
%{
if ( tree_is_pipe_map.count(tree_id) > 0 )
return tree_is_pipe_map.at(tree_id);
else
return false;
%}
rap_code : uint16;
param_desc : SMB_string(unicode, offsetof(param_desc) );
data_desc : SMB_string(unicode, offsetof(data_desc) );
data : bytestring &restofdata;
function set_tree_is_pipe(tree_id: uint16, is_pipe: bool): bool
%{
tree_is_pipe_map[tree_id] = is_pipe;
return true;
%}
} &byteorder = littleendian;
function forward_dce_rpc(pipe_data: bytestring, fid: uint64, is_orig: bool): bool
%{
analyzer::dce_rpc::DCE_RPC_Analyzer *pipe_dcerpc;
if ( fid_to_analyzer_map.count(fid) == 0 )
{
pipe_dcerpc = (analyzer::dce_rpc::DCE_RPC_Analyzer *)analyzer_mgr->InstantiateAnalyzer("DCE_RPC", bro_analyzer()->Conn());
pipe_dcerpc->SetFileID(fid);
fid_to_analyzer_map[fid] = pipe_dcerpc;
}
else
{
pipe_dcerpc = fid_to_analyzer_map.at(fid);
}
pipe_dcerpc->DeliverStream(${pipe_data}.length(), ${pipe_data}.begin(), is_orig);
return true;
%}
};

View file

@ -1,465 +0,0 @@
# CIFS/SMB
# TODO:
# - Built support for unicode strings
# - Unicode as an implicit attribute (as byteorder)
# - &truncation_ok attribute for the last field of a record to deal with partial data
enum TransactionType {
SMB_MAILSLOT_BROWSE, # \MAILSLOT\BROWSE - MS Browse Protocol
SMB_MAILSLOT_LANMAN, # \MAILSLOT\LANMAN - deprecated cmds
SMB_PIPE, # \PIPE\* named pipes?
SMB_RAP, # \PIPE\LANMAN - remote administration protocol
SMB_UNKNOWN, # there's probably lots of these
};
function extract_string(s: SMB_string) : const_bytestring
%{
int length = 0;
char* buf;
const char* sp;
if( s->val_case_index() == 0 )
{
length = s->a()->size();
buf = new char[ length ];
for( int i = 0; i < length; i++)
{
unsigned char t = (*(s->a()))[i];
buf[i] = t;
}
}
else
{
length = s->u()->s()->size();
buf = new char[ length ];
for( int i = 0; i < length; i++)
{
unsigned short temp = (*(s->u()->s()))[i];
buf[i] = temp & 0xff;
}
}
return bytestring((uint8*) buf, length);
%}
function determine_transaction_type(setup_count: int, name: SMB_string): TransactionType
%{
// This logic needs to be verified! the relationship between
// setup_count and type is very unclear.
if ( name == NULL )
return SMB_UNKNOWN;
if ( bytestring_caseprefix( extract_string(name),
"\\PIPE\\LANMAN" ) )
{
return SMB_RAP;
}
else if ( bytestring_caseprefix( extract_string(name),
"\\MAILSLOT\\LANMAN" ) )
{
return SMB_MAILSLOT_LANMAN;
//return SMB_MAILSLOT_BROWSE;
}
else if ( bytestring_caseprefix( extract_string(name),
"\\MAILSLOT\\NET\\NETLOGON" ) )
{
/* Don't really know what to do here, its got a Mailslot
* type but its a depricated packet format that handles
* old windows logon
*/
return SMB_UNKNOWN;
}
else if(setup_count == 2 ||
bytestring_caseprefix( extract_string(name), "\\PIPE\\" ) )
{
return SMB_PIPE;
}
else if (setup_count == 3 ||
bytestring_caseprefix( extract_string(name), "\\MAILSLOT\\" ) )
{
return SMB_MAILSLOT_BROWSE;
}
else
return SMB_UNKNOWN;
%}
function name_string(trans: SMB_transaction): SMB_string
%{
if( trans->trans_type() == 1 )
return trans->name();
else
return NULL;
%}
type SMB_dos_error = record {
error_class : uint8;
reserved : uint8;
error : uint16;
};
type SMB_error (err_status_type: int) = case err_status_type of {
0 -> dos_error: SMB_dos_error;
1 -> status: int32;
};
type SMB_header = record {
protocol : bytestring &length = 4;
command : uint8;
status : SMB_error(err_status_type);
flags : uint8;
flags2 : uint16;
pad : padding[12];
tid : uint16;
pid : uint16;
uid : uint16;
mid : uint16;
} &let {
err_status_type = (flags2 >> 14) & 1;
unicode = (flags2 >> 15) & 1;
} &byteorder = littleendian;
# TODO: compute this as
# let smb_header_length = sizeof(SMB_header);
let smb_header_length = 32;
type SMB_body = record {
word_count : uint8;
parameter_words : uint16[word_count];
byte_count : uint16;
# buffer : uint8[byte_count];
} &let {
body_length = 1 + word_count * 2 + 2 + byte_count;
} &byteorder = littleendian;
type SMB_ascii_string = uint8[] &until($element == 0);
type SMB_unicode_string(offset: int) = record {
pad : padding[offset & 1];
s : uint16[] &until($element == 0);
};
type SMB_string(unicode: bool, offset: int) = case unicode of {
true -> u: SMB_unicode_string(offset);
false -> a: SMB_ascii_string;
};
type SMB_time = record {
two_seconds : uint16;
minutes : uint16;
hours : uint16;
} &byteorder = littleendian;
type SMB_date = record {
day : uint16;
month : uint16;
year : uint16;
} &byteorder = littleendian;
type SMB_andx = record {
command : uint8;
reserved : uint8;
offset : uint16;
} &refcount;
type SMB_generic_andx = record {
word_count : uint8;
andx_u : case word_count of {
0 -> null : empty;
default -> andx : SMB_andx;
};
data : bytestring &restofdata;
} &byteorder = littleendian;
type SMB_dialect = record {
bufferformat : uint8; # must be 0x2
dialectname : SMB_ascii_string;
};
type SMB_negotiate = record {
word_count : uint8; # must be 0
byte_count : uint16;
dialects : SMB_dialect[] &length = byte_count;
} &byteorder = littleendian;
type SMB_negotiate_response = record {
word_count : uint8; # should be 1
dialect_index : uint16;
byte_count : uint16; # should be 0
} &byteorder = littleendian;
type SMB_negotiate_response_long(unicode: bool) = record {
word_count : uint8; # should be 13
dialect_index : uint16;
security_mode : uint16; # bit 0: 0=share 1=user, bit 1: 1=chalenge/response
max_buffer_size : uint16;
max_mpx_count : uint16;
max_number_vcs : uint16;
raw_mode : uint16;
session_key : uint32;
server_time : SMB_time;
server_date : SMB_date;
server_tz : uint16;
enc_key_len : uint16;
reserved : uint16; # must be 0
byte_count : uint16;
encryption_key : uint8[enc_key_len];
primary_domain : SMB_string(unicode, offsetof(primary_domain));
} &byteorder = littleendian;
# pre NT LM 0.12
type SMB_setup_andx_basic(unicode: bool) = record {
word_count : uint8;
andx : SMB_andx;
max_buffer_size : uint16;
max_mpx_count : uint16;
vc_number : uint16;
session_key : uint32;
passwd_length : uint8;
reserved : uint32;
byte_count : uint8;
password : uint8[passwd_length];
name : SMB_string(unicode, offsetof(name));
domain : SMB_string(unicode, offsetof(domain));
native_os : SMB_string(unicode, offsetof(native_os));
native_lanman : SMB_string(unicode, offsetof(native_lanman));
} &byteorder = littleendian;
type SMB_setup_andx_basic_response(unicode: bool) = record {
word_count : uint8;
andx : SMB_andx;
action : uint8;
byte_count : uint8;
native_os : SMB_string(unicode, offsetof(native_os));
native_lanman : SMB_string(unicode, offsetof(native_lanman));
primary_domain : SMB_string(unicode, offsetof(primary_domain));
} &byteorder = littleendian;
# NT LM 0.12 && CAP_EXTENDED_SECURITY
type SMB_setup_andx_ext(unicode: bool) = record {
word_count : uint8;
andx : SMB_andx;
max_buffer_size : uint16;
max_mpx_count : uint16;
vc_number : uint16;
session_key : uint32;
security_length : uint8;
reserved : uint32;
capabilities : uint32;
byte_count : uint8;
security_blob : uint8[security_length];
native_os : SMB_string(unicode, offsetof(native_os));
native_lanman : SMB_string(unicode, offsetof(native_lanman));
} &byteorder = littleendian;
type SMB_setup_andx_ext_response(unicode: bool) = record {
word_count : uint8;
andx : SMB_andx;
action : uint8;
security_length : uint8;
byte_count : uint8;
security_blob : uint8[security_length];
native_os : SMB_string(unicode, offsetof(native_os));
native_lanman : SMB_string(unicode, offsetof(native_lanman));
primary_domain : SMB_string(unicode, offsetof(primary_domain));
} &byteorder = littleendian;
type SMB_logoff_andx(unicode: bool) = record {
word_count : uint8;
andx : SMB_andx;
byte_count : uint16;
} &byteorder = littleendian;
type SMB_tree_connect_andx(unicode: bool) = record {
word_count : uint8;
andx : SMB_andx;
flags : uint16;
password_length : uint16;
byte_count : uint16;
password : uint8[password_length];
path : SMB_string(unicode, offsetof(path));
service : SMB_ascii_string;
} &byteorder = littleendian;
type SMB_close(unicode: bool) = record {
word_count : uint8;
fid : uint16;
time : SMB_time;
byte_count : uint16;
} &byteorder = littleendian;
type SMB_tree_disconnect(unicode: bool) = record {
word_count : uint8;
byte_count : uint16;
} &byteorder = littleendian;
type SMB_nt_create_andx(unicode: bool) = record {
word_count : uint8;
andx : SMB_andx;
reserved : uint8;
name_length : uint16;
flags : uint32;
rest_words : uint8[word_count * 2 - 11];
byte_count : uint16;
name : SMB_string(unicode, offsetof(name));
} &byteorder = littleendian;
type SMB_read_andx = record {
word_count : uint8;
andx : SMB_andx;
fid : uint16;
offset : uint32;
max_count : uint16;
min_count : uint16;
max_count_high : uint16;
remaining : uint16;
offset_high_u : case word_count of {
12-> offset_high : uint32;
10-> null : empty;
};
byte_count : uint16;
} &byteorder = littleendian;
type SMB_read_andx_response = record {
word_count : uint8;
andx : SMB_andx;
remaining : uint16;
data_compact : uint16;
reserved : uint16;
data_len : uint16;
data_offset : uint16;
data_len_high : uint16;
reserved2 : uint16[4];
byte_count : uint16;
pad : padding[padding_length];
data : bytestring &length = data_length;
# Chris: the length here is causing problems - could we be having
# issues with the packet format or is the data_length just not
# right. The problem is that the padding isn't always filled right,
# espeically when its not the first command in the packet.
#data : bytestring &restofdata;
} &let {
data_length = data_len_high * 0x10000 + data_len;
padding_length = byte_count - data_length;
} &byteorder = littleendian;
type SMB_write_andx = record {
word_count : uint8;
andx : SMB_andx;
fid : uint16;
offset : uint32;
reserved : uint32;
write_mode : uint16;
remaining : uint16;
data_len_high : uint16;
data_len : uint16;
data_offset : uint16;
rest_words : uint8[word_count * 2 - offsetof(rest_words) + 1];
byte_count : uint16;
pad : padding to data_offset - smb_header_length;
data : bytestring &length = data_length;
} &let {
data_length = data_len_high * 0x10000 + data_len;
} &byteorder = littleendian;
type SMB_write_andx_response = record {
word_count : uint8;
andx : SMB_andx;
count : uint16; # written bytes
remaining : uint16;
reserved : uint32;
byte_count : uint16;
} &byteorder = littleendian;
type SMB_transaction_data(unicode: bool, count: uint16, sub_cmd: uint16,
trans_type: TransactionType ) = case trans_type of {
SMB_MAILSLOT_BROWSE -> mailslot : 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_PIPE -> pipe : SMB_Pipe_message(unicode, count, sub_cmd);
SMB_UNKNOWN -> unknown : bytestring &restofdata;
default -> data : bytestring &restofdata;
};
type SMB_transaction(trans_type: int, unicode: bool) = record {
word_count : uint8;
total_param_count : uint16;
total_data_count : uint16;
max_param_count : uint16;
max_data_count : uint16;
max_setup_count : uint8;
reserved : uint8;
flags : uint16;
timeout : uint32;
reserved2 : uint16;
param_count : uint16;
param_offset : uint16;
data_count : uint16;
data_offset : uint16;
setup_count : uint8;
reserved3 : uint8;
setup : uint16[setup_count];
byte_count : uint16;
name_u : case trans_type of {
1 -> name: SMB_string(unicode, offsetof(name_u));
2 -> null: empty;
};
pad0 : padding to param_offset - smb_header_length;
parameters : bytestring &length = param_count;
pad1 : padding to data_offset - smb_header_length;
data : SMB_transaction_data(unicode, data_count, sub_cmd,
determine_transaction_type( setup_count, name_string( this )));
} &let {
# does this work?
sub_cmd : uint16 = setup_count ? setup[0] : 0;
} &byteorder = littleendian;
type SMB_transaction_secondary(unicode: bool) = record {
word_count : uint8;
total_param_count : uint16;
total_data_count : uint16;
param_count : uint16;
param_offset : uint16;
param_displacement : uint16;
data_count : uint16;
data_offset : uint16;
data_displacement : uint16;
fid : uint16;
byte_count : uint16;
pad0 : padding to param_offset - smb_header_length;
parameters : bytestring &length = param_count;
pad1 : padding to data_offset - smb_header_length;
data : SMB_transaction_data(unicode, data_count, 0, SMB_UNKNOWN);
} &byteorder = littleendian;
type SMB_transaction_response(unicode: bool) = record {
word_count : uint8;
total_param_count : uint16;
total_data_count : uint16;
reserved : uint16;
param_count : uint16;
param_offset : uint16;
param_displacement : uint16;
data_count : uint16;
data_offset : uint16;
data_displacement : uint16;
setup_count : uint8;
reserved2 : uint8;
setup : uint16[setup_count];
byte_count : uint16;
pad0 : padding to param_offset - smb_header_length;
parameters : bytestring &length = param_count;
pad1 : padding to data_offset - smb_header_length;
data : SMB_transaction_data(unicode, data_count, 0, SMB_UNKNOWN);
} &byteorder = littleendian;
type SMB_get_dfs_referral(unicode: bool) = record {
max_referral_level : uint16;
file_name : SMB_string(unicode, offsetof(file_name));
} &byteorder = littleendian;

View file

@ -0,0 +1,92 @@
function uint8s_to_stringval(data: uint8[]): StringVal
%{
int length = data->size();
uint8 buf[length];
for ( int i = 0; i < length; ++i)
buf[i] = (*data)[i];
const bytestring bs = bytestring(buf, length);
return utf16_bytestring_to_utf8_val(bs);
%}
function extract_string(s: SMB_string) : StringVal
%{
if ( s->unicode() == 0 )
{
int length = s->a()->size();
char buf[length];
for ( int i = 0; i < length; i++)
{
unsigned char t = (*(s->a()))[i];
buf[i] = t;
}
if ( length > 0 && buf[length-1] == 0x00 )
length--;
return new StringVal(length, buf);
}
else
{
return uint8s_to_stringval(s->u()->s());
}
%}
function smb_string2stringval(s: SMB_string) : StringVal
%{
return extract_string(s);
%}
function smb2_string2stringval(s: SMB2_string) : StringVal
%{
return uint8s_to_stringval(s->s());
%}
refine connection SMB_Conn += {
%member{
SMB_unicode_string *me;
%}
%init{
me = 0;
%}
function store_this_unicode_string(s: SMB_unicode_string): bool
%{
me = s;
return true;
%}
function get_prev_elem(): uint8
%{
if ( me && (me->s()->size() & 1) == 0 && me->s()->size() > 1 )
{
return me->s()->at(me->s()->size() - 2);
}
else
return 0xFF;
%}
};
type SMB_ascii_string = uint8[] &until($element == 0x00);
type SMB_unicode_string(offset: int) = record {
pad : uint8[offset & 1] &let {
# Save off a pointer to this string instance.
prev: bool = $context.connection.store_this_unicode_string(this);
};
# Access the end of the string stored in this instance
# to see if the previous character was a null.
s : uint8[] &until($element == 0x00 && $context.connection.get_prev_elem() == 0x00);
} &byteorder=littleendian;
type SMB_string(unicode: bool, offset: int) = case unicode of {
true -> u: SMB_unicode_string(offset);
false -> a: SMB_ascii_string;
};
type SMB2_string(len: int) = record {
s : uint8[len];
};

View file

@ -0,0 +1,51 @@
function SMB_BuildMACTimes(modify: uint64, access: uint64, create: uint64, change: uint64): BroVal
%{
RecordVal* r = new RecordVal(BifType::Record::SMB::MACTimes);
r->Assign(0, filetime2brotime(modify));
r->Assign(1, filetime2brotime(access));
r->Assign(2, filetime2brotime(create));
r->Assign(3, filetime2brotime(change));
return r;
%}
function filetime2brotime(ts: uint64): Val
%{
double secs = (ts / 10000000.0);
// Bro can't support times back to the 1600's
// so we subtract a lot of seconds.
Val* bro_ts = new Val(secs - 11644473600.0, TYPE_TIME);
return bro_ts;
%}
function time_from_lanman(t: SMB_time, d: SMB_date, tz: uint16): Val
%{
tm lTime;
lTime.tm_sec = ${t.two_seconds} * 2;
lTime.tm_min = ${t.minutes};
lTime.tm_hour = ${t.hours};
lTime.tm_mday = ${d.day};
lTime.tm_mon = ${d.month};
lTime.tm_year = 1980 + ${d.year};
double lResult = mktime(&lTime);
return new Val(lResult + tz, TYPE_TIME);
%}
type SMB_timestamp32 = uint32;
type SMB_timestamp = uint64;
type SMB_time = record {
two_seconds : uint16;
minutes : uint16;
hours : uint16;
} &byteorder = littleendian;
type SMB_date = record {
day : uint16;
month : uint16;
year : uint16;
} &byteorder = littleendian;

View file

@ -2,11 +2,135 @@
%include bro.pac
%extern{
#include "events.bif.h"
#include "analyzer/Manager.h"
#include "analyzer/Analyzer.h"
#include "smb1_events.bif.h"
#include "smb2_events.bif.h"
#include "types.bif.h"
#include "smb1_com_check_directory.bif.h"
#include "smb1_com_close.bif.h"
#include "smb1_com_create_directory.bif.h"
#include "smb1_com_echo.bif.h"
#include "smb1_com_logoff_andx.bif.h"
#include "smb1_com_negotiate.bif.h"
#include "smb1_com_nt_cancel.bif.h"
#include "smb1_com_nt_create_andx.bif.h"
#include "smb1_com_query_information.bif.h"
#include "smb1_com_read_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_disconnect.bif.h"
#include "smb1_com_write_andx.bif.h"
#include "smb2_com_close.bif.h"
#include "smb2_com_create.bif.h"
//#include "smb2_com_lock.bif.h"
#include "smb2_com_negotiate.bif.h"
#include "smb2_com_read.bif.h"
#include "smb2_com_session_setup.bif.h"
#include "smb2_com_set_info.bif.h"
#include "smb2_com_tree_connect.bif.h"
//#include "smb2_com_tree_disconnect.bif.h"
#include "smb2_com_write.bif.h"
%}
analyzer SMB withcontext { };
analyzer SMB withcontext {
connection: SMB_Conn;
flow: SMB_Flow;
};
%include smb-protocol.pac
connection SMB_Conn(bro_analyzer: BroAnalyzer) {
upflow = SMB_Flow(true);
downflow = SMB_Flow(false);
};
%include smb-strings.pac
%include smb-common.pac
%include smb-time.pac
%include smb-mailslot.pac
%include smb-pipe.pac
%include smb-gssapi.pac
# SMB1 Commands
%include smb1-com-check-directory.pac
%include smb1-com-close.pac
%include smb1-com-create-directory.pac
%include smb1-com-echo.pac
%include smb1-com-locking-andx.pac
%include smb1-com-logoff-andx.pac
%include smb1-com-negotiate.pac
%include smb1-com-nt-cancel.pac
%include smb1-com-nt-create-andx.pac
%include smb1-com-nt-transact.pac
%include smb1-com-query-information.pac
%include smb1-com-read-andx.pac
%include smb1-com-session-setup-andx.pac
%include smb1-com-transaction-secondary.pac
%include smb1-com-transaction.pac
%include smb1-com-transaction2.pac
%include smb1-com-tree-connect-andx.pac
%include smb1-com-tree-disconnect.pac
%include smb1-com-write-andx.pac
# SMB2 Commands
%include smb2-com-close.pac
%include smb2-com-create.pac
%include smb2-com-ioctl.pac
%include smb2-com-lock.pac
%include smb2-com-negotiate.pac
%include smb2-com-read.pac
%include smb2-com-session-setup.pac
%include smb2-com-set-info.pac
%include smb2-com-tree-connect.pac
%include smb2-com-tree-disconnect.pac
%include smb2-com-write.pac
type uint24 = record {
byte1 : uint8;
byte2 : uint8;
byte3 : uint8;
};
function to_int(num: uint24): uint32
%{
return (num->byte1() << 16) | (num->byte2() << 8) | num->byte3();
%}
type SMB_TCP(is_orig: bool) = record {
# These are technically NetBIOS fields but it's considered
# to be SMB directly over TCP. The fields are essentially
# the NBSS protocol but it's only used for framing here.
message_type : uint8;
len24 : uint24;
body : case message_type of {
# SMB/SMB2 packets are required to use NBSS session messages.
0 -> nbss : SMB_Protocol_Identifier(is_orig, len);
# TODO: support more nbss message types?
default -> skip : bytestring &transient &restofdata;
};
} &let {
len : uint32 = to_int(len24);
} &byteorder = littleendian &length=len+4;
type SMB_Protocol_Identifier(is_orig: bool, msg_len: uint32) = record {
# Sort of cheating by reading this in as an integer instead of a string.
protocol : uint32 &byteorder=bigendian;
smb_1_or_2 : case protocol of {
SMB1 -> smb1 : SMB_PDU(is_orig, msg_len);
SMB2 -> smb2 : SMB2_PDU(is_orig);
default -> unknown : empty;
};
};
%include smb1-protocol.pac
%include smb2-protocol.pac
flow SMB_Flow(is_orig: bool) {
flowunit = SMB_TCP(is_orig) withcontext(connection, this);
};

View file

@ -0,0 +1,37 @@
refine connection SMB_Conn += {
function proc_smb1_check_directory_request(header: SMB_Header, val: SMB1_check_directory_request): bool
%{
if ( smb1_check_directory_request )
BifEvent::generate_smb1_check_directory_request(bro_analyzer(), bro_analyzer()->Conn(),
BuildHeaderVal(header),
smb_string2stringval(${val.directory_name}));
return true;
%}
function proc_smb1_check_directory_response(header: SMB_Header, val: SMB1_check_directory_response): bool
%{
if ( smb1_check_directory_response )
BifEvent::generate_smb1_check_directory_response(bro_analyzer(), bro_analyzer()->Conn(),
BuildHeaderVal(header));
return true;
%}
};
type SMB1_check_directory_request(header: SMB_Header) = record {
word_count : uint8;
byte_count : uint16;
buffer_format : uint8; # must be 0x04
directory_name : SMB_string(header.unicode, offsetof(directory_name));
} &let {
proc : bool = $context.connection.proc_smb1_check_directory_request(header, this);
};
type SMB1_check_directory_response(header: SMB_Header) = record {
word_count : uint8;
byte_count : uint16;
} &let {
proc : bool = $context.connection.proc_smb1_check_directory_response(header, this);
};

View file

@ -0,0 +1,34 @@
refine connection SMB_Conn += {
function proc_smb1_close_request(h: SMB_Header, val: SMB1_close_request): bool
%{
if ( smb1_close_request )
BifEvent::generate_smb1_close_request(bro_analyzer(),
bro_analyzer()->Conn(),
BuildHeaderVal(h),
${val.file_id});
// This is commented out for the moment because it caused problems
// with extraction because the file kept having the same name due
// to repeatedly having the same file uid. This results in files
// effectively falling of SMB solely by expiration instead of
// manually being closed.
//file_mgr->EndOfFile(bro_analyzer()->GetAnalyzerTag(),
// bro_analyzer()->Conn(), h->is_orig());
return true;
%}
};
type SMB1_close_request(header: SMB_Header) = record {
word_count : uint8;
file_id : uint16;
last_modified_time : SMB_timestamp32;
byte_count : uint16;
} &let {
proc : bool = $context.connection.proc_smb1_close_request(header, this);
};

View file

@ -0,0 +1,36 @@
refine connection SMB_Conn += {
function proc_smb1_create_directory_request(header: SMB_Header, val: SMB1_create_directory_request): bool
%{
if ( smb1_create_directory_request )
BifEvent::generate_smb1_create_directory_request(bro_analyzer(), bro_analyzer()->Conn(),
BuildHeaderVal(header),
smb_string2stringval(${val.directory_name}));
return true;
%}
function proc_smb1_create_directory_response(header: SMB_Header, val: SMB1_create_directory_response): bool
%{
if ( smb1_create_directory_response )
BifEvent::generate_smb1_create_directory_response(bro_analyzer(), bro_analyzer()->Conn(),
BuildHeaderVal(header));
return true;
%}
};
type SMB1_create_directory_request(header: SMB_Header) = record {
word_count : uint8;
byte_count : uint16;
buffer_format : uint8;
directory_name : SMB_string(header.unicode, offsetof(directory_name));
} &let {
proc : bool = $context.connection.proc_smb1_create_directory_request(header, this);
};
type SMB1_create_directory_response(header: SMB_Header) = record {
word_count : uint8;
byte_count : uint16;
} &let {
proc : bool = $context.connection.proc_smb1_create_directory_response(header, this);
};

View file

@ -0,0 +1,43 @@
refine connection SMB_Conn += {
function proc_smb1_echo_request(header: SMB_Header, val: SMB1_echo_request): bool
%{
if ( smb1_echo_request )
BifEvent::generate_smb1_echo_request(bro_analyzer(), bro_analyzer()->Conn(),
${val.echo_count}, bytestring_to_val(${val.data}));
return true;
%}
function proc_smb1_echo_response(header: SMB_Header, val: SMB1_echo_response): bool
%{
if ( smb1_echo_response )
BifEvent::generate_smb1_echo_response(bro_analyzer(), bro_analyzer()->Conn(),
${val.seq_num}, bytestring_to_val(${val.data}));
return true;
%}
};
# http://msdn.microsoft.com/en-us/library/ee441746.aspx
type SMB1_echo_request(header: SMB_Header) = record {
word_count : uint8;
echo_count : uint16;
byte_count : uint16;
data : bytestring &length=byte_count;
} &let {
proc : bool = $context.connection.proc_smb1_echo_request(header, this);
};
# http://msdn.microsoft.com/en-us/library/ee441626.aspx
type SMB1_echo_response(header: SMB_Header) = record {
word_count : uint8;
seq_num : uint16;
byte_count : uint16;
data : bytestring &length=byte_count;
} &let {
proc : bool = $context.connection.proc_smb1_echo_response(header, this);
};

View file

@ -0,0 +1,59 @@
refine connection SMB_Conn += {
function proc_smb1_locking_andx_request(header: SMB_Header, val: SMB1_locking_andx_request): bool
%{
//printf("locking_andx_request\n");
return true;
%}
function proc_smb1_locking_andx_response(header: SMB_Header, val: SMB1_locking_andx_response): bool
%{
//printf("locking_andx_response\n");
return true;
%}
};
type LOCKING_ANDX_RANGE32 = record {
pid : uint16;
byte_offset : uint32;
byte_len : uint32;
};
type LOCKING_ANDX_RANGE64 = record {
pid : uint16;
pad : uint16;
byte_offset : uint64;
byte_len : uint64;
};
# http://msdn.microsoft.com/en-us/library/ee442004.aspx
type SMB1_locking_andx_request(header: SMB_Header) = record {
word_count : uint8;
andx : SMB_andx;
file_id : uint16;
type_of_lock : uint8;
new_op_lock_level : uint8;
timeout : uint32;
num_requested_unlocks : uint16;
num_requested_locks : uint16;
bytecount : uint16;
unlocks : case $context.connection.get_offset_len() of {
32 -> unlocks32 : LOCKING_ANDX_RANGE32[num_requested_unlocks];
64 -> unlocks64 : LOCKING_ANDX_RANGE64[num_requested_unlocks];
};
locks : case $context.connection.get_offset_len() of {
32 -> locks32 : LOCKING_ANDX_RANGE32[num_requested_locks];
64 -> locks64 : LOCKING_ANDX_RANGE64[num_requested_locks];
};
} &let {
proc : bool = $context.connection.proc_smb1_locking_andx_request(header, this);
};
# http://msdn.microsoft.com/en-us/library/ee441519.aspx
type SMB1_locking_andx_response(header: SMB_Header) = record {
} &let {
proc : bool = $context.connection.proc_smb1_locking_andx_response(header, this);
};

View file

@ -0,0 +1,19 @@
refine connection SMB_Conn += {
function proc_smb1_logoff_andx(header: SMB_Header, val: SMB1_logoff_andx): bool
%{
if ( smb1_logoff_andx )
BifEvent::generate_smb1_logoff_andx(bro_analyzer(), bro_analyzer()->Conn(), ${val.is_orig});
return true;
%}
};
type SMB1_logoff_andx(header: SMB_Header, is_orig: bool) = record {
word_count : uint8;
andx : SMB_andx;
byte_count : uint16;
} &let {
proc : bool = $context.connection.proc_smb1_logoff_andx(header, this);
};

View file

@ -0,0 +1,250 @@
# This is an original Core Protocol command.
#
# This command is used to initiate an SMB connection between the
# client and the server. An SMB_COM_NEGOTIATE exchange MUST be
# completed before any other SMB messages are sent to the server.
#
# There MUST be only one SMB_COM_NEGOTIATE exchange per SMB
# connection. Subsequent SMB_COM_NEGOTIATE requests received by the
# server MUST be rejected with error responses. The server MUST NOT
# take any other action.
refine connection SMB_Conn += {
function proc_smb1_negotiate_request(header: SMB_Header, val: SMB1_negotiate_request): bool
%{
if ( smb1_negotiate_request )
{
VectorVal* dialects = new VectorVal(string_vec);
for ( unsigned int i = 0; i < ${val.dialects}->size(); ++i )
{
StringVal* dia = smb_string2stringval((*${val.dialects})[i]->name());
dialects->Assign(i, dia);
}
BifEvent::generate_smb1_negotiate_request(bro_analyzer(), bro_analyzer()->Conn(),
BuildHeaderVal(header),
dialects);
}
return true;
%}
function proc_smb1_negotiate_response(header: SMB_Header, val: SMB1_negotiate_response): bool
%{
if ( smb1_negotiate_response )
{
RecordVal* response = new RecordVal(BifType::Record::SMB1::NegotiateResponse);
RecordVal* core;
RecordVal* lanman;
RecordVal* ntlm;
RecordVal* security;
RecordVal* raw;
RecordVal* capabilities;
switch ( ${val.word_count} )
{
case 0x01:
core = new RecordVal(BifType::Record::SMB1::NegotiateResponseCore);
core->Assign(0, new Val(${val.dialect_index}, TYPE_COUNT));
response->Assign(0, core);
break;
case 0x0d:
security = new RecordVal(BifType::Record::SMB1::NegotiateResponseSecurity);
security->Assign(0, new Val(${val.lanman.security_user_level}, TYPE_BOOL));
security->Assign(1, new Val(${val.lanman.security_challenge_response}, TYPE_BOOL));
raw = new RecordVal(BifType::Record::SMB1::NegotiateRawMode);
raw->Assign(0, new Val(${val.lanman.raw_read_supported}, TYPE_BOOL));
raw->Assign(1, new Val(${val.lanman.raw_write_supported}, TYPE_BOOL));
lanman = new RecordVal(BifType::Record::SMB1::NegotiateResponseLANMAN);
lanman->Assign(0, new Val(${val.word_count}, TYPE_COUNT));
lanman->Assign(1, new Val(${val.dialect_index}, TYPE_COUNT));
lanman->Assign(2, security);
lanman->Assign(3, new Val(${val.lanman.max_buffer_size}, TYPE_COUNT));
lanman->Assign(4, new Val(${val.lanman.max_mpx_count}, TYPE_COUNT));
lanman->Assign(5, new Val(${val.lanman.max_number_vcs}, TYPE_COUNT));
lanman->Assign(6, raw);
lanman->Assign(7, new Val(${val.lanman.session_key}, TYPE_COUNT));
lanman->Assign(8, time_from_lanman(${val.lanman.server_time}, ${val.lanman.server_date}, ${val.lanman.server_tz}));
lanman->Assign(9, bytestring_to_val(${val.lanman.encryption_key}));
lanman->Assign(10, smb_string2stringval(${val.lanman.primary_domain}));
response->Assign(1, lanman);
break;
case 0x11:
security = new RecordVal(BifType::Record::SMB1::NegotiateResponseSecurity);
security->Assign(0, new Val(${val.ntlm.security_user_level}, TYPE_BOOL));
security->Assign(1, new Val(${val.ntlm.security_challenge_response}, TYPE_BOOL));
security->Assign(2, new Val(${val.ntlm.security_signatures_enabled}, TYPE_BOOL));
security->Assign(3, new Val(${val.ntlm.security_signatures_required}, TYPE_BOOL));
capabilities = new RecordVal(BifType::Record::SMB1::NegotiateCapabilities);
capabilities->Assign(0, new Val(${val.ntlm.capabilities_raw_mode}, TYPE_BOOL));
capabilities->Assign(1, new Val(${val.ntlm.capabilities_mpx_mode}, TYPE_BOOL));
capabilities->Assign(2, new Val(${val.ntlm.capabilities_unicode}, TYPE_BOOL));
capabilities->Assign(3, new Val(${val.ntlm.capabilities_large_files}, TYPE_BOOL));
capabilities->Assign(4, new Val(${val.ntlm.capabilities_nt_smbs}, TYPE_BOOL));
capabilities->Assign(5, new Val(${val.ntlm.capabilities_rpc_remote_apis}, TYPE_BOOL));
capabilities->Assign(6, new Val(${val.ntlm.capabilities_status32}, TYPE_BOOL));
capabilities->Assign(7, new Val(${val.ntlm.capabilities_level_2_oplocks}, TYPE_BOOL));
capabilities->Assign(8, new Val(${val.ntlm.capabilities_lock_and_read}, TYPE_BOOL));
capabilities->Assign(9, new Val(${val.ntlm.capabilities_nt_find}, TYPE_BOOL));
capabilities->Assign(10, new Val(${val.ntlm.capabilities_dfs}, TYPE_BOOL));
capabilities->Assign(11, new Val(${val.ntlm.capabilities_infolevel_passthru}, TYPE_BOOL));
capabilities->Assign(12, new Val(${val.ntlm.capabilities_large_readx}, TYPE_BOOL));
capabilities->Assign(13, new Val(${val.ntlm.capabilities_large_writex}, TYPE_BOOL));
capabilities->Assign(14, new Val(${val.ntlm.capabilities_unix}, TYPE_BOOL));
capabilities->Assign(15, new Val(${val.ntlm.capabilities_bulk_transfer}, TYPE_BOOL));
capabilities->Assign(16, new Val(${val.ntlm.capabilities_compressed_data}, TYPE_BOOL));
capabilities->Assign(17, new Val(${val.ntlm.capabilities_extended_security}, TYPE_BOOL));
ntlm = new RecordVal(BifType::Record::SMB1::NegotiateResponseNTLM);
ntlm->Assign(0, new Val(${val.word_count}, TYPE_COUNT));
ntlm->Assign(1, new Val(${val.dialect_index}, TYPE_COUNT));
ntlm->Assign(2, security);
ntlm->Assign(3, new Val(${val.ntlm.max_buffer_size}, TYPE_COUNT));
ntlm->Assign(4, new Val(${val.ntlm.max_mpx_count}, TYPE_COUNT));
ntlm->Assign(5, new Val(${val.ntlm.max_number_vcs}, TYPE_COUNT));
ntlm->Assign(6, new Val(${val.ntlm.max_raw_size}, TYPE_COUNT));
ntlm->Assign(7, new Val(${val.ntlm.session_key}, TYPE_COUNT));
ntlm->Assign(8, capabilities);
ntlm->Assign(9, filetime2brotime(${val.ntlm.server_time}));
if ( ${val.ntlm.capabilities_extended_security} == false )
{
ntlm->Assign(10, bytestring_to_val(${val.ntlm.encryption_key}));
ntlm->Assign(11, smb_string2stringval(${val.ntlm.domain_name}));
}
else
{
ntlm->Assign(12, bytestring_to_val(${val.ntlm.server_guid}));
}
response->Assign(2, ntlm);
break;
}
BifEvent::generate_smb1_negotiate_response(bro_analyzer(), bro_analyzer()->Conn(), BuildHeaderVal(header), response);
}
return true;
%}
};
type SMB_dialect = record {
buffer_format : uint8; # must be 0x2 for dialect
name : SMB_string(0,0);
};
type SMB1_negotiate_request(header: SMB_Header) = record {
word_count: uint8; # must be 0
byte_count: uint16;
dialects: SMB_dialect[] &length=byte_count;
} &let {
proc : bool = $context.connection.proc_smb1_negotiate_request(header, this);
};
type SMB1_negotiate_response(header: SMB_Header) = record {
word_count: uint8;
dialect_index: uint16;
response: case word_count of {
0x01 -> core : SMB1_negotiate_core_response;
0x0d -> lanman : SMB1_negotiate_lanman_response(header);
0x11 -> ntlm : SMB1_negotiate_ntlm_response(header);
};
} &let {
proc: bool = $context.connection.proc_smb1_negotiate_response(header, this);
};
type SMB1_negotiate_core_response = record {
byte_count: uint16;
};
type SMB1_negotiate_lanman_response(header: SMB_Header) = record {
security_flags : uint16; # expanded in &let
max_buffer_size : uint16;
max_mpx_count : uint16;
max_number_vcs : uint16;
raw_mode : uint16; # expanded in &let
session_key : uint32;
server_time : SMB_time;
server_date : SMB_date;
server_tz : uint16;
encryption_key_length : uint16;
reserved : uint16; # must be zero
byte_count : uint16; # count of data bytes
encryption_key : bytestring &length=encryption_key_length;
primary_domain : SMB_string(header.unicode, offsetof(primary_domain));
} &let {
security_user_level : bool = ( security_flags & 0x1 ) > 0;
security_challenge_response : bool = ( security_flags & 0x2 ) > 0;
raw_read_supported : bool = ( raw_mode & 0x1 ) > 0;
raw_write_supported : bool = ( raw_mode & 0x2 ) > 0;
};
type SMB1_negotiate_ntlm_response(header: SMB_Header) = record {
security_flags : uint8; # Expanded in &let
max_mpx_count : uint16;
max_number_vcs : uint16;
max_buffer_size : uint32;
max_raw_size : uint32;
session_key : uint32;
capabilities : uint32; # Expanded in &let
server_time : uint64;
server_tz : uint16;
encryption_key_length : uint8;
byte_count : uint16;
encryption_key_present: case capabilities_extended_security of {
false -> encryption_key : bytestring &length=encryption_key_length;
true -> no_key : empty;
} &requires(capabilities_extended_security);
domain_name_present: case capabilities_extended_security of {
false -> domain_name : SMB_string(header.unicode, offsetof(domain_name_present));
true -> no_name : empty;
} &requires(capabilities_extended_security);
server_guid_present: case capabilities_extended_security of {
true -> server_guid : bytestring &length=16;
false -> no_guid : empty;
} &requires(capabilities_extended_security);
security_blob_present: case capabilities_extended_security of {
true -> security_blob : bytestring &length=(byte_count-16);
false -> no_blob : empty;
} &requires(capabilities_extended_security);
} &let {
security_user_level : bool = (security_flags & 0x1) > 0;
security_challenge_response : bool = (security_flags & 0x2) > 0;
security_signatures_enabled : bool = (security_flags & 0x4) > 0;
security_signatures_required : bool = (security_flags & 0x8) > 0;
capabilities_raw_mode : bool = (capabilities & 0x1) > 0;
capabilities_mpx_mode : bool = (capabilities & 0x2) > 0;
capabilities_unicode : bool = (capabilities & 0x4) > 0;
capabilities_large_files : bool = (capabilities & 0x8) > 0;
capabilities_nt_smbs : bool = (capabilities & 0x10) > 0;
capabilities_rpc_remote_apis : bool = (capabilities & 0x20) > 0;
capabilities_status32 : bool = (capabilities & 0x40) > 0;
capabilities_level_2_oplocks : bool = (capabilities & 0x80) > 0;
capabilities_lock_and_read : bool = (capabilities & 0x100) > 0;
capabilities_nt_find : bool = (capabilities & 0x200) > 0;
capabilities_dfs : bool = (capabilities & 0x1000) > 0;
capabilities_infolevel_passthru : bool = (capabilities & 0x2000) > 0;
capabilities_large_readx : bool = (capabilities & 0x4000) > 0;
capabilities_large_writex : bool = (capabilities & 0x8000) > 0;
capabilities_unix : bool = (capabilities & 0x00800000) > 0;
capabilities_reserved : bool = (capabilities & 0x02000000) > 0;
capabilities_bulk_transfer : bool = (capabilities & 0x20000000) > 0;
capabilities_compressed_data : bool = (capabilities & 0x40000000) > 0;
capabilities_extended_security : bool = (capabilities & 0x80000000) > 0;
gssapi_proc : bool = $context.connection.forward_gssapi(security_blob, false) &if(capabilities_extended_security);
};

View file

@ -0,0 +1,18 @@
refine connection SMB_Conn += {
function proc_smb1_nt_cancel_request(header: SMB_Header, val: SMB1_nt_cancel_request): bool
%{
if ( smb1_nt_cancel_request )
BifEvent::generate_smb1_nt_cancel_request(bro_analyzer(), bro_analyzer()->Conn(), BuildHeaderVal(header));
return true;
%}
};
type SMB1_nt_cancel_request(header: SMB_Header) = record {
word_count : uint8;
byte_count : uint16;
} &let {
proc : bool = $context.connection.proc_smb1_nt_cancel_request(header, this);
};

View file

@ -0,0 +1,103 @@
refine connection SMB_Conn += {
function proc_smb1_nt_create_andx_request(header: SMB_Header, val: SMB1_nt_create_andx_request): bool
%{
if ( smb1_nt_create_andx_request )
{
// name_length : uint16;
// flags : uint32;
// root_dir_file_id : uint32;
// desired_access : uint32;
// alloc_size : uint64;
// ext_file_attrs : uint32;
// share_access : uint32;
// create_disposition : uint32;
// create_options : uint32;
// impersonation_level : uint32;
// security_flags : uint8;
//
// byte_count : uint16;
// filename : SMB_string(header.unicode, offsetof(filename)) &length=name_length;
BifEvent::generate_smb1_nt_create_andx_request(bro_analyzer(),
bro_analyzer()->Conn(),
BuildHeaderVal(header),
smb_string2stringval(${val.filename}));
}
return true;
%}
function proc_smb1_nt_create_andx_response(header: SMB_Header, val: SMB1_nt_create_andx_response): bool
%{
if ( smb1_nt_create_andx_response )
{
BifEvent::generate_smb1_nt_create_andx_response(bro_analyzer(),
bro_analyzer()->Conn(),
BuildHeaderVal(header),
${val.file_id},
${val.end_of_file},
SMB_BuildMACTimes(${val.last_write_time},
${val.last_access_time},
${val.create_time},
${val.last_change_time}));
}
if ( ${val.end_of_file} > 0 )
{
//file_mgr->SetSize(${val.end_of_file},
// bro_analyzer()->GetAnalyzerTag(),
// bro_analyzer()->Conn(),
// header->is_orig());
}
return true;
%}
};
type SMB1_nt_create_andx_request(header: SMB_Header) = record {
word_count : uint8;
andx : SMB_andx;
reserved : uint8;
name_length : uint16;
flags : uint32;
root_dir_file_id : uint32;
desired_access : uint32;
alloc_size : uint64;
ext_file_attrs : uint32;
share_access : uint32;
create_disposition : uint32;
create_options : uint32;
impersonation_level : uint32;
security_flags : uint8;
byte_count : uint16;
filename : SMB_string(header.unicode, offsetof(filename));
andx_command : SMB_andx_command(header, 1, andx.command);
} &let {
proc : bool = $context.connection.proc_smb1_nt_create_andx_request(header, this);
};
type SMB1_nt_create_andx_response(header: SMB_Header) = record {
word_count : uint8;
andx : SMB_andx;
oplock_level : uint8;
file_id : uint16;
create_disposition : uint32;
create_time : SMB_timestamp;
last_access_time : SMB_timestamp;
last_write_time : SMB_timestamp;
last_change_time : SMB_timestamp;
ext_file_attrs : uint32;
allocation_size : uint64;
end_of_file : uint64;
resource_type : uint16;
nm_pipe_status : uint16;
directory : uint8;
byte_count : uint16;
} &let {
proc : bool = $context.connection.proc_smb1_nt_create_andx_response(header, this);
};

View file

@ -0,0 +1,29 @@
refine connection SMB_Conn += {
function proc_smb1_nt_transact_request(header: SMB_Header, val: SMB1_nt_transact_request): bool
%{
//printf("nt_transact_request\n");
return true;
%}
function proc_smb1_nt_transact_response(header: SMB_Header, val: SMB1_nt_transact_response): bool
%{
//printf("nt_transact_response\n");
return true;
%}
};
# http://msdn.microsoft.com/en-us/library/ee441534.aspx
type SMB1_nt_transact_request(header: SMB_Header) = record {
word_count : uint8;
} &let {
proc : bool = $context.connection.proc_smb1_nt_transact_request(header, this);
};
# http://msdn.microsoft.com/en-us/library/ee442112.aspx
type SMB1_nt_transact_response(header: SMB_Header) = record {
word_count : uint8;
} &let {
proc : bool = $context.connection.proc_smb1_nt_transact_response(header, this);
};

View file

@ -0,0 +1,77 @@
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;

View 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;

View file

@ -0,0 +1,41 @@
refine connection SMB_Conn += {
function proc_smb1_query_information_request(header: SMB_Header, val: SMB1_query_information_request): bool
%{
if ( smb1_query_information_request )
BifEvent::generate_smb1_query_information_request(bro_analyzer(),
bro_analyzer()->Conn(),
BuildHeaderVal(header),
smb_string2stringval(${val.filename}));
return true;
%}
function proc_smb1_query_information_response(header: SMB_Header, val: SMB1_query_information_response): bool
%{
//printf("query_information_response\n");
return true;
%}
};
type SMB1_query_information_request(header: SMB_Header) = record {
word_count : uint8;
byte_count : uint16;
buffer_format : uint8;
filename : SMB_string(header.unicode, offsetof(filename));
} &let {
proc : bool = $context.connection.proc_smb1_query_information_request(header, this);
};
type SMB1_query_information_response(header: SMB_Header) = record {
word_count : uint8;
file_attribs : uint16;
last_write_time : SMB_time;
file_size : uint32;
reserved : uint16[5];
byte_count : uint16 &check($element == 0);
} &let {
proc : bool = $context.connection.proc_smb1_query_information_response(header, this);
};

View file

@ -0,0 +1,91 @@
refine connection SMB_Conn += {
%member{
// Track read offsets to provide correct
// offsets for file manager.
std::map<uint16,uint64> read_offsets;
%}
function proc_smb1_read_andx_request(h: SMB_Header, val: SMB1_read_andx_request): bool
%{
if ( smb1_read_andx_request )
BifEvent::generate_smb1_read_andx_request(bro_analyzer(),
bro_analyzer()->Conn(),
BuildHeaderVal(h),
${val.file_id},
${val.offset},
${val.max_count});
read_offsets[${h.mid}] = ${val.offset};
return true;
%}
function proc_smb1_read_andx_response(h: SMB_Header, val: SMB1_read_andx_response): bool
%{
if ( smb1_read_andx_response )
BifEvent::generate_smb1_read_andx_response(bro_analyzer(),
bro_analyzer()->Conn(),
BuildHeaderVal(h),
${val.data_len});
if ( ! ${val.is_pipe} && ${val.data_len} > 0 )
{
uint64 offset = read_offsets[${h.mid}];
read_offsets.erase(${h.mid});
file_mgr->DataIn(${val.data}.begin(), ${val.data_len}, offset,
bro_analyzer()->GetAnalyzerTag(),
bro_analyzer()->Conn(), h->is_orig());
}
return true;
%}
};
type SMB1_read_andx_request(header: SMB_Header) = record {
word_count : uint8;
andx : SMB_andx;
file_id : uint16;
offset_low : uint32;
max_count_low : uint16;
min_count : uint16;
max_count_high : uint32;
remaining : uint16;
offset_high_u : case word_count of {
0x0C -> offset_high_tmp : uint32;
default -> null : empty;
};
byte_count : uint16;
} &let {
offset_high : uint32 = (word_count == 0x0C) ? offset_high_tmp : 0;
offset : uint64 = (offset_high * 0x10000) + offset_low;
max_count : uint64 = (max_count_high * 0x10000) + max_count_low;
proc : bool = $context.connection.proc_smb1_read_andx_request(header, this);
} &byteorder=littleendian;
type SMB1_read_andx_response(header: SMB_Header) = record {
word_count : uint8;
andx : SMB_andx;
available : uint16;
data_compact_mode : uint16;
reserved1 : uint16;
data_len_low : uint16;
data_offset : uint16;
data_len_high : uint16;
reserved2 : uint64;
byte_count : uint16;
pad : padding to data_offset - SMB_Header_length;
data : bytestring &length=data_len;
} &let {
is_pipe : bool = $context.connection.get_tree_is_pipe(header.tid);
pipe_proc : bool = $context.connection.forward_dce_rpc(data, 0, false) &if(is_pipe);
padding_len : uint8 = (header.unicode == 1) ? 1 : 0;
data_len : uint32 = (data_len_high << 16) + data_len_low;
proc : bool = $context.connection.proc_smb1_read_andx_response(header, this);
} &byteorder=littleendian;

View file

@ -0,0 +1,234 @@
## This SMB is used to further "Set up" the session normally just
## established via the negotiate protocol.
##
## One primary function is to perform a "user logon" in the case
## where the server is in user level security mode. The UID in the
## SMB header is set by the client to by the userid desired for the
## AccountName and validated by the AccountPassword.
refine connection SMB_Conn += {
function proc_smb1_session_setup_andx_request(header: SMB_Header, val: SMB1_session_setup_andx_request): bool
%{
if ( smb1_session_setup_andx_request )
{
RecordVal* request = new RecordVal(BifType::Record::SMB1::SessionSetupAndXRequest);
RecordVal* capabilities;
request->Assign(0, new Val(${val.word_count}, TYPE_COUNT));
switch ( ${val.word_count} ) {
case 10: // pre NT LM 0.12
request->Assign(1, new Val(${val.lanman.max_buffer_size}, TYPE_COUNT));
request->Assign(2, new Val(${val.lanman.max_mpx_count}, TYPE_COUNT));
request->Assign(3, new Val(${val.lanman.vc_number}, TYPE_COUNT));
request->Assign(4, new Val(${val.lanman.session_key}, TYPE_COUNT));
request->Assign(5, smb_string2stringval(${val.lanman.native_os}));
request->Assign(6, smb_string2stringval(${val.lanman.native_lanman}));
request->Assign(7, smb_string2stringval(${val.lanman.account_name}));
request->Assign(8, bytestring_to_val(${val.lanman.account_password}));
request->Assign(9, smb_string2stringval(${val.lanman.primary_domain}));
break;
case 12: // NT LM 0.12 with extended security
capabilities = new RecordVal(BifType::Record::SMB1::SessionSetupAndXCapabilities);
capabilities->Assign(0, new Val(${val.ntlm_extended_security.capabilities.unicode}, TYPE_BOOL));
capabilities->Assign(1, new Val(${val.ntlm_extended_security.capabilities.large_files}, TYPE_BOOL));
capabilities->Assign(2, new Val(${val.ntlm_extended_security.capabilities.nt_smbs}, TYPE_BOOL));
capabilities->Assign(3, new Val(${val.ntlm_extended_security.capabilities.status32}, TYPE_BOOL));
capabilities->Assign(4, new Val(${val.ntlm_extended_security.capabilities.level_2_oplocks}, TYPE_BOOL));
capabilities->Assign(5, new Val(${val.ntlm_extended_security.capabilities.nt_find}, TYPE_BOOL));
request->Assign(1, new Val(${val.ntlm_extended_security.max_buffer_size}, TYPE_COUNT));
request->Assign(2, new Val(${val.ntlm_extended_security.max_mpx_count}, TYPE_COUNT));
request->Assign(3, new Val(${val.ntlm_extended_security.vc_number}, TYPE_COUNT));
request->Assign(4, new Val(${val.ntlm_extended_security.session_key}, TYPE_COUNT));
request->Assign(5, smb_string2stringval(${val.ntlm_extended_security.native_os}));
request->Assign(6, smb_string2stringval(${val.ntlm_extended_security.native_lanman}));
request->Assign(13, capabilities);
break;
case 13: // NT LM 0.12 without extended security
capabilities = new RecordVal(BifType::Record::SMB1::SessionSetupAndXCapabilities);
capabilities->Assign(0, new Val(${val.ntlm_nonextended_security.capabilities.unicode}, TYPE_BOOL));
capabilities->Assign(1, new Val(${val.ntlm_nonextended_security.capabilities.large_files}, TYPE_BOOL));
capabilities->Assign(2, new Val(${val.ntlm_nonextended_security.capabilities.nt_smbs}, TYPE_BOOL));
capabilities->Assign(3, new Val(${val.ntlm_nonextended_security.capabilities.status32}, TYPE_BOOL));
capabilities->Assign(4, new Val(${val.ntlm_nonextended_security.capabilities.level_2_oplocks}, TYPE_BOOL));
capabilities->Assign(5, new Val(${val.ntlm_nonextended_security.capabilities.nt_find}, TYPE_BOOL));
request->Assign(1, new Val(${val.ntlm_nonextended_security.max_buffer_size}, TYPE_COUNT));
request->Assign(2, new Val(${val.ntlm_nonextended_security.max_mpx_count}, TYPE_COUNT));
request->Assign(3, new Val(${val.ntlm_nonextended_security.vc_number}, TYPE_COUNT));
request->Assign(4, new Val(${val.ntlm_nonextended_security.session_key}, TYPE_COUNT));
request->Assign(5, smb_string2stringval(${val.ntlm_nonextended_security.native_os}));
request->Assign(6, smb_string2stringval(${val.ntlm_nonextended_security.native_lanman}));
request->Assign(7, smb_string2stringval(${val.ntlm_nonextended_security.account_name}));
request->Assign(9, smb_string2stringval(${val.ntlm_nonextended_security.primary_domain}));
request->Assign(10, bytestring_to_val(${val.ntlm_nonextended_security.case_insensitive_password}));
request->Assign(11, bytestring_to_val(${val.ntlm_nonextended_security.case_sensitive_password}));
request->Assign(13, capabilities);
break;
}
BifEvent::generate_smb1_session_setup_andx_request(bro_analyzer(), bro_analyzer()->Conn(), BuildHeaderVal(header), request);
}
return true;
%}
function proc_smb1_session_setup_andx_response(header: SMB_Header, val: SMB1_session_setup_andx_response): bool
%{
if ( smb1_session_setup_andx_response )
{
RecordVal* response = new RecordVal(BifType::Record::SMB1::SessionSetupAndXResponse);
response->Assign(0, new Val(${val.word_count}, TYPE_COUNT));
switch ( ${val.word_count} )
{
case 3: // pre NT LM 0.12
response->Assign(1, new Val(${val.lanman.is_guest}, TYPE_BOOL));
response->Assign(2, smb_string2stringval(${val.lanman.native_os}));
response->Assign(3, smb_string2stringval(${val.lanman.native_lanman}));
response->Assign(4, smb_string2stringval(${val.lanman.primary_domain}));
break;
case 4: // NT LM 0.12
response->Assign(1, new Val(${val.ntlm.is_guest}, TYPE_BOOL));
response->Assign(2, smb_string2stringval(${val.ntlm.native_os}));
response->Assign(3, smb_string2stringval(${val.ntlm.native_lanman}));
response->Assign(4, smb_string2stringval(${val.ntlm.primary_domain}));
//response->Assign(5, bytestring_to_val(${val.ntlm.security_blob}));
break;
default: // Error!
break;
}
BifEvent::generate_smb1_session_setup_andx_response(bro_analyzer(),
bro_analyzer()->Conn(),
BuildHeaderVal(header),
response);
}
return true;
%}
};
type SMB1_session_setup_andx_request(header: SMB_Header) = record {
word_count : uint8;
lanman_or_ntlm : case word_count of {
0x0a -> lanman : SMB1_session_setup_andx_request_lanman(header);
0x0c -> ntlm_extended_security : SMB1_session_setup_andx_request_ntlm_extended_security(header);
0x0d -> ntlm_nonextended_security : SMB1_session_setup_andx_request_ntlm_nonextended_security(header);
};
} &let {
proc: bool = $context.connection.proc_smb1_session_setup_andx_request(header, this);
};
type SMB1_session_setup_andx_response(header: SMB_Header) = record {
word_count : uint8;
lanman_or_ntlm : case word_count of {
0x03 -> lanman: SMB1_session_setup_andx_response_lanman(header);
0x04 -> ntlm: SMB1_session_setup_andx_response_ntlm(header);
default -> error: uint16;
};
} &let {
proc: bool = $context.connection.proc_smb1_session_setup_andx_response(header, this);
};
type SMB1_session_setup_andx_request_lanman(header: SMB_Header) = record {
andx : SMB_andx;
max_buffer_size : uint16;
max_mpx_count : uint16;
vc_number : uint16;
session_key : uint32;
password_length : uint16;
reserved : uint32;
byte_count : uint16;
account_password : bytestring &length=password_length;
# offset + 1 due to word_count in the parent type
account_name : SMB_string(header.unicode, offsetof(account_name) + 1);
primary_domain : SMB_string(header.unicode, offsetof(primary_domain) + 1);
native_os : SMB_string(header.unicode, offsetof(native_os) + 1);
native_lanman : SMB_string(header.unicode, offsetof(native_lanman) + 1);
};
type SMB1_session_setup_andx_response_lanman(header: SMB_Header) = record {
andx : SMB_andx;
action : uint16;
byte_count : uint16;
# offset + 1 due to word_count in the parent type
native_os : SMB_string(header.unicode, offsetof(native_os) + 1);
native_lanman : SMB_string(header.unicode, offsetof(native_lanman) + 1);
primary_domain : SMB_string(header.unicode, offsetof(primary_domain) + 1);
} &let {
is_guest: bool = ( action & 0x1 ) > 0;
};
type SMB1_session_setup_andx_request_ntlm_capabilities = record {
capabilities: uint32;
} &let {
unicode : bool = ( capabilities & 0x0004 ) > 0;
large_files : bool = ( capabilities & 0x0008 ) > 0;
nt_smbs : bool = ( capabilities & 0x0010 ) > 0;
status32 : bool = ( capabilities & 0x0040 ) > 0;
level_2_oplocks : bool = ( capabilities & 0x0080 ) > 0;
nt_find : bool = ( capabilities & 0x0200 ) > 0;
};
type SMB1_session_setup_andx_request_ntlm_nonextended_security(header: SMB_Header) = record {
andx : SMB_andx;
max_buffer_size : uint16;
max_mpx_count : uint16;
vc_number : uint16;
session_key : uint32;
case_insensitive_password_length : uint16;
case_sensitive_password_length : uint16;
reserved : uint32;
capabilities : SMB1_session_setup_andx_request_ntlm_capabilities;
byte_count : uint16;
case_insensitive_password : bytestring &length=case_insensitive_password_length;
case_sensitive_password : bytestring &length=case_sensitive_password_length;
# offset + 1 due to word_count in the parent type
account_name : SMB_string(header.unicode, offsetof(account_name) + 1);
primary_domain : SMB_string(header.unicode, offsetof(primary_domain) + 1);
native_os : SMB_string(header.unicode, offsetof(native_os) + 1);
native_lanman : SMB_string(header.unicode, offsetof(native_lanman) + 1);
};
type SMB1_session_setup_andx_request_ntlm_extended_security(header: SMB_Header) = record {
andx : SMB_andx;
max_buffer_size : uint16;
max_mpx_count : uint16;
vc_number : uint16;
session_key : uint32;
security_blob_length : uint16;
reserved : uint32;
capabilities : SMB1_session_setup_andx_request_ntlm_capabilities;
byte_count : uint16;
security_blob : bytestring &length=security_blob_length;
# offset + 1 due to word_count in the parent type
native_os : SMB_string(header.unicode, offsetof(native_os) + 1);
native_lanman : SMB_string(header.unicode, offsetof(native_lanman) + 1);
} &let {
pipe_proc : bool = $context.connection.forward_gssapi(security_blob, true);
};
type SMB1_session_setup_andx_response_ntlm(header: SMB_Header) = record {
andx : SMB_andx;
action : uint16;
security_blob_length : uint16;
byte_count : uint16;
security_blob : bytestring &length=security_blob_length;
# offset + 1 due to word_count in the parent type
native_os : SMB_string(header.unicode, offsetof(native_os) + 1);
native_lanman : SMB_string(header.unicode, offsetof(native_lanman) + 1);
primary_domain : SMB_string(header.unicode, offsetof(primary_domain) + 1);
} &let {
is_guest : bool = ( action & 0x1 ) > 0;
gssapi_proc : bool = $context.connection.forward_gssapi(security_blob, false);
};

View file

@ -0,0 +1,17 @@
type SMB1_transaction_secondary_request(header: SMB_Header) = record {
word_count : uint8;
total_param_count : uint16;
total_data_count : uint16;
param_count : uint16;
param_offset : uint16;
param_displacement : uint16;
data_count : uint16;
data_offset : uint16;
data_displacement : uint16;
byte_count : uint16;
pad1 : padding to param_offset - SMB_Header_length;
parameters : bytestring &length = param_count;
pad2 : padding to data_offset - SMB_Header_length;
data : SMB1_transaction_data(header, true, data_count, 0, SMB_UNKNOWN);
};

View file

@ -0,0 +1,126 @@
enum Trans_subcommands {
NT_TRANSACT_QUERY_QUOTA = 0x0007,
NT_TRANSACT_SET_QUOTA = 0x0008,
NT_TRANSACT_CREATE2 = 0x0009,
};
refine connection SMB_Conn += {
%member{
map<uint16, bool> is_file_a_pipe;
%}
function get_is_file_a_pipe(id: uint16): bool
%{
if ( is_file_a_pipe.count(id) > 0 )
{
bool is_pipe = is_file_a_pipe.at(id);
is_file_a_pipe.erase(id);
return is_pipe;
}
else
return false;
%}
function set_is_file_a_pipe(id: uint16, is_it: bool): bool
%{
is_file_a_pipe[id] = is_it;
return true;
%}
function proc_smb1_transaction_request(header: SMB_Header, val: SMB1_transaction_request): bool
%{
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;
%}
function proc_smb1_transaction_response(header: SMB_Header, val: SMB1_transaction_response): bool
%{
//printf("transaction_response\n");
return true;
%}
};
type SMB1_transaction_data(header: SMB_Header, is_orig: bool, count: uint16, sub_cmd: uint16,
trans_type: int) = case trans_type of {
# SMB_MAILSLOT_BROWSE -> mailslot : 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);
SMB_PIPE -> pipe_data : bytestring &restofdata;
SMB_UNKNOWN -> unknown : bytestring &restofdata &transient;
default -> data : bytestring &restofdata &transient;
} &let {
pipe_proc : bool = $context.connection.forward_dce_rpc(pipe_data, 0, is_orig) &if(trans_type == SMB_PIPE);
};
type SMB1_transaction_setup(header: SMB_Header) = record {
op_code : uint16;
file_id : uint16;
}
type SMB1_transaction_request(header: SMB_Header) = record {
word_count : uint8;
total_param_count : uint16;
total_data_count : uint16;
max_param_count : uint16;
max_data_count : uint16;
max_setup_count : uint8;
reserved1 : uint8;
flags : uint16;
timeout : uint32;
reserved2 : uint16;
param_count : uint16;
param_offset : uint16;
data_count : uint16;
data_offset : uint16;
setup_count : uint8;
reserved3 : uint8;
setup : SMB1_transaction_setup(header);
byte_count : uint16;
name : SMB_string(header.unicode, offsetof(name));
pad1 : padding to param_offset - SMB_Header_length;
parameters : bytestring &length = param_count;
pad2 : padding to data_offset - SMB_Header_length;
data : SMB1_transaction_data(header, true, data_count, sub_cmd, transtype);
} &let {
sub_cmd : uint16 = setup_count ? setup.op_code : 0;
transtype : int = determine_transaction_type(setup_count, name);
is_pipe : bool = (transtype == SMB_PIPE);
proc_set_pipe : bool = $context.connection.set_is_file_a_pipe(header.mid, is_pipe);
proc : bool = $context.connection.proc_smb1_transaction_request(header, this);
};
type SMB1_transaction_response(header: SMB_Header) = record {
word_count : uint8;
total_param_count : uint16;
total_data_count : uint16;
reserved : uint16;
param_count : uint16;
param_offset : uint16;
param_displacement : uint16;
data_count : uint16;
data_offset : uint16;
data_displacement : uint16;
setup_count : uint8;
reserved2 : uint8;
setup : uint16[setup_count];
byte_count : uint16;
pad0 : padding to param_offset - SMB_Header_length;
parameters : bytestring &length = param_count;
pad1 : padding to data_offset - SMB_Header_length;
data : SMB1_transaction_data(header, false, data_count, 0, is_pipe ? SMB_PIPE : SMB_UNKNOWN)[data_count>0 ? 1 : 0];
} &let {
proc : bool = $context.connection.proc_smb1_transaction_response(header, this);
is_pipe: bool = $context.connection.get_is_file_a_pipe(header.mid);
};

View file

@ -0,0 +1,331 @@
enum Trans2_subcommands {
TRANS2_OPEN2 = 0x0000,
TRANS2_FIND_FIRST2 = 0x0001,
TRANS2_FIND_NEXT2 = 0x0002,
TRANS2_QUERY_FS_INFORMATION = 0x0003,
TRANS2_SET_FS_INFORMATION = 0x0004,
TRANS2_QUERY_PATH_INFORMATION = 0x0005,
TRANS2_SET_PATH_INFORMATION = 0x0006,
TRANS2_QUERY_FILE_INFORMATION = 0x0007,
TRANS2_SET_FILE_INFORMATION = 0x0008,
TRANS2_FSCTL = 0x0009,
TRANS2_IOCTL2 = 0x000a,
TRANS2_FIND_NOTIFY_FIRST = 0x000b,
TRANS2_FIND_NOTIFY_NEXT = 0x000c,
TRANS2_CREATE_DIRECTORY = 0x000d,
TRANS2_SESSION_SETUP = 0x000e,
TRANS2_GET_DFS_REFERRAL = 0x0010,
TRANS2_REPORT_DFS_INCONSISTENCY = 0x0011,
};
refine connection SMB_Conn += {
function proc_smb1_transaction2_request(header: SMB_Header, val: SMB1_transaction2_request): bool
%{
if ( smb1_transaction2_request )
BifEvent::generate_smb1_transaction2_request(bro_analyzer(), bro_analyzer()->Conn(), BuildHeaderVal(header), ${val.sub_cmd});
return true;
%}
function proc_smb1_transaction2_response(header: SMB_Header, val: SMB1_transaction2_response): bool
%{
//if ( smb1_transaction2_response )
// BifEvent::generate_smb1_transaction2_response(bro_analyzer(), bro_analyzer()->Conn(), BuildHeaderVal(header), ${val.sub_cmd});
return true;
%}
};
type SMB1_transaction2_request(header: SMB_Header) = record {
word_count : uint8;
total_param_count : uint16;
total_data_count : uint16;
max_param_count : uint16;
max_data_count : uint16;
max_setup_count : uint8;
reserved1 : uint8;
flags : uint16;
timeout : uint32;
reserved2 : uint16;
param_count : uint16;
param_offset : uint16;
data_count : uint16;
data_offset : uint16;
setup_count : uint8;
reserved3 : uint8;
# I suspect this needs a word_count check
#setup : uint16[setup_count];
sub_cmd : uint16;
byte_count : uint16;
#stuff : bytestring &length=byte_count;
pad1 : padding to (param_offset - SMB_Header_length);
parameters : case sub_cmd of {
TRANS2_FIND_FIRST2 -> find_first2 : trans2_find_first2_request(header);
TRANS2_QUERY_FS_INFORMATION -> query_fs_info : trans2_query_fs_info_request(header);
TRANS2_QUERY_PATH_INFORMATION -> query_path_info : trans2_query_path_info_request(header);
TRANS2_QUERY_FILE_INFORMATION -> query_file_info : trans2_query_file_info_request(header);
TRANS2_SET_FILE_INFORMATION -> set_file_info : trans2_set_file_info_request(header);
TRANS2_GET_DFS_REFERRAL -> get_dfs_referral : trans2_get_dfs_referral_request(header);
default -> blah : bytestring &restofdata &transient;
};
#pad2 : padding to (data_offset - SMB_Header_length);
#data : bytestring &length=data_count;
} &let {
proc : bool = $context.connection.proc_smb1_transaction2_request(header, this);
};
type SMB1_transaction2_response(header: SMB_Header) = record {
word_count : uint8;
total_param_count : uint16;
total_data_count : uint16;
reserved1 : uint16;
param_count : uint16;
param_offset : uint16;
param_displacement : uint16;
data_count : uint16;
data_offset : uint16;
data_displacement : uint16;
setup_count : uint8;
reserved2 : uint8;
#setup : uint16[setup_count];
byte_count : uint16;
stuff : bytestring &length=byte_count;
#pad1 : padding to (param_offset - SMB_Header_length);
#parameters : bytestring &length = byte_count;
#pad2 : padding to (data_offset - SMB_Header_length);
#data : bytestring &length = data_count; # TODO: make SMB1_transaction2_data structure -- SMB1_transaction_data(header, data_count, 0, SMB_UNKNOWN);
} &let {
proc : bool = $context.connection.proc_smb1_transaction2_response(header, this);
};
###########################################
refine connection SMB_Conn += {
function proc_trans2_find_first2_request(header: SMB_Header, val: trans2_find_first2_request): bool
%{
if ( smb1_trans2_find_first2_request )
{
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;
%}
function proc_trans2_find_first2_response(header: SMB_Header, val: trans2_find_first2_response): bool
%{
// TODO: implement this.
//printf("trans2_find_first2 response!\n");
return true;
%}
};
type trans2_find_first2_request(header: SMB_Header) = record {
search_attrs : uint16;
search_count : uint16;
flags : uint16;
info_level : uint16;
search_storage_type : uint32;
file_name : SMB_string(header.unicode, offsetof(file_name));
} &let {
proc : bool = $context.connection.proc_trans2_find_first2_request(header, this);
};
type trans2_find_first2_response(header: SMB_Header) = record {
sid : uint16;
search_count : uint16;
end_of_search : uint16;
ea_error_offset : uint16;
last_name_offset : uint16;
} &let {
proc : bool = $context.connection.proc_trans2_find_first2_response(header, this);
};
###########################################
refine connection SMB_Conn += {
function proc_trans2_query_fs_info_request(header: SMB_Header, val: trans2_query_fs_info_request): bool
%{
// TODO: implement this.
//printf("trans2_query_fs_info request!\n");
return true;
%}
function proc_trans2_query_fs_info_response(header: SMB_Header, val: trans2_query_fs_info_response): bool
%{
// TODO: implement this.
//printf("trans2_query_fs_info response!\n");
return true;
%}
};
type trans2_query_fs_info_request(header: SMB_Header) = record {
# TODO: implement this.
} &let {
proc : bool = $context.connection.proc_trans2_query_fs_info_request(header, this);
};
type trans2_query_fs_info_response(header: SMB_Header) = record {
# TODO: implement this.
} &let {
proc : bool = $context.connection.proc_trans2_query_fs_info_response(header, this);
};
###########################################
refine connection SMB_Conn += {
function proc_trans2_query_path_info_request(header: SMB_Header, val: trans2_query_path_info_request): bool
%{
if ( smb1_trans2_query_path_info_request )
{
BifEvent::generate_smb1_trans2_query_path_info_request(bro_analyzer(), bro_analyzer()->Conn(), \
BuildHeaderVal(header), \
smb_string2stringval(${val.file_name}));
}
return true;
%}
function proc_trans2_query_path_info_response(header: SMB_Header, val: trans2_query_path_info_response): bool
%{
// TODO: implement this.
//printf("trans2_query_path_info response!\n");
return true;
%}
};
type trans2_query_path_info_request(header: SMB_Header) = record {
information_level : uint16;
reserved : uint32;
file_name : SMB_string(header.unicode, offsetof(file_name));
} &let {
proc : bool = $context.connection.proc_trans2_query_path_info_request(header, this);
};
type trans2_query_path_info_response(header: SMB_Header) = record {
# TODO: implement this.
} &let {
proc : bool = $context.connection.proc_trans2_query_path_info_response(header, this);
};
###########################################
refine connection SMB_Conn += {
function proc_trans2_query_file_info_request(header: SMB_Header, val: trans2_query_file_info_request): bool
%{
// TODO: implement this.
//printf("trans2_query_file_info request!\n");
return true;
%}
function proc_trans2_query_file_info_response(header: SMB_Header, val: trans2_query_file_info_response): bool
%{
// TODO: implement this.
//printf("trans2_query_file_info response!\n");
return true;
%}
};
type trans2_query_file_info_request(header: SMB_Header) = record {
file_id : uint16;
information_level : uint16;
} &let {
proc : bool = $context.connection.proc_trans2_query_file_info_request(header, this);
};
type trans2_query_file_info_response(header: SMB_Header) = record {
# TODO: implement this.
} &let {
proc : bool = $context.connection.proc_trans2_query_file_info_response(header, this);
};
###########################################
refine connection SMB_Conn += {
function proc_trans2_set_file_info_request(header: SMB_Header, val: trans2_set_file_info_request): bool
%{
// TODO: implement this.
//printf("trans2_set_file_info request!\n");
return true;
%}
function proc_trans2_set_file_info_response(header: SMB_Header, val: trans2_set_file_info_response): bool
%{
// TODO: implement this.
//printf("trans2_set_file_info response!\n");
return true;
%}
};
type trans2_set_file_info_request(header: SMB_Header) = record {
# TODO: implement this.
} &let {
proc : bool = $context.connection.proc_trans2_set_file_info_request(header, this);
};
type trans2_set_file_info_response(header: SMB_Header) = record {
# TODO: implement this.
} &let {
proc : bool = $context.connection.proc_trans2_set_file_info_response(header, this);
};
###########################################
refine connection SMB_Conn += {
function proc_trans2_get_dfs_referral_request(header: SMB_Header, val: trans2_get_dfs_referral_request): bool
%{
if ( smb1_trans2_get_dfs_referral_request )
{
BifEvent::generate_smb1_trans2_get_dfs_referral_request(bro_analyzer(), bro_analyzer()->Conn(), \
BuildHeaderVal(header), \
smb_string2stringval(${val.file_name}));
}
return true;
%}
function proc_trans2_get_dfs_referral_response(header: SMB_Header, val: trans2_get_dfs_referral_response): bool
%{
// TODO: implement this.
//printf("trans2_get_dfs_referral response!\n");
return true;
%}
};
type trans2_get_dfs_referral_request(header: SMB_Header) = record {
max_referral_level : uint16;
file_name : SMB_string(header.unicode, offsetof(file_name));
} &let {
proc : bool = $context.connection.proc_trans2_get_dfs_referral_request(header, this);
};
type trans2_get_dfs_referral_response(header: SMB_Header) = record {
# TODO: implement this.
} &let {
proc : bool = $context.connection.proc_trans2_get_dfs_referral_response(header, this);
};
###########################################

View file

@ -0,0 +1,55 @@
refine connection SMB_Conn += {
function proc_smb1_tree_connect_andx_request(header: SMB_Header, val: SMB1_tree_connect_andx_request): bool
%{
if ( smb1_tree_connect_andx_request )
BifEvent::generate_smb1_tree_connect_andx_request(bro_analyzer(),
bro_analyzer()->Conn(),
BuildHeaderVal(header),
smb_string2stringval(${val.path}),
smb_string2stringval(${val.service}));
return true;
%}
function proc_smb1_tree_connect_andx_response(header: SMB_Header, val: SMB1_tree_connect_andx_response): bool
%{
set_tree_is_pipe(${header.tid}, strncmp((const char*) smb_string2stringval(${val.service})->Bytes(), "IPC", 3) == 0);
if ( smb1_tree_connect_andx_response )
BifEvent::generate_smb1_tree_connect_andx_response(bro_analyzer(),
bro_analyzer()->Conn(),
BuildHeaderVal(header),
smb_string2stringval(${val.service}),
smb_string2stringval(${val.native_file_system}));
return true;
%}
};
type SMB1_tree_connect_andx_request(header: SMB_Header) = record {
word_count : uint8;
andx : SMB_andx;
flags : uint16;
password_length : uint16;
byte_count : uint16;
password : uint8[password_length];
path : SMB_string(header.unicode, offsetof(path));
service : SMB_string(0, offsetof(service));
} &let {
proc : bool = $context.connection.proc_smb1_tree_connect_andx_request(header, this);
};
type SMB1_tree_connect_andx_response(header: SMB_Header) = record {
word_count : uint8;
andx : SMB_andx;
optional_support : uint16;
pad : padding[(word_count-3)*2];
byte_count : uint16;
service : SMB_string(0, offsetof(service));
native_file_system : SMB_string(header.unicode, offsetof(native_file_system));
} &let {
proc : bool = $context.connection.proc_smb1_tree_connect_andx_response(header, this);
};

View file

@ -0,0 +1,21 @@
refine connection SMB_Conn += {
function proc_smb1_tree_disconnect(header: SMB_Header, val: SMB1_tree_disconnect): bool
%{
if ( smb1_tree_disconnect )
BifEvent::generate_smb1_tree_disconnect(bro_analyzer(),
bro_analyzer()->Conn(),
BuildHeaderVal(header),
${val.is_orig});
return true;
%}
};
type SMB1_tree_disconnect(header: SMB_Header, is_orig: bool) = record {
word_count : uint8;
byte_count : uint16;
} &let {
proc : bool = $context.connection.proc_smb1_tree_disconnect(header, this);
};

View file

@ -0,0 +1,78 @@
refine connection SMB_Conn += {
function proc_smb1_write_andx_request(h: SMB_Header, val: SMB1_write_andx_request): bool
%{
if ( smb1_write_andx_request )
BifEvent::generate_smb1_write_andx_request(bro_analyzer(),
bro_analyzer()->Conn(),
BuildHeaderVal(h),
${val.file_id},
${val.offset},
${val.data_len});
if ( ! ${val.is_pipe} && ${val.data}.length() > 0 )
{
file_mgr->DataIn(${val.data}.begin(), ${val.data}.length(),
${val.offset},
bro_analyzer()->GetAnalyzerTag(),
bro_analyzer()->Conn(), h->is_orig());
}
return true;
%}
function proc_smb1_write_andx_response(h: SMB_Header, val: SMB1_write_andx_response): bool
%{
if ( smb1_write_andx_response )
BifEvent::generate_smb1_write_andx_response(bro_analyzer(),
bro_analyzer()->Conn(),
BuildHeaderVal(h),
${val.written_bytes});
return true;
%}
};
type SMB1_write_andx_request(header: SMB_Header) = record {
word_count : uint8;
andx : SMB_andx;
file_id : uint16;
offset_low : uint32;
timeout : uint32;
write_mode : uint16;
remaining : uint16;
data_len_high : uint16;
data_len_low : uint16;
data_offset : uint16;
offset_high_u : case word_count of {
0x0E -> offset_high_tmp : uint32;
default -> null : empty;
};
byte_count : uint16;
pad : padding to data_offset - SMB_Header_length;
data : bytestring &length=data_len;
} &let {
is_pipe : bool = $context.connection.get_tree_is_pipe(header.tid);
pipe_proc : bool = $context.connection.forward_dce_rpc(data, 0, true) &if(is_pipe);
data_len : uint32 = (data_len_high << 16) + data_len_low;
offset_high : uint32 = (word_count == 0x0E) ? offset_high_tmp : 0;
offset : uint64 = (offset_high * 0x10000) + offset_low;
proc : bool = $context.connection.proc_smb1_write_andx_request(header, this);
};
type SMB1_write_andx_response(header: SMB_Header) = record {
word_count : uint8;
andx : SMB_andx;
written_low : uint16;
remaining : uint16;
written_high : uint16;
reserved : uint16;
byte_count : uint16;
} &let {
written_bytes : uint32 = (written_high * 0x10000) + written_low;
proc : bool = $context.connection.proc_smb1_write_andx_response(header, this);
};

View file

@ -0,0 +1,325 @@
%extern{
#include "file_analysis/Manager.h"
%}
refine connection SMB_Conn += {
function BuildHeaderVal(hdr: SMB_Header): BroVal
%{
RecordVal* r = new RecordVal(BifType::Record::SMB1::Header);
//unsigned int status = 0;
//
//try
// {
// // FIXME: does this work? We need to catch exceptions :-(
// // or use guard functions.
// status = ${hdr.status.error} ||
// ${hdr.status.dos_error.error_class} << 24 ||
// ${hdr.status.dos_error.error_class};
// }
//catch ( const binpac::Exception& )
// { // do nothing
// }
r->Assign(0, new Val(${hdr.command}, TYPE_COUNT));
r->Assign(1, new Val(${hdr.status}, TYPE_COUNT));
r->Assign(2, new Val(${hdr.flags}, TYPE_COUNT));
r->Assign(3, new Val(${hdr.flags2}, TYPE_COUNT));
r->Assign(4, new Val(${hdr.tid}, TYPE_COUNT));
r->Assign(5, new Val(${hdr.pid}, TYPE_COUNT));
r->Assign(6, new Val(${hdr.uid}, TYPE_COUNT));
r->Assign(7, new Val(${hdr.mid}, TYPE_COUNT));
return r;
%}
function proc_smb_message(h: SMB_Header, is_orig: bool): bool
%{
if ( smb1_message )
{
BifEvent::generate_smb1_message(bro_analyzer(), bro_analyzer()->Conn(),
BuildHeaderVal(h),
is_orig);
}
return true;
%}
function proc_smb_empty_response(header: SMB_Header): bool
%{
if ( smb1_empty_response )
{
BifEvent::generate_smb1_empty_response(bro_analyzer(),
bro_analyzer()->Conn(),
BuildHeaderVal(header));
}
return true;
%}
function proc_smb_no_msg(h: SMB_Header, is_orig: bool): bool
%{
if ( ${h.status} == STATUS_SUCCESS )
{
if ( smb1_empty_response )
{
BifEvent::generate_smb1_empty_response(bro_analyzer(), bro_analyzer()->Conn(), BuildHeaderVal(h));
}
}
else
{
BifEvent::generate_smb1_error(bro_analyzer(),
bro_analyzer()->Conn(),
BuildHeaderVal(h), is_orig);
}
return true;
%}
};
type SMB_dos_error = record {
error_class : uint8;
reserved : uint8;
error : uint16;
};
type SMB_error(err_status_type: int) = case err_status_type of {
0 -> dos_error : SMB_dos_error;
default -> error : uint32;
};
type SMB_andx = record {
command : uint8;
reserved : uint8;
offset : uint16;
} &byteorder = littleendian;
type SMB_PDU(is_orig: bool, msg_len: uint32) = record {
header : SMB_Header(is_orig);
message : case msg_len of {
# Message length of 35 means that the actual message is
# only three bytes which means it's an empty response.
35 -> no_msg : SMB_No_Message(header, is_orig);
default -> msg : SMB_Message(header, header.command, is_orig);
};
};
type SMB_No_Message(header: SMB_Header, is_orig: bool) = record {
x : bytestring &length=3 &transient;
} &let {
proc : bool = $context.connection.proc_smb_no_msg(header, is_orig);
};
type SMB_empty_response(header: SMB_Header) = record {
word_count : uint8;
byte_count : uint16;
} &let {
proc : bool = $context.connection.proc_smb_empty_response(header);
};
type SMB_Message(header: SMB_Header, command: uint8, is_orig: bool) = case is_orig of {
true -> request : SMB_Message_Request(header, command, is_orig);
false -> response : SMB_Message_Response(header, command, is_orig);
};
type SMB_andx_command(header: SMB_Header, is_orig: bool, command: uint8) = case command of {
0xff -> no_futher_commands : empty;
default -> message : SMB_Message(header, command, is_orig);
};
type SMB_Message_Request(header: SMB_Header, command: uint8, is_orig: bool) = case command of {
# SMB1 Command Extensions
#SMB_COM_OPEN_ANDX -> open_andx : SMB_open_andx_request(header);
SMB_COM_READ_ANDX -> read_andx : SMB1_read_andx_request(header);
SMB_COM_WRITE_ANDX -> write_andx : SMB1_write_andx_request(header);
SMB_COM_TRANSACTION2 -> transaction2 : SMB1_transaction2_request(header);
SMB_COM_NEGOTIATE -> negotiate : SMB1_negotiate_request(header);
SMB_COM_SESSION_SETUP_ANDX -> session_setup_andx : SMB1_session_setup_andx_request(header);
SMB_COM_TREE_CONNECT_ANDX -> tree_connect_andx : SMB1_tree_connect_andx_request(header);
SMB_COM_NT_TRANSACT -> nt_transact : SMB1_nt_transact_request(header);
SMB_COM_NT_CREATE_ANDX -> nt_create_andx : SMB1_nt_create_andx_request(header);
# SMB_COM_CREATE_DIRECTORY -> create_directory : SMB1_create_directory_request(header);
# #SMB_COM_DELETE_DIRECTORY -> delete_directory : SMB_delete_directory_request(header);
# #SMB_COM_OPEN -> open : SMB_open_request(header);
# #SMB_COM_CREATE -> create : SMB_create_request(header);
SMB_COM_CLOSE -> close : SMB1_close_request(header);
# #SMB_COM_FLUSH -> flush : SMB_flush_request(header);
# #SMB_COM_DELETE -> delete : SMB_delete_request(header);
# #SMB_COM_RENAME -> rename : SMB_rename_request(header);
SMB_COM_QUERY_INFORMATION -> query_information : SMB1_query_information_request(header);
# #SMB_COM_SET_INFORMATION -> set_information : SMB_set_information_request(header);
# #SMB_COM_READ -> read : SMB_read_request(header);
# #SMB_COM_WRITE -> write : SMB_write_request(header);
# #SMB_COM_LOCK_BYTE_RANGE -> lock_byte_range : SMB_lock_byte_range_request(header);
# #SMB_COM_UNLOCK_BYTE_RANGE -> unlock_byte_range : SMB_unlock_byte_range_request(header);
# #SMB_COM_CREATE_TEMPORARY -> create_temporary : SMB_create_temporary_request(header);
# #SMB_COM_CREATE_NEW -> create_new : SMB_create_new_request(header);
SMB_COM_CHECK_DIRECTORY -> check_directory : SMB1_check_directory_request(header);
# #SMB_COM_PROCESS_EXIT -> process_exit : SMB_process_exit_request(header);
# #SMB_COM_SEEK -> seek : SMB_seek_request(header);
# #SMB_COM_LOCK_AND_READ -> lock_and_read : SMB_lock_and_read_request(header);
# #SMB_COM_WRITE_AND_UNLOCK -> write_and_unlock : SMB_write_and_unlock_request(header);
# #SMB_COM_READ_RAW -> read_raw : SMB_read_raw_request(header);
# #SMB_COM_READ_MPX -> read_mpx : SMB_read_mpx_request(header);
# #SMB_COM_READ_MPX_SECONDARY -> read_mpx_secondary : SMB_read_mpx_secondary_request(header);
# #SMB_COM_WRITE_RAW -> write_raw : SMB_write_raw_request(header);
# #SMB_COM_WRITE_MPX -> write_mpx : SMB_write_mpx_request(header);
# #SMB_COM_WRITE_MPX_SECONDARY -> write_mpx_secondary : SMB_write_mpx_secondary_request(header);
# #SMB_COM_WRITE_COMPLETE -> write_complete : SMB_write_complete_request(header);
# #SMB_COM_QUERY_SERVER -> query_server : SMB_query_server_request(header);
# #SMB_COM_SET_INFORMATION2 -> set_information2 : SMB_set_information2_request(header);
# #SMB_COM_QUERY_INFORMATION2 -> query_information2 : SMB_query_information2_request(header);
SMB_COM_LOCKING_ANDX -> locking_andx : SMB1_locking_andx_request(header);
SMB_COM_TRANSACTION -> transaction : SMB1_transaction_request(header);
# SMB_COM_TRANSACTION_SECONDARY -> transaction_secondary : SMB1_transaction_secondary_request(header);
# #SMB_COM_IOCTL -> ioctl : SMB_ioctl_request(header);
# #SMB_COM_IOCTL_SECONDARY -> ioctl_secondary : SMB_ioctl_secondary_request(header);
# #SMB_COM_COPY -> copy : SMB_copy_request(header);
# #SMB_COM_MOVE -> move : SMB_move_request(header);
SMB_COM_ECHO -> echo : SMB1_echo_request(header);
# #SMB_COM_WRITE_AND_CLOSE -> write_and_close : SMB_write_and_close_request(header);
# #SMB_COM_NEW_FILE_SIZE -> new_file_size : SMB_new_file_size_request(header);
# #SMB_COM_CLOSE_AND_TREE_DISC -> close_and_tree_disc : SMB_close_and_tree_disc_request(header);
# #SMB_COM_TRANSACTION2_SECONDARY -> transaction2_secondary : SMB1_transaction2_secondary_request(header);
# #SMB_COM_FIND_CLOSE2 -> find_close2 : SMB_find_close2_request(header);
# #SMB_COM_FIND_NOTIFY_CLOSE -> find_notify_close : SMB_find_notify_close_request(header);
# #SMB_COM_TREE_CONNECT -> tree_connect : SMB_tree_connect_request(header);
SMB_COM_TREE_DISCONNECT -> tree_disconnect : SMB1_tree_disconnect(header, is_orig);
SMB_COM_LOGOFF_ANDX -> logoff_andx : SMB1_logoff_andx(header, is_orig);
# #SMB_COM_QUERY_INFORMATION_DISK -> query_information_disk : SMB_query_information_disk_request(header);
# #SMB_COM_SEARCH -> search : SMB_search_request(header);
# #SMB_COM_FIND -> find : SMB_find_request(header);
# #SMB_COM_FIND_UNIQUE -> find_unique : SMB_find_unique_request(header);
# #SMB_COM_FIND_CLOSE -> find_close : SMB_find_close_request(header);
# #SMB_COM_NT_TRANSACT_SECONDARY -> nt_transact_secondary : SMB_nt_transact_secondary_request(header);
SMB_COM_NT_CANCEL -> nt_cancel : SMB1_nt_cancel_request(header);
# #SMB_COM_NT_RENAME -> nt_rename : SMB_nt_rename_request(header);
# #SMB_COM_OPEN_PRINT_FILE -> open_print_file : SMB_open_print_file_request(header);
# #SMB_COM_WRITE_PRINT_FILE -> write_print_file : SMB_write_print_file_request(header);
# #SMB_COM_CLOSE_PRINT_FILE -> close_print_file : SMB_close_print_file_request(header);
# #SMB_COM_GET_PRINT_QUEUE -> get_print_queue : SMB_get_print_queue_request(header);
# #SMB_COM_READ_BULK -> read_bulk : SMB_read_bulk_request(header);
# #SMB_COM_WRITE_BULK -> write_bulk : SMB_write_bulk_request(header);
# #SMB_COM_WRITE_BULK_DATA -> write_bulk_data : SMB_write_bulk_data_request(header);
default -> unknown_msg : bytestring &restofdata; # TODO: do something different here!
} &byteorder = littleendian;
type SMB_Message_Response(header: SMB_Header, command: uint8, is_orig: bool) = case command of {
# SMB1 Command Extensions
#SMB_COM_OPEN_ANDX -> open_andx : SMB_open_andx_response(header);
SMB_COM_READ_ANDX -> read_andx : SMB1_read_andx_response(header);
SMB_COM_WRITE_ANDX -> write_andx : SMB1_write_andx_response(header);
SMB_COM_TRANSACTION2 -> transaction2 : SMB1_transaction2_response(header);
SMB_COM_NEGOTIATE -> negotiate : SMB1_negotiate_response(header);
SMB_COM_SESSION_SETUP_ANDX -> session_setup_andx : SMB1_session_setup_andx_response(header);
SMB_COM_TREE_CONNECT_ANDX -> tree_connect_andx : SMB1_tree_connect_andx_response(header);
SMB_COM_NT_TRANSACT -> nt_transact : SMB1_nt_transact_response(header);
SMB_COM_NT_CREATE_ANDX -> nt_create_andx : SMB1_nt_create_andx_response(header);
# SMB_COM_CREATE_DIRECTORY -> create_directory : SMB1_create_directory_response(header);
# #SMB_COM_DELETE_DIRECTORY -> delete_directory : SMB_delete_directory_response(header);
# #SMB_COM_OPEN -> open : SMB_open_response(header);
# #SMB_COM_CREATE -> create : SMB_create_response(header);
SMB_COM_CLOSE -> close : SMB_empty_response(header);
# #SMB_COM_FLUSH -> flush : SMB_flush_response(header);
# #SMB_COM_DELETE -> delete : SMB_delete_response(header);
# #SMB_COM_RENAME -> rename : SMB_rename_response(header);
SMB_COM_QUERY_INFORMATION -> query_information : SMB1_query_information_response(header);
# #SMB_COM_SET_INFORMATION -> set_information : SMB_set_information_response(header);
# #SMB_COM_READ -> read : SMB_read_response(header);
# #SMB_COM_WRITE -> write : SMB_write_response(header);
# #SMB_COM_LOCK_BYTE_RANGE -> lock_byte_range : SMB_lock_byte_range_response(header);
# #SMB_COM_UNLOCK_BYTE_RANGE -> unlock_byte_range : SMB_unlock_byte_range_response(header);
# #SMB_COM_CREATE_TEMPORARY -> create_temporary : SMB_create_temporary_response(header);
# #SMB_COM_CREATE_NEW -> create_new : SMB_create_new_response(header);
SMB_COM_CHECK_DIRECTORY -> check_directory : SMB1_check_directory_response(header);
# #SMB_COM_PROCESS_EXIT -> process_exit : SMB_process_exit_response(header);
# #SMB_COM_SEEK -> seek : SMB_seek_response(header);
# #SMB_COM_LOCK_AND_READ -> lock_and_read : SMB_lock_and_read_response(header);
# #SMB_COM_WRITE_AND_UNLOCK -> write_and_unlock : SMB_write_and_unlock_response(header);
# #SMB_COM_READ_RAW -> read_raw : SMB_read_raw_response(header);
# #SMB_COM_READ_MPX -> read_mpx : SMB_read_mpx_response(header);
# #SMB_COM_READ_MPX_SECONDARY -> read_mpx_secondary : SMB_read_mpx_secondary_response(header);
# #SMB_COM_WRITE_RAW -> write_raw : SMB_write_raw_response(header);
# #SMB_COM_WRITE_MPX -> write_mpx : SMB_write_mpx_response(header);
# #SMB_COM_WRITE_MPX_SECONDARY -> write_mpx_secondary : SMB_write_mpx_secondary_response(header);
# #SMB_COM_WRITE_COMPLETE -> write_complete : SMB_write_complete_response(header);
# #SMB_COM_QUERY_SERVER -> query_server : SMB_query_server_response(header);
# #SMB_COM_SET_INFORMATION2 -> set_information2 : SMB_set_information2_response(header);
# #SMB_COM_QUERY_INFORMATION2 -> query_information2 : SMB_query_information2_response(header);
SMB_COM_LOCKING_ANDX -> locking_andx : SMB1_locking_andx_response(header);
SMB_COM_TRANSACTION -> transaction : SMB1_transaction_response(header);
# #SMB_COM_IOCTL -> ioctl : SMB_ioctl_response(header);
# #SMB_COM_IOCTL_SECONDARY -> ioctl_secondary : SMB_ioctl_secondary_response(header);
# #SMB_COM_COPY -> copy : SMB_copy_response(header);
# #SMB_COM_MOVE -> move : SMB_move_response(header);
SMB_COM_ECHO -> echo : SMB1_echo_response(header);
# #SMB_COM_WRITE_AND_CLOSE -> write_and_close : SMB_write_and_close_response(header);
# #SMB_COM_NEW_FILE_SIZE -> new_file_size : SMB_new_file_size_response(header);
# #SMB_COM_CLOSE_AND_TREE_DISC -> close_and_tree_disc : SMB_close_and_tree_disc_response(header);
# #SMB_COM_TRANSACTION2_SECONDARY -> transaction2_secondary : SMB1_transaction2_secondary_response(header);
# #SMB_COM_FIND_CLOSE2 -> find_close2 : SMB_find_close2_response(header);
# #SMB_COM_FIND_NOTIFY_CLOSE -> find_notify_close : SMB_find_notify_close_response(header);
# #SMB_COM_TREE_CONNECT -> tree_connect : SMB_tree_connect_response(header);
SMB_COM_TREE_DISCONNECT -> tree_disconnect : SMB1_tree_disconnect(header, is_orig);
SMB_COM_LOGOFF_ANDX -> logoff_andx : SMB1_logoff_andx(header, is_orig);
# #SMB_COM_QUERY_INFORMATION_DISK -> query_information_disk : SMB_query_information_disk_response(header);
# #SMB_COM_SEARCH -> search : SMB_search_response(header);
# #SMB_COM_FIND -> find : SMB_find_response(header);
# #SMB_COM_FIND_UNIQUE -> find_unique : SMB_find_unique_response(header);
# #SMB_COM_FIND_CLOSE -> find_close : SMB_find_close_response(header);
# #SMB_COM_NT_TRANSACT_SECONDARY -> nt_transact_secondary : SMB_nt_transact_secondary_response(header);
#SMB_COM_NT_CANCEL -> nt_cancel : SMB1_nt_cancel_response(header);
# #SMB_COM_NT_RENAME -> nt_rename : SMB_nt_rename_response(header);
# #SMB_COM_OPEN_PRINT_FILE -> open_print_file : SMB_open_print_file_response(header);
# #SMB_COM_WRITE_PRINT_FILE -> write_print_file : SMB_write_print_file_response(header);
# #SMB_COM_CLOSE_PRINT_FILE -> close_print_file : SMB_close_print_file_response(header);
# #SMB_COM_GET_PRINT_QUEUE -> get_print_queue : SMB_get_print_queue_response(header);
# #SMB_COM_READ_BULK -> read_bulk : SMB_read_bulk_response(header);
# #SMB_COM_WRITE_BULK -> write_bulk : SMB_write_bulk_response(header);
# #SMB_COM_WRITE_BULK_DATA -> write_bulk_data : SMB_write_bulk_data_response(header);
default -> unknown_msg : bytestring &restofdata;
} &byteorder = littleendian;
type SMB_Header(is_orig: bool) = record {
command : uint8;
#status : SMB_error(err_status_type);
status : uint32;
flags : uint8;
flags2 : uint16;
pid_high : uint16;
security_features : uint8[8];
reserved : uint16;
tid : uint16;
pid_low : uint16;
uid : uint16;
mid : uint16;
} &let {
err_status_type = (flags2 >> 14) & 1;
unicode = (flags2 >> 15) & 1;
pid = (pid_high * 0x10000) + pid_low;
proc : bool = $context.connection.proc_smb_message(this, is_orig);
} &byteorder=littleendian;
# TODO: compute this as
# let SMB_Header_length = sizeof(SMB_Header);
let SMB_Header_length = 32;
refine connection SMB_Conn += {
%member{
int offset_len;
%}
%init{
// This needs to be set to some actual value.
// TODO: figure out where the hell to get this value from...
offset_len = 64;
%}
function get_offset_len(): int
%{
return offset_len;
%}
};

View file

@ -0,0 +1,15 @@
## Generated for SMB/CIFS requests of type *check directory*.
##
## c: The connection.
##
## hdr: The parsed header of the SMB message.
##
## directory_name: The directory name to check for existence.
event smb1_check_directory_request%(c: connection, hdr: SMB1::Header, directory_name: string%);
## Generated for SMB/CIFS responses of type *check directory*.
##
## c: The connection.
##
## hdr: The parsed header of the SMB message.
event smb1_check_directory_response%(c: connection, hdr: SMB1::Header%);

View file

@ -0,0 +1,13 @@
## Generated for SMB/CIFS request messages of type *close*.
##
## See `Wikipedia <http://en.wikipedia.org/wiki/Server_Message_Block>`__ for
## more information about the SMB/CIFS protocol. Bro's SMB/CIFS analyzer parses
## both SMB-over-NetBIOS on ports 138/139 and SMB-over-TCP on port 445.
##
## c: The connection.
##
## hdr: The parsed header of the SMB message.
##
## file_id: The file identifier being closed.
event smb1_close_request%(c: connection, hdr: SMB1::Header, file_id: count%);

View file

@ -0,0 +1,19 @@
## Generated for SMB/CIFS requests of type *create directory*. This is also
## a deprecated command which has been replaced by the trans2_create_directory
## subcommand.
##
## c: The connection.
##
## hdr: The parsed header of the SMB message.
##
## directory_name: The name of the directory to create.
event smb1_create_directory_request%(c: connection, hdr: SMB1::Header, directory_name: string%);
## Generated for SMB/CIFS responses of type *create directory*. This is also
## a deprecated command which has been replaced by the trans2_create_directory
## subcommand.
##
## c: The connection.
##
## hdr: The parsed header of the SMB message.
event smb1_create_directory_response%(c: connection, hdr: SMB1::Header%);

View file

@ -0,0 +1,21 @@
## Generated for SMB/CIFS requests of type *echo*.
##
## c: The connection.
##
## hdr: The parsed header of the SMB message.
##
## echo_count: The number of times the server should echo the data back.
##
## data: The data for the server to echo.
event smb1_echo_request%(c: connection, echo_count: count, data: string%);
## Generated for SMB/CIFS responses of type *negotiate*.
##
## c: The connection.
##
## hdr: The parsed header of the SMB message.
##
## seq_num: The sequence number of this echo reply.
##
## data: The data echoed back from the client.
event smb1_echo_response%(c: connection, seq_num: count, data: string%);

View file

@ -0,0 +1,11 @@
## Generated for SMB/CIFS messages of type *logoff andx*.
##
## See `Wikipedia <http://en.wikipedia.org/wiki/Server_Message_Block>`__ for
## more information about the SMB/CIFS protocol. Bro's SMB/CIFS analyzer parses
## both SMB-over-NetBIOS on ports 138/139 and SMB-over-TCP on port 445.
##
## c: The connection.
##
## is_orig: Indicates which host sent the logoff message..
event smb1_logoff_andx%(c: connection, is_orig: bool%);

Some files were not shown because too many files have changed in this diff Show more