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

# Conflicts:
#	scripts/base/protocols/dce-rpc/main.bro
#	scripts/base/protocols/ntlm/main.bro
#	scripts/policy/protocols/smb/smb1-main.bro
#	src/analyzer/protocol/smb/smb-common.pac
#	src/analyzer/protocol/smb/smb-strings.pac
#	src/analyzer/protocol/smb/smb1-com-locking-andx.pac
#	src/analyzer/protocol/smb/smb1-com-logoff-andx.pac
#	src/analyzer/protocol/smb/smb1-com-nt-create-andx.pac
#	src/analyzer/protocol/smb/smb1-com-open-andx.pac
#	src/analyzer/protocol/smb/smb1-com-read-andx.pac
#	src/analyzer/protocol/smb/smb1-com-session-setup-andx.pac
#	src/analyzer/protocol/smb/smb1-com-transaction-secondary.pac
#	src/analyzer/protocol/smb/smb1-com-transaction.pac
#	src/analyzer/protocol/smb/smb1-com-tree-connect-andx.pac
#	src/analyzer/protocol/smb/smb1-com-write-andx.pac
#	src/analyzer/protocol/smb/smb1-protocol.pac
This commit is contained in:
Seth Hall 2016-08-08 15:46:49 -04:00
parent 203e63416e
commit 520ac8d92c
162 changed files with 12058 additions and 3781 deletions

15
NEWS
View file

