Complete breakout of SMB, GSSAPI, and NTLM

- Looser coupling between these analyzers.
 - New ntlm.log (still pretty early)
 - Improved string handling for NTLM (convert UTF16 to UTF8)
 - SMB2 analyzer now supports GSSAPI.
 - Improved abstraction of DCE_RPC operations (still not finished)
 - Lots of whitespace cleanup.
This commit is contained in:
Seth Hall 2016-04-03 04:17:20 -04:00
parent ff3437d157
commit 5b5589e167
22 changed files with 446 additions and 926 deletions

View file

@ -2315,18 +2315,10 @@ type ntp_msg: record {
};
module SMB;
module NTLM;
export {
## MAC times for a file.
type SMB::MACTimes: record {
modified : time &log;
accessed : time &log;
created : time &log;
changed : time &log;
} &log;
type SMB::NTLMVersion: record {
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
@ -2337,7 +2329,7 @@ export {
ntlmssp : count;
};
type SMB::NTLMNegotiateFlags: record {
type NTLM::NegotiateFlags: record {
## If set, requires 56-bit encryption
negotiate_56 : bool;
## If set, requests an explicit key exchange
@ -2349,7 +2341,6 @@ export {
## 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
@ -2393,18 +2384,18 @@ export {
negotiate_unicode : bool;
};
type SMB::NTLMNegotiate: record {
type NTLM::Negotiate: record {
## The negotiate flags
flags : SMB::NTLMNegotiateFlags;
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 : SMB::NTLMVersion &optional;
version : NTLM::Version &optional;
};
type SMB::NTLMAVs: record {
type NTLM::AVs: record {
## The server's NetBIOS computer name
nb_computer_name : string;
## The server's NetBIOS domain name
@ -2422,7 +2413,7 @@ export {
## The associated timestamp, if present
timestamp : time &optional;
## Indicates that the client is providing
## mess achine ID created at computer startup to
## a machine ID created at computer startup to
## identify the calling machine
single_host_id : count &optional;
@ -2430,23 +2421,23 @@ export {
target_name : string &optional;
};
type SMB::NTLMChallenge: record {
type NTLM::Challenge: record {
## The negotiate flags
flags : SMB::NTLMNegotiateFlags;
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 : SMB::NTLMVersion &optional;
version : NTLM::Version &optional;
## Attribute-value pairs specified by the server
target_info : SMB::NTLMAVs &optional;
target_info : NTLM::AVs &optional;
};
type SMB::NTLMAuthenticate: record {
type NTLM::Authenticate: record {
## The negotiate flags
flags : SMB::NTLMNegotiateFlags;
flags : NTLM::NegotiateFlags;
## The domain or computer name hosting the account
domain_name : string;
## The name of the user to be authenticated.
@ -2454,9 +2445,20 @@ export {
## The name of the computer to which the user was logged on.
workstation : string;
## The Windows version information, if supplied
version : SMB::NTLMVersion &optional;
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;

View file

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

View file

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

View file

@ -0,0 +1,54 @@
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: string &log &optional;
hostname: string &log &optional;
domainname: string &log &optional;
};
}
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
{
#print request;
}
event ntlm_challenge(c: connection, challenge: NTLM::Challenge) &priority=5
{
#print "challenge!!!!!";
#print challenge;
}
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 ntlm_authenticate(c: connection, request: NTLM::Authenticate) &priority=-5
{
Log::write(NTLM::LOG, c$ntlm);
}

View file

@ -281,51 +281,6 @@ event smb1_session_setup_andx_response(c: connection, hdr: SMB1::Header, respons
}
}
event smb_ntlm_negotiate(c: connection, hdr: SMB1::Header, request: SMB::NTLMNegotiate)
{
c$smb_state$current_cmd$sub_command = "NTLMSSP_NEGOTIATE";
}
event smb_ntlm_authenticate(c: connection, hdr: SMB1::Header, request: SMB::NTLMAuthenticate) &priority=5
{
c$smb_state$current_cmd$sub_command = "NTLMSSP_AUTHENTICATE";
c$smb_state$current_auth = SMB::AuthInfo($ts=network_time(), $uid=c$uid, $id=c$id);
if ( request?$domain_name )
c$smb_state$current_auth$domainname = request$domain_name;
if ( request?$workstation )
c$smb_state$current_auth$hostname = request$workstation;
if ( request?$user_name )
c$smb_state$current_auth$username = request$user_name;
local user: string = "";
if ( ( request?$domain_name && request$domain_name != "" ) && ( request?$user_name && request$user_name != "" ) )
user = fmt("%s\\%s", request$domain_name, request$user_name);
else if ( ( request?$workstation && request$workstation != "" ) && ( request?$user_name && request$user_name != "" ) )
user = fmt("%s\\%s", request$workstation, request$user_name);
else if ( request?$user_name && request$user_name != "" )
user = request$user_name;
else if ( request?$domain_name && request$domain_name != "" )
user = fmt("%s\\", request$domain_name);
else if ( request?$workstation && request$workstation != "" )
user = fmt("%s", request$workstation);
if ( user != "" )
{
c$smb_state$current_cmd$argument = user;
}
if ( hdr$uid !in c$smb_state$uid_map )
{
c$smb_state$uid_map[hdr$uid] = user;
}
}
event smb_ntlm_authenticate(c: connection, hdr: SMB1::Header, request: SMB::NTLMAuthenticate) &priority=5
{
Log::write(SMB::AUTH_LOG, c$smb_state$current_auth);
}
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];

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)
@ -25,6 +26,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

@ -10,7 +10,9 @@ bro_plugin_pac(
dce_rpc.pac
dce_rpc-protocol.pac
dce_rpc-analyzer.pac
dce_rpc-gssapi.pac
endpoint-atsvc.pac
endpoint-epmapper.pac
endpoint-atsvc.pac)
)
bro_plugin_end()

View file

@ -0,0 +1,42 @@
refine connection DCE_RPC_Conn += {
%member{
analyzer::Analyzer *gssapi;
analyzer::Analyzer *ntlm;
%}
%init{
gssapi = analyzer_mgr->InstantiateAnalyzer("GSSAPI", bro_analyzer->Conn());
ntlm = analyzer_mgr->InstantiateAnalyzer("NTLM", bro_analyzer->Conn());
%}
%cleanup{
if ( gssapi )
delete gssapi;
if ( ntlm )
delete ntlm;
%}
function forward_auth(auth: DCE_RPC_Auth, is_orig: bool): bool
%{
switch ( ${auth.type} )
{
case 0x0a:
if ( ntlm )
ntlm->DeliverStream(${auth.blob}.length(), ${auth.blob}.begin(), is_orig);
break;
//case 0xXX:
// if ( gssapi )
// gssapi->DeliverStream(${data}.length(), ${data}.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

@ -23,5 +23,6 @@ flow DCE_RPC_Flow(is_orig: bool) {
flowunit = DCE_RPC_PDU(is_orig) withcontext(connection, this);
};
%include epmapper.pac
%include endpoint-atsvc.pac
%include endpoint-epmapper.pac
%include dce_rpc-analyzer.pac

View file

@ -1,38 +1,41 @@
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 : SMB_string(unicode, offsetof(string));
};
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;
};
#%include ../smb/smb-strings.pac
#
#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 : SMB_string(unicode, offsetof(string));
#};
#
#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

@ -6,9 +6,6 @@ include_directories(AFTER ${CMAKE_CURRENT_BINARY_DIR}/../dce-rpc)
bro_plugin_begin(Bro SMB)
bro_plugin_cc(SMB.cc Plugin.cc)
bro_plugin_bif(
smb_ntlmssp.bif
smb_pipe.bif
smb1_com_check_directory.bif
smb1_com_close.bif
smb1_com_create_directory.bif
@ -40,6 +37,7 @@ bro_plugin_bif(
smb2_com_write.bif
smb2_events.bif
smb_pipe.bif
types.bif)
bro_plugin_pac(
smb.pac
@ -47,8 +45,8 @@ bro_plugin_pac(
smb-strings.pac
smb-time.pac
smb-pipe.pac
smb-gssapi.pac
smb-mailslot.pac
smb-ntlmssp.pac
smb1-protocol.pac
smb1-com-check-directory.pac

View file

@ -10,14 +10,6 @@
namespace analyzer { namespace smb {
enum IPC_named_pipe {
IPC_NONE,
IPC_LOCATOR,
IPC_EPMAPPER,
IPC_SAMR, // Security Account Manager
};
class Contents_SMB : public tcp::TCP_SupportAnalyzer {
public:
Contents_SMB(Connection* conn, bool orig);

View file

@ -0,0 +1,11 @@
refine connection SMB_Conn += {
function forward_gssapi(data: bytestring, is_orig: bool): bool
%{
if ( gssapi )
gssapi->DeliverStream(${data}.length(), ${data}.begin(), is_orig);
return true;
%}
};

View file

@ -1,57 +0,0 @@
# Supporting types for ASN.1
#
# From the Kerberos analyzer
#
# TODO: Figure out a way to include this code only once.
type ASN1Encoding = record {
meta: ASN1EncodingMeta;
content: bytestring &length = meta.length;
};
type ASN1EncodingMeta = record {
tag: uint8;
len: uint8;
more_len: bytestring &length = long_len ? len & 0x7f : 0;
} &let {
long_len: bool = len & 0x80;
length: uint64 = long_len ? binary_to_int64(more_len) : len & 0x7f;
index: uint8 = tag - 160;
};
type ASN1Integer = record {
encoding: ASN1Encoding;
};
type ASN1OctetString = record {
encoding: ASN1Encoding;
};
type SequenceElement(grab_content: bool) = record {
index_meta: ASN1EncodingMeta;
have_content: case grab_content of {
true -> data: ASN1Encoding;
false -> meta: ASN1EncodingMeta;
};
} &let {
index: uint8 = index_meta.index;
length: uint64 = index_meta.length;
};
type Array = record {
array_meta: ASN1EncodingMeta;
data: ASN1Encoding[];
};
function binary_to_int64(bs: bytestring): int64
%{
int64 rval = 0;
for ( int i = 0; i < bs.length(); ++i )
{
uint64 byte = bs[i];
rval |= byte << (8 * (bs.length() - (i + 1)));
}
return rval;
%}

View file

@ -1,430 +0,0 @@
refine connection SMB_Conn += {
function build_negotiate_flag_record(val: SMB_NTLM_Negotiate_Flags): BroVal
%{
RecordVal* flags = new RecordVal(BifType::Record::SMB::NTLMNegotiateFlags);
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 build_version_record(val: SMB_NTLM_Version): BroVal
%{
RecordVal* result = new RecordVal(BifType::Record::SMB::NTLMVersion);
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: SMB_NTLM_AV_Pair_Sequence): BroVal
%{
RecordVal* result = new RecordVal(BifType::Record::SMB::NTLMAVs);
for ( uint i = 0; ${val.pairs[i].id} != 0; i++ )
{
switch ( ${val.pairs[i].id} )
{
case 1:
result->Assign(0, uint8s_to_stringval(${val.pairs[i].nb_computer_name.data}));
break;
case 2:
result->Assign(1, uint8s_to_stringval(${val.pairs[i].nb_domain_name.data}));
break;
case 3:
result->Assign(2, uint8s_to_stringval(${val.pairs[i].dns_computer_name.data}));
break;
case 4:
result->Assign(3, uint8s_to_stringval(${val.pairs[i].dns_domain_name.data}));
break;
case 5:
result->Assign(4, uint8s_to_stringval(${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, uint8s_to_stringval(${val.pairs[i].target_name.data}));
break;
}
}
return result;
%}
function proc_smb_ntlm_ssp(header: SMB_Header, val:SMB_NTLM_SSP): bool
%{
if ( ${val.gssapi.is_init} )
return true;
for ( uint i = 0; i < ${val.gssapi.resp.args}->size(); ++i )
{
switch ( ${val.gssapi.resp.args[i].seq_meta.index} )
{
case 0:
if ( ${val.gssapi.resp.args[i].args.neg_state} == 0 )
BifEvent::generate_smb_ntlm_accepted(bro_analyzer(), bro_analyzer()->Conn(), BuildHeaderVal(header));
break;
default:
break;
}
}
return true;
%}
function proc_smb_ntlm_negotiate(header: SMB_Header, val: SMB_NTLM_Negotiate): bool
%{
RecordVal* result = new RecordVal(BifType::Record::SMB::NTLMNegotiate);
result->Assign(0, build_negotiate_flag_record(${val.flags}));
if ( ${val.flags.negotiate_oem_domain_supplied} )
result->Assign(1, uint8s_to_stringval(${val.domain_name.string.data}));
if ( ${val.flags.negotiate_oem_workstation_supplied} )
result->Assign(2, uint8s_to_stringval(${val.workstation.string.data}));
if ( ${val.flags.negotiate_version} )
result->Assign(3, build_version_record(${val.version}));
BifEvent::generate_smb_ntlm_negotiate(bro_analyzer(), bro_analyzer()->Conn(), BuildHeaderVal(header), result);
return true;
%}
function proc_smb_ntlm_challenge(header: SMB_Header, val: SMB_NTLM_Challenge): bool
%{
RecordVal* result = new RecordVal(BifType::Record::SMB::NTLMChallenge);
result->Assign(0, build_negotiate_flag_record(${val.flags}));
if ( ${val.flags.request_target} )
result->Assign(1, uint8s_to_stringval(${val.target_name.string.data}));
if ( ${val.flags.negotiate_version} )
result->Assign(2, build_version_record(${val.version}));
if ( ${val.flags.negotiate_target_info} )
result->Assign(3, build_av_record(${val.target_info}));
BifEvent::generate_smb_ntlm_challenge(bro_analyzer(), bro_analyzer()->Conn(), BuildHeaderVal(header), result);
return true;
%}
function proc_smb_ntlm_authenticate(header: SMB_Header, val: SMB_NTLM_Authenticate): bool
%{
RecordVal* result = new RecordVal(BifType::Record::SMB::NTLMAuthenticate);
result->Assign(0, build_negotiate_flag_record(${val.flags}));
if ( ${val.domain_name_fields.length} > 0 )
result->Assign(1, uint8s_to_stringval(${val.domain_name.string.data}));
if ( ${val.user_name_fields.length} > 0 )
result->Assign(2, uint8s_to_stringval(${val.user_name.string.data}));
if ( ${val.workstation_fields.length} > 0 )
result->Assign(3, uint8s_to_stringval(${val.workstation.string.data}));
if ( ${val.flags.negotiate_version} )
result->Assign(4, build_version_record(${val.version}));
BifEvent::generate_smb_ntlm_authenticate(bro_analyzer(), bro_analyzer()->Conn(), BuildHeaderVal(header), result);
return true;
%}
};
type GSSAPI_NEG_TOKEN(header: SMB_Header) = 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(header);
false -> resp: GSSAPI_NEG_TOKEN_RESP(header);
};
} &let {
is_init: bool = (wrapper.tag == 0x60);
};
type GSSAPI_NEG_TOKEN_INIT(header: SMB_Header) = record {
seq_meta : ASN1EncodingMeta;
args : GSSAPI_NEG_TOKEN_INIT_Arg(header)[];
};
type GSSAPI_NEG_TOKEN_INIT_Arg(header: SMB_Header) = record {
seq_meta : ASN1EncodingMeta;
args : GSSAPI_NEG_TOKEN_INIT_Arg_Data(header, seq_meta.index) &length=seq_meta.length;
};
type GSSAPI_NEG_TOKEN_INIT_Arg_Data(header: SMB_Header, index: uint8) = case index of {
0 -> mech_type_list : ASN1Encoding;
1 -> req_flags : ASN1Encoding;
2 -> mech_token : SMB_NTLM_SSP_Token(header);
3 -> mech_list_mic : ASN1OctetString;
};
type GSSAPI_NEG_TOKEN_RESP(header: SMB_Header) = record {
seq_meta : ASN1EncodingMeta;
args : GSSAPI_NEG_TOKEN_RESP_Arg(header)[];
};
type GSSAPI_NEG_TOKEN_RESP_Arg(header: SMB_Header) = record {
seq_meta : ASN1EncodingMeta;
args : GSSAPI_NEG_TOKEN_RESP_Arg_Data(header, seq_meta.index) &length=seq_meta.length;
};
type GSSAPI_NEG_TOKEN_RESP_Arg_Data(header: SMB_Header, index: uint8) = case index of {
0 -> neg_state : ASN1Integer;
1 -> supported_mech : ASN1Encoding;
2 -> response_token : SMB_NTLM_SSP_Token(header);
3 -> mech_list_mic : ASN1OctetString;
default -> def : bytestring &restofdata &transient;
};
type SMB_NTLM_SSP(header: SMB_Header) = record {
gssapi: GSSAPI_NEG_TOKEN(header);
} &let {
proc: bool = $context.connection.proc_smb_ntlm_ssp(header, this);
};
type SMB_NTLM_SSP_Token(header: SMB_Header) = record {
meta : ASN1EncodingMeta;
signature : bytestring &length=8;
msg_type : uint32;
msg : case msg_type of {
1 -> negotiate : SMB_NTLM_Negotiate(header, offsetof(msg) - offsetof(signature));
2 -> challenge : SMB_NTLM_Challenge(header, offsetof(msg) - offsetof(signature));
3 -> authenticate : SMB_NTLM_Authenticate(header, offsetof(msg) - offsetof(signature));
default -> def : bytestring &restofdata &transient;
};
};
type SMB_NTLM_Negotiate(header: SMB_Header, offset: uint16) = record {
flags : SMB_NTLM_Negotiate_Flags;
domain_name_fields : SMB_NTLM_StringData;
workstation_fields : SMB_NTLM_StringData;
version_present : case flags.negotiate_version of {
true -> version : SMB_NTLM_Version;
false -> no_version : empty;
};
payload : bytestring &restofdata;
} &let {
absolute_offset : uint16 = offsetof(payload) + offset;
domain_name : SMB_NTLM_String(domain_name_fields, absolute_offset, flags.negotiate_unicode) withinput payload &if(flags.negotiate_oem_domain_supplied);
workstation : SMB_NTLM_String(workstation_fields, absolute_offset, flags.negotiate_unicode) withinput payload &if(flags.negotiate_oem_workstation_supplied);
proc : bool = $context.connection.proc_smb_ntlm_negotiate(header, this);
};
type SMB_NTLM_Challenge(header: SMB_Header, offset: uint16) = record {
target_name_fields : SMB_NTLM_StringData;
flags : SMB_NTLM_Negotiate_Flags;
challenge : uint64;
reserved : padding[8];
target_info_fields : SMB_NTLM_StringData;
version_present : case flags.negotiate_version of {
true -> version : SMB_NTLM_Version;
false -> no_version : empty;
};
payload : bytestring &restofdata;
} &let {
absolute_offset : uint16 = offsetof(payload) + offset;
target_name : SMB_NTLM_String(target_name_fields, absolute_offset, flags.negotiate_unicode) withinput payload &if(flags.request_target);
target_info : SMB_NTLM_AV_Pair_Sequence(target_info_fields.offset - absolute_offset) withinput payload &if(flags.negotiate_target_info);
proc : bool = $context.connection.proc_smb_ntlm_challenge(header, this);
};
type SMB_NTLM_Authenticate(header: SMB_Header, offset: uint16) = record {
lm_challenge_response_fields : SMB_NTLM_StringData;
nt_challenge_response_fields : SMB_NTLM_StringData;
domain_name_fields : SMB_NTLM_StringData;
user_name_fields : SMB_NTLM_StringData;
workstation_fields : SMB_NTLM_StringData;
encrypted_session_key_fields : SMB_NTLM_StringData;
flags : SMB_NTLM_Negotiate_Flags;
version_present : case flags.negotiate_version of {
true -> version : SMB_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 : SMB_NTLM_String(domain_name_fields, absolute_offset, flags.negotiate_unicode) withinput payload &if(domain_name_fields.length > 0);
user_name : SMB_NTLM_String(user_name_fields, absolute_offset, flags.negotiate_unicode) withinput payload &if(user_name_fields.length > 0);
workstation : SMB_NTLM_String(workstation_fields, absolute_offset , flags.negotiate_unicode) withinput payload &if(workstation_fields.length > 0);
encrypted_session_key : SMB_NTLM_String(encrypted_session_key_fields, absolute_offset, flags.negotiate_unicode) withinput payload &if(flags.negotiate_key_exch);
proc : bool = $context.connection.proc_smb_ntlm_authenticate(header, this);
};
type SMB_NTLM_Version = record {
major_version : uint8;
minor_version : uint8;
build_number : uint16;
reserved : padding[3];
ntlm_revision : uint8;
};
type SMB_NTLM_StringData = record {
length : uint16;
max_length : uint16;
offset : uint32;
};
type SMB_Fixed_Length_String(unicode: bool) = record {
data: uint8[] &restofdata;
};
type SMB_NTLM_String(fields: SMB_NTLM_StringData, offset: uint16, unicode: bool) = record {
pad1 : padding to fields.offset - offset;
string : SMB_Fixed_Length_String(unicode) &length=fields.length;
};
type SMB_NTLM_AV_Pair_Sequence(offset: uint16) = record {
pad1 : padding to offset;
pairs : SMB_NTLM_AV_Pair[] &until($element.last);
};
type SMB_NTLM_AV_Pair = record {
id : uint16;
length : uint16;
value_case : case id of {
0x0000 -> av_eol : empty;
0x0001 -> nb_computer_name : SMB_Fixed_Length_String(true) &length=length;
0x0002 -> nb_domain_name : SMB_Fixed_Length_String(true) &length=length;
0x0003 -> dns_computer_name : SMB_Fixed_Length_String(true) &length=length;
0x0004 -> dns_domain_name : SMB_Fixed_Length_String(true) &length=length;
0x0005 -> dns_tree_name : SMB_Fixed_Length_String(true) &length=length;
0x0006 -> av_flags : uint32;
0x0007 -> timestamp : uint64;
0x0008 -> single_host : SMB_NTLM_Single_Host;
0x0009 -> target_name : SMB_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 SMB_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 SMB_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 SMB_NTLM_Response(offset: uint16) = record {
padpad : padding to offset;
response : bytestring &length=24;
};
type SMB_NTLMv2_Response(flags: SMB_NTLM_Negotiate_Flags, offset: uint16) = record {
padpad : padding to offset;
response : bytestring &length=16;
client_challenge : SMB_NTLMv2_Client_Challenge(flags);
};
type SMB_NTLMv2_Client_Challenge(flags: SMB_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 : SMB_NTLM_AV_Pair_Sequence(0);
};
type SMB_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

@ -4,16 +4,12 @@
%extern{
#include "analyzer/Manager.h"
#include "analyzer/Analyzer.h"
// #include "analyzer/protocol/dce-rpc/DCE_RPC.h"
#include "smb1_events.bif.h"
#include "smb2_events.bif.h"
#include "types.bif.h"
#include "smb_ntlmssp.bif.h"
#include "smb_pipe.bif.h"
#include "smb1_com_check_directory.bif.h"
#include "smb1_com_close.bif.h"
#include "smb1_com_create_directory.bif.h"
@ -57,9 +53,9 @@ connection SMB_Conn(bro_analyzer: BroAnalyzer) {
%include smb-strings.pac
%include smb-common.pac
%include smb-time.pac
%include smb-ntlmssp-asn1.pac
%include smb-ntlmssp.pac
%include smb-mailslot.pac
%include smb-pipe.pac
%include smb-gssapi.pac
# SMB1 Commands
%include smb1-com-check-directory.pac
@ -82,9 +78,6 @@ connection SMB_Conn(bro_analyzer: BroAnalyzer) {
%include smb1-com-tree-disconnect.pac
%include smb1-com-write-andx.pac
%include smb-mailslot.pac
%include smb-pipe.pac
# SMB2 Commands
%include smb2-com-close.pac
%include smb2-com-create.pac
@ -146,14 +139,18 @@ flow SMB_Flow(is_orig: bool) {
refine connection SMB_Conn += {
%member{
analyzer::Analyzer *dcerpc;
analyzer::Analyzer *gssapi;
%}
%init{
dcerpc = analyzer_mgr->InstantiateAnalyzer("DCE_RPC", bro_analyzer->Conn());
gssapi = analyzer_mgr->InstantiateAnalyzer("GSSAPI", bro_analyzer->Conn());
%}
%cleanup{
if ( dcerpc )
delete dcerpc;
if ( gssapi )
delete gssapi;
%}
};

View file

@ -161,7 +161,6 @@ refine connection SMB_Conn += {
else
{
ntlm->Assign(12, bytestring_to_val(${val.ntlm.server_guid}));
// ntlm->Assign(13, bytestring_to_val(${val.ntlm.security_blob}));
}
response->Assign(2, ntlm);
@ -250,7 +249,7 @@ type SMB1_negotiate_ntlm_response(header: SMB_Header) = record {
false -> no_guid : empty;
} &requires(capabilities_extended_security);
security_blob_present: case capabilities_extended_security of {
true -> security_blob: SMB_NTLM_SSP(header) &length=(byte_count-16);
true -> security_blob : bytestring &length=(byte_count-16);
false -> no_blob : empty;
} &requires(capabilities_extended_security);
} &let {
@ -277,5 +276,7 @@ type SMB1_negotiate_ntlm_response(header: SMB_Header) = record {
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

@ -47,7 +47,6 @@ refine connection SMB_Conn += {
request->Assign(5, smb_string2stringval(${val.ntlm_extended_security.native_os}));
request->Assign(6, smb_string2stringval(${val.ntlm_extended_security.native_lanman}));
//request->Assign(12, bytestring_to_val(${val.ntlm_extended_security.security_blob}));
request->Assign(13, capabilities);
break;
@ -107,7 +106,10 @@ refine connection SMB_Conn += {
break;
}
BifEvent::generate_smb1_session_setup_andx_response(bro_analyzer(), bro_analyzer()->Conn(), BuildHeaderVal(header), response);
BifEvent::generate_smb1_session_setup_andx_response(bro_analyzer(),
bro_analyzer()->Conn(),
BuildHeaderVal(header),
response);
}
return true;
@ -207,10 +209,12 @@ type SMB1_session_setup_andx_request_ntlm_extended_security(header: SMB_Header)
reserved : uint32;
capabilities : SMB1_session_setup_andx_request_ntlm_capabilities;
byte_count : uint16;
security_blob : SMB_NTLM_SSP(header) &length=security_blob_length;
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 {
@ -218,12 +222,13 @@ type SMB1_session_setup_andx_response_ntlm(header: SMB_Header) = record {
action : uint16;
security_blob_length : uint16;
byte_count : uint16;
security_blob : SMB_NTLM_SSP(header) &length=security_blob_length;
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

@ -62,7 +62,12 @@ type SMB2_negotiate_response(header: SMB2_Header) = record {
max_write_size : uint32;
system_time : SMB_timestamp;
server_start_time : SMB_timestamp;
security : SMB2_security;
security_offset : uint16;
security_length : uint16;
pad1 : padding to security_offset - header.head_length;
security_blob : bytestring &length=security_length;
} &byteorder=littleendian, &let {
proc : bool = $context.connection.proc_smb2_negotiate_response(header, this);
gssapi_proc : bool = $context.connection.forward_gssapi(security_blob, false);
};

View file

@ -45,20 +45,27 @@ type SMB2_session_setup_request(header: SMB2_Header) = record {
security_mode : uint8;
capabilities : uint32;
channel : uint32;
security : SMB2_security;
security_offset : uint16;
security_length : uint16;
pad1 : padding to security_offset - header.head_length;
security_blob : bytestring &length=security_length;
} &let {
proc: bool = $context.connection.proc_smb2_session_setup_request(header, this);
gssapi_proc : bool = $context.connection.forward_gssapi(security_blob, true);
};
type SMB2_session_setup_response(header: SMB2_Header) = record {
structure_size : uint16;
session_flags : uint16;
security : SMB2_security;
security_offset : uint16;
security_length : uint16;
pad1 : padding to security_offset - header.head_length;
security_blob : bytestring &length=security_length;
} &let {
flag_guest = (session_flags & 0x1) > 0;
flag_anonymous = (session_flags & 0x2) > 0;
flag_encrypt = (session_flags & 0x4) > 0;
} &let {
proc: bool = $context.connection.proc_smb2_session_setup_response(header, this);
gssapi_proc : bool = $context.connection.forward_gssapi(security_blob, false);
};

View file

@ -202,14 +202,6 @@ type SMB2_Header(is_orig: bool) = record {
proc : bool = $context.connection.proc_smb2_message(this, is_orig);
} &byteorder=littleendian;
type SMB2_security = record {
buffer_offset : uint16;
buffer_len : uint16;
# TODO: handle previous session IDs
sec_buffer : bytestring &length = buffer_len;
} &byteorder = littleendian;
# file ids and guids are the same thing and need unified somehow.
type SMB2_guid = record {
persistent : uint64;

View file

@ -1,64 +0,0 @@
## Generated for SMB/CIFS requests that contain a security blob with a GSSAPI NTLM message 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.
##
## negotiate: The parsed data of the NTLM message. See init-bare for more details.
##
event smb_ntlm_negotiate%(c: connection, hdr: SMB1::Header, request: SMB::NTLMNegotiate%);
## Generated for SMB/CIFS requests that contain a security blob with a GSSAPI NTLM message of type *challenge*.
##
## 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.
##
## negotiate: The parsed data of the NTLM message. See init-bare for more details.
##
event smb_ntlm_challenge%(c: connection, hdr: SMB1::Header, request: SMB::NTLMChallenge%);
## Generated for SMB/CIFS requests that contain a security blob with a GSSAPI NTLM message of type *authenticate*.
##
## 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.
##
## negotiate: The parsed data of the NTLM message. See init-bare for more details.
##
event smb_ntlm_authenticate%(c: connection, hdr: SMB1::Header, request: SMB::NTLMAuthenticate%);
## Generated for SMB/CIFS requests that contain a security blob with a GSSAPI message of type *accept-completed*.
##
## 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.
##
event smb_ntlm_accepted%(c: connection, hdr: SMB1::Header%);
#### Types
type SMB::NTLMNegotiate: record;
type SMB::NTLMChallenge: record;
type SMB::NTLMAuthenticate: record;
type SMB::NTLMNegotiateFlags: record;
type SMB::NTLMVersion: record;
type SMB::NTLMAVs: record;