Merge branch 'smb3-negotiate-response' of https://github.com/mauropalumbo75/zeek

* 'smb3-negotiate-response' of https://github.com/mauropalumbo75/zeek:
  added test and pcap files for smb 3.1.1 negotiate-response
  smb3.1.1 additions to negotiate-response command

I made several modifications:

  - Code format, style, naming changes

  - For completeness/correctness, I added parsing support for the remaining
    context type structures.

  - Moved the optional padding before the NegotiateContextList field to
    also require the 0x0311 dialect version (some failures in
    pre-existing unit tests pointed this out as an issue)
This commit is contained in:
Jon Siwek 2019-03-21 14:10:44 -07:00
commit 1b76d92e97
11 changed files with 264 additions and 26 deletions

View file

@ -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)

10
NEWS
View file

@ -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
---------------------

View file

@ -1 +1 @@
2.6-175
2.6-178

2
doc

@ -1 +1 @@
Subproject commit 79d4293fb0a7b03b7c3ae84c633b14f51c836a8d
Subproject commit ee040c581f8a4e1ea82a7ec5f17d5f36f1184324

View file

@ -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

View file

@ -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,24 +65,65 @@ 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
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; # must be 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];
negotiate_context_count : uint16; # reserved to 0 if not smb 3.1.1
server_guid : SMB2_guid;
capabilities : uint32;
max_transact_size : uint32;
@ -64,8 +133,13 @@ type SMB2_negotiate_response(header: SMB2_Header) = record {
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);

View file

@ -100,6 +100,74 @@ refine connection SMB_Conn += {
std::map<uint64,uint64> 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);

View file

@ -5,3 +5,9 @@ type SMB1::Header: record;
type SMB2::Header: record;
type SMB2::GUID: record;
type SMB2::FileAttrs: record;
type SMB2::PreAuthIntegrityCapabilities: record;
type SMB2::EncryptionCapabilities: record;
type SMB2::CompressionCapabilities: record;
type SMB2::NegotiateContextValue: record;
type SMB2::NegotiateContextValues: vector;

View file

@ -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=<uninitialized>, compression_info=<uninitialized>, netname=<uninitialized>], [context_type=2, data_length=4, preauth_info=<uninitialized>, encryption_info=[cipher_count=1, ciphers=[1]], compression_info=<uninitialized>, netname=<uninitialized>]]]

Binary file not shown.

View file

@ -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);
}