@ -52,6 +52,21 @@ Log Changes
New Functionality
-----------------
- SMB analyzer. This is the rewrite that has been in development for
several years. The scripts are currently not loaded by default and
must be loaded manually by loading policy/protocols/smb. The next
release will load the smb scripts by default.
- Implements SMB1+2.
- Fully integrated with the file analysis framework so that files
transferred over SMB can be analyzed.
- Includes GSSAPI and NTLM analyzer and reimplements the DCE-RPC
analyzer.
- New logs: smb_files.log, smb_mapping.log, ntlm.log, and dce_rpc.log
- Not every possible SMB command or functionality is implemented, but
generally, file handling should work whenever files are transferred.
Please speak up on the mailing list if there is an obvious oversight.
- Bro now includes the NetControl framework. The framework allows for easy
interaction of Bro with hard- and software switches, firewalls, etc.

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,12 +2380,156 @@ 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.
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;
};
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
@ -2393,70 +2537,351 @@ global samba_cmds: table[count] of string &redef
## 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`).
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; ##< TODO.
tid: count; ##< Tree ID.
pid: count; ##< Process ID.
uid: count; ##< User ID.
mid: count; ##< TODO.
mid: count; ##< Multiplex ID.
};
## 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 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;
## 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.
## 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;
};
## Deprecated.
##
## .. todo:: Remove. It's still declared internally but doesn't seem used anywhere
## else.
type smb_tree_connect : record {
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;
password: string;
path: string;
service: string;
## 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;
};
## Deprecated.
##
## .. todo:: Remove. It's still declared internally but doesn't seem used anywhere
## else.
type smb_negotiate : table[count] of 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;
};
}
module SMB2;
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,269 @@
@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_RENAME,
FILE_DELETE,
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;
## If the rename action was seen, this will
## the file's previous name.
prev_name : string &log &optional;
## Last time this file was modified.
times : SMB::MACTimes &log &optional;
};
## This record is for the smb_mapping.log
type TreeInfo: record {
## Time when the tree was mapped.
ts : time &log &optional;
## Unique ID of the connection the tree was mapped over.
uid : string &log;
## ID of the connection the tree was mapped over.
id : conn_id &log;
## Name of the tree path.
path : string &log &optional;
## The type of resource of the tree (disk share, printer share, named pipe, etc.)
service : string &log &optional;
## File system of the tree.
native_file_system : string &log &optional;
## If this is SMB2, a share type will be included. For SMB1,
## the type of share will be deduced and included as well.
share_type : string &log &default="DISK";
};
## This record is for the smb_cmd.log
type CmdInfo: record {
## Timestamp of the command request
ts : time &log;
## Unique ID of the connection the request was sent over
uid : string &log;
## ID of the connection the request was sent over
id : conn_id &log;
## The command sent by the client
command : string &log;
## The subcommand sent by the client, if present
sub_command : string &log &optional;
## Command argument sent by the client, if any
argument : string &log &optional;
## Server reply to the client's command
status : string &log &optional;
## Round trip time from the request to the response.
rtt : interval &log &optional;
## Version of SMB for the command
version : string &log;
## Authenticated username, if available
username : string &log &optional;
## If this is related to a tree, this is the tree
## that was used for the current command.
tree : string &log &optional;
## The type of tree (disk share, printer share, named pipe, etc.)
tree_service : string &log &optional;
## If the command referenced a file, store it here.
referenced_file : FileInfo &log &optional;
## If the command referenced a tree, store it here.
referenced_tree : TreeInfo &optional;
};
## This record stores the SMB state of in-flight commands,
## the file and tree map of the connection.
type State: record {
## A reference to the current command.
current_cmd : CmdInfo &optional;
## A reference to the current file.
current_file : FileInfo &optional;
## A reference to the current tree.
current_tree : TreeInfo &optional;
## Indexed on MID to map responses to requests.
pending_cmds : table[count] of CmdInfo &optional;
## File map to retrieve file information based on the file ID.
fid_map : table[count] of FileInfo &optional;
## Tree map to retrieve tree information based on the tree ID.
tid_map : table[count] of TreeInfo &optional;
## User map to retrieve user name based on the user ID.
uid_map : table[count] of string &optional;
## Pipe map to retrieve UUID based on the file ID of a pipe.
pipe_map : table[count] of string &optional;
## A set of recent files to avoid logging the same
## files over and over in the smb files log.
## This only applies to files seen in a single connection.
recent_files : set[string] &default=string_set() &read_expire=3min;
};
## Optionally write out the SMB commands log. This is
## primarily useful for debugging so is disabled by default.
const write_cmd_log = F &redef;
## Everything below here is used internally in the SMB scripts.
redef record connection += {
smb_state : State &optional;
};
## Internal use only
## Some commands shouldn't be logged by the smb1_message event
const deferred_logging_cmds: set[string] = {
"NEGOTIATE",
"READ_ANDX",
"SESSION_SETUP_ANDX",
"TREE_CONNECT_ANDX",
};
## This is an internally used function.
const set_current_file: function(smb_state: State, file_id: count) &redef;
## This is an internally used function.
const write_file_log: function(state: State) &redef;
}
redef record FileInfo += {
## ID referencing this file.
fid : count &optional;
## UUID referencing this file if DCE/RPC
uuid : string &optional;
};
const ports = { 139/tcp, 445/tcp };
redef likely_server_ports += { ports };
event bro_init() &priority=5
{
Log::create_stream(SMB::CMD_LOG, [$columns=SMB::CmdInfo]);
Log::create_stream(SMB::FILES_LOG, [$columns=SMB::FileInfo]);
Log::create_stream(SMB::MAPPING_LOG, [$columns=SMB::TreeInfo]);
Analyzer::register_for_ports(Analyzer::ANALYZER_SMB, ports);
}
function set_current_file(smb_state: State, file_id: count)
{
if ( file_id !in smb_state$fid_map )
{
smb_state$fid_map[file_id] = smb_state$current_cmd$referenced_file;
smb_state$fid_map[file_id]$fid = file_id;
}
smb_state$current_cmd$referenced_file = smb_state$fid_map[file_id];
smb_state$current_file = smb_state$current_cmd$referenced_file;
}
function write_file_log(state: State)
{
local f = state$current_file;
if ( f?$name &&
f$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,314 @@
@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];
}
event smb2_create_response(c: connection, hdr: SMB2::Header, file_id: SMB2::GUID, file_size: count, times: SMB::MACTimes, attrs: SMB2::FileAttrs) &priority=-5
{
SMB::write_file_log(c$smb_state);
}
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::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_file_rename(c: connection, hdr: SMB2::Header, file_id: SMB2::GUID, dst_filename: string) &priority=5
{
SMB::set_current_file(c$smb_state, file_id$persistent+file_id$volatile);
if ( c$smb_state$current_file?$name )
c$smb_state$current_file$prev_name = c$smb_state$current_file$name;
c$smb_state$current_file$name = dst_filename;
switch ( c$smb_state$current_tree$share_type )
{
case "DISK":
c$smb_state$current_file$action = SMB::FILE_RENAME;
break;
default:
c$smb_state$current_file$action = SMB::FILE_RENAME;
break;
}
}
event smb2_file_rename(c: connection, hdr: SMB2::Header, file_id: SMB2::GUID, dst_filename: string) &priority=-5
{
SMB::write_file_log(c$smb_state);
}
event smb2_file_delete(c: connection, hdr: SMB2::Header, file_id: SMB2::GUID, delete_pending: bool) &priority=5
{
SMB::set_current_file(c$smb_state, file_id$persistent+file_id$volatile);
if ( ! delete_pending )
{
print "huh...";
}
switch ( c$smb_state$current_tree$share_type )
{
case "DISK":
c$smb_state$current_file$action = SMB::FILE_DELETE;
break;
default:
c$smb_state$current_file$action = SMB::FILE_DELETE;
break;
}
}
event smb2_file_delete(c: connection, hdr: SMB2::Header, file_id: SMB2::GUID, delete_pending: bool) &priority=-5
{
SMB::write_file_log(c$smb_state);
}
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

