lib/rex/proto/smb/client.rb in librex-0.0.68 vs lib/rex/proto/smb/client.rb in librex-0.0.70
- old
+ new
@@ -24,1929 +24,2040 @@
EVADE = Rex::Proto::SMB::Evasions
NTLM_CRYPT = Rex::Proto::NTLM::Crypt
NTLM_CONST = Rex::Proto::NTLM::Constants
NTLM_UTILS = Rex::Proto::NTLM::Utils
- def initialize(socket)
- self.socket = socket
- self.native_os = 'Windows 2000 2195'
- self.native_lm = 'Windows 2000 5.0'
- self.encrypt_passwords = true
- self.extended_security = false
- self.multiplex_id = rand(0xffff)
- self.process_id = rand(0xffff)
- self.read_timeout = 10
- self.evasion_opts = {
+ def initialize(socket)
+ self.socket = socket
+ self.native_os = 'Windows 2000 2195'
+ self.native_lm = 'Windows 2000 5.0'
+ self.encrypt_passwords = true
+ self.extended_security = false
+ self.multiplex_id = rand(0xffff)
+ self.process_id = rand(0xffff)
+ self.read_timeout = 10
+ self.evasion_opts = {
- # Padding is performed between packet headers and data
- 'pad_data' => EVADE::EVASION_NONE,
+ # Padding is performed between packet headers and data
+ 'pad_data' => EVADE::EVASION_NONE,
- # File path padding is performed on all open/create calls
- 'pad_file' => EVADE::EVASION_NONE,
+ # File path padding is performed on all open/create calls
+ 'pad_file' => EVADE::EVASION_NONE,
- # Modify the \PIPE\ string in trans_named_pipe calls
- 'obscure_trans_pipe' => EVADE::EVASION_NONE,
- }
+ # Modify the \PIPE\ string in trans_named_pipe calls
+ 'obscure_trans_pipe' => EVADE::EVASION_NONE,
+ }
- self.verify_signature = false
- self.use_ntlmv2 = false
- self.usentlm2_session = true
- self.send_lm = true
- self.use_lanman_key = false
- self.send_ntlm = true
+ self.verify_signature = false
+ self.use_ntlmv2 = false
+ self.usentlm2_session = true
+ self.send_lm = true
+ self.use_lanman_key = false
+ self.send_ntlm = true
- # Signing
- self.sequence_counter = 0
- self.signing_key = ''
- self.require_signing = false
+ # Signing
+ self.sequence_counter = 0
+ self.signing_key = ''
+ self.require_signing = false
- #Misc
- self.spnopt = {}
+ #Misc
+ self.spnopt = {}
- end
+ end
- # Read a SMB packet from the socket
- def smb_recv
+ # Read a SMB packet from the socket
+ def smb_recv
- data = socket.timed_read(4, self.read_timeout)
- if (data.nil? or data.length < 4)
- raise XCEPT::NoReply
- end
+ data = socket.timed_read(4, self.read_timeout)
+ if (data.nil? or data.length < 4)
+ raise XCEPT::NoReply
+ end
- recv_len = data[2,2].unpack('n')[0]
- if (recv_len == 0)
- return data
- end
+ recv_len = data[2,2].unpack('n')[0]
+ if (recv_len == 0)
+ return data
+ end
- recv_len += 4
+ recv_len += 4
- while (data.length != recv_len)
- buff = ''
+ while (data.length != recv_len)
+ buff = ''
- begin
- buff << self.socket.timed_read(recv_len - data.length, self.read_timeout)
- rescue Timeout::Error
- rescue
- raise XCEPT::ReadPacket
- end
+ begin
+ buff << self.socket.timed_read(recv_len - data.length, self.read_timeout)
+ rescue Timeout::Error
+ rescue
+ raise XCEPT::ReadPacket
+ end
- if (buff.nil? or buff.length == 0)
- raise XCEPT::ReadPacket
- end
+ if (buff.nil? or buff.length == 0)
+ raise XCEPT::ReadPacket
+ end
- data << buff
- end
+ data << buff
+ end
- #signing
- if self.require_signing && self.signing_key != ''
- if self.verify_signature
- raise XCEPT::IncorrectSigningError if not CRYPT::is_signature_correct?(self.signing_key,self.sequence_counter,data)
- end
- self.sequence_counter += 1
- end
+ #signing
+ if self.require_signing && self.signing_key != ''
+ if self.verify_signature
+ raise XCEPT::IncorrectSigningError if not CRYPT::is_signature_correct?(self.signing_key,self.sequence_counter,data)
+ end
+ self.sequence_counter += 1
+ end
- return data
+ return data
- end
+ end
- # Send a SMB packet down the socket
- def smb_send(data, evasion_level=0)
+ # Send a SMB packet down the socket
+ def smb_send(data, evasion_level=0)
- # evasion_level is ignored, since real evasion happens
- # in the actual socket layer
+ # evasion_level is ignored, since real evasion happens
+ # in the actual socket layer
- size = 0
- wait = 0
+ size = 0
+ wait = 0
- #signing
- if self.require_signing && self.signing_key != ''
- data = CRYPT::sign_smb_packet(self.signing_key, self.sequence_counter, data)
- self.sequence_counter += 1
- end
+ #signing
+ if self.require_signing && self.signing_key != ''
+ data = CRYPT::sign_smb_packet(self.signing_key, self.sequence_counter, data)
+ self.sequence_counter += 1
+ end
- begin
- # Just send the packet and return
- if (size == 0 or size >= data.length)
- return self.socket.put(data)
- end
+ begin
+ # Just send the packet and return
+ if (size == 0 or size >= data.length)
+ return self.socket.put(data)
+ end
- # Break the packet up into chunks and wait between them
- ret = 0
- while ( (chunk = data.slice!(0, size)).length > 0 )
- ret = self.socket.put(chunk)
- if (wait > 0)
- ::IO.select(nil, nil, nil, wait)
- end
- end
- return ret
- end
- end
+ # Break the packet up into chunks and wait between them
+ ret = 0
+ while ( (chunk = data.slice!(0, size)).length > 0 )
+ ret = self.socket.put(chunk)
+ if (wait > 0)
+ ::IO.select(nil, nil, nil, wait)
+ end
+ end
+ return ret
+ end
+ end
- # Set the SMB parameters to some reasonable defaults
- def smb_defaults(packet)
- packet.v['MultiplexID'] = self.multiplex_id.to_i
- packet.v['TreeID'] = self.last_tree_id.to_i
- packet.v['UserID'] = self.auth_user_id.to_i
- packet.v['ProcessID'] = self.process_id.to_i
- end
+ # Set the SMB parameters to some reasonable defaults
+ def smb_defaults(packet)
+ packet.v['MultiplexID'] = self.multiplex_id.to_i
+ packet.v['TreeID'] = self.last_tree_id.to_i
+ packet.v['UserID'] = self.auth_user_id.to_i
+ packet.v['ProcessID'] = self.process_id.to_i
+ end
+ # Receive a full SMB reply and cache the parsed packet
+ def smb_recv_and_cache
+ @smb_recv_cache ||= []
- # The main dispatcher for all incoming SMB packets
- def smb_recv_parse(expected_type, ignore_errors = false)
+ # This will throw an exception if it fails to read the whole packet
+ data = self.smb_recv
- # This will throw an exception if it fails to read the whole packet
- data = self.smb_recv
+ pkt = CONST::SMB_BASE_PKT.make_struct
+ pkt.from_s(data)
- pkt = CONST::SMB_BASE_PKT.make_struct
- pkt.from_s(data)
- res = pkt
+ # Store the received packet into the cache
+ @smb_recv_cache << [ pkt, data, Time.now ]
+ end
- begin
- case pkt['Payload']['SMB'].v['Command']
+ # Scan the packet receive cache for a matching response
+ def smb_recv_cache_find_match(expected_type)
+
+ clean = []
+ found = nil
- when CONST::SMB_COM_NEGOTIATE
- res = smb_parse_negotiate(pkt, data)
+ @smb_recv_cache.each do |cent|
+ pkt, data, tstamp = cent
- when CONST::SMB_COM_SESSION_SETUP_ANDX
- res = smb_parse_session_setup(pkt, data)
+ # Return matching packets and mark for removal
+ if pkt['Payload']['SMB'].v['Command'] == expected_type
+ found = [pkt,data]
+ clean << cent
+ end
- when CONST::SMB_COM_TREE_CONNECT_ANDX
- res = smb_parse_tree_connect(pkt, data)
+ # Purge any packets older than 5 minutes
+ if Time.now.to_i - tstamp.to_i > 300
+ clean << cent
+ end
- when CONST::SMB_COM_TREE_DISCONNECT
- res = smb_parse_tree_disconnect(pkt, data)
+ break if found
+ end
- when CONST::SMB_COM_NT_CREATE_ANDX
- res = smb_parse_create(pkt, data)
+ clean.each do |cent|
+ @smb_recv_cache.delete(cent)
+ end
- when CONST::SMB_COM_TRANSACTION, CONST::SMB_COM_TRANSACTION2
- res = smb_parse_trans(pkt, data)
+ found
+ end
- when CONST::SMB_COM_NT_TRANSACT
- res = smb_parse_nttrans(pkt, data)
+ # The main dispatcher for all incoming SMB packets
+ def smb_recv_parse(expected_type, ignore_errors = false)
- when CONST::SMB_COM_NT_TRANSACT_SECONDARY
- res = smb_parse_nttrans(pkt, data)
+ pkt = nil
+ data = nil
- when CONST::SMB_COM_OPEN_ANDX
- res = smb_parse_open(pkt, data)
+ # This allows for some leeway when a previous response has not
+ # been processed but a new request was sent. The old response
+ # will eventually be timed out of the cache.
+ 1.upto(3) do |attempt|
+ smb_recv_and_cache
+ pkt,data = smb_recv_cache_find_match(expected_type)
+ break if pkt
+ end
- when CONST::SMB_COM_WRITE_ANDX
- res = smb_parse_write(pkt, data)
+ begin
+ case pkt['Payload']['SMB'].v['Command']
- when CONST::SMB_COM_READ_ANDX
- res = smb_parse_read(pkt, data)
+ when CONST::SMB_COM_NEGOTIATE
+ res = smb_parse_negotiate(pkt, data)
- when CONST::SMB_COM_CLOSE
- res = smb_parse_close(pkt, data)
+ when CONST::SMB_COM_SESSION_SETUP_ANDX
+ res = smb_parse_session_setup(pkt, data)
- when CONST::SMB_COM_DELETE
- res = smb_parse_delete(pkt, data)
+ when CONST::SMB_COM_TREE_CONNECT_ANDX
+ res = smb_parse_tree_connect(pkt, data)
- else
- raise XCEPT::InvalidCommand
- end
+ when CONST::SMB_COM_TREE_DISCONNECT
+ res = smb_parse_tree_disconnect(pkt, data)
- if (pkt['Payload']['SMB'].v['Command'] != expected_type)
- raise XCEPT::InvalidType
- end
+ when CONST::SMB_COM_NT_CREATE_ANDX
+ res = smb_parse_create(pkt, data)
- if (ignore_errors == false and pkt['Payload']['SMB'].v['ErrorClass'] != 0)
- raise XCEPT::ErrorCode
- end
+ when CONST::SMB_COM_TRANSACTION, CONST::SMB_COM_TRANSACTION2
+ res = smb_parse_trans(pkt, data)
- rescue XCEPT::InvalidWordCount, XCEPT::InvalidCommand, XCEPT::InvalidType, XCEPT::ErrorCode
- $!.word_count = pkt['Payload']['SMB'].v['WordCount']
- $!.command = pkt['Payload']['SMB'].v['Command']
- $!.error_code = pkt['Payload']['SMB'].v['ErrorClass']
- raise $!
- end
+ when CONST::SMB_COM_NT_TRANSACT
+ res = smb_parse_nttrans(pkt, data)
- return res
- end
+ when CONST::SMB_COM_NT_TRANSACT_SECONDARY
+ res = smb_parse_nttrans(pkt, data)
- # Process incoming SMB_COM_NEGOTIATE packets
- def smb_parse_negotiate(pkt, data)
- #Process NTLM negotiate responses
- if (pkt['Payload']['SMB'].v['WordCount'] == 17)
- res = CONST::SMB_NEG_RES_NT_PKT.make_struct
- res.from_s(data)
- return res
- end
+ when CONST::SMB_COM_OPEN_ANDX
+ res = smb_parse_open(pkt, data)
- # Process LANMAN negotiate responses
- if (pkt['Payload']['SMB'].v['WordCount'] == 13)
- res = CONST::SMB_NEG_RES_LM_PKT.make_struct
- res.from_s(data)
- return res
- end
+ when CONST::SMB_COM_WRITE_ANDX
+ res = smb_parse_write(pkt, data)
- # Process ERROR negotiate responses
- if (pkt['Payload']['SMB'].v['WordCount'] == 1)
- res = CONST::SMB_NEG_RES_ERR_PKT.make_struct
- res.from_s(data)
- return res
- end
+ when CONST::SMB_COM_READ_ANDX
+ res = smb_parse_read(pkt, data)
- # Process SMB error responses
- if (pkt['Payload']['SMB'].v['WordCount'] == 0)
- return pkt
- end
+ when CONST::SMB_COM_CLOSE
+ res = smb_parse_close(pkt, data)
- raise XCEPT::InvalidWordCount
- end
+ when CONST::SMB_COM_DELETE
+ res = smb_parse_delete(pkt, data)
- # Process incoming SMB_COM_SESSION_SETUP_ANDX packets
- def smb_parse_session_setup(pkt, data)
- # Process NTLMSSP 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
+ else
+ raise XCEPT::InvalidCommand
+ end
- # Process LANMAN responses
- if (pkt['Payload']['SMB'].v['WordCount'] == 3)
- res = CONST::SMB_SETUP_RES_PKT.make_struct
- res.from_s(data)
- return res
- end
+ if (ignore_errors == false and pkt['Payload']['SMB'].v['ErrorClass'] != 0)
+ raise XCEPT::ErrorCode
+ end
- # Process SMB error responses
- if (pkt['Payload']['SMB'].v['WordCount'] == 0)
- return pkt
- end
+ rescue XCEPT::InvalidWordCount, XCEPT::InvalidCommand, XCEPT::ErrorCode
+ $!.word_count = pkt['Payload']['SMB'].v['WordCount']
+ $!.command = pkt['Payload']['SMB'].v['Command']
+ $!.error_code = pkt['Payload']['SMB'].v['ErrorClass']
+ raise $!
+ end
- raise XCEPT::InvalidWordCount
- end
+ return res
+ end
- # Process incoming SMB_COM_TREE_CONNECT_ANDX packets
- def smb_parse_tree_connect(pkt, data)
+ # Process incoming SMB_COM_NEGOTIATE packets
+ def smb_parse_negotiate(pkt, data)
+ #Process NTLM negotiate responses
+ if (pkt['Payload']['SMB'].v['WordCount'] == 17)
+ res = CONST::SMB_NEG_RES_NT_PKT.make_struct
+ res.from_s(data)
+ return res
+ end
- if (pkt['Payload']['SMB'].v['WordCount'] == 3)
- res = CONST::SMB_TREE_CONN_RES_PKT.make_struct
- res.from_s(data)
- return res
- end
+ # Process LANMAN negotiate responses
+ if (pkt['Payload']['SMB'].v['WordCount'] == 13)
+ res = CONST::SMB_NEG_RES_LM_PKT.make_struct
+ res.from_s(data)
+ return res
+ end
- # Process SMB error responses
- if (pkt['Payload']['SMB'].v['WordCount'] == 0)
- return pkt
- end
+ # Process ERROR negotiate responses
+ if (pkt['Payload']['SMB'].v['WordCount'] == 1)
+ res = CONST::SMB_NEG_RES_ERR_PKT.make_struct
+ res.from_s(data)
+ return res
+ end
- raise XCEPT::InvalidWordCount
- end
+ # Process SMB error responses
+ if (pkt['Payload']['SMB'].v['WordCount'] == 0)
+ return pkt
+ end
- # Process incoming SMB_COM_TREE_DISCONNECT packets
- def smb_parse_tree_disconnect(pkt, data)
+ raise XCEPT::InvalidWordCount
+ end
- # Process SMB responses
- if (pkt['Payload']['SMB'].v['WordCount'] == 0)
- res = CONST::SMB_TREE_DISCONN_RES_PKT.make_struct
- res.from_s(data)
- return res
- end
+ # Process incoming SMB_COM_SESSION_SETUP_ANDX packets
+ def smb_parse_session_setup(pkt, data)
+ # Process NTLMSSP 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
- raise XCEPT::InvalidWordCount
- end
+ # Process 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 incoming SMB_COM_NT_CREATE_ANDX packets
- def smb_parse_create(pkt, data)
+ # Process SMB error responses
+ if (pkt['Payload']['SMB'].v['WordCount'] == 0)
+ return pkt
+ end
- # Windows says 42, but Samba says 34, same structure :-/
- if (pkt['Payload']['SMB'].v['WordCount'] == 42)
- res = CONST::SMB_CREATE_RES_PKT.make_struct
- res.from_s(data)
- return res
- end
+ raise XCEPT::InvalidWordCount
+ end
- if (pkt['Payload']['SMB'].v['WordCount'] == 34)
- res = CONST::SMB_CREATE_RES_PKT.make_struct
- res.from_s(data)
- return res
- end
+ # Process incoming SMB_COM_TREE_CONNECT_ANDX packets
+ def smb_parse_tree_connect(pkt, data)
- # Process SMB error responses
- if (pkt['Payload']['SMB'].v['WordCount'] == 0)
- return pkt
- end
+ if (pkt['Payload']['SMB'].v['WordCount'] == 3)
+ res = CONST::SMB_TREE_CONN_RES_PKT.make_struct
+ res.from_s(data)
+ return res
+ end
- raise XCEPT::InvalidWordCount
- end
+ # Process SMB error responses
+ if (pkt['Payload']['SMB'].v['WordCount'] == 0)
+ return pkt
+ end
- # Process incoming SMB_COM_TRANSACTION packets
- def smb_parse_trans(pkt, data)
+ raise XCEPT::InvalidWordCount
+ end
- if (pkt['Payload']['SMB'].v['WordCount'] == 10)
- res = CONST::SMB_TRANS_RES_PKT.make_struct
- res.from_s(data)
- return res
- end
+ # Process incoming SMB_COM_TREE_DISCONNECT packets
+ def smb_parse_tree_disconnect(pkt, data)
- # Process SMB error responses
- if (pkt['Payload']['SMB'].v['WordCount'] == 0)
- return pkt
- end
+ # Process SMB responses
+ if (pkt['Payload']['SMB'].v['WordCount'] == 0)
+ res = CONST::SMB_TREE_DISCONN_RES_PKT.make_struct
+ res.from_s(data)
+ return res
+ end
- raise XCEPT::InvalidWordCount
- end
+ raise XCEPT::InvalidWordCount
+ end
- # Process incoming SMB_COM_NT_TRANSACT packets
- def smb_parse_nttrans(pkt, data)
+ # Process incoming SMB_COM_NT_CREATE_ANDX packets
+ def smb_parse_create(pkt, data)
- # Process SMB error responses
- if (pkt['Payload']['SMB'].v['WordCount'] == 0)
- return pkt
- end
+ # Windows says 42, but Samba says 34, same structure :-/
+ if (pkt['Payload']['SMB'].v['WordCount'] == 42)
+ res = CONST::SMB_CREATE_RES_PKT.make_struct
+ res.from_s(data)
+ return res
+ end
- if (pkt['Payload']['SMB'].v['WordCount'] >= 18)
- res = CONST::SMB_NTTRANS_RES_PKT.make_struct
- res.from_s(data)
- return res
- end
+ if (pkt['Payload']['SMB'].v['WordCount'] == 34)
+ res = CONST::SMB_CREATE_RES_PKT.make_struct
+ res.from_s(data)
+ return res
+ end
- raise XCEPT::InvalidWordCount
- end
+ # Process SMB error responses
+ if (pkt['Payload']['SMB'].v['WordCount'] == 0)
+ return pkt
+ end
- # Process incoming SMB_COM_OPEN_ANDX packets
- def smb_parse_open(pkt, data)
- # Process open responses
- if (pkt['Payload']['SMB'].v['WordCount'] == 15)
- res = CONST::SMB_OPEN_RES_PKT.make_struct
- res.from_s(data)
- return res
- end
+ raise XCEPT::InvalidWordCount
+ end
- # Process SMB error responses
- if (pkt['Payload']['SMB'].v['WordCount'] == 0)
- return pkt
- end
+ # Process incoming SMB_COM_TRANSACTION packets
+ def smb_parse_trans(pkt, data)
- raise XCEPT::InvalidWordCount
- end
+ if (pkt['Payload']['SMB'].v['WordCount'] == 10)
+ res = CONST::SMB_TRANS_RES_PKT.make_struct
+ res.from_s(data)
+ return res
+ end
- # Process incoming SMB_COM_WRITE_ANDX packets
- def smb_parse_write(pkt, data)
+ # Process SMB error responses
+ if (pkt['Payload']['SMB'].v['WordCount'] == 0)
+ return pkt
+ end
- # Process write responses
- if (pkt['Payload']['SMB'].v['WordCount'] == 6)
- res = CONST::SMB_WRITE_RES_PKT.make_struct
- res.from_s(data)
- return res
- end
+ raise XCEPT::InvalidWordCount
+ end
- # Process SMB error responses
- if (pkt['Payload']['SMB'].v['WordCount'] == 0)
- return pkt
- end
+ # Process incoming SMB_COM_NT_TRANSACT packets
+ def smb_parse_nttrans(pkt, data)
- raise XCEPT::InvalidWordCount
- end
+ # Process SMB error responses
+ if (pkt['Payload']['SMB'].v['WordCount'] == 0)
+ return pkt
+ end
- # Process incoming SMB_COM_READ_ANDX packets
- def smb_parse_read(pkt, data)
+ if (pkt['Payload']['SMB'].v['WordCount'] >= 18)
+ res = CONST::SMB_NTTRANS_RES_PKT.make_struct
+ res.from_s(data)
+ return res
+ end
- # Process read responses
- if (pkt['Payload']['SMB'].v['WordCount'] == 12)
- res = CONST::SMB_READ_RES_PKT.make_struct
- res.from_s(data)
- return res
- end
+ raise XCEPT::InvalidWordCount
+ end
- # Process SMB error responses
- if (pkt['Payload']['SMB'].v['WordCount'] == 0)
- return pkt
- end
+ # Process incoming SMB_COM_OPEN_ANDX packets
+ def smb_parse_open(pkt, data)
+ # Process open responses
+ if (pkt['Payload']['SMB'].v['WordCount'] == 15)
+ res = CONST::SMB_OPEN_RES_PKT.make_struct
+ res.from_s(data)
+ return res
+ end
- raise XCEPT::InvalidWordCount
- end
+ # Process SMB error responses
+ if (pkt['Payload']['SMB'].v['WordCount'] == 0)
+ return pkt
+ end
- # Process incoming SMB_COM_CLOSE packets
- def smb_parse_close(pkt, data)
+ raise XCEPT::InvalidWordCount
+ end
- # Process SMB error responses
- if (pkt['Payload']['SMB'].v['WordCount'] == 0)
- return pkt
- end
+ # Process incoming SMB_COM_WRITE_ANDX packets
+ def smb_parse_write(pkt, data)
- raise XCEPT::InvalidWordCount
- end
+ # Process write responses
+ if (pkt['Payload']['SMB'].v['WordCount'] == 6)
+ res = CONST::SMB_WRITE_RES_PKT.make_struct
+ res.from_s(data)
+ return res
+ end
- # Process incoming SMB_COM_DELETE packets
- def smb_parse_delete(pkt, data)
+ # Process SMB error responses
+ if (pkt['Payload']['SMB'].v['WordCount'] == 0)
+ return pkt
+ end
- # Process SMB error responses
- if (pkt['Payload']['SMB'].v['WordCount'] == 0)
- res = CONST::SMB_DELETE_RES_PKT.make_struct
- res.from_s(data)
- return res
- end
+ raise XCEPT::InvalidWordCount
+ end
- raise XCEPT::InvalidWordCount
- end
+ # Process incoming SMB_COM_READ_ANDX packets
+ def smb_parse_read(pkt, data)
- # Request a SMB session over NetBIOS
- def session_request(name = '*SMBSERVER', do_recv = true)
+ # Process read responses
+ if (pkt['Payload']['SMB'].v['WordCount'] == 12)
+ res = CONST::SMB_READ_RES_PKT.make_struct
+ res.from_s(data)
+ return res
+ end
- name ||= '*SMBSERVER'
+ # Process SMB error responses
+ if (pkt['Payload']['SMB'].v['WordCount'] == 0)
+ return pkt
+ end
- data = ''
- data << "\x20" + UTILS.nbname_encode(name) + "\x00"
- data << "\x20" + CONST::NETBIOS_REDIR + "\x00"
+ raise XCEPT::InvalidWordCount
+ end
- pkt = CONST::NBRAW_PKT.make_struct
- pkt.v['Type'] = 0x81
- pkt['Payload'].v['Payload'] = data
+ # Process incoming SMB_COM_CLOSE packets
+ def smb_parse_close(pkt, data)
- # Most SMB implementations can't handle this being fragmented
- ret = self.smb_send(pkt.to_s, EVADE::EVASION_NONE)
- return ret if not do_recv
+ # Process SMB error responses
+ if (pkt['Payload']['SMB'].v['WordCount'] == 0)
+ return pkt
+ end
- res = self.smb_recv
+ raise XCEPT::InvalidWordCount
+ end
- ack = CONST::NBRAW_PKT.make_struct
- ack.from_s(res)
+ # Process incoming SMB_COM_DELETE packets
+ def smb_parse_delete(pkt, data)
- if (ack.v['Type'] != 130)
- raise XCEPT::NetbiosSessionFailed
- end
+ # Process SMB error responses
+ if (pkt['Payload']['SMB'].v['WordCount'] == 0)
+ res = CONST::SMB_DELETE_RES_PKT.make_struct
+ res.from_s(data)
+ return res
+ end
- return ack
- end
+ raise XCEPT::InvalidWordCount
+ end
- # Negotiate a SMB dialect
- def negotiate(smb_extended_security=true, do_recv = true)
+ # Request a SMB session over NetBIOS
+ def session_request(name = '*SMBSERVER', do_recv = true)
- dialects = ['LANMAN1.0', 'LM1.2X002' ]
+ name ||= '*SMBSERVER'
- if (self.encrypt_passwords)
- dialects.push('NT LANMAN 1.0', 'NT LM 0.12')
- end
+ data = ''
+ data << "\x20" + UTILS.nbname_encode(name) + "\x00"
+ data << "\x20" + CONST::NETBIOS_REDIR + "\x00"
- data = dialects.collect { |dialect| "\x02" + dialect + "\x00" }.join('')
+ pkt = CONST::NBRAW_PKT.make_struct
+ pkt.v['Type'] = 0x81
+ pkt['Payload'].v['Payload'] = data
- pkt = CONST::SMB_NEG_PKT.make_struct
- self.smb_defaults(pkt['Payload']['SMB'])
+ # Most SMB implementations can't handle this being fragmented
+ ret = self.smb_send(pkt.to_s, EVADE::EVASION_NONE)
+ return ret if not do_recv
- pkt['Payload']['SMB'].v['Command'] = CONST::SMB_COM_NEGOTIATE
- pkt['Payload']['SMB'].v['Flags1'] = 0x18
+ res = self.smb_recv
- if(smb_extended_security)
- pkt['Payload']['SMB'].v['Flags2'] = 0x2801
- else
- pkt['Payload']['SMB'].v['Flags2'] = 0xc001
- end
+ ack = CONST::NBRAW_PKT.make_struct
+ ack.from_s(res)
- pkt['Payload'].v['Payload'] = data
+ if (ack.v['Type'] != 130)
+ raise XCEPT::NetbiosSessionFailed
+ end
- ret = self.smb_send(pkt.to_s, EVADE::EVASION_NONE)
- return ret if not do_recv
+ return ack
+ end
- ack = self.smb_recv_parse(CONST::SMB_COM_NEGOTIATE)
+ # Negotiate a SMB dialect
+ def negotiate(smb_extended_security=true, do_recv = true)
- idx = ack['Payload'].v['Dialect']
+ dialects = ['LANMAN1.0', 'LM1.2X002' ]
- # Check for failed dialect selection
- if (idx < 0 or idx >= dialects.length)
- return nil
- end
+ if (self.encrypt_passwords)
+ dialects.push('NT LANMAN 1.0', 'NT LM 0.12')
+ end
- # Set the selected dialect
- self.dialect = dialects[idx]
+ data = dialects.collect { |dialect| "\x02" + dialect + "\x00" }.join('')
- # Does the server support extended security negotiation?
- if (ack['Payload'].v['Capabilities'] & 0x80000000 != 0)
- self.extended_security = true
- end
+ pkt = CONST::SMB_NEG_PKT.make_struct
+ self.smb_defaults(pkt['Payload']['SMB'])
- # Set the security mode
- self.security_mode = ack['Payload'].v['SecurityMode']
+ pkt['Payload']['SMB'].v['Command'] = CONST::SMB_COM_NEGOTIATE
+ pkt['Payload']['SMB'].v['Flags1'] = 0x18
- #set require_signing
- if (ack['Payload'].v['SecurityMode'] & 0x08 != 0)
- self.require_signing = true
- end
+ if(smb_extended_security)
+ pkt['Payload']['SMB'].v['Flags2'] = 0x2801
+ else
+ pkt['Payload']['SMB'].v['Flags2'] = 0xc001
+ end
- # Set the challenge key
- if (ack['Payload'].v['EncryptionKey'] != nil)
- self.challenge_key = ack['Payload'].v['EncryptionKey']
- else
- # Handle Windows NT 4.0 responses
- if (ack['Payload'].v['KeyLength'] > 0)
- self.challenge_key = ack['Payload'].v['Payload'][0, ack['Payload'].v['KeyLength']]
- end
- end
+ pkt['Payload'].v['Payload'] = data
- # Set the session identifier
- if (ack['Payload'].v['SessionKey'] != nil)
- self.session_id = ack['Payload'].v['SessionKey']
- end
+ ret = self.smb_send(pkt.to_s, EVADE::EVASION_NONE)
+ return ret if not do_recv
- # Extract the payload (GUID/SecurityBlob)
- buf = ack['Payload'].v['Payload'] || ''
+ ack = self.smb_recv_parse(CONST::SMB_COM_NEGOTIATE)
- # Set the server GUID
- if (self.extended_security and buf.length >= 16)
- self.server_guid = buf[0,16]
- end
+ idx = ack['Payload'].v['Dialect']
- # Set the server SecurityBlob
- if (self.extended_security and buf.length > 16)
- # buf[16, buf.length - 16]
- end
+ # Check for failed dialect selection
+ if (idx < 0 or idx >= dialects.length)
+ return nil
+ end
- # The number of 100-nanosecond intervals that have elapsed since January 1, 1601, in
- # Coordinated Universal Time (UTC) format.
- # We convert it to a friendly Time object here
- self.system_time = UTILS.time_smb_to_unix(ack['Payload'].v['SystemTimeHigh'],ack['Payload'].v['SystemTimeLow'])
- self.system_time = ::Time.at( self.system_time )
+ # Set the selected dialect
+ self.dialect = dialects[idx]
- # A signed 16-bit signed integer that represents the server's time zone, in minutes,
- # from UTC. The time zone of the server MUST be expressed in minutes, plus or minus,
- # from UTC.
- # NOTE: althought the spec says +/- it doesn't say that it should be inverted :-/
- system_zone = ack['Payload'].v['ServerTimeZone']
- # Convert the ServerTimeZone to _seconds_ and back into a signed integer :-/
- if (system_zone & 0x8000) == 0x8000
- system_zone = (( (~system_zone) & 0x0FFF ) + 1 )
- else
- system_zone *= -1
- end
- self.system_zone = system_zone * 60
+ # Does the server support extended security negotiation?
+ if (ack['Payload'].v['Capabilities'] & 0x80000000 != 0)
+ self.extended_security = true
+ end
- return ack
- end
+ # Set the security mode
+ self.security_mode = ack['Payload'].v['SecurityMode']
+ #set require_signing
+ if (ack['Payload'].v['SecurityMode'] & 0x08 != 0)
+ self.require_signing = true
+ end
- # Authenticate and establish a session
- def session_setup(*args)
+ # Set the challenge key
+ if (ack['Payload'].v['EncryptionKey'] != nil)
+ self.challenge_key = ack['Payload'].v['EncryptionKey']
+ else
+ # Handle Windows NT 4.0 responses
+ if (ack['Payload'].v['KeyLength'] > 0)
+ self.challenge_key = ack['Payload'].v['Payload'][0, ack['Payload'].v['KeyLength']]
+ end
+ end
- if (self.dialect =~ /^(NT LANMAN 1.0|NT LM 0.12)$/)
+ # Set the session identifier
+ if (ack['Payload'].v['SessionKey'] != nil)
+ self.session_id = ack['Payload'].v['SessionKey']
+ end
- if (self.challenge_key)
- return self.session_setup_no_ntlmssp(*args)
- end
+ # Extract the payload (GUID/SecurityBlob)
+ buf = ack['Payload'].v['Payload'] || ''
- if ( self.extended_security )
- return self.session_setup_with_ntlmssp(*args)
- end
+ # Set the server GUID
+ if (self.extended_security and buf.length >= 16)
+ self.server_guid = buf[0,16]
+ end
- end
+ # Set the server SecurityBlob
+ if (self.extended_security and buf.length > 16)
+ # buf[16, buf.length - 16]
+ end
- return self.session_setup_clear(*args)
- end
+ # The number of 100-nanosecond intervals that have elapsed since January 1, 1601, in
+ # Coordinated Universal Time (UTC) format.
+ # We convert it to a friendly Time object here
+ self.system_time = UTILS.time_smb_to_unix(ack['Payload'].v['SystemTimeHigh'],ack['Payload'].v['SystemTimeLow'])
+ self.system_time = ::Time.at( self.system_time )
- # Authenticate using clear-text passwords
- def session_setup_clear(user = '', pass = '', domain = '', do_recv = true)
+ # A signed 16-bit signed integer that represents the server's time zone, in minutes,
+ # from UTC. The time zone of the server MUST be expressed in minutes, plus or minus,
+ # from UTC.
+ # NOTE: althought the spec says +/- it doesn't say that it should be inverted :-/
+ system_zone = ack['Payload'].v['ServerTimeZone']
+ # Convert the ServerTimeZone to _seconds_ and back into a signed integer :-/
+ if (system_zone & 0x8000) == 0x8000
+ system_zone = (( (~system_zone) & 0x0FFF ) + 1 )
+ else
+ system_zone *= -1
+ end
+ self.system_zone = system_zone * 60
- data = [ pass, user, domain, self.native_os, self.native_lm ].collect{ |a| a + "\x00" }.join('');
+ return ack
+ end
- 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
- if self.require_signing
- #ascii
- pkt['Payload']['SMB'].v['Flags2'] = 0x2807
- else
- #ascii
- pkt['Payload']['SMB'].v['Flags2'] = 0x2801
- end
+ # Authenticate and establish a session
+ def session_setup(*args)
- 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
+ if (self.dialect =~ /^(NT LANMAN 1.0|NT LM 0.12)$/)
- ret = self.smb_send(pkt.to_s)
- return ret if not do_recv
+ if (self.challenge_key)
+ return self.session_setup_no_ntlmssp(*args)
+ end
- ack = self.smb_recv_parse(CONST::SMB_COM_SESSION_SETUP_ANDX)
+ if ( self.extended_security )
+ return self.session_setup_with_ntlmssp(*args)
+ end
- if (ack['Payload'].v['Action'] != 1 and user.length > 0)
- self.auth_user = user
- end
+ end
- self.auth_user_id = ack['Payload']['SMB'].v['UserID']
+ return self.session_setup_clear(*args)
+ end
- info = ack['Payload'].v['Payload'].split(/\x00/)
- self.peer_native_os = info[0]
- self.peer_native_lm = info[1]
- self.default_domain = info[2]
+ # Authenticate using clear-text passwords
+ def session_setup_clear(user = '', pass = '', domain = '', do_recv = true)
- return ack
- end
+ data = [ pass, user, domain, self.native_os, self.native_lm ].collect{ |a| a + "\x00" }.join('');
- # Authenticate without NTLMSSP
- def session_setup_no_ntlmssp(user = '', pass = '', domain = '', do_recv = true)
+ pkt = CONST::SMB_SETUP_LANMAN_PKT.make_struct
+ self.smb_defaults(pkt['Payload']['SMB'])
- # Requires a challenge key to have been seen during negotiation
- raise XCEPT::NTLM1MissingChallenge if not self.challenge_key
+ pkt['Payload']['SMB'].v['Command'] = CONST::SMB_COM_SESSION_SETUP_ANDX
+ pkt['Payload']['SMB'].v['Flags1'] = 0x18
+ if self.require_signing
+ #ascii
+ pkt['Payload']['SMB'].v['Flags2'] = 0x2807
+ else
+ #ascii
+ pkt['Payload']['SMB'].v['Flags2'] = 0x2801
+ end
- #
- # We can not yet handle signing in this situation
- # But instead of throwing an exception,we will disable signing, continue and hope for the best.
- #
+ 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
- #raise XCEPT::SigningError if self.require_signing
- self.require_signing = false if self.require_signing
+ ret = self.smb_send(pkt.to_s)
+ return ret if not do_recv
+ ack = self.smb_recv_parse(CONST::SMB_COM_SESSION_SETUP_ANDX)
- if NTLM_UTILS.is_pass_ntlm_hash?(pass)
- arglm = {
- :lm_hash => [ pass.upcase()[0,32] ].pack('H32'),
- :challenge => self.challenge_key
- }
- hash_lm = NTLM_CRYPT::lm_response(arglm)
+ if (ack['Payload'].v['Action'] != 1 and user.length > 0)
+ self.auth_user = user
+ end
- argntlm = {
- :ntlm_hash => [ pass.upcase()[33,65] ].pack('H32'),
- :challenge => self.challenge_key
- }
- hash_nt = NTLM_CRYPT::ntlm_response(argntlm)
- else
- hash_lm = pass.length > 0 ? NTLM_CRYPT.lanman_des(pass, self.challenge_key) : ''
- hash_nt = pass.length > 0 ? NTLM_CRYPT.ntlm_md4(pass, self.challenge_key) : ''
- end
+ self.auth_user_id = ack['Payload']['SMB'].v['UserID']
- data = ''
- data << hash_lm
- data << hash_nt
- data << user + "\x00"
- data << domain + "\x00"
- data << self.native_os + "\x00"
- data << self.native_lm + "\x00"
+ info = ack['Payload'].v['Payload'].split(/\x00/n)
+ self.peer_native_os = info[0]
+ self.peer_native_lm = info[1]
+ self.default_domain = info[2]
- pkt = CONST::SMB_SETUP_NTLMV1_PKT.make_struct
- self.smb_defaults(pkt['Payload']['SMB'])
+ return ack
+ end
- 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
+ # Authenticate without NTLMSSP
+ def session_setup_no_ntlmssp(user = '', pass = '', domain = '', do_recv = true)
- ret = self.smb_send(pkt.to_s)
- return ret if not do_recv
+ # Requires a challenge key to have been seen during negotiation
+ raise XCEPT::NTLM1MissingChallenge if not self.challenge_key
- ack = self.smb_recv_parse(CONST::SMB_COM_SESSION_SETUP_ANDX)
+ #
+ # We can not yet handle signing in this situation
+ # But instead of throwing an exception,we will disable signing, continue and hope for the best.
+ #
- if (ack['Payload'].v['Action'] != 1 and user.length > 0)
- self.auth_user = user
- end
+ #raise XCEPT::SigningError if self.require_signing
+ self.require_signing = false if self.require_signing
- self.auth_user_id = ack['Payload']['SMB'].v['UserID']
- info = ack['Payload'].v['Payload'].split(/\x00/)
+ if NTLM_UTILS.is_pass_ntlm_hash?(pass)
+ arglm = {
+ :lm_hash => [ pass.upcase()[0,32] ].pack('H32'),
+ :challenge => self.challenge_key
+ }
+ hash_lm = NTLM_CRYPT::lm_response(arglm)
- self.peer_native_os = info[0]
- self.peer_native_lm = info[1]
- self.default_domain = info[2]
+ argntlm = {
+ :ntlm_hash => [ pass.upcase()[33,65] ].pack('H32'),
+ :challenge => self.challenge_key
+ }
+ hash_nt = NTLM_CRYPT::ntlm_response(argntlm)
+ else
+ hash_lm = pass.length > 0 ? NTLM_CRYPT.lanman_des(pass, self.challenge_key) : ''
+ hash_nt = pass.length > 0 ? NTLM_CRYPT.ntlm_md4(pass, self.challenge_key) : ''
+ end
- return ack
- end
+ 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'])
- # Authenticate without ntlmssp with a precomputed hash pair
- def session_setup_no_ntlmssp_prehash(user, domain, hash_lm, hash_nt, do_recv = true)
+ 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
- data = ''
- data << hash_lm
- data << hash_nt
- data << user + "\x00"
- data << domain + "\x00"
- data << self.native_os + "\x00"
- data << self.native_lm + "\x00"
+ ret = self.smb_send(pkt.to_s)
+ return ret if not do_recv
- pkt = CONST::SMB_SETUP_NTLMV1_PKT.make_struct
- self.smb_defaults(pkt['Payload']['SMB'])
+ ack = self.smb_recv_parse(CONST::SMB_COM_SESSION_SETUP_ANDX)
- 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
+ if (ack['Payload'].v['Action'] != 1 and user.length > 0)
+ self.auth_user = user
+ end
- ret = self.smb_send(pkt.to_s)
- return ret if not do_recv
+ self.auth_user_id = ack['Payload']['SMB'].v['UserID']
- ack = self.smb_recv_parse(CONST::SMB_COM_SESSION_SETUP_ANDX)
+ info = ack['Payload'].v['Payload'].split(/\x00/n)
- if (ack['Payload'].v['Action'] != 1 and user.length > 0)
- self.auth_user = user
- end
+ self.peer_native_os = info[0]
+ self.peer_native_lm = info[1]
+ self.default_domain = info[2]
- self.auth_user_id = ack['Payload']['SMB'].v['UserID']
+ return ack
+ end
- info = ack['Payload'].v['Payload'].split(/\x00/)
- self.peer_native_os = info[0]
- self.peer_native_lm = info[1]
- self.default_domain = info[2]
+ # Authenticate without ntlmssp with a precomputed hash pair
+ def session_setup_no_ntlmssp_prehash(user, domain, hash_lm, hash_nt, do_recv = true)
- return ack
- end
+ data = ''
+ data << hash_lm
+ data << hash_nt
+ data << user + "\x00"
+ data << domain + "\x00"
+ data << self.native_os + "\x00"
+ data << self.native_lm + "\x00"
- # Authenticate using extended security negotiation
- def session_setup_with_ntlmssp(user = '', pass = '', domain = '', name = nil, do_recv = true)
+ pkt = CONST::SMB_SETUP_NTLMV1_PKT.make_struct
+ self.smb_defaults(pkt['Payload']['SMB'])
- ntlm_options = {
- :signing => self.require_signing,
- :usentlm2_session => self.usentlm2_session,
- :use_ntlmv2 => self.use_ntlmv2,
- :send_lm => self.send_lm,
- :send_ntlm => self.send_ntlm,
- :use_lanman_key => self.use_lanman_key
- }
+ 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
- ntlmssp_flags = NTLM_UTILS.make_ntlm_flags(ntlm_options)
+ ret = self.smb_send(pkt.to_s)
+ return ret if not do_recv
- if (name == nil)
- name = Rex::Text.rand_text_alphanumeric(16)
- end
+ ack = self.smb_recv_parse(CONST::SMB_COM_SESSION_SETUP_ANDX)
- blob = NTLM_UTILS.make_ntlmssp_secblob_init(domain, name, ntlmssp_flags)
+ if (ack['Payload'].v['Action'] != 1 and user.length > 0)
+ self.auth_user = user
+ end
- native_data = ''
- native_data << self.native_os + "\x00"
- native_data << self.native_lm + "\x00"
+ self.auth_user_id = ack['Payload']['SMB'].v['UserID']
- pkt = CONST::SMB_SETUP_NTLMV2_PKT.make_struct
- self.smb_defaults(pkt['Payload']['SMB'])
+ info = ack['Payload'].v['Payload'].split(/\x00/n)
- pkt['Payload']['SMB'].v['Command'] = CONST::SMB_COM_SESSION_SETUP_ANDX
- pkt['Payload']['SMB'].v['Flags1'] = 0x18
- if require_signing
- #ascii
- pkt['Payload']['SMB'].v['Flags2'] = 0x2807
- else
- #ascii
- pkt['Payload']['SMB'].v['Flags2'] = 0x2801
- end
- pkt['Payload']['SMB'].v['WordCount'] = 12
- pkt['Payload'].v['AndX'] = 255
- pkt['Payload'].v['MaxBuff'] = 0xffdf
- pkt['Payload'].v['MaxMPX'] = 2
- pkt['Payload'].v['VCNum'] = 1
- pkt['Payload'].v['SecurityBlobLen'] = blob.length
- pkt['Payload'].v['Capabilities'] = 0x800000d4
- pkt['Payload'].v['SessionKey'] = self.session_id
- pkt['Payload'].v['Payload'] = blob + native_data
+ self.peer_native_os = info[0]
+ self.peer_native_lm = info[1]
+ self.default_domain = info[2]
- ret = self.smb_send(pkt.to_s)
+ return ack
+ end
- return ret if not do_recv
+ # Authenticate using extended security negotiation
+ def session_setup_with_ntlmssp(user = '', pass = '', domain = '', name = nil, do_recv = true)
- ack = self.smb_recv_parse(CONST::SMB_COM_SESSION_SETUP_ANDX, true)
+ ntlm_options = {
+ :signing => self.require_signing,
+ :usentlm2_session => self.usentlm2_session,
+ :use_ntlmv2 => self.use_ntlmv2,
+ :send_lm => self.send_lm,
+ :send_ntlm => self.send_ntlm,
+ :use_lanman_key => self.use_lanman_key
+ }
+ ntlmssp_flags = NTLM_UTILS.make_ntlm_flags(ntlm_options)
- # The server doesn't know about NTLM_NEGOTIATE
- if (ack['Payload']['SMB'].v['ErrorClass'] == 0x00020002)
- return session_setup_no_ntlmssp(user, pass, domain)
- end
+ if (name == nil)
+ name = Rex::Text.rand_text_alphanumeric(16)
+ end
- # Make sure the error code tells us to continue processing
- if (ack['Payload']['SMB'].v['ErrorClass'] != 0xc0000016)
- failure = XCEPT::ErrorCode.new
- failure.word_count = ack['Payload']['SMB'].v['WordCount']
- failure.command = ack['Payload']['SMB'].v['Command']
- failure.error_code = ack['Payload']['SMB'].v['ErrorClass']
- raise failure
- end
+ blob = NTLM_UTILS.make_ntlmssp_secblob_init(domain, name, ntlmssp_flags)
- # Extract the SecurityBlob from the response
- data = ack['Payload'].v['Payload']
- blob = data.slice!(0, ack['Payload'].v['SecurityBlobLen'])
+ native_data = ''
+ native_data << self.native_os + "\x00"
+ native_data << self.native_lm + "\x00"
- # Extract the native lanman and os strings
- info = data.split(/\x00/)
- self.peer_native_os = info[0]
- self.peer_native_lm = info[1]
+ pkt = CONST::SMB_SETUP_NTLMV2_PKT.make_struct
+ self.smb_defaults(pkt['Payload']['SMB'])
- # Save the temporary UserID for use in the next request
- temp_user_id = ack['Payload']['SMB'].v['UserID']
+ pkt['Payload']['SMB'].v['Command'] = CONST::SMB_COM_SESSION_SETUP_ANDX
+ pkt['Payload']['SMB'].v['Flags1'] = 0x18
+ if require_signing
+ #ascii
+ pkt['Payload']['SMB'].v['Flags2'] = 0x2807
+ else
+ #ascii
+ pkt['Payload']['SMB'].v['Flags2'] = 0x2801
+ end
+ pkt['Payload']['SMB'].v['WordCount'] = 12
+ pkt['Payload'].v['AndX'] = 255
+ pkt['Payload'].v['MaxBuff'] = 0xffdf
+ pkt['Payload'].v['MaxMPX'] = 2
+ pkt['Payload'].v['VCNum'] = 1
+ pkt['Payload'].v['SecurityBlobLen'] = blob.length
+ pkt['Payload'].v['Capabilities'] = 0x800000d4
+ pkt['Payload'].v['SessionKey'] = self.session_id
+ pkt['Payload'].v['Payload'] = blob + native_data
- # Get default data
- blob_data = NTLM_UTILS.parse_ntlm_type_2_blob(blob)
- self.challenge_key = blob_data[:challenge_key]
- server_ntlmssp_flags = blob_data[:server_ntlmssp_flags] #else should raise an error
- #netbios name
- self.default_name = blob_data[:default_name] || ''
- #netbios domain
- self.default_domain = blob_data[:default_domain] || ''
- #dns name
- self.dns_host_name = blob_data[:dns_host_name] || ''
- #dns domain
- self.dns_domain_name = blob_data[:dns_domain_name] || ''
- #Client time
- chall_MsvAvTimestamp = blob_data[:chall_MsvAvTimestamp] || ''
+ ret = self.smb_send(pkt.to_s)
+ return ret if not do_recv
- resp_lm, resp_ntlm, client_challenge, ntlm_cli_challenge = NTLM_UTILS.create_lm_ntlm_responses(user, pass, self.challenge_key, domain,
- default_name, default_domain, dns_host_name,
- dns_domain_name, chall_MsvAvTimestamp ,
- self.spnopt, ntlm_options)
- enc_session_key = ''
- self.sequence_counter = 0
+ ack = self.smb_recv_parse(CONST::SMB_COM_SESSION_SETUP_ANDX, true)
- if self.require_signing
- self.signing_key, enc_session_key, ntlmssp_flags = NTLM_UTILS.create_session_key(ntlmssp_flags, server_ntlmssp_flags, user, pass, domain,
- self.challenge_key, client_challenge, ntlm_cli_challenge,
- ntlm_options)
- end
- # Create the security blob data
- blob = NTLM_UTILS.make_ntlmssp_secblob_auth(domain, name, user, resp_lm, resp_ntlm, enc_session_key, ntlmssp_flags)
+ # The server doesn't know about NTLM_NEGOTIATE
+ if (ack['Payload']['SMB'].v['ErrorClass'] == 0x00020002)
+ return session_setup_no_ntlmssp(user, pass, domain)
+ end
- pkt = CONST::SMB_SETUP_NTLMV2_PKT.make_struct
- self.smb_defaults(pkt['Payload']['SMB'])
+ # Make sure the error code tells us to continue processing
+ if (ack['Payload']['SMB'].v['ErrorClass'] != 0xc0000016)
+ failure = XCEPT::ErrorCode.new
+ failure.word_count = ack['Payload']['SMB'].v['WordCount']
+ failure.command = ack['Payload']['SMB'].v['Command']
+ failure.error_code = ack['Payload']['SMB'].v['ErrorClass']
+ raise failure
+ end
- pkt['Payload']['SMB'].v['Command'] = CONST::SMB_COM_SESSION_SETUP_ANDX
- pkt['Payload']['SMB'].v['Flags1'] = 0x18
- if self.require_signing
- #ascii
- pkt['Payload']['SMB'].v['Flags2'] = 0x2807
- else
- #ascii
- pkt['Payload']['SMB'].v['Flags2'] = 0x2801
- end
- pkt['Payload']['SMB'].v['WordCount'] = 12
- pkt['Payload']['SMB'].v['UserID'] = temp_user_id
- pkt['Payload'].v['AndX'] = 255
- pkt['Payload'].v['MaxBuff'] = 0xffdf
- pkt['Payload'].v['MaxMPX'] = 2
- pkt['Payload'].v['VCNum'] = 1
- pkt['Payload'].v['Capabilities'] = 0x8000d05c
- pkt['Payload'].v['SessionKey'] = self.session_id
- pkt['Payload'].v['SecurityBlobLen'] = blob.length
- pkt['Payload'].v['Payload'] = blob + native_data
+ # Extract the SecurityBlob from the response
+ data = ack['Payload'].v['Payload']
+ blob = data.slice!(0, ack['Payload'].v['SecurityBlobLen'])
- # NOTE: if do_recv is set to false, we cant reach here...
- self.smb_send(pkt.to_s)
+ # Extract the native lanman and os strings
+ info = data.split(/\x00/n)
+ self.peer_native_os = info[0]
+ self.peer_native_lm = info[1]
- ack = self.smb_recv_parse(CONST::SMB_COM_SESSION_SETUP_ANDX, true)
+ # Save the temporary UserID for use in the next request
+ temp_user_id = ack['Payload']['SMB'].v['UserID']
- # Make sure that authentication succeeded
- if (ack['Payload']['SMB'].v['ErrorClass'] != 0)
+ # Get default data
+ blob_data = NTLM_UTILS.parse_ntlm_type_2_blob(blob)
+ self.challenge_key = blob_data[:challenge_key]
+ server_ntlmssp_flags = blob_data[:server_ntlmssp_flags] #else should raise an error
+ #netbios name
+ self.default_name = blob_data[:default_name] || ''
+ #netbios domain
+ self.default_domain = blob_data[:default_domain] || ''
+ #dns name
+ self.dns_host_name = blob_data[:dns_host_name] || ''
+ #dns domain
+ self.dns_domain_name = blob_data[:dns_domain_name] || ''
+ #Client time
+ chall_MsvAvTimestamp = blob_data[:chall_MsvAvTimestamp] || ''
- if (user.length == 0)
- # Ensure that signing is disabled when we hit this corner case
- self.require_signing = false
- # Fall back to the non-ntlmssp authentication method
- return self.session_setup_no_ntlmssp(user, pass, domain)
- end
+ resp_lm, resp_ntlm, client_challenge, ntlm_cli_challenge = NTLM_UTILS.create_lm_ntlm_responses(user, pass, self.challenge_key, domain,
+ default_name, default_domain, dns_host_name,
+ dns_domain_name, chall_MsvAvTimestamp ,
+ self.spnopt, ntlm_options)
+ enc_session_key = ''
+ self.sequence_counter = 0
- failure = XCEPT::ErrorCode.new
- failure.word_count = ack['Payload']['SMB'].v['WordCount']
- failure.command = ack['Payload']['SMB'].v['Command']
- failure.error_code = ack['Payload']['SMB'].v['ErrorClass']
- raise failure
- end
+ if self.require_signing
+ self.signing_key, enc_session_key, ntlmssp_flags = NTLM_UTILS.create_session_key(ntlmssp_flags, server_ntlmssp_flags, user, pass, domain,
+ self.challenge_key, client_challenge, ntlm_cli_challenge,
+ ntlm_options)
+ end
- self.auth_user_id = ack['Payload']['SMB'].v['UserID']
+ # Create the security blob data
+ blob = NTLM_UTILS.make_ntlmssp_secblob_auth(domain, name, user, resp_lm, resp_ntlm, enc_session_key, ntlmssp_flags)
- if (ack['Payload'].v['Action'] != 1 and user.length > 0)
- self.auth_user = user
- end
+ pkt = CONST::SMB_SETUP_NTLMV2_PKT.make_struct
+ self.smb_defaults(pkt['Payload']['SMB'])
- return ack
- end
+ pkt['Payload']['SMB'].v['Command'] = CONST::SMB_COM_SESSION_SETUP_ANDX
+ pkt['Payload']['SMB'].v['Flags1'] = 0x18
+ if self.require_signing
+ #ascii
+ pkt['Payload']['SMB'].v['Flags2'] = 0x2807
+ else
+ #ascii
+ pkt['Payload']['SMB'].v['Flags2'] = 0x2801
+ end
+ pkt['Payload']['SMB'].v['WordCount'] = 12
+ pkt['Payload']['SMB'].v['UserID'] = temp_user_id
+ pkt['Payload'].v['AndX'] = 255
+ pkt['Payload'].v['MaxBuff'] = 0xffdf
+ pkt['Payload'].v['MaxMPX'] = 2
+ pkt['Payload'].v['VCNum'] = 1
+ pkt['Payload'].v['Capabilities'] = 0x8000d05c
+ pkt['Payload'].v['SessionKey'] = self.session_id
+ pkt['Payload'].v['SecurityBlobLen'] = blob.length
+ pkt['Payload'].v['Payload'] = blob + native_data
+ # NOTE: if do_recv is set to false, we cant reach here...
+ self.smb_send(pkt.to_s)
- # An exploit helper function for sending arbitrary SPNEGO blobs
- def session_setup_with_ntlmssp_blob(blob = '', do_recv = true)
- native_data = ''
- native_data << self.native_os + "\x00"
- native_data << self.native_lm + "\x00"
+ ack = self.smb_recv_parse(CONST::SMB_COM_SESSION_SETUP_ANDX, true)
- pkt = CONST::SMB_SETUP_NTLMV2_PKT.make_struct
- self.smb_defaults(pkt['Payload']['SMB'])
+ # Make sure that authentication succeeded
+ if (ack['Payload']['SMB'].v['ErrorClass'] != 0)
- pkt['Payload']['SMB'].v['Command'] = CONST::SMB_COM_SESSION_SETUP_ANDX
- pkt['Payload']['SMB'].v['Flags1'] = 0x18
- pkt['Payload']['SMB'].v['Flags2'] = 0x2801
- pkt['Payload']['SMB'].v['WordCount'] = 12
- pkt['Payload']['SMB'].v['UserID'] = 0
- pkt['Payload'].v['AndX'] = 255
- pkt['Payload'].v['MaxBuff'] = 0xffdf
- pkt['Payload'].v['MaxMPX'] = 2
- pkt['Payload'].v['VCNum'] = 1
- pkt['Payload'].v['SecurityBlobLen'] = blob.length
- pkt['Payload'].v['Capabilities'] = 0x8000d05c
- pkt['Payload'].v['SessionKey'] = self.session_id
- pkt['Payload'].v['Payload'] = blob + native_data
+ if (user.length == 0)
+ # Ensure that signing is disabled when we hit this corner case
+ self.require_signing = false
- ret = self.smb_send(pkt.to_s)
- return ret if not do_recv
+ # Fall back to the non-ntlmssp authentication method
+ return self.session_setup_no_ntlmssp(user, pass, domain)
+ end
- self.smb_recv_parse(CONST::SMB_COM_SESSION_SETUP_ANDX, false)
- end
+ failure = XCEPT::ErrorCode.new
+ failure.word_count = ack['Payload']['SMB'].v['WordCount']
+ failure.command = ack['Payload']['SMB'].v['Command']
+ failure.error_code = ack['Payload']['SMB'].v['ErrorClass']
+ raise failure
+ end
+ self.auth_user_id = ack['Payload']['SMB'].v['UserID']
- # Authenticate using extended security negotiation (NTLMSSP), but stop half-way, using the temporary ID
- def session_setup_with_ntlmssp_temp(domain = '', name = nil, do_recv = true)
+ if (ack['Payload'].v['Action'] != 1 and user.length > 0)
+ self.auth_user = user
+ end
- if (name == nil)
- name = Rex::Text.rand_text_alphanumeric(16)
- end
+ return ack
+ end
- blob = NTLM_UTILS.make_ntlmssp_secblob_init(domain, name)
- native_data = ''
- native_data << self.native_os + "\x00"
- native_data << self.native_lm + "\x00"
+ # An exploit helper function for sending arbitrary SPNEGO blobs
+ def session_setup_with_ntlmssp_blob(blob = '', do_recv = true, userid = 0)
+ native_data = ''
+ native_data << self.native_os + "\x00"
+ native_data << self.native_lm + "\x00"
- pkt = CONST::SMB_SETUP_NTLMV2_PKT.make_struct
- self.smb_defaults(pkt['Payload']['SMB'])
+ pkt = CONST::SMB_SETUP_NTLMV2_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'] = 0x2801
- pkt['Payload']['SMB'].v['WordCount'] = 12
- pkt['Payload'].v['AndX'] = 255
- pkt['Payload'].v['MaxBuff'] = 0xffdf
- pkt['Payload'].v['MaxMPX'] = 2
- pkt['Payload'].v['VCNum'] = 1
- pkt['Payload'].v['SecurityBlobLen'] = blob.length
- pkt['Payload'].v['Capabilities'] = 0x8000d05c
- pkt['Payload'].v['SessionKey'] = self.session_id
- pkt['Payload'].v['Payload'] = blob + native_data
+ pkt['Payload']['SMB'].v['Command'] = CONST::SMB_COM_SESSION_SETUP_ANDX
+ pkt['Payload']['SMB'].v['Flags1'] = 0x18
+ pkt['Payload']['SMB'].v['Flags2'] = 0x2801
+ pkt['Payload']['SMB'].v['WordCount'] = 12
+ pkt['Payload']['SMB'].v['UserID'] = userid
+ pkt['Payload'].v['AndX'] = 255
+ pkt['Payload'].v['MaxBuff'] = 0xffdf
+ pkt['Payload'].v['MaxMPX'] = 2
+ pkt['Payload'].v['VCNum'] = 1
+ pkt['Payload'].v['SecurityBlobLen'] = blob.length
+ pkt['Payload'].v['Capabilities'] = 0x8000d05c
+ pkt['Payload'].v['SessionKey'] = self.session_id
+ pkt['Payload'].v['Payload'] = blob + native_data
- ret = self.smb_send(pkt.to_s)
- return ret if not do_recv
+ ret = self.smb_send(pkt.to_s)
+ return ret if not do_recv
- ack = self.smb_recv_parse(CONST::SMB_COM_SESSION_SETUP_ANDX, true)
+ self.smb_recv_parse(CONST::SMB_COM_SESSION_SETUP_ANDX, false)
+ end
- # The server doesn't know about NTLM_NEGOTIATE, try ntlmv1
- if (ack['Payload']['SMB'].v['ErrorClass'] == 0x00020002)
- return session_setup_no_ntlmssp(user, pass, domain)
- end
- # Make sure the error code tells us to continue processing
- if (ack['Payload']['SMB'].v['ErrorClass'] != 0xc0000016)
- failure = XCEPT::ErrorCode.new
- failure.word_count = ack['Payload']['SMB'].v['WordCount']
- failure.command = ack['Payload']['SMB'].v['Command']
- failure.error_code = ack['Payload']['SMB'].v['ErrorClass']
- raise failure
- end
+ # Authenticate using extended security negotiation (NTLMSSP), but stop half-way, using the temporary ID
+ def session_setup_with_ntlmssp_temp(domain = '', name = nil, do_recv = true)
- # Extract the SecurityBlob from the response
- data = ack['Payload'].v['Payload']
- blob = data.slice!(0, ack['Payload'].v['SecurityBlobLen'])
+ if (name == nil)
+ name = Rex::Text.rand_text_alphanumeric(16)
+ end
- # Extract the native lanman and os strings
- info = data.split(/\x00/)
- self.peer_native_os = info[0]
- self.peer_native_lm = info[1]
+ blob = NTLM_UTILS.make_ntlmssp_secblob_init(domain, name)
- # Save the temporary UserID for use in the next request
- self.auth_user_id = ack['Payload']['SMB'].v['UserID']
+ native_data = ''
+ native_data << self.native_os + "\x00"
+ native_data << self.native_lm + "\x00"
- # Extract the NTLM challenge key the lazy way
- cidx = blob.index("NTLMSSP\x00\x02\x00\x00\x00")
+ pkt = CONST::SMB_SETUP_NTLMV2_PKT.make_struct
+ self.smb_defaults(pkt['Payload']['SMB'])
- if (cidx == -1)
- raise XCEPT::NTLM2MissingChallenge
- end
+ pkt['Payload']['SMB'].v['Command'] = CONST::SMB_COM_SESSION_SETUP_ANDX
+ pkt['Payload']['SMB'].v['Flags1'] = 0x18
+ pkt['Payload']['SMB'].v['Flags2'] = 0x2801
+ pkt['Payload']['SMB'].v['WordCount'] = 12
+ pkt['Payload'].v['AndX'] = 255
+ pkt['Payload'].v['MaxBuff'] = 0xffdf
+ pkt['Payload'].v['MaxMPX'] = 2
+ pkt['Payload'].v['VCNum'] = 1
+ pkt['Payload'].v['SecurityBlobLen'] = blob.length
+ pkt['Payload'].v['Capabilities'] = 0x8000d05c
+ pkt['Payload'].v['SessionKey'] = self.session_id
+ pkt['Payload'].v['Payload'] = blob + native_data
- # Store the challenge key
- self.challenge_key = blob[cidx + 24, 8]
+ ret = self.smb_send(pkt.to_s)
+ return ret if not do_recv
- return ack
- end
+ ack = self.smb_recv_parse(CONST::SMB_COM_SESSION_SETUP_ANDX, true)
- # Connect to a specified share with an optional password
- def tree_connect(share = 'IPC$', pass = '', do_recv = true)
+ # The server doesn't know about NTLM_NEGOTIATE, try ntlmv1
+ if (ack['Payload']['SMB'].v['ErrorClass'] == 0x00020002)
+ return session_setup_no_ntlmssp(user, pass, domain)
+ end
- data = [ pass, share, '?????' ].collect{ |a| a + "\x00" }.join('');
+ # Make sure the error code tells us to continue processing
+ if (ack['Payload']['SMB'].v['ErrorClass'] != 0xc0000016)
+ failure = XCEPT::ErrorCode.new
+ failure.word_count = ack['Payload']['SMB'].v['WordCount']
+ failure.command = ack['Payload']['SMB'].v['Command']
+ failure.error_code = ack['Payload']['SMB'].v['ErrorClass']
+ raise failure
+ end
- pkt = CONST::SMB_TREE_CONN_PKT.make_struct
- self.smb_defaults(pkt['Payload']['SMB'])
+ # Extract the SecurityBlob from the response
+ data = ack['Payload'].v['Payload']
+ blob = data.slice!(0, ack['Payload'].v['SecurityBlobLen'])
- pkt['Payload']['SMB'].v['Command'] = CONST::SMB_COM_TREE_CONNECT_ANDX
- pkt['Payload']['SMB'].v['Flags1'] = 0x18
- if self.require_signing
- #ascii
- pkt['Payload']['SMB'].v['Flags2'] = 0x2807
- else
- #ascii
- pkt['Payload']['SMB'].v['Flags2'] = 0x2801
- end
+ # Extract the native lanman and os strings
+ info = data.split(/\x00/n)
+ self.peer_native_os = info[0]
+ self.peer_native_lm = info[1]
- 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
+ # Save the temporary UserID for use in the next request
+ self.auth_user_id = ack['Payload']['SMB'].v['UserID']
- ret = self.smb_send(pkt.to_s)
- return ret if not do_recv
+ # Extract the NTLM challenge key the lazy way
+ cidx = blob.index("NTLMSSP\x00\x02\x00\x00\x00")
- ack = self.smb_recv_parse(CONST::SMB_COM_TREE_CONNECT_ANDX)
+ if (cidx == -1)
+ raise XCEPT::NTLM2MissingChallenge
+ end
- self.last_tree_id = ack['Payload']['SMB'].v['TreeID']
- # why bother?
- # info = ack['Payload'].v['Payload'].split(/\x00/)
+ # Store the challenge key
+ self.challenge_key = blob[cidx + 24, 8]
- return ack
- end
+ return ack
+ end
- # Disconnect from the current tree
- def tree_disconnect(tree_id = self.last_tree_id, do_recv = true)
+ # Connect to a specified share with an optional password
+ def tree_connect(share = 'IPC$', pass = '', do_recv = true)
- pkt = CONST::SMB_TREE_DISCONN_PKT.make_struct
- self.smb_defaults(pkt['Payload']['SMB'])
+ data = [ pass, share, '?????' ].collect{ |a| a + "\x00" }.join('');
- pkt['Payload']['SMB'].v['Command'] = CONST::SMB_COM_TREE_DISCONNECT
- pkt['Payload']['SMB'].v['Flags1'] = 0x18
- if self.require_signing
- #ascii
- pkt['Payload']['SMB'].v['Flags2'] = 0x2807
- else
- #ascii
- pkt['Payload']['SMB'].v['Flags2'] = 0x2801
- end
+ pkt = CONST::SMB_TREE_CONN_PKT.make_struct
+ self.smb_defaults(pkt['Payload']['SMB'])
+ pkt['Payload']['SMB'].v['TreeID'] = 0
- pkt['Payload']['SMB'].v['WordCount'] = 0
- pkt['Payload']['SMB'].v['TreeID'] = tree_id
+ pkt['Payload']['SMB'].v['Command'] = CONST::SMB_COM_TREE_CONNECT_ANDX
+ pkt['Payload']['SMB'].v['Flags1'] = 0x18
+ if self.require_signing
+ #ascii
+ pkt['Payload']['SMB'].v['Flags2'] = 0x2807
+ else
+ #ascii
+ pkt['Payload']['SMB'].v['Flags2'] = 0x2801
+ end
- ret = self.smb_send(pkt.to_s)
- return ret if not do_recv
+ 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
- ack = self.smb_recv_parse(CONST::SMB_COM_TREE_DISCONNECT)
+ ret = self.smb_send(pkt.to_s)
+ return ret if not do_recv
- if (tree_id == self.last_tree_id)
- self.last_tree_id = 0
- end
+ ack = self.smb_recv_parse(CONST::SMB_COM_TREE_CONNECT_ANDX)
- return ack
- end
+ self.last_tree_id = ack['Payload']['SMB'].v['TreeID']
+ # why bother?
+ # info = ack['Payload'].v['Payload'].split(/\x00/)
- # Returns a SMB_CREATE_RES response for a given named pipe
- def create_pipe(filename, disposition = 1, impersonation = 2)
- self.create(filename)
- end
+ return ack
+ end
- # Creates a file or opens an existing pipe
- def create(filename, disposition = 1, impersonation = 2, do_recv = true)
+ # Disconnect from the current tree
+ def tree_disconnect(tree_id = self.last_tree_id, do_recv = true)
- pkt = CONST::SMB_CREATE_PKT.make_struct
- self.smb_defaults(pkt['Payload']['SMB'])
+ pkt = CONST::SMB_TREE_DISCONN_PKT.make_struct
+ self.smb_defaults(pkt['Payload']['SMB'])
- pkt['Payload']['SMB'].v['Command'] = CONST::SMB_COM_NT_CREATE_ANDX
- pkt['Payload']['SMB'].v['Flags1'] = 0x18
- if self.require_signing
- #ascii
- pkt['Payload']['SMB'].v['Flags2'] = 0x2807
- else
- #ascii
- pkt['Payload']['SMB'].v['Flags2'] = 0x2801
- end
+ pkt['Payload']['SMB'].v['Command'] = CONST::SMB_COM_TREE_DISCONNECT
+ pkt['Payload']['SMB'].v['Flags1'] = 0x18
+ if self.require_signing
+ #ascii
+ pkt['Payload']['SMB'].v['Flags2'] = 0x2807
+ else
+ #ascii
+ pkt['Payload']['SMB'].v['Flags2'] = 0x2801
+ end
- pkt['Payload']['SMB'].v['WordCount'] = 24
+ pkt['Payload']['SMB'].v['WordCount'] = 0
+ pkt['Payload']['SMB'].v['TreeID'] = tree_id
- pkt['Payload'].v['AndX'] = 255
- pkt['Payload'].v['FileNameLen'] = filename.length
- pkt['Payload'].v['CreateFlags'] = 0x16
- pkt['Payload'].v['AccessMask'] = 0x02000000 # Maximum Allowed
- pkt['Payload'].v['ShareAccess'] = 7
- pkt['Payload'].v['CreateOptions'] = 0
- pkt['Payload'].v['Impersonation'] = impersonation
- pkt['Payload'].v['Disposition'] = disposition
- pkt['Payload'].v['Payload'] = filename + "\x00"
+ ret = self.smb_send(pkt.to_s)
+ return ret if not do_recv
- ret = self.smb_send(pkt.to_s)
- return ret if not do_recv
+ ack = self.smb_recv_parse(CONST::SMB_COM_TREE_DISCONNECT)
- ack = self.smb_recv_parse(CONST::SMB_COM_NT_CREATE_ANDX)
+ if (tree_id == self.last_tree_id)
+ self.last_tree_id = 0
+ end
- # Save off the FileID
- if (ack['Payload'].v['FileID'] > 0)
- self.last_file_id = ack['Payload'].v['FileID']
- end
+ return ack
+ end
- return ack
- end
+ # Returns a SMB_CREATE_RES response for a given named pipe
+ def create_pipe(filename, disposition = 1, impersonation = 2)
+ self.create(filename)
+ end
- # Deletes a file from a share
- def delete(filename, tree_id = self.last_tree_id, do_recv = true)
+ # Creates a file or opens an existing pipe
+ def create(filename, disposition = 1, impersonation = 2, do_recv = true)
- pkt = CONST::SMB_DELETE_PKT.make_struct
- self.smb_defaults(pkt['Payload']['SMB'])
+ pkt = CONST::SMB_CREATE_PKT.make_struct
+ self.smb_defaults(pkt['Payload']['SMB'])
- pkt['Payload']['SMB'].v['Command'] = CONST::SMB_COM_DELETE
- pkt['Payload']['SMB'].v['Flags1'] = 0x18
- if self.require_signing
- #ascii
- pkt['Payload']['SMB'].v['Flags2'] = 0x2807
- else
- #ascii
- pkt['Payload']['SMB'].v['Flags2'] = 0x2801
- end
+ pkt['Payload']['SMB'].v['Command'] = CONST::SMB_COM_NT_CREATE_ANDX
+ pkt['Payload']['SMB'].v['Flags1'] = 0x18
+ if self.require_signing
+ #ascii
+ pkt['Payload']['SMB'].v['Flags2'] = 0x2807
+ else
+ #ascii
+ pkt['Payload']['SMB'].v['Flags2'] = 0x2801
+ end
- pkt['Payload']['SMB'].v['TreeID'] = tree_id
- pkt['Payload']['SMB'].v['WordCount'] = 1
+ pkt['Payload']['SMB'].v['WordCount'] = 24
- pkt['Payload'].v['SearchAttributes'] = 0x06
- pkt['Payload'].v['BufferFormat'] = 4
- pkt['Payload'].v['Payload'] = filename + "\x00"
+ pkt['Payload'].v['AndX'] = 255
+ pkt['Payload'].v['FileNameLen'] = filename.length
+ pkt['Payload'].v['CreateFlags'] = 0x16
+ pkt['Payload'].v['AccessMask'] = 0x02000000 # Maximum Allowed
+ pkt['Payload'].v['ShareAccess'] = 7
+ pkt['Payload'].v['CreateOptions'] = 0
+ pkt['Payload'].v['Impersonation'] = impersonation
+ pkt['Payload'].v['Disposition'] = disposition
+ pkt['Payload'].v['Payload'] = filename + "\x00"
- ret = self.smb_send(pkt.to_s)
- return ret if not do_recv
+ ret = self.smb_send(pkt.to_s)
+ return ret if not do_recv
- ack = self.smb_recv_parse(CONST::SMB_COM_DELETE)
+ ack = self.smb_recv_parse(CONST::SMB_COM_NT_CREATE_ANDX)
- return ack
- end
+ # Save off the FileID
+ if (ack['Payload'].v['FileID'] > 0)
+ self.last_file_id = ack['Payload'].v['FileID']
+ end
- # Opens an existing file or creates a new one
- def open(filename, mode = 0x12, access = 0x42, do_recv = true)
+ return ack
+ end
- pkt = CONST::SMB_OPEN_PKT.make_struct
- self.smb_defaults(pkt['Payload']['SMB'])
+ # Deletes a file from a share
+ def delete(filename, tree_id = self.last_tree_id, do_recv = true)
- pkt['Payload']['SMB'].v['Command'] = CONST::SMB_COM_OPEN_ANDX
- pkt['Payload']['SMB'].v['Flags1'] = 0x18
- if self.require_signing
- #ascii
- pkt['Payload']['SMB'].v['Flags2'] = 0x2807
- else
- #ascii
- pkt['Payload']['SMB'].v['Flags2'] = 0x2801
- end
+ pkt = CONST::SMB_DELETE_PKT.make_struct
+ self.smb_defaults(pkt['Payload']['SMB'])
- pkt['Payload']['SMB'].v['WordCount'] = 15
+ pkt['Payload']['SMB'].v['Command'] = CONST::SMB_COM_DELETE
+ pkt['Payload']['SMB'].v['Flags1'] = 0x18
+ if self.require_signing
+ #ascii
+ pkt['Payload']['SMB'].v['Flags2'] = 0x2807
+ else
+ #ascii
+ pkt['Payload']['SMB'].v['Flags2'] = 0x2801
+ end
- pkt['Payload'].v['AndX'] = 255
- pkt['Payload'].v['Access'] = access
- pkt['Payload'].v['SearchAttributes'] = 0x06
- pkt['Payload'].v['OpenFunction'] = mode
- pkt['Payload'].v['Payload'] = filename + "\x00"
+ pkt['Payload']['SMB'].v['TreeID'] = tree_id
+ pkt['Payload']['SMB'].v['WordCount'] = 1
- ret = self.smb_send(pkt.to_s)
- return ret if not do_recv
+ pkt['Payload'].v['SearchAttributes'] = 0x06
+ pkt['Payload'].v['BufferFormat'] = 4
+ pkt['Payload'].v['Payload'] = filename + "\x00"
- ack = self.smb_recv_parse(CONST::SMB_COM_OPEN_ANDX)
+ ret = self.smb_send(pkt.to_s)
+ return ret if not do_recv
- # Save off the FileID
- if (ack['Payload'].v['FileID'] > 0)
- self.last_file_id = ack['Payload'].v['FileID']
- end
+ ack = self.smb_recv_parse(CONST::SMB_COM_DELETE)
- return ack
- end
+ return ack
+ end
- # Closes an open file handle
- def close(file_id = self.last_file_id, tree_id = self.last_tree_id, do_recv = true)
+ # Opens an existing file or creates a new one
+ def open(filename, mode = 0x12, access = 0x42, do_recv = true)
- pkt = CONST::SMB_CLOSE_PKT.make_struct
- self.smb_defaults(pkt['Payload']['SMB'])
+ pkt = CONST::SMB_OPEN_PKT.make_struct
+ self.smb_defaults(pkt['Payload']['SMB'])
- pkt['Payload']['SMB'].v['Command'] = CONST::SMB_COM_CLOSE
- pkt['Payload']['SMB'].v['Flags1'] = 0x18
- if self.require_signing
- #ascii
- pkt['Payload']['SMB'].v['Flags2'] = 0x2807
- else
- #ascii
- pkt['Payload']['SMB'].v['Flags2'] = 0x2801
- end
+ pkt['Payload']['SMB'].v['Command'] = CONST::SMB_COM_OPEN_ANDX
+ pkt['Payload']['SMB'].v['Flags1'] = 0x18
+ if self.require_signing
+ #ascii
+ pkt['Payload']['SMB'].v['Flags2'] = 0x2807
+ else
+ #ascii
+ pkt['Payload']['SMB'].v['Flags2'] = 0x2801
+ end
- pkt['Payload']['SMB'].v['TreeID'] = tree_id
- pkt['Payload']['SMB'].v['WordCount'] = 3
+ pkt['Payload']['SMB'].v['WordCount'] = 15
- pkt['Payload'].v['FileID'] = file_id
- pkt['Payload'].v['LastWrite'] = -1
+ pkt['Payload'].v['AndX'] = 255
+ pkt['Payload'].v['Access'] = access
+ pkt['Payload'].v['SearchAttributes'] = 0x06
+ pkt['Payload'].v['OpenFunction'] = mode
+ pkt['Payload'].v['Payload'] = filename + "\x00"
- ret = self.smb_send(pkt.to_s)
- return ret if not do_recv
+ ret = self.smb_send(pkt.to_s)
+ return ret if not do_recv
- ack = self.smb_recv_parse(CONST::SMB_COM_CLOSE)
+ ack = self.smb_recv_parse(CONST::SMB_COM_OPEN_ANDX)
- return ack
- end
+ # Save off the FileID
+ if (ack['Payload'].v['FileID'] > 0)
+ self.last_file_id = ack['Payload'].v['FileID']
+ end
- # Writes data to an open file handle
- def write(file_id = self.last_file_id, offset = 0, data = '', do_recv = true)
- pkt = CONST::SMB_WRITE_PKT.make_struct
- self.smb_defaults(pkt['Payload']['SMB'])
+ return ack
+ end
- data_offset = pkt.to_s.length - 4
+ # Closes an open file handle
+ def close(file_id = self.last_file_id, tree_id = self.last_tree_id, do_recv = true)
- filler = EVADE.make_offset_filler(evasion_opts['pad_data'], 4096 - data.length - data_offset)
+ pkt = CONST::SMB_CLOSE_PKT.make_struct
+ self.smb_defaults(pkt['Payload']['SMB'])
- pkt['Payload']['SMB'].v['Command'] = CONST::SMB_COM_WRITE_ANDX
- pkt['Payload']['SMB'].v['Flags1'] = 0x18
- if self.require_signing
- #ascii
- pkt['Payload']['SMB'].v['Flags2'] = 0x2805
- else
- #ascii
- pkt['Payload']['SMB'].v['Flags2'] = 0x2801
- end
+ pkt['Payload']['SMB'].v['Command'] = CONST::SMB_COM_CLOSE
+ pkt['Payload']['SMB'].v['Flags1'] = 0x18
+ if self.require_signing
+ #ascii
+ pkt['Payload']['SMB'].v['Flags2'] = 0x2807
+ else
+ #ascii
+ pkt['Payload']['SMB'].v['Flags2'] = 0x2801
+ end
- pkt['Payload']['SMB'].v['WordCount'] = 14
+ pkt['Payload']['SMB'].v['TreeID'] = tree_id
+ pkt['Payload']['SMB'].v['WordCount'] = 3
- pkt['Payload'].v['AndX'] = 255
- pkt['Payload'].v['FileID'] = file_id
- pkt['Payload'].v['Offset'] = offset
- pkt['Payload'].v['Reserved2'] = -1
- pkt['Payload'].v['WriteMode'] = 8
- pkt['Payload'].v['Remaining'] = data.length
- # pkt['Payload'].v['DataLenHigh'] = (data.length / 65536).to_i
- pkt['Payload'].v['DataLenLow'] = (data.length % 65536).to_i
- pkt['Payload'].v['DataOffset'] = data_offset + filler.length
- pkt['Payload'].v['Payload'] = filler + data
+ pkt['Payload'].v['FileID'] = file_id
+ pkt['Payload'].v['LastWrite'] = -1
- ret = self.smb_send(pkt.to_s)
- return ret if not do_recv
+ ret = self.smb_send(pkt.to_s)
+ return ret if not do_recv
- ack = self.smb_recv_parse(CONST::SMB_COM_WRITE_ANDX)
+ ack = self.smb_recv_parse(CONST::SMB_COM_CLOSE)
- return ack
- end
+ return ack
+ end
+ # Writes data to an open file handle
+ def write(file_id = self.last_file_id, offset = 0, data = '', do_recv = true)
+ pkt = CONST::SMB_WRITE_PKT.make_struct
+ self.smb_defaults(pkt['Payload']['SMB'])
- # Reads data from an open file handle
- def read(file_id = self.last_file_id, offset = 0, data_length = 64000, do_recv = true)
+ data_offset = pkt.to_s.length - 4
- pkt = CONST::SMB_READ_PKT.make_struct
- self.smb_defaults(pkt['Payload']['SMB'])
+ filler = EVADE.make_offset_filler(evasion_opts['pad_data'], 4096 - data.length - data_offset)
- pkt['Payload']['SMB'].v['Command'] = CONST::SMB_COM_READ_ANDX
- pkt['Payload']['SMB'].v['Flags1'] = 0x18
- if self.require_signing
- #ascii
- pkt['Payload']['SMB'].v['Flags2'] = 0x2807
- else
- #ascii
- pkt['Payload']['SMB'].v['Flags2'] = 0x2801
- end
+ pkt['Payload']['SMB'].v['Command'] = CONST::SMB_COM_WRITE_ANDX
+ pkt['Payload']['SMB'].v['Flags1'] = 0x18
+ if self.require_signing
+ #ascii
+ pkt['Payload']['SMB'].v['Flags2'] = 0x2805
+ else
+ #ascii
+ pkt['Payload']['SMB'].v['Flags2'] = 0x2801
+ end
- pkt['Payload']['SMB'].v['WordCount'] = 10
+ pkt['Payload']['SMB'].v['WordCount'] = 14
- pkt['Payload'].v['AndX'] = 255
- pkt['Payload'].v['FileID'] = file_id
- pkt['Payload'].v['Offset'] = offset
- # pkt['Payload'].v['MaxCountHigh'] = (data_length / 65536).to_i
- pkt['Payload'].v['MaxCountLow'] = (data_length % 65536).to_i
- pkt['Payload'].v['MinCount'] = data_length
- pkt['Payload'].v['Reserved2'] = -1
+ pkt['Payload'].v['AndX'] = 255
+ pkt['Payload'].v['FileID'] = file_id
+ pkt['Payload'].v['Offset'] = offset
+ pkt['Payload'].v['Reserved2'] = -1
+ pkt['Payload'].v['WriteMode'] = 8
+ pkt['Payload'].v['Remaining'] = data.length
+ # pkt['Payload'].v['DataLenHigh'] = (data.length / 65536).to_i
+ pkt['Payload'].v['DataLenLow'] = (data.length % 65536).to_i
+ pkt['Payload'].v['DataOffset'] = data_offset + filler.length
+ pkt['Payload'].v['Payload'] = filler + data
- ret = self.smb_send(pkt.to_s)
- return ret if not do_recv
+ ret = self.smb_send(pkt.to_s)
+ return ret if not do_recv
- ack = self.smb_recv_parse(CONST::SMB_COM_READ_ANDX, true)
+ ack = self.smb_recv_parse(CONST::SMB_COM_WRITE_ANDX)
- err = ack['Payload']['SMB'].v['ErrorClass']
+ return ack
+ end
- # Catch some non-fatal error codes
- if (err != 0 && err != CONST::SMB_ERROR_BUFFER_OVERFLOW)
- failure = XCEPT::ErrorCode.new
- failure.word_count = ack['Payload']['SMB'].v['WordCount']
- failure.command = ack['Payload']['SMB'].v['Command']
- failure.error_code = ack['Payload']['SMB'].v['ErrorClass']
- raise failure
- end
- return ack
- end
+ # Reads data from an open file handle
+ def read(file_id = self.last_file_id, offset = 0, data_length = 64000, do_recv = true)
+ pkt = CONST::SMB_READ_PKT.make_struct
+ self.smb_defaults(pkt['Payload']['SMB'])
- # Perform a transaction against a named pipe
- def trans_named_pipe(file_id, data = '', no_response = nil)
- pipe = EVADE.make_trans_named_pipe_name(evasion_opts['pad_file'])
- self.trans(pipe, '', data, 2, [0x26, file_id].pack('vv'), no_response)
- end
+ pkt['Payload']['SMB'].v['Command'] = CONST::SMB_COM_READ_ANDX
+ pkt['Payload']['SMB'].v['Flags1'] = 0x18
+ if self.require_signing
+ #ascii
+ pkt['Payload']['SMB'].v['Flags2'] = 0x2807
+ else
+ #ascii
+ pkt['Payload']['SMB'].v['Flags2'] = 0x2801
+ end
- # Perform a mailslot write over SMB
- # Warning: This can kill srv.sys unless MS06-035 is applied
- def trans_mailslot (name, data = '')
- # Setup data must be:
- # Operation: 1 (write)
- # Priority: 0
- # Class: Reliable
- self.trans_maxzero(name, '', data, 3, [1, 0, 1].pack('vvv'), true )
- end
+ pkt['Payload']['SMB'].v['WordCount'] = 10
- # Perform a transaction against a given pipe name
- def trans(pipe, param = '', body = '', setup_count = 0, setup_data = '', no_response = false, do_recv = true)
+ pkt['Payload'].v['AndX'] = 255
+ pkt['Payload'].v['FileID'] = file_id
+ pkt['Payload'].v['Offset'] = offset
+ # pkt['Payload'].v['MaxCountHigh'] = (data_length / 65536).to_i
+ pkt['Payload'].v['MaxCountLow'] = (data_length % 65536).to_i
+ pkt['Payload'].v['MinCount'] = data_length
+ pkt['Payload'].v['Reserved2'] = -1
- # Null-terminate the pipe parameter if needed
- if (pipe[-1,1] != "\x00")
- pipe << "\x00"
- end
+ ret = self.smb_send(pkt.to_s)
+ return ret if not do_recv
- pkt = CONST::SMB_TRANS_PKT.make_struct
- self.smb_defaults(pkt['Payload']['SMB'])
+ ack = self.smb_recv_parse(CONST::SMB_COM_READ_ANDX, true)
- # Packets larger than mlen will cause XP SP2 to disconnect us ;-(
- mlen = 4200
+ err = ack['Payload']['SMB'].v['ErrorClass']
- # Figure out how much space is taken up by our current arguments
- xlen = pipe.length + param.length + body.length
+ # Catch some non-fatal error codes
+ if (err != 0 && err != CONST::SMB_ERROR_BUFFER_OVERFLOW)
+ failure = XCEPT::ErrorCode.new
+ failure.word_count = ack['Payload']['SMB'].v['WordCount']
+ failure.command = ack['Payload']['SMB'].v['Command']
+ failure.error_code = ack['Payload']['SMB'].v['ErrorClass']
+ raise failure
+ end
- filler1 = ''
- filler2 = ''
+ return ack
+ end
- # Fill any available space depending on the evasion settings
- if (xlen < mlen)
- filler1 = EVADE.make_offset_filler(evasion_opts['pad_data'], (mlen-xlen)/2)
- filler2 = EVADE.make_offset_filler(evasion_opts['pad_data'], (mlen-xlen)/2)
- end
- # Squish the whole thing together
- data = pipe + filler1 + param + filler2 + body
+ # Perform a transaction against a named pipe
+ def trans_named_pipe(file_id, data = '', no_response = nil)
+ pipe = EVADE.make_trans_named_pipe_name(evasion_opts['pad_file'])
+ self.trans(pipe, '', data, 2, [0x26, file_id].pack('vv'), no_response)
+ end
- # Throw some form of a warning out?
- if (data.length > mlen)
- # XXX This call will more than likely fail :-(
- end
+ # Perform a mailslot write over SMB
+ # Warning: This can kill srv.sys unless MS06-035 is applied
+ def trans_mailslot (name, data = '')
+ # Setup data must be:
+ # Operation: 1 (write)
+ # Priority: 0
+ # Class: Reliable
+ self.trans_maxzero(name, '', data, 3, [1, 0, 1].pack('vvv'), true )
+ end
- # Calculate all of the offsets
- base_offset = pkt.to_s.length + (setup_count * 2) - 4
- param_offset = base_offset + pipe.length + filler1.length
- data_offset = param_offset + filler2.length + param.length
+ # Perform a transaction against a given pipe name
+ def trans(pipe, param = '', body = '', setup_count = 0, setup_data = '', no_response = false, do_recv = true)
- pkt['Payload']['SMB'].v['Command'] = CONST::SMB_COM_TRANSACTION
- pkt['Payload']['SMB'].v['Flags1'] = 0x18
- if self.require_signing
- #ascii
- pkt['Payload']['SMB'].v['Flags2'] = 0x2807
- else
- #ascii
- pkt['Payload']['SMB'].v['Flags2'] = 0x2801
- end
+ # Null-terminate the pipe parameter if needed
+ if (pipe[-1,1] != "\x00")
+ pipe << "\x00"
+ end
- pkt['Payload']['SMB'].v['WordCount'] = 14 + setup_count
+ pkt = CONST::SMB_TRANS_PKT.make_struct
+ self.smb_defaults(pkt['Payload']['SMB'])
- pkt['Payload'].v['ParamCountTotal'] = param.length
- pkt['Payload'].v['DataCountTotal'] = body.length
- pkt['Payload'].v['ParamCountMax'] = 1024
- pkt['Payload'].v['DataCountMax'] = 65504
- pkt['Payload'].v['ParamCount'] = param.length
- pkt['Payload'].v['ParamOffset'] = param_offset
- pkt['Payload'].v['DataCount'] = body.length
- pkt['Payload'].v['DataOffset'] = data_offset
- pkt['Payload'].v['SetupCount'] = setup_count
- pkt['Payload'].v['SetupData'] = setup_data
+ # Packets larger than mlen will cause XP SP2 to disconnect us ;-(
+ mlen = 4200
- pkt['Payload'].v['Payload'] = data
+ # Figure out how much space is taken up by our current arguments
+ xlen = pipe.length + param.length + body.length
- if no_response
- pkt['Payload'].v['Flags'] = 2
- end
+ filler1 = ''
+ filler2 = ''
- ret = self.smb_send(pkt.to_s)
- return ret if no_response or not do_recv
+ # Fill any available space depending on the evasion settings
+ if (xlen < mlen)
+ filler1 = EVADE.make_offset_filler(evasion_opts['pad_data'], (mlen-xlen)/2)
+ filler2 = EVADE.make_offset_filler(evasion_opts['pad_data'], (mlen-xlen)/2)
+ end
- self.smb_recv_parse(CONST::SMB_COM_TRANSACTION)
- end
+ # Squish the whole thing together
+ data = pipe + filler1 + param + filler2 + body
+ # Throw some form of a warning out?
+ if (data.length > mlen)
+ # XXX This call will more than likely fail :-(
+ end
+ # Calculate all of the offsets
+ base_offset = pkt.to_s.length + (setup_count * 2) - 4
+ param_offset = base_offset + pipe.length + filler1.length
+ data_offset = param_offset + filler2.length + param.length
- # Perform a transaction against a given pipe name
- # Difference from trans: sets MaxParam/MaxData to zero
- # This is required to trigger mailslot bug :-(
- def trans_maxzero(pipe, param = '', body = '', setup_count = 0, setup_data = '', no_response = false, do_recv = true)
+ pkt['Payload']['SMB'].v['Command'] = CONST::SMB_COM_TRANSACTION
+ pkt['Payload']['SMB'].v['Flags1'] = 0x18
+ if self.require_signing
+ #ascii
+ pkt['Payload']['SMB'].v['Flags2'] = 0x2807
+ else
+ #ascii
+ pkt['Payload']['SMB'].v['Flags2'] = 0x2801
+ end
- # Null-terminate the pipe parameter if needed
- if (pipe[-1] != 0)
- pipe << "\x00"
- end
+ pkt['Payload']['SMB'].v['WordCount'] = 14 + setup_count
- pkt = CONST::SMB_TRANS_PKT.make_struct
- self.smb_defaults(pkt['Payload']['SMB'])
+ pkt['Payload'].v['ParamCountTotal'] = param.length
+ pkt['Payload'].v['DataCountTotal'] = body.length
+ pkt['Payload'].v['ParamCountMax'] = 1024
+ pkt['Payload'].v['DataCountMax'] = 65000
+ pkt['Payload'].v['ParamCount'] = param.length
+ pkt['Payload'].v['ParamOffset'] = param_offset
+ pkt['Payload'].v['DataCount'] = body.length
+ pkt['Payload'].v['DataOffset'] = data_offset
+ pkt['Payload'].v['SetupCount'] = setup_count
+ pkt['Payload'].v['SetupData'] = setup_data
- # Packets larger than mlen will cause XP SP2 to disconnect us ;-(
- mlen = 4200
+ pkt['Payload'].v['Payload'] = data
- # Figure out how much space is taken up by our current arguments
- xlen = pipe.length + param.length + body.length
+ if no_response
+ pkt['Payload'].v['Flags'] = 2
+ end
- filler1 = ''
- filler2 = ''
+ ret = self.smb_send(pkt.to_s)
+ return ret if no_response or not do_recv
- # Fill any available space depending on the evasion settings
- if (xlen < mlen)
- filler1 = EVADE.make_offset_filler(evasion_opts['pad_data'], (mlen-xlen)/2)
- filler2 = EVADE.make_offset_filler(evasion_opts['pad_data'], (mlen-xlen)/2)
- end
+ self.smb_recv_parse(CONST::SMB_COM_TRANSACTION)
+ end
- # Squish the whole thing together
- data = pipe + filler1 + param + filler2 + body
- # Throw some form of a warning out?
- if (data.length > mlen)
- # XXX This call will more than likely fail :-(
- end
- # Calculate all of the offsets
- base_offset = pkt.to_s.length + (setup_count * 2) - 4
- param_offset = base_offset + pipe.length + filler1.length
- data_offset = param_offset + filler2.length + param.length
+ # Perform a transaction against a given pipe name
+ # Difference from trans: sets MaxParam/MaxData to zero
+ # This is required to trigger mailslot bug :-(
+ def trans_maxzero(pipe, param = '', body = '', setup_count = 0, setup_data = '', no_response = false, do_recv = true)
- pkt['Payload']['SMB'].v['Command'] = CONST::SMB_COM_TRANSACTION
- pkt['Payload']['SMB'].v['Flags1'] = 0x18
- if self.require_signing
- #ascii
- pkt['Payload']['SMB'].v['Flags2'] = 0x2807
- else
- #ascii
- pkt['Payload']['SMB'].v['Flags2'] = 0x2801
- end
+ # Null-terminate the pipe parameter if needed
+ if (pipe[-1] != 0)
+ pipe << "\x00"
+ end
- pkt['Payload']['SMB'].v['WordCount'] = 14 + setup_count
+ pkt = CONST::SMB_TRANS_PKT.make_struct
+ self.smb_defaults(pkt['Payload']['SMB'])
- pkt['Payload'].v['ParamCountTotal'] = param.length
- pkt['Payload'].v['DataCountTotal'] = body.length
- pkt['Payload'].v['ParamCountMax'] = 0
- pkt['Payload'].v['DataCountMax'] = 0
- pkt['Payload'].v['ParamCount'] = param.length
- pkt['Payload'].v['ParamOffset'] = param_offset
- pkt['Payload'].v['DataCount'] = body.length
- pkt['Payload'].v['DataOffset'] = data_offset
- pkt['Payload'].v['SetupCount'] = setup_count
- pkt['Payload'].v['SetupData'] = setup_data
+ # Packets larger than mlen will cause XP SP2 to disconnect us ;-(
+ mlen = 4200
- pkt['Payload'].v['Payload'] = data
+ # Figure out how much space is taken up by our current arguments
+ xlen = pipe.length + param.length + body.length
- if no_response
- pkt['Payload'].v['Flags'] = 2
- end
+ filler1 = ''
+ filler2 = ''
- ret = self.smb_send(pkt.to_s)
- return ret if no_response or not do_recv
+ # Fill any available space depending on the evasion settings
+ if (xlen < mlen)
+ filler1 = EVADE.make_offset_filler(evasion_opts['pad_data'], (mlen-xlen)/2)
+ filler2 = EVADE.make_offset_filler(evasion_opts['pad_data'], (mlen-xlen)/2)
+ end
- self.smb_recv_parse(CONST::SMB_COM_TRANSACTION)
- end
+ # Squish the whole thing together
+ data = pipe + filler1 + param + filler2 + body
+ # Throw some form of a warning out?
+ if (data.length > mlen)
+ # XXX This call will more than likely fail :-(
+ end
- # Perform a transaction against a given pipe name (no null terminator)
- def trans_nonull(pipe, param = '', body = '', setup_count = 0, setup_data = '', no_response = false, do_recv = true)
+ # Calculate all of the offsets
+ base_offset = pkt.to_s.length + (setup_count * 2) - 4
+ param_offset = base_offset + pipe.length + filler1.length
+ data_offset = param_offset + filler2.length + param.length
- 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
+ if self.require_signing
+ #ascii
+ pkt['Payload']['SMB'].v['Flags2'] = 0x2807
+ else
+ #ascii
+ pkt['Payload']['SMB'].v['Flags2'] = 0x2801
+ end
- # Packets larger than mlen will cause XP SP2 to disconnect us ;-(
- mlen = 4200
+ pkt['Payload']['SMB'].v['WordCount'] = 14 + setup_count
- # Figure out how much space is taken up by our current arguments
- xlen = pipe.length + param.length + body.length
+ pkt['Payload'].v['ParamCountTotal'] = param.length
+ pkt['Payload'].v['DataCountTotal'] = body.length
+ pkt['Payload'].v['ParamCountMax'] = 0
+ pkt['Payload'].v['DataCountMax'] = 0
+ pkt['Payload'].v['ParamCount'] = param.length
+ pkt['Payload'].v['ParamOffset'] = param_offset
+ pkt['Payload'].v['DataCount'] = body.length
+ pkt['Payload'].v['DataOffset'] = data_offset
+ pkt['Payload'].v['SetupCount'] = setup_count
+ pkt['Payload'].v['SetupData'] = setup_data
- filler1 = ''
- filler2 = ''
+ pkt['Payload'].v['Payload'] = data
- # Fill any available space depending on the evasion settings
- if (xlen < mlen)
- filler1 = EVADE.make_offset_filler(evasion_opts['pad_data'], (mlen-xlen)/2)
- filler2 = EVADE.make_offset_filler(evasion_opts['pad_data'], (mlen-xlen)/2)
- end
+ if no_response
+ pkt['Payload'].v['Flags'] = 2
+ end
- # Squish the whole thing together
- data = pipe + filler1 + param + filler2 + body
+ ret = self.smb_send(pkt.to_s)
+ return ret if no_response or not do_recv
- # Throw some form of a warning out?
- if (data.length > mlen)
- # XXX This call will more than likely fail :-(
- end
+ self.smb_recv_parse(CONST::SMB_COM_TRANSACTION)
+ end
- # Calculate all of the offsets
- base_offset = pkt.to_s.length + (setup_count * 2) - 4
- param_offset = base_offset + pipe.length + filler1.length
- data_offset = param_offset + filler2.length + param.length
- pkt['Payload']['SMB'].v['Command'] = CONST::SMB_COM_TRANSACTION
- pkt['Payload']['SMB'].v['Flags1'] = 0x18
- if self.require_signing
- #ascii
- pkt['Payload']['SMB'].v['Flags2'] = 0x2807
- else
- #ascii
- pkt['Payload']['SMB'].v['Flags2'] = 0x2801
- end
+ # Perform a transaction against a given pipe name (no null terminator)
+ def trans_nonull(pipe, param = '', body = '', setup_count = 0, setup_data = '', no_response = false, do_recv = true)
- pkt['Payload']['SMB'].v['WordCount'] = 14 + setup_count
+ pkt = CONST::SMB_TRANS_PKT.make_struct
+ self.smb_defaults(pkt['Payload']['SMB'])
- pkt['Payload'].v['ParamCountTotal'] = param.length
- pkt['Payload'].v['DataCountTotal'] = body.length
- pkt['Payload'].v['ParamCountMax'] = 0
- pkt['Payload'].v['DataCountMax'] = 0
- pkt['Payload'].v['ParamCount'] = param.length
- pkt['Payload'].v['ParamOffset'] = param_offset
- pkt['Payload'].v['DataCount'] = body.length
- pkt['Payload'].v['DataOffset'] = data_offset
- pkt['Payload'].v['SetupCount'] = setup_count
- pkt['Payload'].v['SetupData'] = setup_data
+ # Packets larger than mlen will cause XP SP2 to disconnect us ;-(
+ mlen = 4200
- pkt['Payload'].v['Payload'] = data
+ # Figure out how much space is taken up by our current arguments
+ xlen = pipe.length + param.length + body.length
- if no_response
- pkt['Payload'].v['Flags'] = 2
- end
+ filler1 = ''
+ filler2 = ''
- ret = self.smb_send(pkt.to_s)
- return ret if no_response or not do_recv
+ # Fill any available space depending on the evasion settings
+ if (xlen < mlen)
+ filler1 = EVADE.make_offset_filler(evasion_opts['pad_data'], (mlen-xlen)/2)
+ filler2 = EVADE.make_offset_filler(evasion_opts['pad_data'], (mlen-xlen)/2)
+ end
- self.smb_recv_parse(CONST::SMB_COM_TRANSACTION)
- end
+ # Squish the whole thing together
+ data = pipe + filler1 + param + filler2 + body
- # Perform a transaction2 request using the specified subcommand, parameters, and data
- def trans2(subcommand, param = '', body = '', do_recv = true)
+ # Throw some form of a warning out?
+ if (data.length > mlen)
+ # XXX This call will more than likely fail :-(
+ end
- setup_count = 1
- setup_data = [subcommand].pack('v')
+ # Calculate all of the offsets
+ base_offset = pkt.to_s.length + (setup_count * 2) - 4
+ param_offset = base_offset + pipe.length + filler1.length
+ data_offset = param_offset + filler2.length + param.length
- data = param + body
+ pkt['Payload']['SMB'].v['Command'] = CONST::SMB_COM_TRANSACTION
+ pkt['Payload']['SMB'].v['Flags1'] = 0x18
+ if self.require_signing
+ #ascii
+ pkt['Payload']['SMB'].v['Flags2'] = 0x2807
+ else
+ #ascii
+ pkt['Payload']['SMB'].v['Flags2'] = 0x2801
+ end
- pkt = CONST::SMB_TRANS2_PKT.make_struct
- self.smb_defaults(pkt['Payload']['SMB'])
+ pkt['Payload']['SMB'].v['WordCount'] = 14 + setup_count
- base_offset = pkt.to_s.length + (setup_count * 2) - 4
- param_offset = base_offset
- data_offset = param_offset + param.length
+ pkt['Payload'].v['ParamCountTotal'] = param.length
+ pkt['Payload'].v['DataCountTotal'] = body.length
+ pkt['Payload'].v['ParamCountMax'] = 0
+ pkt['Payload'].v['DataCountMax'] = 0
+ pkt['Payload'].v['ParamCount'] = param.length
+ pkt['Payload'].v['ParamOffset'] = param_offset
+ pkt['Payload'].v['DataCount'] = body.length
+ pkt['Payload'].v['DataOffset'] = data_offset
+ pkt['Payload'].v['SetupCount'] = setup_count
+ pkt['Payload'].v['SetupData'] = setup_data
- pkt['Payload']['SMB'].v['Command'] = CONST::SMB_COM_TRANSACTION2
- pkt['Payload']['SMB'].v['Flags1'] = 0x18
- if self.require_signing
- #ascii
- pkt['Payload']['SMB'].v['Flags2'] = 0x2807
- else
- #ascii
- pkt['Payload']['SMB'].v['Flags2'] = 0x2801
- end
+ pkt['Payload'].v['Payload'] = data
- pkt['Payload']['SMB'].v['WordCount'] = 14 + setup_count
+ if no_response
+ pkt['Payload'].v['Flags'] = 2
+ end
- pkt['Payload'].v['ParamCountTotal'] = param.length
- pkt['Payload'].v['DataCountTotal'] = body.length
- pkt['Payload'].v['ParamCountMax'] = 1024
- pkt['Payload'].v['DataCountMax'] = 65504
- pkt['Payload'].v['ParamCount'] = param.length
- pkt['Payload'].v['ParamOffset'] = param_offset
- pkt['Payload'].v['DataCount'] = body.length
- pkt['Payload'].v['DataOffset'] = data_offset
- pkt['Payload'].v['SetupCount'] = setup_count
- pkt['Payload'].v['SetupData'] = setup_data
+ ret = self.smb_send(pkt.to_s)
+ return ret if no_response or not do_recv
- pkt['Payload'].v['Payload'] = data
+ self.smb_recv_parse(CONST::SMB_COM_TRANSACTION)
+ end
- ret = self.smb_send(pkt.to_s)
- return ret if not do_recv
+ # Perform a transaction2 request using the specified subcommand, parameters, and data
+ def trans2(subcommand, param = '', body = '', do_recv = true)
- ack = self.smb_recv_parse(CONST::SMB_COM_TRANSACTION2)
+ setup_count = 1
+ setup_data = [subcommand].pack('v')
- return ack
- end
+ data = param + body
+ pkt = CONST::SMB_TRANS2_PKT.make_struct
+ self.smb_defaults(pkt['Payload']['SMB'])
- # Perform a nttransaction request using the specified subcommand, parameters, and data
- def nttrans(subcommand, param = '', body = '', setup_count = 0, setup_data = '', do_recv = true)
+ base_offset = pkt.to_s.length + (setup_count * 2) - 4
+ param_offset = base_offset
+ data_offset = param_offset + param.length
- data = param + body
+ pkt['Payload']['SMB'].v['Command'] = CONST::SMB_COM_TRANSACTION2
+ pkt['Payload']['SMB'].v['Flags1'] = 0x18
+ if self.require_signing
+ #ascii
+ pkt['Payload']['SMB'].v['Flags2'] = 0x2807
+ else
+ #ascii
+ pkt['Payload']['SMB'].v['Flags2'] = 0x2801
+ end
- pkt = CONST::SMB_NTTRANS_PKT.make_struct
- self.smb_defaults(pkt['Payload']['SMB'])
+ pkt['Payload']['SMB'].v['WordCount'] = 14 + setup_count
- base_offset = pkt.to_s.length + (setup_count * 2) - 4
- param_offset = base_offset
- data_offset = param_offset + param.length
+ pkt['Payload'].v['ParamCountTotal'] = param.length
+ pkt['Payload'].v['DataCountTotal'] = body.length
+ pkt['Payload'].v['ParamCountMax'] = 1024
+ pkt['Payload'].v['DataCountMax'] = 65000
+ pkt['Payload'].v['ParamCount'] = param.length
+ pkt['Payload'].v['ParamOffset'] = param_offset
+ pkt['Payload'].v['DataCount'] = body.length
+ pkt['Payload'].v['DataOffset'] = data_offset
+ pkt['Payload'].v['SetupCount'] = setup_count
+ pkt['Payload'].v['SetupData'] = setup_data
- pkt['Payload']['SMB'].v['Command'] = CONST::SMB_COM_NT_TRANSACT
- pkt['Payload']['SMB'].v['Flags1'] = 0x18
- if self.require_signing
- #ascii
- pkt['Payload']['SMB'].v['Flags2'] = 0x2807
- else
- #ascii
- pkt['Payload']['SMB'].v['Flags2'] = 0x2801
- end
+ pkt['Payload'].v['Payload'] = data
- pkt['Payload']['SMB'].v['WordCount'] = 19 + setup_count
+ ret = self.smb_send(pkt.to_s)
+ return ret if not do_recv
- pkt['Payload'].v['ParamCountTotal'] = param.length
- pkt['Payload'].v['DataCountTotal'] = body.length
- pkt['Payload'].v['ParamCountMax'] = 1024
- pkt['Payload'].v['DataCountMax'] = 65504
- pkt['Payload'].v['ParamCount'] = param.length
- pkt['Payload'].v['ParamOffset'] = param_offset
- pkt['Payload'].v['DataCount'] = body.length
- pkt['Payload'].v['DataOffset'] = data_offset
- pkt['Payload'].v['SetupCount'] = setup_count
- pkt['Payload'].v['SetupData'] = setup_data
- pkt['Payload'].v['Subcommand'] = subcommand
+ ack = self.smb_recv_parse(CONST::SMB_COM_TRANSACTION2)
- pkt['Payload'].v['Payload'] = data
+ return ack
+ end
- ret = self.smb_send(pkt.to_s)
- return ret if not do_recv
- ack = self.smb_recv_parse(CONST::SMB_COM_NT_TRANSACT)
- return ack
- end
+ # Perform a nttransaction request using the specified subcommand, parameters, and data
+ def nttrans(subcommand, param = '', body = '', setup_count = 0, setup_data = '', do_recv = true)
- # Perform a nttransaction request using the specified subcommand, parameters, and data
- def nttrans_secondary(param = '', body = '', do_recv = true)
+ data = param + body
- data = param + body
+ pkt = CONST::SMB_NTTRANS_PKT.make_struct
+ self.smb_defaults(pkt['Payload']['SMB'])
- pkt = CONST::SMB_NTTRANS_SECONDARY_PKT.make_struct
- self.smb_defaults(pkt['Payload']['SMB'])
+ base_offset = pkt.to_s.length + (setup_count * 2) - 4
+ param_offset = base_offset
+ data_offset = param_offset + param.length
- base_offset = pkt.to_s.length - 4
- param_offset = base_offset
- data_offset = param_offset + param.length
+ pkt['Payload']['SMB'].v['Command'] = CONST::SMB_COM_NT_TRANSACT
+ pkt['Payload']['SMB'].v['Flags1'] = 0x18
+ if self.require_signing
+ #ascii
+ pkt['Payload']['SMB'].v['Flags2'] = 0x2807
+ else
+ #ascii
+ pkt['Payload']['SMB'].v['Flags2'] = 0x2801
+ end
- pkt['Payload']['SMB'].v['Command'] = CONST::SMB_COM_NT_TRANSACT_SECONDARY
- pkt['Payload']['SMB'].v['Flags1'] = 0x18
- if self.require_signing
- #ascii
- pkt['Payload']['SMB'].v['Flags2'] = 0x2807
- else
- #ascii
- pkt['Payload']['SMB'].v['Flags2'] = 0x2801
- end
+ pkt['Payload']['SMB'].v['WordCount'] = 19 + setup_count
- pkt['Payload']['SMB'].v['WordCount'] = 18
+ pkt['Payload'].v['ParamCountTotal'] = param.length
+ pkt['Payload'].v['DataCountTotal'] = body.length
+ pkt['Payload'].v['ParamCountMax'] = 1024
+ pkt['Payload'].v['DataCountMax'] = 65000
+ pkt['Payload'].v['ParamCount'] = param.length
+ pkt['Payload'].v['ParamOffset'] = param_offset
+ pkt['Payload'].v['DataCount'] = body.length
+ pkt['Payload'].v['DataOffset'] = data_offset
+ pkt['Payload'].v['SetupCount'] = setup_count
+ pkt['Payload'].v['SetupData'] = setup_data
+ pkt['Payload'].v['Subcommand'] = subcommand
- pkt['Payload'].v['ParamCountTotal'] = param.length
- pkt['Payload'].v['DataCountTotal'] = body.length
- pkt['Payload'].v['ParamCount'] = param.length
- pkt['Payload'].v['ParamOffset'] = param_offset
- pkt['Payload'].v['DataCount'] = body.length
- pkt['Payload'].v['DataOffset'] = data_offset
+ pkt['Payload'].v['Payload'] = data
- pkt['Payload'].v['Payload'] = data
+ ret = self.smb_send(pkt.to_s)
+ return ret if not do_recv
- ret = self.smb_send(pkt.to_s)
- return ret if not do_recv
+ ack = self.smb_recv_parse(CONST::SMB_COM_NT_TRANSACT)
+ return ack
+ end
- ack = self.smb_recv_parse(CONST::SMB_COM_NT_TRANSACT_SECONDARY)
- return ack
- end
+ # Perform a nttransaction request using the specified subcommand, parameters, and data
+ def nttrans_secondary(param = '', body = '', do_recv = true)
- def queryfs(level)
- parm = [level].pack('v')
+ data = param + body
- begin
- resp = trans2(CONST::TRANS2_QUERY_FS_INFO, parm, '')
+ pkt = CONST::SMB_NTTRANS_SECONDARY_PKT.make_struct
+ self.smb_defaults(pkt['Payload']['SMB'])
- pcnt = resp['Payload'].v['ParamCount']
- dcnt = resp['Payload'].v['DataCount']
- poff = resp['Payload'].v['ParamOffset']
- doff = resp['Payload'].v['DataOffset']
+ base_offset = pkt.to_s.length - 4
+ param_offset = base_offset
+ data_offset = param_offset + param.length
- # Get the raw packet bytes
- resp_rpkt = resp.to_s
+ pkt['Payload']['SMB'].v['Command'] = CONST::SMB_COM_NT_TRANSACT_SECONDARY
+ pkt['Payload']['SMB'].v['Flags1'] = 0x18
+ if self.require_signing
+ #ascii
+ pkt['Payload']['SMB'].v['Flags2'] = 0x2807
+ else
+ #ascii
+ pkt['Payload']['SMB'].v['Flags2'] = 0x2801
+ end
- # Remove the NetBIOS header
- resp_rpkt.slice!(0, 4)
+ pkt['Payload']['SMB'].v['WordCount'] = 18
- resp_parm = resp_rpkt[poff, pcnt]
- resp_data = resp_rpkt[doff, dcnt]
- return resp_data
+ pkt['Payload'].v['ParamCountTotal'] = param.length
+ pkt['Payload'].v['DataCountTotal'] = body.length
+ pkt['Payload'].v['ParamCount'] = param.length
+ pkt['Payload'].v['ParamOffset'] = param_offset
+ pkt['Payload'].v['DataCount'] = body.length
+ pkt['Payload'].v['DataOffset'] = data_offset
- rescue ::Exception
- raise $!
- end
- end
+ pkt['Payload'].v['Payload'] = data
- def symlink(src,dst)
- parm = [513, 0x00000000].pack('vV') + src + "\x00"
+ ret = self.smb_send(pkt.to_s)
+ return ret if not do_recv
- begin
- resp = trans2(CONST::TRANS2_SET_PATH_INFO, parm, dst + "\x00")
+ ack = self.smb_recv_parse(CONST::SMB_COM_NT_TRANSACT_SECONDARY)
+ return ack
+ end
- pcnt = resp['Payload'].v['ParamCount']
- dcnt = resp['Payload'].v['DataCount']
- poff = resp['Payload'].v['ParamOffset']
- doff = resp['Payload'].v['DataOffset']
+ def queryfs(level)
+ parm = [level].pack('v')
- # Get the raw packet bytes
- resp_rpkt = resp.to_s
+ begin
+ resp = trans2(CONST::TRANS2_QUERY_FS_INFO, parm, '')
- # Remove the NetBIOS header
- resp_rpkt.slice!(0, 4)
+ pcnt = resp['Payload'].v['ParamCount']
+ dcnt = resp['Payload'].v['DataCount']
+ poff = resp['Payload'].v['ParamOffset']
+ doff = resp['Payload'].v['DataOffset']
- resp_parm = resp_rpkt[poff, pcnt]
- resp_data = resp_rpkt[doff, dcnt]
- return resp_data
+ # Get the raw packet bytes
+ resp_rpkt = resp.to_s
- rescue ::Exception
- raise $!
- end
- end
+ # Remove the NetBIOS header
+ resp_rpkt.slice!(0, 4)
- # Obtains allocation information on the mounted tree
- def queryfs_info_allocation
- data = queryfs(CONST::SMB_INFO_ALLOCATION)
- head = %w{fs_id sectors_per_unit unit_total units_available bytes_per_sector}
- vals = data.unpack('VVVVv')
- info = { }
- head.each_index {|i| info[head[i]]=vals[i]}
- return info
- end
+ resp_parm = resp_rpkt[poff, pcnt]
+ resp_data = resp_rpkt[doff, dcnt]
+ return resp_data
- # Obtains volume information on the mounted tree
- def queryfs_info_volume
- data = queryfs(CONST::SMB_INFO_VOLUME)
- vals = data.unpack('VCA*')
- return {
- 'serial' => vals[0],
- 'label' => vals[2][0,vals[1]].gsub("\x00", '')
- }
- end
+ rescue ::Exception
+ raise $!
+ end
+ end
- # Obtains file system volume information on the mounted tree
- def queryfs_fs_volume
- data = queryfs(CONST::SMB_QUERY_FS_VOLUME_INFO)
- vals = data.unpack('VVVVCCA*')
- return {
- 'create_time' => (vals[1] << 32) + vals[0],
- 'serial' => vals[2],
- 'label' => vals[6][0,vals[3]].gsub("\x00", '')
- }
- end
+ def symlink(src,dst)
+ parm = [513, 0x00000000].pack('vV') + src + "\x00"
- # Obtains file system size information on the mounted tree
- def queryfs_fs_size
- data = queryfs(CONST::SMB_QUERY_FS_SIZE_INFO)
- vals = data.unpack('VVVVVV')
- return {
- 'total_alloc_units' => (vals[1] << 32) + vals[0],
- 'total_free_units' => (vals[3] << 32) + vals[2],
- 'sectors_per_unit' => vals[4],
- 'bytes_per_sector' => vals[5]
- }
- end
+ begin
+ resp = trans2(CONST::TRANS2_SET_PATH_INFO, parm, dst + "\x00")
- # Obtains file system device information on the mounted tree
- def queryfs_fs_device
- data = queryfs(CONST::SMB_QUERY_FS_DEVICE_INFO)
- vals = data.unpack('VV')
- return {
- 'device_type' => vals[0],
- 'device_chars' => vals[1],
- }
- end
+ pcnt = resp['Payload'].v['ParamCount']
+ dcnt = resp['Payload'].v['DataCount']
+ poff = resp['Payload'].v['ParamOffset']
+ doff = resp['Payload'].v['DataOffset']
- # Obtains file system attribute information on the mounted tree
- def queryfs_fs_attribute
- data = queryfs(CONST::SMB_QUERY_FS_ATTRIBUTE_INFO)
- vals = data.unpack('VVVA*')
- return {
- 'fs_attributes' => vals[0],
- 'max_file_name' => vals[1],
- 'fs_name' => vals[3][0, vals[2]].gsub("\x00", '')
- }
- end
+ # Get the raw packet bytes
+ resp_rpkt = resp.to_s
- # Enumerates a specific path on the mounted tree
- def find_first(path)
- files = { }
- parm = [
- 26, # Search for ALL files
- 512, # Maximum search count
- 6, # Resume and Close on End of Search
- 260, # Level of interest
- 0, # Storage type is zero
- ].pack('vvvvV') + path + "\x00"
+ # Remove the NetBIOS header
+ resp_rpkt.slice!(0, 4)
- begin
- resp = trans2(CONST::TRANS2_FIND_FIRST2, parm, '')
+ resp_parm = resp_rpkt[poff, pcnt]
+ resp_data = resp_rpkt[doff, dcnt]
+ return resp_data
- pcnt = resp['Payload'].v['ParamCount']
- dcnt = resp['Payload'].v['DataCount']
- poff = resp['Payload'].v['ParamOffset']
- doff = resp['Payload'].v['DataOffset']
+ rescue ::Exception
+ raise $!
+ end
+ end
- # Get the raw packet bytes
- resp_rpkt = resp.to_s
+ # Obtains allocation information on the mounted tree
+ def queryfs_info_allocation
+ data = queryfs(CONST::SMB_INFO_ALLOCATION)
+ head = %w{fs_id sectors_per_unit unit_total units_available bytes_per_sector}
+ vals = data.unpack('VVVVv')
+ info = { }
+ head.each_index {|i| info[head[i]]=vals[i]}
+ return info
+ end
- # Remove the NetBIOS header
- resp_rpkt.slice!(0, 4)
+ # Obtains volume information on the mounted tree
+ def queryfs_info_volume
+ data = queryfs(CONST::SMB_INFO_VOLUME)
+ vals = data.unpack('VCA*')
+ return {
+ 'serial' => vals[0],
+ 'label' => vals[2][0,vals[1]].gsub("\x00", '')
+ }
+ end
- resp_parm = resp_rpkt[poff, pcnt]
- resp_data = resp_rpkt[doff, dcnt]
+ # Obtains file system volume information on the mounted tree
+ def queryfs_fs_volume
+ data = queryfs(CONST::SMB_QUERY_FS_VOLUME_INFO)
+ vals = data.unpack('VVVVCCA*')
+ return {
+ 'create_time' => (vals[1] << 32) + vals[0],
+ 'serial' => vals[2],
+ 'label' => vals[6][0,vals[3]].gsub("\x00", '')
+ }
+ end
- # search id, search count, end of search, error offset, last name offset
- sid, scnt, eos, eoff, loff = resp_parm.unpack('v5')
+ # Obtains file system size information on the mounted tree
+ def queryfs_fs_size
+ data = queryfs(CONST::SMB_QUERY_FS_SIZE_INFO)
+ vals = data.unpack('VVVVVV')
+ return {
+ 'total_alloc_units' => (vals[1] << 32) + vals[0],
+ 'total_free_units' => (vals[3] << 32) + vals[2],
+ 'sectors_per_unit' => vals[4],
+ 'bytes_per_sector' => vals[5]
+ }
+ end
- didx = 0
- while (didx < resp_data.length)
- info_buff = resp_data[didx, 70]
- break if info_buff.length != 70
- info = info_buff.unpack(
- 'V'+ # Next Entry Offset
- 'V'+ # File Index
- 'VV'+ # Time Create
- 'VV'+ # Time Last Access
- 'VV'+ # Time Last Write
- 'VV'+ # Time Change
- 'VV'+ # End of File
- 'VV'+ # Allocation Size
- 'V'+ # File Attributes
- 'V'+ # File Name Length
- 'V'+ # Extended Attr List Length
- 'C'+ # Short File Name Length
- 'C' # Reserved
- )
- name = resp_data[didx + 70 + 24, info[15]].sub!(/\x00+$/, '')
- files[name] =
- {
- 'type' => (info[14] & 0x10) ? 'D' : 'F',
- 'attr' => info[14],
- 'info' => info
- }
+ # Obtains file system device information on the mounted tree
+ def queryfs_fs_device
+ data = queryfs(CONST::SMB_QUERY_FS_DEVICE_INFO)
+ vals = data.unpack('VV')
+ return {
+ 'device_type' => vals[0],
+ 'device_chars' => vals[1],
+ }
+ end
- break if info[0] == 0
- didx += info[0]
- end
+ # Obtains file system attribute information on the mounted tree
+ def queryfs_fs_attribute
+ data = queryfs(CONST::SMB_QUERY_FS_ATTRIBUTE_INFO)
+ vals = data.unpack('VVVA*')
+ return {
+ 'fs_attributes' => vals[0],
+ 'max_file_name' => vals[1],
+ 'fs_name' => vals[3][0, vals[2]].gsub("\x00", '')
+ }
+ end
- last_search_id = sid
+ # Enumerates a specific path on the mounted tree
+ def find_first(path)
+ files = { }
+ parm = [
+ 26, # Search for ALL files
+ 20, # Maximum search count
+ 6, # Resume and Close on End of Search
+ 260, # Level of interest
+ 0, # Storage type is zero
+ ].pack('vvvvV') + path + "\x00"
- rescue ::Exception
- raise $!
- end
+ resp = trans2(CONST::TRANS2_FIND_FIRST2, parm, '')
+ search_next = 0
- return files
- end
+ # Loop until we run out of results
+ loop do
+ pcnt = resp['Payload'].v['ParamCount']
+ dcnt = resp['Payload'].v['DataCount']
+ poff = resp['Payload'].v['ParamOffset']
+ doff = resp['Payload'].v['DataOffset']
- # TODO: Finish this method... requires search_id, resume_key, and filename from first
-=begin
- def find_next(path, sid = last_search_id)
+ # Get the raw packet bytes
+ resp_rpkt = resp.to_s
- parm = [
- sid, # Search ID
- 512, # Maximum search count
- 260, # Level of interest
- 0, # Resume key from previous
- 1, # Close search if end of search
- ].pack('vvvVv') + path + "\x00"
+ # Remove the NetBIOS header
+ resp_rpkt.slice!(0, 4)
- return files
- end
-=end
+ resp_parm = resp_rpkt[poff, pcnt]
+ resp_data = resp_rpkt[doff, dcnt]
- # Creates a new directory on the mounted tree
- def create_directory(name)
- files = { }
- parm = [0].pack('V') + name + "\x00"
- resp = trans2(CONST::TRANS2_CREATE_DIRECTORY, parm, '')
- end
+ if search_next == 0
+ # search id, search count, end of search, error offset, last name offset
+ sid, scnt, eos, eoff, loff = resp_parm.unpack('v5')
+ else
+ # FIND_NEXT doesn't return a SID
+ scnt, eos, eoff, loff = resp_parm.unpack('v4')
+ end
+ didx = 0
+ while (didx < resp_data.length)
+ info_buff = resp_data[didx, 70]
+ break if info_buff.length != 70
+
+ info = info_buff.unpack(
+ 'V'+ # Next Entry Offset
+ 'V'+ # File Index
+ 'VV'+ # Time Create
+ 'VV'+ # Time Last Access
+ 'VV'+ # Time Last Write
+ 'VV'+ # Time Change
+ 'VV'+ # End of File
+ 'VV'+ # Allocation Size
+ 'V'+ # File Attributes
+ 'V'+ # File Name Length
+ 'V'+ # Extended Attr List Length
+ 'C'+ # Short File Name Length
+ 'C' # Reserved
+ )
+
+ name = resp_data[didx + 70 + 24, info[15]]
+
+ # Verify that the filename was actually present
+ break unless name
+
+ # Key the file list minus any trailing nulls
+ files[name.sub(/\x00+$/n, '')] =
+ {
+ 'type' => ( info[14] & CONST::SMB_EXT_FILE_ATTR_DIRECTORY == 0 ) ? 'F' : 'D',
+ 'attr' => info[14],
+ 'info' => info
+ }
+
+ break if info[0] == 0
+ didx += info[0]
+ end
+
+ last_search_id = sid
+ last_offset = loff
+ last_filename = name
+
+ # Exit the search if we reached the end of our results
+ break if (eos != 0 or last_search_id.nil? or last_offset.to_i == 0)
+
+ # If we aren't at the end of the search, run find_next
+ resp = find_next(last_search_id, last_offset, last_filename)
+
+ # Flip bit so response params will parse correctly
+ search_next = 1
+ end
+
+ files
+ end
+
+ # Supplements find_first if file/dir count exceeds max search count
+ def find_next(sid, resume_key, last_filename)
+
+ parm = [
+ sid, # Search ID
+ 20, # Maximum search count (Size of 20 keeps response to 1 packet)
+ 260, # Level of interest
+ resume_key, # Resume key from previous (Last name offset)
+ 6, # Close search if end of search
+ ].pack('vvvVv') +
+ last_filename.to_s + # Last filename returned from find_first or find_next
+ "\x00" # Terminate the file name
+
+ # Returns the FIND_NEXT2 response packet for parsing by the find_first function
+ trans2(CONST::TRANS2_FIND_NEXT2, parm, '')
+ end
+
+ # Recursively search for files matching a regular expression
+ def file_search(current_path, regex, depth)
+ depth -= 1
+ return [] if depth < 0
+
+ results = find_first(current_path + "*")
+ files = []
+
+ results.each_pair do |fname, finfo|
+
+ # Skip current and parent directory results
+ next if %W{. ..}.include?(fname)
+
+ # Verify the results contain an attribute
+ next unless finfo and finfo['attr']
+
+ if finfo['attr'] & CONST::SMB_EXT_FILE_ATTR_DIRECTORY == 0
+ # Add any matching files to our result set
+ files << "#{current_path}#{fname}" if fname =~ regex
+ else
+ # Recurse into the discovery subdirectory for more files
+ begin
+ search_path = "#{current_path}#{fname}\\"
+ file_search(search_path, regex, depth).each {|fn| files << fn }
+ rescue Rex::Proto::SMB::Exceptions::ErrorCode => e
+
+ # Ignore common errors related to permissions and non-files
+ if %W{
+ STATUS_ACCESS_DENIED
+ STATUS_NO_SUCH_FILE
+ STATUS_OBJECT_NAME_NOT_FOUND
+ STATUS_OBJECT_PATH_NOT_FOUND
+ }.include? e.get_error(e.error_code)
+ next
+ end
+
+ $stderr.puts [e, e.get_error(e.error_code), search_path]
+
+ raise e
+ end
+ end
+
+ end
+
+ files.uniq
+ end
+
+ # Creates a new directory on the mounted tree
+ def create_directory(name)
+ files = { }
+ parm = [0].pack('V') + name + "\x00"
+ resp = trans2(CONST::TRANS2_CREATE_DIRECTORY, parm, '')
+ end
+
# public read/write methods
- attr_accessor :native_os, :native_lm, :encrypt_passwords, :extended_security, :read_timeout, :evasion_opts
- attr_accessor :verify_signature, :use_ntlmv2, :usentlm2_session, :send_lm, :use_lanman_key, :send_ntlm
- attr_accessor :system_time, :system_zone
- #misc
- attr_accessor :spnopt # used for SPN
+ attr_accessor :native_os, :native_lm, :encrypt_passwords, :extended_security, :read_timeout, :evasion_opts
+ attr_accessor :verify_signature, :use_ntlmv2, :usentlm2_session, :send_lm, :use_lanman_key, :send_ntlm
+ attr_accessor :system_time, :system_zone
+ attr_accessor :spnopt
# public read methods
- attr_reader :dialect, :session_id, :challenge_key, :peer_native_lm, :peer_native_os
- attr_reader :default_domain, :default_name, :auth_user, :auth_user_id
- attr_reader :multiplex_id, :last_tree_id, :last_file_id, :process_id, :last_search_id
- attr_reader :dns_host_name, :dns_domain_name
- attr_reader :security_mode, :server_guid
- #signing related
- attr_reader :sequence_counter,:signing_key, :require_signing
+ attr_reader :dialect, :session_id, :challenge_key, :peer_native_lm, :peer_native_os
+ attr_reader :default_domain, :default_name, :auth_user, :auth_user_id
+ attr_reader :multiplex_id, :last_tree_id, :last_file_id, :process_id, :last_search_id
+ attr_reader :dns_host_name, :dns_domain_name
+ attr_reader :security_mode, :server_guid
+ attr_reader :sequence_counter,:signing_key, :require_signing
-# private methods
- attr_writer :dialect, :session_id, :challenge_key, :peer_native_lm, :peer_native_os
- attr_writer :default_domain, :default_name, :auth_user, :auth_user_id
- attr_writer :dns_host_name, :dns_domain_name
- attr_writer :multiplex_id, :last_tree_id, :last_file_id, :process_id, :last_search_id
- attr_writer :security_mode, :server_guid
- #signing related
- attr_writer :sequence_counter,:signing_key, :require_signing
+# private write methods
+ attr_writer :dialect, :session_id, :challenge_key, :peer_native_lm, :peer_native_os
+ attr_writer :default_domain, :default_name, :auth_user, :auth_user_id
+ attr_writer :dns_host_name, :dns_domain_name
+ attr_writer :multiplex_id, :last_tree_id, :last_file_id, :process_id, :last_search_id
+ attr_writer :security_mode, :server_guid
+ attr_writer :sequence_counter,:signing_key, :require_signing
- attr_accessor :socket
-
+ attr_accessor :socket
end
end
end
end