# 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::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::BifType::Record::SMB1::NegotiateResponse); switch ( ${val.word_count} ) { case 0x01: { auto core = zeek::make_intrusive(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::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::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::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::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::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::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); };