Skip to content

Commit 62fa143

Browse files
committed
Merge branch 'upstream/master' into multi-transport-support
Merged with HD's stuff as he fixed up a few things that I had done too. Conflicts: lib/msf/base/sessions/meterpreter_options.rb lib/rex/post/meterpreter/client_core.rb lib/rex/post/meterpreter/packet_dispatcher.rb
2 parents 2321171 + c540ba4 commit 62fa143

File tree

7 files changed

+147
-41
lines changed

7 files changed

+147
-41
lines changed

lib/msf/base/serializer/readable_text.rb

Lines changed: 56 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -527,6 +527,8 @@ def self.dump_sessions(framework, opts={})
527527
indent = opts[:indent] || DefaultIndent
528528
col = opts[:col] || DefaultColumnWrap
529529

530+
return dump_sessions_verbose(framework, opts) if verbose
531+
530532
columns =
531533
[
532534
'Id',
@@ -535,9 +537,6 @@ def self.dump_sessions(framework, opts={})
535537
'Connection'
536538
]
537539

538-
columns << 'Via' if verbose
539-
columns << 'PayloadId' if verbose
540-
541540
tbl = Rex::Ui::Text::Table.new(
542541
'Indent' => indent,
543542
'Header' => "Active sessions",
@@ -554,12 +553,7 @@ def self.dump_sessions(framework, opts={})
554553

555554
row = [ session.sid.to_s, session.type.to_s, sinfo, session.tunnel_to_s + " (#{session.session_host})" ]
556555
if session.respond_to? :platform
557-
row[1] += " " + session.platform
558-
end
559-
560-
if verbose
561-
row << session.via_exploit.to_s
562-
row << session.payload_uuid.to_s
556+
row[1] << (" " + session.platform)
563557
end
564558

565559
tbl << row
@@ -568,6 +562,59 @@ def self.dump_sessions(framework, opts={})
568562
return framework.sessions.length > 0 ? tbl.to_s : "#{tbl.header_to_s}No active sessions.\n"
569563
end
570564

565+
# Dumps the list of active sessions in verbose mode
566+
#
567+
# @param framework [Msf::Framework] the framework to dump.
568+
# @param opts [Hash] the options to dump with.
569+
# @option opts :session_ids [Array] the list of sessions to dump (no
570+
# effect).
571+
# @return [String] the formatted list of sessions.
572+
def self.dump_sessions_verbose(framework, opts={})
573+
ids = (opts[:session_ids] || framework.sessions.keys).sort
574+
575+
out = "Active sessions\n" +
576+
"===============\n\n"
577+
578+
if framework.sessions.length == 0
579+
out << "No active sessions.\n"
580+
return out
581+
end
582+
583+
framework.sessions.each_sorted do |k|
584+
session = framework.sessions[k]
585+
586+
sess_info = session.info.to_s
587+
sess_id = session.sid.to_s
588+
sess_tunnel = session.tunnel_to_s + " (#{session.session_host})"
589+
sess_via = session.via_exploit.to_s
590+
sess_type = session.type.to_s
591+
sess_uuid = session.payload_uuid.to_s
592+
sess_checkin = "<none>"
593+
sess_machine_id = session.machine_id.to_s
594+
595+
if session.respond_to? :platform
596+
sess_type << (" " + session.platform)
597+
end
598+
599+
if session.respond_to?(:last_checkin) && session.last_checkin
600+
sess_checkin = "#{(Time.now.to_i - session.last_checkin.to_i)}s ago @ #{session.last_checkin.to_s}"
601+
end
602+
603+
out << " Session ID: #{sess_id}\n"
604+
out << " Type: #{sess_type}\n"
605+
out << " Info: #{sess_info}\n"
606+
out << " Tunnel: #{sess_tunnel}\n"
607+
out << " Via: #{sess_via}\n"
608+
out << " UUID: #{sess_uuid}\n"
609+
out << " MachineID: #{sess_machine_id}\n"
610+
out << " CheckIn: #{sess_checkin}\n"
611+
out << "\n"
612+
end
613+
614+
out << "\n"
615+
return out
616+
end
617+
571618
# Dumps the list of running jobs.
572619
#
573620
# @param framework [Msf::Framework] the framework.

lib/msf/base/sessions/meterpreter.rb

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -255,9 +255,10 @@ def reset_ui
255255
def kill
256256
begin
257257
cleanup_meterpreter
258-
self.sock.close
258+
self.sock.close if self.sock
259259
rescue ::Exception
260260
end
261+
# deregister will actually trigger another cleanup
261262
framework.sessions.deregister(self)
262263
end
263264

@@ -298,6 +299,24 @@ def load_priv()
298299
console.disable_output = original
299300
end
300301

302+
#
303+
# Validate session information by checking for a machine_id response
304+
#
305+
def is_valid_session?(timeout=10)
306+
return true if self.machine_id
307+
308+
begin
309+
self.machine_id = self.core.machine_id(timeout)
310+
return true
311+
rescue ::Rex::Post::Meterpreter::RequestError
312+
# This meterpreter doesn't support core_machine_id
313+
return true
314+
rescue ::Exception => e
315+
dlog("Session #{self.sid} did not respond to validation request #{e.class}: #{e}")
316+
end
317+
false
318+
end
319+
301320
#
302321
# Populate the session information.
303322
#
@@ -452,6 +471,7 @@ def create(param)
452471
attr_accessor :binary_suffix
453472
attr_accessor :console # :nodoc:
454473
attr_accessor :skip_ssl
474+
attr_accessor :skip_cleanup
455475
attr_accessor :target_id
456476

457477
protected

lib/msf/base/sessions/meterpreter_options.rb

Lines changed: 35 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ def initialize(info = {})
1212
register_advanced_options(
1313
[
1414
OptBool.new('AutoLoadStdapi', [true, "Automatically load the Stdapi extension", true]),
15+
OptBool.new('AutoVerifySession', [true, "Automatically verify and drop invalid sessions", true]),
1516
OptString.new('InitialAutoRunScript', [false, "An initial script to run on session creation (before AutoRunScript)", '']),
1617
OptString.new('AutoRunScript', [false, "A script to run automatically on session creation.", '']),
1718
OptBool.new('AutoSystemInfo', [true, "Automatically capture system information on initialization.", true]),
@@ -39,31 +40,49 @@ def on_session(session)
3940

4041
session.init_ui(self.user_input, self.user_output)
4142

42-
if (datastore['AutoLoadStdapi'] == true)
43+
valid = true
4344

44-
session.load_stdapi
45+
if datastore['AutoVerifySession'] == true
46+
if not session.is_valid_session?
47+
print_error("Meterpreter session #{session.sid} is not valid and will be closed")
48+
valid = false
49+
end
50+
end
51+
52+
if valid
53+
54+
if datastore['AutoLoadStdapi'] == true
4555

46-
if datastore['AutoSystemInfo']
47-
session.load_session_info
56+
session.load_stdapi
57+
58+
if datastore['AutoSystemInfo']
59+
session.load_session_info
60+
end
61+
62+
if session.platform =~ /win32|win64/i
63+
session.load_priv rescue nil
64+
end
4865
end
4966

50-
if session.platform =~ /win32|win64/i
51-
session.load_priv rescue nil
67+
if session.platform =~ /android/i
68+
if datastore['AutoLoadAndroid']
69+
session.load_android
70+
end
5271
end
53-
end
5472

55-
if session.platform =~ /android/i
56-
if datastore['AutoLoadAndroid']
57-
session.load_android
73+
[ 'InitialAutoRunScript', 'AutoRunScript' ].each do |key|
74+
if (datastore[key].empty? == false)
75+
args = Shellwords.shellwords( datastore[key] )
76+
print_status("Session ID #{session.sid} (#{session.tunnel_to_s}) processing #{key} '#{datastore[key]}'")
77+
session.execute_script(args.shift, *args)
78+
end
5879
end
5980
end
6081

61-
[ 'InitialAutoRunScript', 'AutoRunScript' ].each do |key|
62-
if (datastore[key].empty? == false)
63-
args = Shellwords.shellwords( datastore[key] )
64-
print_status("Session ID #{session.sid} (#{session.tunnel_to_s}) processing #{key} '#{datastore[key]}'")
65-
session.execute_script(args.shift, *args)
66-
end
82+
# Terminate the session without cleanup if it did not validate
83+
if not valid
84+
session.skip_cleanup = true
85+
session.kill
6786
end
6887

6988
}

lib/msf/core/session.rb

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -377,10 +377,6 @@ def alive?
377377
#
378378
attr_accessor :info
379379
#
380-
# The identifier for the machine of the current session
381-
#
382-
attr_accessor :machine_id
383-
#
384380
# The unique identifier of this session
385381
#
386382
attr_accessor :uuid
@@ -393,6 +389,10 @@ def alive?
393389
#
394390
attr_accessor :payload_uuid
395391
#
392+
# The unique machine identifier for the host that created this session
393+
#
394+
attr_accessor :machine_id
395+
#
396396
# The actual exploit module instance that created this session
397397
#
398398
attr_accessor :exploit

lib/rex/post/meterpreter/client.rb

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -85,11 +85,18 @@ def initialize(sock,opts={})
8585
# Cleans up the meterpreter instance, terminating the dispatcher thread.
8686
#
8787
def cleanup_meterpreter
88-
ext.aliases.each_value do | extension |
89-
extension.cleanup if extension.respond_to?( 'cleanup' )
88+
if not self.skip_cleanup
89+
ext.aliases.each_value do | extension |
90+
extension.cleanup if extension.respond_to?( 'cleanup' )
91+
end
9092
end
93+
9194
dispatcher_thread.kill if dispatcher_thread
92-
core.shutdown rescue nil
95+
96+
if not self.skip_cleanup
97+
core.shutdown rescue nil
98+
end
99+
93100
shutdown_passive_dispatcher
94101
end
95102

@@ -474,6 +481,10 @@ def unicode_filter_decode(str)
474481
# A list of the commands
475482
#
476483
attr_reader :commands
484+
#
485+
# The timestamp of the last received response
486+
#
487+
attr_accessor :last_checkin
477488

478489
protected
479490
attr_accessor :parser, :ext_aliases # :nodoc:

lib/rex/post/meterpreter/client_core.rb

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -306,14 +306,16 @@ def uuid
306306
return Msf::Payload::UUID.new({:raw => id})
307307
end
308308

309-
def machine_id
309+
def machine_id(timeout=nil)
310310
request = Packet.create_request('core_machine_id')
311311

312-
response = client.send_request(request)
312+
args = [ request ]
313+
args << timeout if timeout
314+
315+
response = client.send_request(*args)
313316

314-
id = response.get_tlv_value(TLV_TYPE_MACHINE_ID)
315-
# TODO: Determine if we're going to MD5/SHA1 this
316-
return Rex::Text.md5(id)
317+
mid = response.get_tlv_value(TLV_TYPE_MACHINE_ID)
318+
return Rex::Text.md5(mid)
317319
end
318320

319321
def transport_add(opts={})

lib/rex/post/meterpreter/packet_dispatcher.rb

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -79,9 +79,11 @@ def initialize_passive_dispatcher
7979

8080
def shutdown_passive_dispatcher
8181
return if not self.passive_service
82-
resource = self.conn_id
83-
resource += "/" unless resource.end_with?("/")
84-
self.passive_service.remove_resource(resource)
82+
83+
# Ensure that there is only one leading and trailing slash on the URI
84+
resource_uri = "/" + self.conn_id.to_s.gsub(/(^\/|\/$)/, '') + "/"
85+
86+
self.passive_service.remove_resource(resource_uri)
8587

8688
# If there are no more resources registered on the service, stop it entirely
8789
if self.passive_service.resources.empty?
@@ -104,6 +106,8 @@ def on_passive_request(cli, req)
104106
resp['Content-Type'] = 'application/octet-stream'
105107
resp['Connection'] = 'close'
106108

109+
self.last_checkin = Time.now
110+
107111
# If the first 4 bytes are "RECV", return the oldest packet from the outbound queue
108112
if req.body[0,4] == "RECV"
109113
rpkt = send_queue.shift
@@ -496,6 +500,9 @@ def dispatch_inbound_packet(packet, client = nil)
496500
client = self
497501
end
498502

503+
# Update our last reply time
504+
client.last_checkin = Time.now
505+
499506
# If the packet is a response, try to notify any potential
500507
# waiters
501508
if ((resp = packet.response?))

0 commit comments

Comments
 (0)