From 04abd76d8dd219bbd59985273e97ba69c5e5122b Mon Sep 17 00:00:00 2001 From: HD Moore Date: Thu, 22 Sep 2005 04:04:06 +0000 Subject: [PATCH] More SMB stuff git-svn-id: file:///home/svn/incoming/trunk@2863 4d416f70-5f16-0410-b530-b9f4589650da --- dev/bugs/break_struct2.rb | 52 ------- lib/rex/proto/smb/client.rb | 249 +++++++++++++++++++++++++++++- lib/rex/proto/smb/client.rb.ut.rb | 14 +- lib/rex/proto/smb/constants.rb | 132 +++++++++++++++- lib/rex/proto/smb/utils.rb | 2 +- 5 files changed, 380 insertions(+), 69 deletions(-) delete mode 100644 dev/bugs/break_struct2.rb diff --git a/dev/bugs/break_struct2.rb b/dev/bugs/break_struct2.rb deleted file mode 100644 index 468d0390ea..0000000000 --- a/dev/bugs/break_struct2.rb +++ /dev/null @@ -1,52 +0,0 @@ -$:.unshift(File.join(File.dirname(__FILE__), '../../lib')) - -require 'rex/struct2' - -# Create a NetBIOS session packet template -def self.make_nbs (template) - Rex::Struct2::CStructTemplate.new( - [ 'uint8', 'Type', 0 ], - [ 'uint8', 'Flags', 0 ], - [ 'uint16n', 'PayloadLen', 0 ], - [ 'template', 'Payload', template ] - ).create_restraints( - [ 'Payload', 'PayloadLen', nil, true ] - ) -end - -# The SMB header template -SMB_HDR = Rex::Struct2::CStructTemplate.new( - [ 'uint32n', 'Magic', 0xff534d42 ], - [ 'uint8', 'Command', 0 ], - [ 'uint32v', 'ErrorClass', 0 ], - [ 'uint8', 'Flags1', 0 ], - [ 'uint16v', 'Flags2', 0 ], - [ 'uint16v', 'ProcessIDHigh', 0 ], - [ 'uint32v', 'Signature1', 0 ], - [ 'uint32v', 'Signature2', 0 ], - [ 'uint16v', 'Reserved1', 0 ], - [ 'uint16v', 'TreeID', 0 ], - [ 'uint16v', 'ProcessID', 0 ], - [ 'uint16v', 'UserID', 0 ], - [ 'uint16v', 'MultiplexID', 0 ], - [ 'uint8', 'WordCount', 0 ] -) - -# A SMB template for SMB Tree Connect requests -SMB_TREE_CONN_HDR_PKT = Rex::Struct2::CStructTemplate.new( - [ 'template', 'SMB', SMB_HDR ], - [ 'uint8', 'AndX', 0 ], - [ 'uint8', 'Reserved1', 0 ], - [ 'uint16v', 'AndXOffset', 0 ], - [ 'uint16v', 'Flags', 0 ], - [ 'uint16v', 'PasswordLen', 0 ], - [ 'uint16v', 'ByteCount', 0 ], - [ 'string', 'Payload' ] -).create_restraints( - [ 'Payload', 'ByteCount', nil, true ] -) -SMB_TREE_CONN_PKT = self.make_nbs(SMB_TREE_CONN_HDR_PKT) - - -x = SMB_TREE_CONN_PKT.make_struct -p x.to_s diff --git a/lib/rex/proto/smb/client.rb b/lib/rex/proto/smb/client.rb index 4c4e8027e8..9a00b2905d 100644 --- a/lib/rex/proto/smb/client.rb +++ b/lib/rex/proto/smb/client.rb @@ -84,8 +84,11 @@ UTILS = Rex::Proto::SMB::Utils when CONST::SMB_COM_SESSION_SETUP_ANDX return smb_parse_session_setup(pkt, data) + when CONST::SMB_COM_TREE_CONNECT_ANDX + return smb_parse_tree_connect(pkt, data) + else - puts "Unknown" + pkt['Payload']['SMB'].v['Command'].to_s + puts "Unknown >> " + pkt['Payload']['SMB'].v['Command'].to_s return pkt end @@ -114,24 +117,59 @@ UTILS = Rex::Proto::SMB::Utils return res end + # Process SMB error responses + if (pkt['Payload']['SMB'].v['WordCount'] == 0) + return pkt + end + puts "Unknown WordCount: " + pkt['Payload']['SMB'].v['WordCount'].to_s return pkt end # Process incoming SMB_COM_SESSION_SETUP_ANDX packets def smb_parse_session_setup(pkt, data) - # Process NTLM negotiate responses + # Process NTLMv2 negotiate responses if (pkt['Payload']['SMB'].v['WordCount'] == 4) res = CONST::SMB_SETUP_NTLMV2_RES_PKT.make_struct res.from_s(data) return res end - + + # Process NTLMv1 and LANMAN responses + if (pkt['Payload']['SMB'].v['WordCount'] == 3) + res = CONST::SMB_SETUP_RES_PKT.make_struct + res.from_s(data) + return res + end + + # Process SMB error responses + if (pkt['Payload']['SMB'].v['WordCount'] == 0) + return pkt + end + puts "Unknown WordCount: " + pkt['Payload']['SMB'].v['WordCount'].to_s return pkt end + # Process incoming SMB_COM_TREE_CONNECT_ANDX packets + def smb_parse_tree_connect(pkt, data) + + if (pkt['Payload']['SMB'].v['WordCount'] == 3) + res = CONST::SMB_TREE_CONN_RES_PKT.make_struct + res.from_s(data) + return res + end + + # Process SMB error responses + if (pkt['Payload']['SMB'].v['WordCount'] == 0) + return pkt + end + + puts "Unknown WordCount: " + pkt['Payload']['SMB'].v['WordCount'].to_s + return pkt + end + # Request a SMB session over NetBIOS def session_request (name = '*SMBSERVER') @@ -237,9 +275,120 @@ UTILS = Rex::Proto::SMB::Utils return nil end + + # Authenticate using clear-text passwords + def session_setup_clear(user = '', pass = '', domain = '') + + data = '' + data << pass + "\x00" + data << user + "\x00" + data << domain + "\x00" + data << self.native_os + "\x00" + data << self.native_lm + "\x00" + + pkt = CONST::SMB_SETUP_LANMAN_PKT.make_struct + self.smb_defaults(pkt['Payload']['SMB']) + + pkt['Payload']['SMB'].v['Command'] = CONST::SMB_COM_SESSION_SETUP_ANDX + pkt['Payload']['SMB'].v['Flags1'] = 0x18 + pkt['Payload']['SMB'].v['Flags2'] = 0x2001 + pkt['Payload']['SMB'].v['WordCount'] = 10 + pkt['Payload'].v['AndX'] = 255 + pkt['Payload'].v['MaxBuff'] = 0xffdf + pkt['Payload'].v['MaxMPX'] = 2 + pkt['Payload'].v['VCNum'] = 1 + pkt['Payload'].v['PasswordLen'] = pass.length + 1 + pkt['Payload'].v['Capabilities'] = 64 + pkt['Payload'].v['SessionKey'] = self.session_id + pkt['Payload'].v['Payload'] = data + + self.smb_send(pkt.to_s) + ack = self.smb_recv_parse + + # Make sure the response we received was the correct type + if (ack['Payload']['SMB'].v['Command'] != CONST::SMB_COM_SESSION_SETUP_ANDX) + return nil + end + + if (ack['Payload']['SMB'].v['ErrorClass'] != 0) + return ack + end + + if (ack['Payload'].v['Action'] != 1 and user.length > 0) + self.auth_user = user + end + + self.auth_user_id = ack['Payload']['SMB'].v['UserID'] + + info = ack['Payload'].v['Payload'].split(/\x00/) + self.peer_native_os = info[0] + self.peer_native_lm = info[1] + self.default_domain = info[2] + + # XXX what to do on error? + return ack + end + # Authenticate using NTLMv1 + def session_setup_ntlmv1(user = '', pass = '', domain = '') - # Authenticate using extended security negotiation + hash_lm = pass.length > 0 ? CRYPT.lanman_des(pass, self.challenge_key) : '' + hash_nt = pass.length > 0 ? CRYPT.ntlm_md4(pass, self.challenge_key) : '' + + data = '' + data << hash_lm + data << hash_nt + data << user + "\x00" + data << domain + "\x00" + data << self.native_os + "\x00" + data << self.native_lm + "\x00" + + pkt = CONST::SMB_SETUP_NTLMV1_PKT.make_struct + self.smb_defaults(pkt['Payload']['SMB']) + + pkt['Payload']['SMB'].v['Command'] = CONST::SMB_COM_SESSION_SETUP_ANDX + pkt['Payload']['SMB'].v['Flags1'] = 0x18 + pkt['Payload']['SMB'].v['Flags2'] = 0x2001 + pkt['Payload']['SMB'].v['WordCount'] = 13 + pkt['Payload'].v['AndX'] = 255 + pkt['Payload'].v['MaxBuff'] = 0xffdf + pkt['Payload'].v['MaxMPX'] = 2 + pkt['Payload'].v['VCNum'] = 1 + pkt['Payload'].v['PasswordLenLM'] = hash_lm.length + pkt['Payload'].v['PasswordLenNT'] = hash_nt.length + pkt['Payload'].v['Capabilities'] = 64 + pkt['Payload'].v['SessionKey'] = self.session_id + pkt['Payload'].v['Payload'] = data + + self.smb_send(pkt.to_s) + ack = self.smb_recv_parse + + # Make sure the response we received was the correct type + if (ack['Payload']['SMB'].v['Command'] != CONST::SMB_COM_SESSION_SETUP_ANDX) + return nil + end + + if (ack['Payload']['SMB'].v['ErrorClass'] != 0) + return ack + end + + if (ack['Payload'].v['Action'] != 1 and user.length > 0) + self.auth_user = user + end + + self.auth_user_id = ack['Payload']['SMB'].v['UserID'] + + + info = ack['Payload'].v['Payload'].split(/\x00/) + self.peer_native_os = info[0] + self.peer_native_lm = info[1] + self.default_domain = info[2] + + # XXX what to do on error? + return ack + end + + # Authenticate using extended security negotiation (NTLMv2) def session_setup_ntlmv2(user = '', pass = '', domain = '', name = 'WORKSTATION1') data = '' @@ -339,13 +488,97 @@ UTILS = Rex::Proto::SMB::Utils if (ack['Payload']['SMB'].v['Command'] != CONST::SMB_COM_SESSION_SETUP_ANDX) return nil end - - # We want to see no error message + if (ack['Payload']['SMB'].v['ErrorClass'] != 0) - return nil - end + return ack + end + + self.auth_user_id = ack['Payload']['SMB'].v['UserID'] + + # XXX what do on error? return ack end + + + # Connect to a specified share with an optional password + def tree_connect(share = 'IPC$', pass = '') + + data = '' + data << pass + "\x00" + data << share + "\x00" + data << '?????' + "\x00" + + pkt = CONST::SMB_TREE_CONN_PKT.make_struct + self.smb_defaults(pkt['Payload']['SMB']) + + pkt['Payload']['SMB'].v['Command'] = CONST::SMB_COM_TREE_CONNECT_ANDX + pkt['Payload']['SMB'].v['Flags1'] = 0x18 + pkt['Payload']['SMB'].v['Flags2'] = 0x2001 + pkt['Payload']['SMB'].v['WordCount'] = 4 + pkt['Payload'].v['AndX'] = 255 + pkt['Payload'].v['PasswordLen'] = pass.length + 1 + pkt['Payload'].v['Capabilities'] = 64 + pkt['Payload'].v['Payload'] = data + + self.smb_send(pkt.to_s) + ack = self.smb_recv_parse + + # Make sure the response we received was the correct type + if (ack['Payload']['SMB'].v['Command'] != CONST::SMB_COM_TREE_CONNECT_ANDX) + return nil + end + + if (ack['Payload']['SMB'].v['ErrorClass'] != 0) + return ack + end + + self.last_tree_id = ack['Payload']['SMB'].v['TreeID'] + + info = ack['Payload'].v['Payload'].split(/\x00/) + return ack + end + + + # Connect to a specified share with an optional password + def trans (pipe, param = '', body = '', setup_count = 0, setup_data = '') + + # null-terminate the pipe parameter if needed + if (pipe[-1] != 0) + pipe << "\x00" + end + + data = pipe + param + body + + + pkt = CONST::SMB_TRANS_PKT.make_struct + self.smb_defaults(pkt['Payload']['SMB']) + + pkt['Payload']['SMB'].v['Command'] = CONST::SMB_COM_TRANSACTION + pkt['Payload']['SMB'].v['Flags1'] = 0x18 + pkt['Payload']['SMB'].v['Flags2'] = 0x2001 + pkt['Payload']['SMB'].v['WordCount'] = 14 + setup_count + pkt['Payload'].v['AndX'] = 255 + pkt['Payload'].v['PasswordLen'] = pass.length + 1 + pkt['Payload'].v['Capabilities'] = 64 + pkt['Payload'].v['Payload'] = data + + self.smb_send(pkt.to_s) + ack = self.smb_recv_parse + + # Make sure the response we received was the correct type + if (ack['Payload']['SMB'].v['Command'] != CONST::SMB_COM_TRANSACTION) + return nil + end + + if (ack['Payload']['SMB'].v['ErrorClass'] != 0) + return ack + end + + self.last_tree_id = ack['Payload']['SMB'].v['TreeID'] + + info = ack['Payload'].v['Payload'].split(/\x00/) + return ack + end # public methods attr_accessor :native_os, :native_lm, :encrypt_passwords, :extended_security diff --git a/lib/rex/proto/smb/client.rb.ut.rb b/lib/rex/proto/smb/client.rb.ut.rb index c278177ca9..dfc21e946f 100644 --- a/lib/rex/proto/smb/client.rb.ut.rb +++ b/lib/rex/proto/smb/client.rb.ut.rb @@ -12,7 +12,7 @@ class Rex::Proto::SMB::Client::UnitTest < Test::Unit::TestCase Klass = Rex::Proto::SMB::Client - @@host = '192.168.10.184' + @@host = '192.168.0.42' @@port = 139 def test_smb_session_request @@ -40,7 +40,17 @@ class Rex::Proto::SMB::Client::UnitTest < Test::Unit::TestCase ok = c.session_setup_ntlmv2 assert_kind_of(Rex::Struct2::CStruct, ok) - + + ok = c.session_setup_ntlmv1 + assert_kind_of(Rex::Struct2::CStruct, ok) + + ok = c.session_setup_clear + assert_kind_of(Rex::Struct2::CStruct, ok) + + ok = c.tree_connect + assert_kind_of(Rex::Struct2::CStruct, ok) + + end diff --git a/lib/rex/proto/smb/constants.rb b/lib/rex/proto/smb/constants.rb index d234438c55..643c5a3478 100644 --- a/lib/rex/proto/smb/constants.rb +++ b/lib/rex/proto/smb/constants.rb @@ -55,7 +55,7 @@ end # A raw NetBIOS session template NBRAW_HDR_PKT = Rex::Struct2::CStructTemplate.new( - [ 'string', 'Payload' ] + [ 'string', 'Payload', nil, ''] ) NBRAW_PKT = self.make_nbs(NBRAW_HDR_PKT) @@ -93,7 +93,7 @@ SMB_NEG_HDR_PKT = Rex::Struct2::CStructTemplate.new( [ 'template', 'SMB', SMB_HDR ], [ 'uint16v', 'ByteCount', 0 ], - [ 'string', 'Payload' ] + [ 'string', 'Payload', nil, '' ] ).create_restraints( [ 'Payload', 'ByteCount', nil, true ] ) @@ -116,7 +116,7 @@ SMB_NEG_RES_LM_HDR_PKT = Rex::Struct2::CStructTemplate.new( [ 'uint16v', 'KeyLength', 0 ], [ 'uint16v', 'Reserved1', 0 ], [ 'uint16v', 'ByteCount', 0 ], - [ 'string', 'EncryptionKey' ] + [ 'string', 'EncryptionKey', nil, '' ] ).create_restraints( [ 'EncryptionKey', 'ByteCount', nil, true ] ) @@ -140,7 +140,7 @@ SMB_NEG_RES_NT_HDR_PKT = Rex::Struct2::CStructTemplate.new( [ 'uint8', 'KeyLength', 0 ], [ 'uint16v', 'ByteCount', 0 ], [ 'string', 'GUID', 16, '' ], - [ 'string', 'SecurityBlob' ] + [ 'string', 'SecurityBlob', nil, '' ] ) SMB_NEG_RES_NT_PKT = self.make_nbs(SMB_NEG_RES_NT_HDR_PKT) @@ -154,6 +154,64 @@ SMB_NEG_RES_ERR_HDR_PKT = Rex::Struct2::CStructTemplate.new( SMB_NEG_RES_ERR_PKT = self.make_nbs(SMB_NEG_RES_ERR_HDR_PKT) +# A SMB template for SMB Session Setup responses (LANMAN/NTLMV1) +SMB_SETUP_RES_HDR_PKT = Rex::Struct2::CStructTemplate.new( + [ 'template', 'SMB', SMB_HDR ], + [ 'uint8', 'AndX', 0 ], + [ 'uint8', 'Reserved1', 0 ], + [ 'uint16v', 'AndXOffset', 0 ], + [ 'uint16v', 'Action', 0 ], + [ 'uint16v', 'SecurityBlobLen', 0 ], + [ 'uint16v', 'ByteCount', 0 ], + [ 'string', 'Payload', nil, '' ] +).create_restraints( + [ 'Payload', 'ByteCount', nil, true ] +) +SMB_SETUP_RES_PKT = self.make_nbs(SMB_SETUP_RES_HDR_PKT) + + +# A SMB template for SMB Session Setup requests (LANMAN) +SMB_SETUP_LANMAN_HDR_PKT = Rex::Struct2::CStructTemplate.new( + [ 'template', 'SMB', SMB_HDR ], + [ 'uint8', 'AndX', 0 ], + [ 'uint8', 'Reserved1', 0 ], + [ 'uint16v', 'AndXOffset', 0 ], + [ 'uint16v', 'MaxBuff', 0 ], + [ 'uint16v', 'MaxMPX', 0 ], + [ 'uint16v', 'VCNum', 0 ], + [ 'uint32v', 'SessionKey', 0 ], + [ 'uint16v', 'PasswordLen', 0 ], + [ 'uint32v', 'Reserved2', 0 ], + [ 'uint16v', 'ByteCount', 0 ], + [ 'string', 'Payload', nil, '' ] +).create_restraints( + [ 'Payload', 'ByteCount', nil, true ] +) +SMB_SETUP_LANMAN_PKT = self.make_nbs(SMB_SETUP_LANMAN_HDR_PKT) + + +# A SMB template for SMB Session Setup requests (NTLMV1) +SMB_SETUP_NTLMV1_HDR_PKT = Rex::Struct2::CStructTemplate.new( + [ 'template', 'SMB', SMB_HDR ], + [ 'uint8', 'AndX', 0 ], + [ 'uint8', 'Reserved1', 0 ], + [ 'uint16v', 'AndXOffset', 0 ], + [ 'uint16v', 'MaxBuff', 0 ], + [ 'uint16v', 'MaxMPX', 0 ], + [ 'uint16v', 'VCNum', 0 ], + [ 'uint32v', 'SessionKey', 0 ], + [ 'uint16v', 'PasswordLenLM', 0 ], + [ 'uint16v', 'PasswordLenNT', 0 ], + [ 'uint32v', 'Reserved2', 0 ], + [ 'uint32v', 'Capabilities', 0 ], + [ 'uint16v', 'ByteCount', 0 ], + [ 'string', 'Payload', nil, '' ] +).create_restraints( + [ 'Payload', 'ByteCount', nil, true ] +) +SMB_SETUP_NTLMV1_PKT = self.make_nbs(SMB_SETUP_NTLMV1_HDR_PKT) + + # A SMB template for SMB Session Setup requests (NTLMV2) SMB_SETUP_NTLMV2_HDR_PKT = Rex::Struct2::CStructTemplate.new( [ 'template', 'SMB', SMB_HDR ], @@ -168,12 +226,13 @@ SMB_SETUP_NTLMV2_HDR_PKT = Rex::Struct2::CStructTemplate.new( [ 'uint32v', 'Reserved2', 0 ], [ 'uint32v', 'Capabilities', 0 ], [ 'uint16v', 'ByteCount', 0 ], - [ 'string', 'Payload' ] + [ 'string', 'Payload', nil, '' ] ).create_restraints( [ 'Payload', 'ByteCount', nil, true ] ) SMB_SETUP_NTLMV2_PKT = self.make_nbs(SMB_SETUP_NTLMV2_HDR_PKT) + # A SMB template for SMB Session Setup responses (NTLMV2) SMB_SETUP_NTLMV2_RES_HDR_PKT = Rex::Struct2::CStructTemplate.new( [ 'template', 'SMB', SMB_HDR ], @@ -183,12 +242,73 @@ SMB_SETUP_NTLMV2_RES_HDR_PKT = Rex::Struct2::CStructTemplate.new( [ 'uint16v', 'Action', 0 ], [ 'uint16v', 'SecurityBlobLen', 0 ], [ 'uint16v', 'ByteCount', 0 ], - [ 'string', 'Payload' ] + [ 'string', 'Payload', nil, '' ] ).create_restraints( [ 'Payload', 'ByteCount', nil, true ] ) SMB_SETUP_NTLMV2_RES_PKT = self.make_nbs(SMB_SETUP_NTLMV2_RES_HDR_PKT) + +# A SMB template for SMB Tree Connect requests +SMB_TREE_CONN_HDR_PKT = Rex::Struct2::CStructTemplate.new( + [ 'template', 'SMB', SMB_HDR ], + [ 'uint8', 'AndX', 0 ], + [ 'uint8', 'Reserved1', 0 ], + [ 'uint16v', 'AndXOffset', 0 ], + [ 'uint16v', 'Flags', 0 ], + [ 'uint16v', 'PasswordLen', 0 ], + [ 'uint16v', 'ByteCount', 0 ], + [ 'string', 'Payload', nil, '' ] +).create_restraints( + [ 'Payload', 'ByteCount', nil, true ] +) +SMB_TREE_CONN_PKT = self.make_nbs(SMB_TREE_CONN_HDR_PKT) + + +# A SMB template for SMB Tree Connect requests +SMB_TREE_CONN_RES_HDR_PKT = Rex::Struct2::CStructTemplate.new( + [ 'template', 'SMB', SMB_HDR ], + [ 'uint8', 'AndX', 0 ], + [ 'uint8', 'Reserved1', 0 ], + [ 'uint16v', 'AndXOffset', 0 ], + [ 'uint16v', 'OptionalSupport', 0 ], + [ 'uint16v', 'ByteCount', 0 ], + [ 'string', 'Payload', nil, '' ] +).create_restraints( + [ 'Payload', 'ByteCount', nil, true ] +) +SMB_TREE_CONN_RES_PKT = self.make_nbs(SMB_TREE_CONN_RES_HDR_PKT) + + +# A SMB template for SMB Transaction requests +SMB_TRANS_HDR_PKT = Rex::Struct2::CStructTemplate.new( + [ 'template', 'SMB', SMB_HDR ], + [ 'uint16v', 'ParamCountTotal', 0 ], + [ 'uint16v', 'DataCountTotal', 0 ], + [ 'uint16v', 'ParamCountMax', 0 ], + [ 'uint16v', 'DataCountMax', 0 ], + [ 'uint8', 'SetupCountMax', 0 ], + [ 'uint8', 'Reserved1', 0 ], + [ 'uint16v', 'Flags', 0 ], + [ 'uint32v', 'Timeout', 0 ], + [ 'uint16v', 'Reserved1', 0 ], + + [ 'uint16v', 'ParamCount', 0 ], + [ 'uint16v', 'ParamOffset', 0 ], + [ 'uint16v', 'DataCount', 0 ], + [ 'uint16v', 'DataOffset', 0 ], + [ 'uint8', 'SetupCount', 0 ], + [ 'uint8', 'Reserved3', 0 ], + [ 'string', 'SetupData', nil, '' ], # SetupCount * 2 + [ 'uint16v', 'ByteCount', 0 ], + [ 'string', 'Payload', nil, '' ] +).create_restraints( + [ 'Payload', 'ByteCount', nil, true ], + [ 'SetupData', 'SetupCount', nil, true, nil, nil, proc { |i| i * 2 }, nil ] +) +SMB_TRANS_PKT = self.make_nbs(SMB_TRANS_HDR_PKT) + + end end end diff --git a/lib/rex/proto/smb/utils.rb b/lib/rex/proto/smb/utils.rb index b65069a7ca..7650c4fa84 100644 --- a/lib/rex/proto/smb/utils.rb +++ b/lib/rex/proto/smb/utils.rb @@ -90,7 +90,7 @@ require 'rex/text' ) ) ) - puts "Returning: " + blob.length.to_s + return blob end