@ -76,6 +76,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

@ -3,187 +3,33 @@
#ifndef ANALYZER_PROTOCOL_DCE_RPC_DCE_RPC_H
#define ANALYZER_PROTOCOL_DCE_RPC_DCE_RPC_H
// NOTE: This is a somewhat crude analyzer for DCE/RPC (used on Microsoft
// Windows systems) and shouldn't be considered as stable.
#include "NetVar.h"
#include "analyzer/protocol/tcp/TCP.h"
#include "analyzer/protocol/dce-rpc/events.bif.h"
#include "IPAddr.h"
#include "dce_rpc_simple_pac.h"
#include "dce_rpc_pac.h"
namespace analyzer { namespace dce_rpc {
class UUID {
public:
UUID();
UUID(const u_char data[16]);
UUID(const binpac::bytestring &uuid);
UUID(const char* s);
const char* to_string() const { return s.c_str(); }
const string& str() const { return s; }
bool operator==(const UUID& u) const
{ return s == u.str(); }
bool operator<(const UUID& u) const
{ return s < u.str(); }
protected:
u_char data[16];
string s;
};
const char* uuid_to_string(const u_char* uuid_data);
struct dce_rpc_endpoint_addr {
// All fields are in host byteorder.
IPAddr addr;
u_short port;
TransportProto proto;
dce_rpc_endpoint_addr()
{
addr = IPAddr();
port = 0;
proto = TRANSPORT_UNKNOWN;
}
bool is_valid_addr() const
{ return addr != IPAddr() && port != 0 && proto != TRANSPORT_UNKNOWN; }
bool operator<(dce_rpc_endpoint_addr const &e) const
{
if ( addr != e.addr )
return addr < e.addr;
if ( proto != e.proto )
return proto < e.proto;
if ( port != e.port )
return port < e.port;
return false;
}
string to_string() const
{
static char buf[128];
snprintf(buf, sizeof(buf), "%s/%d/%s",
addr.AsString().c_str(), port,
proto == TRANSPORT_TCP ? "tcp" :
(proto == TRANSPORT_UDP ? "udp" : "?"));
return string(buf);
}
};
/*
enum DCE_RPC_PTYPE {
DCE_RPC_REQUEST, DCE_RPC_PING, DCE_RPC_RESPONSE, DCE_RPC_FAULT,
DCE_RPC_WORKING, DCE_RPC_NOCALL, DCE_RPC_REJECT, DCE_RPC_ACK,
DCE_RPC_CL_CANCEL, DCE_RPC_FACK, DCE_RPC_CANCEL_ACK, DCE_RPC_BIND,
DCE_RPC_BIND_ACK, DCE_RPC_BIND_NAK, DCE_RPC_ALTER_CONTEXT,
DCE_RPC_ALTER_CONTEXT_RESP, DCE_RPC_SHUTDOWN, DCE_RPC_CO_CANCEL,
DCE_RPC_ORPHANED,
};
*/
#define DCE_RPC_HEADER_LENGTH 16
class DCE_RPC_Header {
public:
DCE_RPC_Header(analyzer::Analyzer* a, const u_char* bytes);
BifEnum::dce_rpc_ptype PTYPE() const { return ptype; }
int FragLen() const { return frag_len; }
int LittleEndian() const { return bytes[4] >> 4; }
bool Fragmented() const { return fragmented; }
void Weird(const char* msg) { analyzer->Weird(msg); }
void SetBytes(const u_char* b) { bytes = b; }
protected:
analyzer::Analyzer* analyzer;
const u_char* bytes;
BifEnum::dce_rpc_ptype ptype;
int frag_len;
bool fragmented;
};
// Create a general DCE_RPC_Session class so that it can be used in
// case the RPC conversation is tunneled through other connections,
// e.g. through an SMB session.
class DCE_RPC_Session {
public:
DCE_RPC_Session(analyzer::Analyzer* a);
virtual ~DCE_RPC_Session() {}
virtual void DeliverPDU(int is_orig, int len, const u_char* data);
static bool LooksLikeRPC(int len, const u_char* msg);
static bool any_dce_rpc_event()
{ return dce_rpc_message || dce_rpc_bind || dce_rpc_request; }
protected:
void DeliverBind(const binpac::DCE_RPC_Simple::DCE_RPC_PDU* pdu);
void DeliverRequest(const binpac::DCE_RPC_Simple::DCE_RPC_PDU* pdu);
void DeliverResponse(const binpac::DCE_RPC_Simple::DCE_RPC_PDU* pdu);
void DeliverEpmapperRequest(
const binpac::DCE_RPC_Simple::DCE_RPC_PDU* pdu,
const binpac::DCE_RPC_Simple::DCE_RPC_Request* req);
void DeliverEpmapperResponse(
const binpac::DCE_RPC_Simple::DCE_RPC_PDU* pdu,
const binpac::DCE_RPC_Simple::DCE_RPC_Response* resp);
void DeliverEpmapperMapResponse(
const binpac::DCE_RPC_Simple::DCE_RPC_PDU* pdu,
const binpac::DCE_RPC_Simple::DCE_RPC_Response* resp);
analyzer::Analyzer* analyzer;
UUID if_uuid;
BifEnum::dce_rpc_if_id if_id;
int opnum;
struct {
dce_rpc_endpoint_addr addr;
UUID uuid;
} mapped;
};
class Contents_DCE_RPC_Analyzer : public tcp::TCP_SupportAnalyzer {
public:
Contents_DCE_RPC_Analyzer(Connection* conn, bool orig, DCE_RPC_Session* session,
bool speculative);
~Contents_DCE_RPC_Analyzer();
protected:
virtual void DeliverStream(int len, const u_char* data, bool orig);
virtual void DeliverPDU(int len, const u_char* data);
void InitState();
int speculation;
u_char* msg_buf;
int msg_len;
int buf_n; // number of bytes in msg_buf
int buf_len; // size off msg_buf
DCE_RPC_Header* hdr;
bool ParseHeader();
DCE_RPC_Session* session;
};
class DCE_RPC_Analyzer : public tcp::TCP_ApplicationAnalyzer {
public:
DCE_RPC_Analyzer(Connection* conn, bool speculative = false);
DCE_RPC_Analyzer(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});
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(),
BifEvent::generate_dce_rpc_request(bro_analyzer(),
bro_analyzer()->Conn(),
fid,
${req.opnum},
bytestring_to_val(${req.stub}));
${req.stub}.length());
}
${connection}->set_cont_id_opnum_map(${req.p_cont_id},
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,56 @@
%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 )
{
gssapi->Done();
delete gssapi;
}
if ( ntlm )
{
ntlm->Done();
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,15 +25,25 @@ 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;
@ -43,10 +53,10 @@ type NDR_Format = record {
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 {
type DCE_RPC_Header(is_orig: bool) = record {
rpc_vers : uint8 &check(rpc_vers == 5);
rpc_vers_minor : uint8;
PTYPE : uint8;
@ -60,44 +70,55 @@ type DCE_RPC_Header = record {
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;
type ContextRequest = record {
id : uint16;
num_syntaxes : uint8;
reserved : padding[1];
abstract_syntax : p_syntax_id_t;
transfer_syntaxes : p_syntax_id_t[n_transfer_syn];
abstract_syntax : Syntax;
transfer_syntaxes : Syntax[num_syntaxes];
};
type p_cont_list_t = record {
n_context_elem : uint8;
type ContextReply = record {
ack_result : uint16;
ack_reason : uint16;
syntax : Syntax;
};
type ContextList(is_request: bool) = record {
num_contexts : uint8;
reserved : padding[3];
p_cont_elem : p_cont_elem_t[n_context_elem];
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;
context_list : ContextList(1);
};
type DCE_RPC_AlterContext = record {
type DCE_RPC_Bind_Ack = record {
max_xmit_frag : uint16;
max_recv_frag : uint16;
assoc_group_id : uint32;
p_context_elem : p_cont_list_t;
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;
context_id : uint16;
opnum : uint16;
# object : uuid;
# stub_pad_0 : padding align 8;
@ -106,20 +127,49 @@ type DCE_RPC_Request = record {
type DCE_RPC_Response = record {
alloc_hint : uint32;
p_cont_id : p_context_id_t;
context_id : uint16;
cancel_count : uint8;
reserved : uint8;
# stub_pad_0 : padding align 8;
stub : bytestring &restofdata;
};
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;
type DCE_RPC_AlterContext = record {
max_xmit_frag : uint16;
max_recv_frag : uint16;
assoc_group_id : uint32;
contexts : ContextList(0);
};
type DCE_RPC_Auth(header: DCE_RPC_Header) = uint8[header.auth_length];
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);
};
%include epmapper.pac
type DCE_RPC_Body(header: DCE_RPC_Header) = case header.PTYPE of {
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) = record {
type : uint8;
level : uint8;
pad_len : uint8;
reserved : uint8;
context_id : uint32;
blob : bytestring &length=header.auth_length;
};

View file

@ -2,6 +2,7 @@
%include bro.pac
%extern{
#include "types.bif.h"
#include "events.bif.h"
%}
@ -10,5 +11,18 @@ analyzer DCE_RPC withcontext {
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

@ -0,0 +1 @@
# Empty.

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,6 +1,5 @@
// See the file in the main distribution directory for copyright.
#include "plugin/Plugin.h"
#include "SMB.h"

File diff suppressed because it is too large Load diff

View file

@ -1,200 +1,36 @@
// See the file "COPYING" in the main distribution directory for copyright.
#ifndef ANALYZER_PROTOCOL_SMB_SMB_H
#define ANALYZER_PROTOCOL_SMB_SMB_H
// SMB (CIFS) analyzer.
// 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 "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();
virtual void DeliverStream(int len, const u_char* data, bool orig);
protected:
void InitMsgBuf();
void DeliverSMB(int len, const u_char* data);
SMB_Session* smb_session;
u_char dshdr[4];
u_char* msg_buf;
int msg_len;
int buf_n; // number of bytes in msg_buf
int buf_len; // size off msg_buf
};
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;
bool HasSMBHeader(int len, const u_char* data);
void NeedResync() {
delete interp;
interp = 0;
}
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

@ -1,75 +0,0 @@
SMB_COMMAND(SMB_COM_CREATE_DIRECTORY, 0x00)
SMB_COMMAND(SMB_COM_DELETE_DIRECTORY, 0x01)
SMB_COMMAND(SMB_COM_OPEN, 0x02)
SMB_COMMAND(SMB_COM_CREATE, 0x03)
SMB_COMMAND(SMB_COM_CLOSE, 0x04)
SMB_COMMAND(SMB_COM_FLUSH, 0x05)
SMB_COMMAND(SMB_COM_DELETE, 0x06)
SMB_COMMAND(SMB_COM_RENAME, 0x07)
SMB_COMMAND(SMB_COM_QUERY_INFORMATION, 0x08)
SMB_COMMAND(SMB_COM_SET_INFORMATION, 0x09)
SMB_COMMAND(SMB_COM_READ, 0x0A)
SMB_COMMAND(SMB_COM_WRITE, 0x0B)
SMB_COMMAND(SMB_COM_LOCK_BYTE_RANGE, 0x0C)
SMB_COMMAND(SMB_COM_UNLOCK_BYTE_RANGE, 0x0D)
SMB_COMMAND(SMB_COM_CREATE_TEMPORARY, 0x0E)
SMB_COMMAND(SMB_COM_CREATE_NEW, 0x0F)
SMB_COMMAND(SMB_COM_CHECK_DIRECTORY, 0x10)
SMB_COMMAND(SMB_COM_PROCESS_EXIT, 0x11)
SMB_COMMAND(SMB_COM_SEEK, 0x12)
SMB_COMMAND(SMB_COM_LOCK_AND_READ, 0x13)
SMB_COMMAND(SMB_COM_WRITE_AND_UNLOCK, 0x14)
SMB_COMMAND(SMB_COM_READ_RAW, 0x1A)
SMB_COMMAND(SMB_COM_READ_MPX, 0x1B)
SMB_COMMAND(SMB_COM_READ_MPX_SECONDARY, 0x1C)
SMB_COMMAND(SMB_COM_WRITE_RAW, 0x1D)
SMB_COMMAND(SMB_COM_WRITE_MPX, 0x1E)
SMB_COMMAND(SMB_COM_WRITE_MPX_SECONDARY, 0x1F)
SMB_COMMAND(SMB_COM_WRITE_COMPLETE, 0x20)
SMB_COMMAND(SMB_COM_QUERY_SERVER, 0x21)
SMB_COMMAND(SMB_COM_SET_INFORMATION2, 0x22)
SMB_COMMAND(SMB_COM_QUERY_INFORMATION2, 0x23)
SMB_COMMAND(SMB_COM_LOCKING_ANDX, 0x24)
SMB_COMMAND(SMB_COM_TRANSACTION, 0x25)
SMB_COMMAND(SMB_COM_TRANSACTION_SECONDARY, 0x26)
SMB_COMMAND(SMB_COM_IOCTL, 0x27)
SMB_COMMAND(SMB_COM_IOCTL_SECONDARY, 0x28)
SMB_COMMAND(SMB_COM_COPY, 0x29)
SMB_COMMAND(SMB_COM_MOVE, 0x2A)
SMB_COMMAND(SMB_COM_ECHO, 0x2B)
SMB_COMMAND(SMB_COM_WRITE_AND_CLOSE, 0x2C)
SMB_COMMAND(SMB_COM_OPEN_ANDX, 0x2D)
SMB_COMMAND(SMB_COM_READ_ANDX, 0x2E)
SMB_COMMAND(SMB_COM_WRITE_ANDX, 0x2F)
SMB_COMMAND(SMB_COM_NEW_FILE_SIZE, 0x30)
SMB_COMMAND(SMB_COM_CLOSE_AND_TREE_DISC, 0x31)
SMB_COMMAND(SMB_COM_TRANSACTION2, 0x32)
SMB_COMMAND(SMB_COM_TRANSACTION2_SECONDARY, 0x33)
SMB_COMMAND(SMB_COM_FIND_CLOSE2, 0x34)
SMB_COMMAND(SMB_COM_FIND_NOTIFY_CLOSE, 0x35)
// Used by Xenix/Unix 0x60 - 0x6E.
SMB_COMMAND(SMB_COM_TREE_CONNECT, 0x70)
SMB_COMMAND(SMB_COM_TREE_DISCONNECT, 0x71)
SMB_COMMAND(SMB_COM_NEGOTIATE, 0x72)
SMB_COMMAND(SMB_COM_SESSION_SETUP_ANDX, 0x73)
SMB_COMMAND(SMB_COM_LOGOFF_ANDX, 0x74)
SMB_COMMAND(SMB_COM_TREE_CONNECT_ANDX, 0x75)
SMB_COMMAND(SMB_COM_QUERY_INFORMATION_DISK, 0x80)
SMB_COMMAND(SMB_COM_SEARCH, 0x81)
SMB_COMMAND(SMB_COM_FIND, 0x82)
SMB_COMMAND(SMB_COM_FIND_UNIQUE, 0x83)
SMB_COMMAND(SMB_COM_FIND_CLOSE, 0x84)
SMB_COMMAND(SMB_COM_NT_TRANSACT, 0xA0)
SMB_COMMAND(SMB_COM_NT_TRANSACT_SECONDARY, 0xA1)
SMB_COMMAND(SMB_COM_NT_CREATE_ANDX, 0xA2)
SMB_COMMAND(SMB_COM_NT_CANCEL, 0xA4)
SMB_COMMAND(SMB_COM_NT_RENAME, 0xA5)
SMB_COMMAND(SMB_COM_OPEN_PRINT_FILE, 0xC0)
SMB_COMMAND(SMB_COM_WRITE_PRINT_FILE, 0xC1)
SMB_COMMAND(SMB_COM_CLOSE_PRINT_FILE, 0xC2)
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)

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,252 @@
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
%{
if ( name == NULL )
{
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;
}
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,136 @@
%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_ioctl.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,88 @@
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 )
{
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,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,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