diff --git a/CHANGES b/CHANGES index 2532c481da..c807dbc422 100644 --- a/CHANGES +++ b/CHANGES @@ -1,4 +1,8 @@ +2.6-178 | 2019-03-21 14:10:44 -0700 + + * Add support for parsing SMB 3.1.1 NegotiateContextList response values (Mauro Palumbo) + 2.6-175 | 2019-03-20 19:25:11 -0700 * Parse SMB2 TRANSFORM_HEADER messages and generate new smb2_transform_header event (Mauro Palumbo) diff --git a/NEWS b/NEWS index 43a30f3312..89b024a424 100644 --- a/NEWS +++ b/NEWS @@ -51,6 +51,16 @@ New Functionality - Support for NFLOG link-layer type. +- Support for some SMB 3.x features + + - An ``smb2_transform_header`` event is raised after parsing + TRANSFORM_HEADER structures associated with encrypted messages. + + - The ``SMB2::NegotiateResponse`` record now contains + ``negotiate_context_count`` and ``negotiate_context_values`` fields + containing capability information found in an SMB 3.1.1 dialect's + negotiation message. + Changed Functionality --------------------- diff --git a/VERSION b/VERSION index fb3e90e739..91b521efc5 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -2.6-175 +2.6-178 diff --git a/doc b/doc index 79d4293fb0..ee040c581f 160000 --- a/doc +++ b/doc @@ -1 +1 @@ -Subproject commit 79d4293fb0a7b03b7c3ae84c633b14f51c836a8d +Subproject commit ee040c581f8a4e1ea82a7ec5f17d5f36f1184324 diff --git a/scripts/base/init-bare.bro b/scripts/base/init-bare.bro index f9ceffcdd5..0c32cebcc5 100644 --- a/scripts/base/init-bare.bro +++ b/scripts/base/init-bare.bro @@ -3226,6 +3226,64 @@ export { attrs : SMB2::FileAttrs; }; + ## Preauthentication information as defined in SMB v. 3.1.1 + ## + ## For more information, see MS-SMB2:2.3.1.1 + ## + type SMB2::PreAuthIntegrityCapabilities: record { + ## The number of hash algorithms. + hash_alg_count : count; + ## The salt length. + salt_length : count; + ## An array of hash algorithms (counts). + hash_alg : vector of count; + ## The salt. + salt : string; + }; + + ## Encryption information as defined in SMB v. 3.1.1 + ## + ## For more information, see MS-SMB2:2.3.1.2 + ## + type SMB2::EncryptionCapabilities: record { + ## The number of ciphers. + cipher_count : count; + ## An array of ciphers. + ciphers : vector of count; + }; + + ## Compression information as defined in SMB v. 3.1.1 + ## + ## For more information, see MS-SMB2:2.3.1.3 + ## + type SMB2::CompressionCapabilities: record { + ## The number of algorithms. + alg_count : count; + ## An array of compression algorithms. + algs : vector of count; + }; + + ## The context type information as defined in SMB v. 3.1.1 + ## + ## For more information, see MS-SMB2:2.3.1 + ## + type SMB2::NegotiateContextValue: record { + ## Specifies the type of context (preauth or encryption). + context_type : count; + ## The length in byte of the data field. + data_length : count; + ## The preauthentication information. + preauth_info : SMB2::PreAuthIntegrityCapabilities &optional; + ## The encryption information. + encryption_info : SMB2::EncryptionCapabilities &optional; + ## The compression information. + compression_info : SMB2::CompressionCapabilities &optional; + ## Indicates the server name the client must connect to. + netname: string &optional; + }; + + type SMB2::NegotiateContextValues: vector of SMB2::NegotiateContextValue; + ## The response to an SMB2 *negotiate* request, which is used by tghe client to notify the server ## what dialects of the SMB2 protocol the client understands. ## @@ -3244,6 +3302,11 @@ export { system_time : time; ## The SMB2 server start time. server_start_time : time; + + ## The number of negotiate context values in SMB v. 3.1.1, otherwise reserved to 0. + negotiate_context_count : count; + ## An array of context values in SMB v. 3.1.1. + negotiate_context_values : SMB2::NegotiateContextValues; }; ## The request sent by the client to request a new authenticated session diff --git a/src/analyzer/protocol/smb/smb2-com-negotiate.pac b/src/analyzer/protocol/smb/smb2-com-negotiate.pac index 39311e4ee3..41175fcfdd 100644 --- a/src/analyzer/protocol/smb/smb2-com-negotiate.pac +++ b/src/analyzer/protocol/smb/smb2-com-negotiate.pac @@ -1,3 +1,21 @@ +enum smb3_capabilities { + SMB2_GLOBAL_CAP_DFS = 0x00, + SMB2_GLOBAL_CAP_LEASING = 0x02, + SMB2_GLOBAL_CAP_LARGE_MTU = 0x04, + SMB2_GLOBAL_CAP_MULTI_CHANNE = 0x08, + SMB2_GLOBAL_CAP_PERSISTENT_HANDLES = 0x10, + SMB2_GLOBAL_CAP_DIRECTORY_LEASING = 0x20, + SMB2_GLOBAL_CAP_ENCRYPTION = 0x40, +}; + +enum smb3_context_type { + SMB2_PREAUTH_INTEGRITY_CAPABILITIES = 0x0001, + SMB2_ENCRYPTION_CAPABILITIES = 0x0002, + SMB2_COMPRESSION_CAPABILITIES = 0x0004, + SMB2_NETNAME_NEGOTIATE_CONTEXT_ID = 0x0005, +}; + + refine connection SMB_Conn += { function proc_smb2_negotiate_request(h: SMB2_Header, val: SMB2_negotiate_request) : bool @@ -25,9 +43,19 @@ refine connection SMB_Conn += { nr->Assign(0, val_mgr->GetCount(${val.dialect_revision})); nr->Assign(1, val_mgr->GetCount(${val.security_mode})); - nr->Assign(2, BuildSMB2GUID(${val.server_guid})), + nr->Assign(2, BuildSMB2GUID(${val.server_guid})); nr->Assign(3, filetime2brotime(${val.system_time})); nr->Assign(4, filetime2brotime(${val.server_start_time})); + nr->Assign(5, val_mgr->GetCount(${val.negotiate_context_count})); + + VectorVal* cv = new VectorVal(BifType::Vector::SMB2::NegotiateContextValues); + + if ( ${val.dialect_revision} == 0x0311 ) + for ( auto i = 0u; i < ${val.smb3_ncl.vals}->size(); ++i ) + cv->Assign(i, BuildSMB2ContextVal(${val.smb3_ncl.vals[i]})); + + nr->Assign(6, cv); + BifEvent::generate_smb2_negotiate_response(bro_analyzer(), bro_analyzer()->Conn(), BuildSMB2HeaderVal(h), nr); @@ -37,35 +65,81 @@ refine connection SMB_Conn += { %} }; +type SMB3_preauth_integrity_capabilities = record { + hash_alg_count : uint16; + salt_length : uint16; + hash_alg : uint16[hash_alg_count]; + salt : bytestring &length = salt_length; +}; + +type SMB3_encryption_capabilities = record { + cipher_count : uint16; + ciphers : uint16[cipher_count]; +}; + +type SMB3_compression_capabilities = record { + alg_count : uint16; + pad: uint16; + reserved : uint32; + algs : uint16[alg_count]; +}; + +type SMB3_netname_negotiate_context_id(len: uint16) = record { + net_name: bytestring &length = len; +}; + +type SMB3_negotiate_context_value = record { + context_type : uint16; # specify the type of context + data_length : uint16; # the length of the data field + reserved : uint32; # ignored + data : case context_type of { + SMB2_PREAUTH_INTEGRITY_CAPABILITIES -> preauth_integrity_capabilities : SMB3_preauth_integrity_capabilities; + SMB2_ENCRYPTION_CAPABILITIES -> encryption_capabilities : SMB3_encryption_capabilities; + SMB2_COMPRESSION_CAPABILITIES -> compression_capabilities : SMB3_compression_capabilities; + SMB2_NETNAME_NEGOTIATE_CONTEXT_ID -> netname_negotiate_context_id : SMB3_netname_negotiate_context_id(data_length); + }; + pad : padding align 4; +}; + type SMB2_negotiate_request(header: SMB2_Header) = record { - structure_size : uint16; # client MUST set this to 36 - dialect_count : uint16; # must be > 0 - security_mode : uint16; # there is a list of required modes - reserved : padding[2]; # must be set to 0 - capabilities : uint32; # must be set to 0 - client_guid : SMB2_guid; # guid if client implements SMB 2.1 dialect, otherwise set to 0 - client_start_time : SMB_timestamp; # must be set to 0 + structure_size : uint16; # client MUST set this to 36 + dialect_count : uint16; # must be > 0 + security_mode : uint16; # there is a list of required modes + reserved : padding[2]; # must be set to 0 + capabilities : uint32; # must be set to 0 if SMB 2.x, otherwise if SMB 3.x one of enum smb2_capabilities + client_guid : SMB2_guid; # guid if client implements SMB 2.1 dialect, otherwise set to 0 + client_start_time : SMB_timestamp; dialects : uint16[dialect_count]; } &byteorder=littleendian, &let { proc : bool = $context.connection.proc_smb2_negotiate_request(header, this); }; +type NegotiateContextList(len: uint16) = record { + pad : padding align 8; + vals : SMB3_negotiate_context_value[len]; +} + type SMB2_negotiate_response(header: SMB2_Header) = record { - structure_size : uint16; - security_mode : uint16; - dialect_revision : uint16; - reserved : padding[2]; - server_guid : SMB2_guid; - capabilities : uint32; - max_transact_size : uint32; - max_read_size : uint32; - max_write_size : uint32; - system_time : SMB_timestamp; - server_start_time : SMB_timestamp; - security_offset : uint16; - security_length : uint16; - pad1 : padding to security_offset - header.head_length; - security_blob : bytestring &length=security_length; + structure_size : uint16; + security_mode : uint16; + dialect_revision : uint16; + negotiate_context_count : uint16; # reserved to 0 if not smb 3.1.1 + server_guid : SMB2_guid; + capabilities : uint32; + max_transact_size : uint32; + max_read_size : uint32; + max_write_size : uint32; + system_time : SMB_timestamp; + server_start_time : SMB_timestamp; + security_offset : uint16; + security_length : uint16; + negotiate_context_offset : uint32; + pad1 : padding to security_offset - header.head_length; + security_blob : bytestring &length=security_length; + negotiate_context_list : case dialect_revision of { + 0x0311 -> smb3_ncl : NegotiateContextList(negotiate_context_count); + default -> unknown : empty; + }; } &byteorder=littleendian, &let { proc : bool = $context.connection.proc_smb2_negotiate_response(header, this); gssapi_proc : bool = $context.connection.forward_gssapi(security_blob, false); diff --git a/src/analyzer/protocol/smb/smb2-protocol.pac b/src/analyzer/protocol/smb/smb2-protocol.pac index f5095a66d1..6424e86d53 100644 --- a/src/analyzer/protocol/smb/smb2-protocol.pac +++ b/src/analyzer/protocol/smb/smb2-protocol.pac @@ -100,6 +100,74 @@ refine connection SMB_Conn += { std::map smb2_request_tree_id; %} + function BuildSMB2ContextVal(ncv: SMB3_negotiate_context_value): BroVal + %{ + RecordVal* r = new RecordVal(BifType::Record::SMB2::NegotiateContextValue); + + r->Assign(0, val_mgr->GetCount(${ncv.context_type})); + r->Assign(1, val_mgr->GetCount(${ncv.data_length})); + + switch ( ${ncv.context_type} ) { + case SMB2_PREAUTH_INTEGRITY_CAPABILITIES: + { + RecordVal* rpreauth = new RecordVal(BifType::Record::SMB2::PreAuthIntegrityCapabilities); + rpreauth->Assign(0, val_mgr->GetCount(${ncv.preauth_integrity_capabilities.hash_alg_count})); + rpreauth->Assign(1, val_mgr->GetCount(${ncv.preauth_integrity_capabilities.salt_length})); + + VectorVal* ha = new VectorVal(internal_type("index_vec")->AsVectorType()); + + for ( int i = 0; i < (${ncv.preauth_integrity_capabilities.hash_alg_count}); ++i ) + ha->Assign(i, val_mgr->GetCount(${ncv.preauth_integrity_capabilities.hash_alg[i]})); + + rpreauth->Assign(2, ha); + rpreauth->Assign(3, bytestring_to_val(${ncv.preauth_integrity_capabilities.salt})); + r->Assign(2, rpreauth); + } + break; + + case SMB2_ENCRYPTION_CAPABILITIES: + { + RecordVal* rencr = new RecordVal(BifType::Record::SMB2::EncryptionCapabilities); + rencr->Assign(0, val_mgr->GetCount(${ncv.encryption_capabilities.cipher_count})); + + VectorVal* c = new VectorVal(internal_type("index_vec")->AsVectorType()); + + for ( int i = 0; i < (${ncv.encryption_capabilities.cipher_count}); ++i ) + c->Assign(i, val_mgr->GetCount(${ncv.encryption_capabilities.ciphers[i]})); + + rencr->Assign(1, c); + r->Assign(3, rencr); + } + break; + + case SMB2_COMPRESSION_CAPABILITIES: + { + RecordVal* rcomp = new RecordVal(BifType::Record::SMB2::CompressionCapabilities); + rcomp->Assign(0, val_mgr->GetCount(${ncv.compression_capabilities.alg_count})); + + VectorVal* c = new VectorVal(internal_type("index_vec")->AsVectorType()); + + for ( int i = 0; i < (${ncv.compression_capabilities.alg_count}); ++i ) + c->Assign(i, val_mgr->GetCount(${ncv.compression_capabilities.algs[i]})); + + rcomp->Assign(1, c); + r->Assign(4, rcomp); + } + break; + + case SMB2_NETNAME_NEGOTIATE_CONTEXT_ID: + { + r->Assign(5, bytestring_to_val(${ncv.netname_negotiate_context_id.net_name})); + } + break; + + default: + break; + } + + return r; + %} + function BuildSMB2HeaderVal(hdr: SMB2_Header): BroVal %{ RecordVal* r = new RecordVal(BifType::Record::SMB2::Header); diff --git a/src/analyzer/protocol/smb/types.bif b/src/analyzer/protocol/smb/types.bif index 4714046a62..2e469b1772 100644 --- a/src/analyzer/protocol/smb/types.bif +++ b/src/analyzer/protocol/smb/types.bif @@ -4,4 +4,10 @@ type SMB1::Header: record; type SMB2::Header: record; type SMB2::GUID: record; -type SMB2::FileAttrs: record; \ No newline at end of file +type SMB2::FileAttrs: record; + +type SMB2::PreAuthIntegrityCapabilities: record; +type SMB2::EncryptionCapabilities: record; +type SMB2::CompressionCapabilities: record; +type SMB2::NegotiateContextValue: record; +type SMB2::NegotiateContextValues: vector; diff --git a/testing/btest/Baseline/scripts.base.protocols.smb.smb311/.stdout b/testing/btest/Baseline/scripts.base.protocols.smb.smb311/.stdout new file mode 100644 index 0000000000..9245b4307a --- /dev/null +++ b/testing/btest/Baseline/scripts.base.protocols.smb.smb311/.stdout @@ -0,0 +1 @@ +smb2_negotiate_response 192.168.100.168 -> 10.160.67.244:445 [dialect_revision=785, security_mode=3, server_guid=[persistent=5167561042355431755, volatile=7583560952700542861], system_time=1547145849.626981, server_start_time=1540586308.948775, negotiate_context_count=2, negotiate_context_values=[[context_type=1, data_length=38, preauth_info=[hash_alg_count=1, salt_length=32, hash_alg=[1], salt=\x17\xa3\x95(\x0d\x0dt\xecZ\xe5\x0e\x1a\xef\x85\x07]U\x99\x86B\xd0\xeb\xc8\x08\xe0\x0a\xad\x01p\x9a/\xb7], encryption_info=, compression_info=, netname=], [context_type=2, data_length=4, preauth_info=, encryption_info=[cipher_count=1, ciphers=[1]], compression_info=, netname=]]] diff --git a/testing/btest/Traces/smb/smb311.pcap b/testing/btest/Traces/smb/smb311.pcap new file mode 100644 index 0000000000..b6f4521676 Binary files /dev/null and b/testing/btest/Traces/smb/smb311.pcap differ diff --git a/testing/btest/scripts/base/protocols/smb/smb311.test b/testing/btest/scripts/base/protocols/smb/smb311.test new file mode 100644 index 0000000000..22f232c14a --- /dev/null +++ b/testing/btest/scripts/base/protocols/smb/smb311.test @@ -0,0 +1,12 @@ +# @TEST-EXEC: bro -b -C -r $TRACES/smb/smb311.pcap %INPUT +# @TEST-EXEC: test ! -f dpd.log +# @TEST-EXEC: test ! -f weird.log +# @TEST-EXEC: btest-diff .stdout + +@load base/protocols/smb + +# Add some tests for SMB3 +event smb2_negotiate_response(c: connection, hdr: SMB2::Header, nr: SMB2::NegotiateResponse) + { + print fmt("smb2_negotiate_response %s -> %s:%d %s", c$id$orig_h, c$id$resp_h, c$id$resp_p, nr); + }