zeek/src/analyzer/protocol/smb/smb1-com-negotiate.pac
Tim Wojtulewicz 8efddf87d9 SMB: Init lanman time and set TZ correctly before calling mktime
This makes a change to the record for smb1-negotiate-response as well.
Times and dates are two 2-byte sections, not three. Switch the parsing
to just use two uint16s, and pass those into the lanman time conversion
function.
2025-09-29 08:56:49 -07:00

253 lines
11 KiB
JavaScript

# 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 )
{
auto dialects = zeek::make_intrusive<zeek::VectorVal>(zeek::id::string_vec);
for ( unsigned int i = 0; i < ${val.dialects}->size(); ++i )
{
auto dia = smb_string2stringval((*${val.dialects})[i]->name());
dialects->Assign(i, std::move(dia));
}
zeek::BifEvent::enqueue_smb1_negotiate_request(zeek_analyzer(), zeek_analyzer()->Conn(),
SMBHeaderVal(header),
std::move(dialects));
}
return true;
%}
function proc_smb1_negotiate_response(header: SMB_Header, val: SMB1_negotiate_response): bool
%{
if ( smb1_negotiate_response )
{
auto response = zeek::make_intrusive<zeek::RecordVal>(zeek::BifType::Record::SMB1::NegotiateResponse);
switch ( ${val.word_count} )
{
case 0x01:
{
auto core = zeek::make_intrusive<zeek::RecordVal>(zeek::BifType::Record::SMB1::NegotiateResponseCore);
core->Assign(0, ${val.dialect_index});
response->Assign(0, std::move(core));
}
break;
case 0x0d:
{
auto security = zeek::make_intrusive<zeek::RecordVal>(zeek::BifType::Record::SMB1::NegotiateResponseSecurity);
security->Assign(0, ${val.lanman.security_user_level});
security->Assign(1, ${val.lanman.security_challenge_response});
auto raw = zeek::make_intrusive<zeek::RecordVal>(zeek::BifType::Record::SMB1::NegotiateRawMode);
raw->Assign(0, ${val.lanman.raw_read_supported});
raw->Assign(1, ${val.lanman.raw_write_supported});
auto lanman = zeek::make_intrusive<zeek::RecordVal>(zeek::BifType::Record::SMB1::NegotiateResponseLANMAN);
lanman->Assign(0, ${val.word_count});
lanman->Assign(1, ${val.dialect_index});
lanman->Assign(2, std::move(security));
lanman->Assign(3, ${val.lanman.max_buffer_size});
lanman->Assign(4, ${val.lanman.max_mpx_count});
lanman->Assign(5, ${val.lanman.max_number_vcs});
lanman->Assign(6, std::move(raw));
lanman->Assign(7, ${val.lanman.session_key});
lanman->Assign(8, time_from_lanman(${val.lanman.server_time}, ${val.lanman.server_date}, ${val.lanman.server_tz}));
lanman->Assign(9, to_stringval(${val.lanman.encryption_key}));
lanman->Assign(10, smb_string2stringval(${val.lanman.primary_domain}));
response->Assign(1, std::move(lanman));
}
break;
case 0x11:
{
auto security = zeek::make_intrusive<zeek::RecordVal>(zeek::BifType::Record::SMB1::NegotiateResponseSecurity);
security->Assign(0, ${val.ntlm.security_user_level});
security->Assign(1, ${val.ntlm.security_challenge_response});
security->Assign(2, ${val.ntlm.security_signatures_enabled});
security->Assign(3, ${val.ntlm.security_signatures_required});
auto capabilities = zeek::make_intrusive<zeek::RecordVal>(zeek::BifType::Record::SMB1::NegotiateCapabilities);
capabilities->Assign(0, ${val.ntlm.capabilities_raw_mode});
capabilities->Assign(1, ${val.ntlm.capabilities_mpx_mode});
capabilities->Assign(2, ${val.ntlm.capabilities_unicode});
capabilities->Assign(3, ${val.ntlm.capabilities_large_files});
capabilities->Assign(4, ${val.ntlm.capabilities_nt_smbs});
capabilities->Assign(5, ${val.ntlm.capabilities_rpc_remote_apis});
capabilities->Assign(6, ${val.ntlm.capabilities_status32});
capabilities->Assign(7, ${val.ntlm.capabilities_level_2_oplocks});
capabilities->Assign(8, ${val.ntlm.capabilities_lock_and_read});
capabilities->Assign(9, ${val.ntlm.capabilities_nt_find});
capabilities->Assign(10, ${val.ntlm.capabilities_dfs});
capabilities->Assign(11, ${val.ntlm.capabilities_infolevel_passthru});
capabilities->Assign(12, ${val.ntlm.capabilities_large_readx});
capabilities->Assign(13, ${val.ntlm.capabilities_large_writex});
capabilities->Assign(14, ${val.ntlm.capabilities_unix});
capabilities->Assign(15, ${val.ntlm.capabilities_bulk_transfer});
capabilities->Assign(16, ${val.ntlm.capabilities_compressed_data});
capabilities->Assign(17, ${val.ntlm.capabilities_extended_security});
auto ntlm = zeek::make_intrusive<zeek::RecordVal>(zeek::BifType::Record::SMB1::NegotiateResponseNTLM);
ntlm->Assign(0, ${val.word_count});
ntlm->Assign(1, ${val.dialect_index});
ntlm->Assign(2, std::move(security));
ntlm->Assign(3, ${val.ntlm.max_buffer_size});
ntlm->Assign(4, ${val.ntlm.max_mpx_count});
ntlm->Assign(5, ${val.ntlm.max_number_vcs});
ntlm->Assign(6, ${val.ntlm.max_raw_size});
ntlm->Assign(7, ${val.ntlm.session_key});
ntlm->Assign(8, std::move(capabilities));
ntlm->Assign(9, filetime2zeektime(${val.ntlm.server_time}));
if ( ${val.ntlm.capabilities_extended_security} == false )
{
ntlm->Assign(10, to_stringval(${val.ntlm.encryption_key}));
ntlm->Assign(11, smb_string2stringval(${val.ntlm.domain_name}));
}
else
{
ntlm->Assign(12, to_stringval(${val.ntlm.server_guid}));
}
response->Assign(2, std::move(ntlm));
}
break;
}
zeek::BifEvent::enqueue_smb1_negotiate_response(zeek_analyzer(),
zeek_analyzer()->Conn(),
SMBHeaderVal(header),
std::move(response));
}
return true;
%}
};
type SMB_dialect = record {
buffer_format : uint8; # must be 0x2 for dialect
name : SMB_string(false, 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 : uint16;
server_date : uint16;
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);
};