Pivot session stability, display and handling
parent
fdc9864b61
commit
7acd772c10
|
@ -17,7 +17,7 @@ PATH
|
|||
metasploit-concern
|
||||
metasploit-credential
|
||||
metasploit-model
|
||||
metasploit-payloads (= 1.3.1)
|
||||
metasploit-payloads (= 1.3.3)
|
||||
metasploit_data_models
|
||||
metasploit_payloads-mettle (= 0.2.2)
|
||||
msgpack
|
||||
|
@ -150,7 +150,7 @@ GEM
|
|||
activemodel (~> 4.2.6)
|
||||
activesupport (~> 4.2.6)
|
||||
railties (~> 4.2.6)
|
||||
metasploit-payloads (1.3.1)
|
||||
metasploit-payloads (1.3.3)
|
||||
metasploit_data_models (2.0.15)
|
||||
activerecord (~> 4.2.6)
|
||||
activesupport (~> 4.2.6)
|
||||
|
|
|
@ -40,12 +40,19 @@ class Meterpreter < Rex::Post::Meterpreter::Client
|
|||
true
|
||||
end
|
||||
|
||||
def tunnel_to_s
|
||||
if self.pivot_session
|
||||
"Pivot via [#{self.pivot_session.tunnel_to_s}]"
|
||||
else
|
||||
super
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# Initializes a meterpreter session instance using the supplied rstream
|
||||
# that is to be used as the client's connection to the server.
|
||||
#
|
||||
def initialize(rstream, opts={})
|
||||
STDERR.puts("init in meterpreter\n")
|
||||
super
|
||||
|
||||
opts[:capabilities] = {
|
||||
|
@ -327,14 +334,14 @@ class Meterpreter < Rex::Post::Meterpreter::Client
|
|||
#
|
||||
# Terminates the session
|
||||
#
|
||||
def kill
|
||||
def kill(reason='')
|
||||
begin
|
||||
cleanup_meterpreter
|
||||
self.sock.close if self.sock
|
||||
rescue ::Exception
|
||||
end
|
||||
# deregister will actually trigger another cleanup
|
||||
framework.sessions.deregister(self)
|
||||
framework.sessions.deregister(self, reason)
|
||||
end
|
||||
|
||||
#
|
||||
|
|
|
@ -205,8 +205,6 @@ protected
|
|||
if self.session.respond_to?('create_session')
|
||||
s = self.session.create_session(conn, opts)
|
||||
else
|
||||
STDERR.puts("create_session caller: #{caller.inspect}\n")
|
||||
STDERR.puts("create_session opts: #{opts.inspect}\n")
|
||||
s = self.session.new(conn, opts)
|
||||
end
|
||||
rescue ::Exception => e
|
||||
|
@ -242,15 +240,12 @@ protected
|
|||
# new session.
|
||||
#
|
||||
def register_session(session)
|
||||
STDERR.puts("Registering session\n")
|
||||
# Register the session with the framework
|
||||
framework.sessions.register(session)
|
||||
|
||||
STDERR.puts("Calling on_session\n")
|
||||
# Call the handler's on_session() method
|
||||
on_session(session)
|
||||
session.bootstrap(datastore)
|
||||
STDERR.puts("Called on_session\n")
|
||||
|
||||
# If there is an exploit associated with this payload, then let's notify
|
||||
# anyone who is interested that this exploit succeeded
|
||||
|
|
|
@ -89,7 +89,6 @@ module Msf::Payload::Stager
|
|||
#
|
||||
# @return [String,nil]
|
||||
def stage_payload(opts = {})
|
||||
STDERR.puts("In stager stage_payload: #{opts.inspect}\n")
|
||||
if module_info['Stage']
|
||||
return module_info['Stage']['Payload']
|
||||
end
|
||||
|
|
|
@ -68,8 +68,6 @@ module Payload::Windows::MeterpreterLoader
|
|||
end
|
||||
|
||||
def stage_payload(opts={})
|
||||
STDERR.puts("In stage_payload: #{opts.inspect}\n")
|
||||
|
||||
stage_meterpreter(opts) + generate_config(opts)
|
||||
end
|
||||
|
||||
|
|
|
@ -74,8 +74,8 @@ module Payload::Windows::ReverseNamedPipe
|
|||
#{asm_reverse_named_pipe(opts)}
|
||||
^
|
||||
|
||||
"\xCC" + Metasm::Shellcode.assemble(Metasm::X86.new, combined_asm).encode_string
|
||||
#Metasm::Shellcode.assemble(Metasm::X86.new, combined_asm).encode_string
|
||||
#"\xCC" + Metasm::Shellcode.assemble(Metasm::X86.new, combined_asm).encode_string
|
||||
Metasm::Shellcode.assemble(Metasm::X86.new, combined_asm).encode_string
|
||||
end
|
||||
|
||||
#
|
||||
|
|
|
@ -59,8 +59,6 @@ private
|
|||
session_guid = [SecureRandom.uuid.gsub(/-/, '')].pack('H*')
|
||||
end
|
||||
|
||||
STDERR.puts("**** Session config expiration: #{opts[:expiration]}\n")
|
||||
|
||||
session_data = [
|
||||
0, # comms socket, patched in by the stager
|
||||
exit_func, # exit function identifer
|
||||
|
|
|
@ -81,7 +81,6 @@ class Client
|
|||
# which communication with the server will be performed.
|
||||
#
|
||||
def initialize(sock, opts={})
|
||||
STDERR.puts("init in client\n")
|
||||
init_meterpreter(sock, opts)
|
||||
end
|
||||
|
||||
|
@ -89,6 +88,16 @@ class Client
|
|||
# Cleans up the meterpreter instance, terminating the dispatcher thread.
|
||||
#
|
||||
def cleanup_meterpreter
|
||||
self.pivots.keys.each do |k|
|
||||
pivot = self.pivots[k]
|
||||
pivot.pivoted_session.kill('Pivot closed')
|
||||
pivot.pivoted_session.shutdown_passive_dispatcher
|
||||
end
|
||||
|
||||
if self.pivot_session
|
||||
self.pivot_session.remove_pivot(self.session_guid)
|
||||
end
|
||||
|
||||
if not self.skip_cleanup
|
||||
ext.aliases.each_value do | extension |
|
||||
extension.cleanup if extension.respond_to?( 'cleanup' )
|
||||
|
@ -108,7 +117,6 @@ class Client
|
|||
# Initializes the meterpreter client instance
|
||||
#
|
||||
def init_meterpreter(sock,opts={})
|
||||
STDERR.puts("init_meterpreter in client.rb\n")
|
||||
self.sock = sock
|
||||
self.parser = PacketParser.new
|
||||
self.ext = ObjectAliases.new
|
||||
|
@ -137,11 +145,6 @@ class Client
|
|||
self.passive_dispatcher = opts[:passive_dispatcher]
|
||||
end
|
||||
|
||||
STDERR.puts("Expr; #{self.expiration.inspect}\n")
|
||||
STDERR.puts("Comm: #{self.comm_timeout.inspect}\n")
|
||||
STDERR.puts("TOT: #{self.retry_total.inspect}\n")
|
||||
STDERR.puts("Wait: #{self.retry_wait.inspect}\n")
|
||||
|
||||
self.response_timeout = opts[:timeout] || self.class.default_timeout
|
||||
self.send_keepalives = true
|
||||
|
||||
|
|
|
@ -734,30 +734,21 @@ class ClientCore < Extension
|
|||
# Negotiates the use of encryption at the TLV level
|
||||
#
|
||||
def negotiate_tlv_encryption
|
||||
STDERR.puts("negotiate_tlv_encryption entry: #{client.inspect}\n")
|
||||
sym_key = nil
|
||||
rsa_key = OpenSSL::PKey::RSA.new(2048)
|
||||
rsa_pub_key = rsa_key.public_key
|
||||
|
||||
STDERR.puts("negotiate_tlv_encryption 1\n")
|
||||
request = Packet.create_request('core_negotiate_tlv_encryption')
|
||||
STDERR.puts("negotiate_tlv_encryption 2\n")
|
||||
request.add_tlv(TLV_TYPE_RSA_PUB_KEY, rsa_pub_key.to_pem)
|
||||
STDERR.puts("negotiate_tlv_encryption 3\n")
|
||||
|
||||
begin
|
||||
response = client.send_request(request)
|
||||
STDERR.puts("negotiate_tlv_encryption 4\n")
|
||||
key_enc = response.get_tlv_value(TLV_TYPE_ENC_SYM_KEY)
|
||||
STDERR.puts("negotiate_tlv_encryption 5\n")
|
||||
key_type = response.get_tlv_value(TLV_TYPE_SYM_KEY_TYPE)
|
||||
STDERR.puts("negotiate_tlv_encryption 6\n")
|
||||
|
||||
if key_enc
|
||||
STDERR.puts("negotiate_tlv_encryption 7\n")
|
||||
sym_key = rsa_key.private_decrypt(key_enc, OpenSSL::PKey::RSA::PKCS1_PADDING)
|
||||
else
|
||||
STDERR.puts("negotiate_tlv_encryption 8\n")
|
||||
sym_key = response.get_tlv_value(TLV_TYPE_SYM_KEY)
|
||||
end
|
||||
rescue OpenSSL::PKey::RSAError, Rex::Post::Meterpreter::RequestError
|
||||
|
|
|
@ -80,7 +80,6 @@ class Kiwi < Extension
|
|||
elsif output =~ /^ERROR.*SamLookupNamesInDomain/m
|
||||
result[:error] = 'Invalid user.'
|
||||
else
|
||||
STDERR.puts(output)
|
||||
result[:error] = 'Unknown error.'
|
||||
end
|
||||
else
|
||||
|
|
|
@ -236,6 +236,11 @@ class Tlv
|
|||
when TLV_TYPE_SYM_KEY; "SYM-KEY"
|
||||
when TLV_TYPE_ENC_SYM_KEY; "ENC-SYM-KEY"
|
||||
|
||||
when TLV_TYPE_PIVOT_ID; "PIVOT-ID"
|
||||
when TLV_TYPE_PIVOT_STAGE_DATA; "PIVOT-STAGE-DATA"
|
||||
when TLV_TYPE_PIVOT_STAGE_DATA_SIZE; "PIVOT-STAGE-DATA-SIZE"
|
||||
when TLV_TYPE_PIVOT_NAMED_PIPE_NAME; "PIVOT-NAMED-PIPE-NAME"
|
||||
|
||||
#when Extensions::Stdapi::TLV_TYPE_NETWORK_INTERFACE; 'network-interface'
|
||||
#when Extensions::Stdapi::TLV_TYPE_IP; 'ip-address'
|
||||
#when Extensions::Stdapi::TLV_TYPE_NETMASK; 'netmask'
|
||||
|
|
|
@ -161,7 +161,6 @@ module PacketDispatcher
|
|||
if self.pivot_session
|
||||
opts[:session_guid] = self.session_guid
|
||||
opts[:tlv_enc_key] = self.tlv_enc_key
|
||||
STDERR.puts("Pivot session, setting guid: #{self.session_guid.unpack('H*')[0]}...\n")
|
||||
return self.pivot_session.send_packet(packet, opts)
|
||||
end
|
||||
|
||||
|
@ -178,16 +177,10 @@ module PacketDispatcher
|
|||
tlv_enc_key = opts[:tlv_enc_key]
|
||||
end
|
||||
|
||||
STDERR.puts(" Current Session GUID: #{self.session_guid.unpack('H*')[0]}\n")
|
||||
STDERR.puts("Provided Session GUID: #{session_guid.unpack('H*')[0]}\n")
|
||||
STDERR.puts("Using tlv_enc_key: #{tlv_enc_key.inspect}\n")
|
||||
|
||||
bytes = 0
|
||||
raw = packet.to_r(session_guid, tlv_enc_key)
|
||||
err = nil
|
||||
|
||||
STDERR.puts("Outgoing packet is #{raw.length} bytes\n")
|
||||
|
||||
# Short-circuit send when using a passive dispatcher
|
||||
if self.passive_service
|
||||
send_queue.push(raw)
|
||||
|
@ -249,45 +242,35 @@ module PacketDispatcher
|
|||
# @param timeout [Integer,nil] number of seconds to wait, or nil to wait
|
||||
# forever
|
||||
def send_packet_wait_response(packet, timeout)
|
||||
STDERR.puts("send_packet_wait_response entry\n")
|
||||
# First, add the waiter association for the supplied packet
|
||||
waiter = add_response_waiter(packet)
|
||||
STDERR.puts("send_packet_wait_response 1\n")
|
||||
|
||||
bytes_written = send_packet(packet)
|
||||
STDERR.puts("send_packet_wait_response 2\n")
|
||||
|
||||
# Transmit the packet
|
||||
if (bytes_written.to_i <= 0)
|
||||
STDERR.puts("send_packet_wait_response 3\n")
|
||||
# Remove the waiter if we failed to send the packet.
|
||||
remove_response_waiter(waiter)
|
||||
return nil
|
||||
end
|
||||
|
||||
STDERR.puts("send_packet_wait_response 4\n")
|
||||
if not timeout
|
||||
return nil
|
||||
end
|
||||
STDERR.puts("send_packet_wait_response 5 #{waiter.inspect}\n")
|
||||
|
||||
# Wait for the supplied time interval
|
||||
response = waiter.wait(timeout)
|
||||
STDERR.puts("send_packet_wait_response 6\n")
|
||||
|
||||
# Remove the waiter from the list of waiters in case it wasn't
|
||||
# removed. This happens if the waiter timed out above.
|
||||
remove_response_waiter(waiter)
|
||||
STDERR.puts("send_packet_wait_response 7\n")
|
||||
|
||||
# wire in the UUID for this, as it should be part of every response
|
||||
# packet
|
||||
if response && !self.payload_uuid
|
||||
STDERR.puts("send_packet_wait_response 8\n")
|
||||
uuid = response.get_tlv_value(TLV_TYPE_UUID)
|
||||
self.payload_uuid = Msf::Payload::UUID.new({:raw => uuid}) if uuid
|
||||
end
|
||||
STDERR.puts("send_packet_wait_response 9\n")
|
||||
|
||||
# Return the response packet, if any
|
||||
return response
|
||||
|
@ -493,11 +476,9 @@ module PacketDispatcher
|
|||
#
|
||||
def add_response_waiter(request, completion_routine = nil, completion_param = nil)
|
||||
if self.pivot_session
|
||||
STDERR.puts("Adding waiter in pivoted session: #{self.pivot_session}\n")
|
||||
return self.pivot_session.add_response_waiter(request, completion_routine, completion_param)
|
||||
end
|
||||
|
||||
STDERR.puts("Adding waiting in current session: #{self.inspect}\n")
|
||||
waiter = PacketResponseWaiter.new(request.rid, completion_routine, completion_param)
|
||||
|
||||
self.waiters << waiter
|
||||
|
@ -559,17 +540,12 @@ module PacketDispatcher
|
|||
def dispatch_inbound_packet(packet)
|
||||
handled = false
|
||||
|
||||
STDERR.puts("Inbound packet: #{packet.session_guid.unpack('H*')[0]}\n")
|
||||
pivot = self.find_pivot(packet.session_guid)
|
||||
STDERR.puts("Pivot is: #{pivot.inspect}\n")
|
||||
|
||||
tlv_enc_key = self.tlv_enc_key
|
||||
STDERR.puts("Getting enc key\n")
|
||||
tlv_enc_key = pivot.pivoted_session.tlv_enc_key if pivot
|
||||
STDERR.puts("Got enc key #{tlv_enc_key.inspect}\n")
|
||||
|
||||
packet.from_r(tlv_enc_key)
|
||||
STDERR.puts("Decrypted packet: #{packet.inspect}\n")
|
||||
|
||||
# Update our last reply time
|
||||
self.last_checkin = Time.now
|
||||
|
@ -577,7 +553,6 @@ module PacketDispatcher
|
|||
|
||||
# If the packet is a response, try to notify any potential
|
||||
# waiters
|
||||
STDERR.puts("Notifying response waiter for packet")
|
||||
if packet.response? && notify_response_waiter(packet)
|
||||
return true
|
||||
end
|
||||
|
|
|
@ -12,9 +12,12 @@ class PivotListener
|
|||
|
||||
attr_accessor :session_class
|
||||
|
||||
def initialize(session_class)
|
||||
attr_accessor :display_name
|
||||
|
||||
def initialize(session_class, display_name)
|
||||
self.id = [SecureRandom.uuid.gsub(/-/, '')].pack('H*')
|
||||
self.session_class = session_class
|
||||
self.display_name = display_name
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -25,11 +28,8 @@ class Pivot
|
|||
#
|
||||
attr_accessor :client
|
||||
|
||||
attr_accessor :pivot_session_guid
|
||||
|
||||
attr_accessor :pivoted_session
|
||||
|
||||
|
||||
# Class modifications to support global pivot message
|
||||
# dispatching without having to register a per-instance handler
|
||||
class << self
|
||||
|
@ -39,17 +39,35 @@ class Pivot
|
|||
# to the appropriate class instance's DIO handler
|
||||
def request_handler(client, packet)
|
||||
if packet.method == 'core_pivot_session_new'
|
||||
STDERR.puts("Received pivot packet! #{packet.inspect}\n")
|
||||
session_guid = packet.get_tlv_value(TLV_TYPE_SESSION_GUID)
|
||||
listener_id = packet.get_tlv_value(TLV_TYPE_PIVOT_ID)
|
||||
Pivot.new(client, session_guid, listener_id)
|
||||
client.add_pivot(Pivot.new(client, session_guid, listener_id))
|
||||
elsif packet.method == 'core_pivot_session_died'
|
||||
session_guid = packet.get_tlv_value(TLV_TYPE_SESSION_GUID)
|
||||
pivot = client.find_pivot(session_guid)
|
||||
if pivot
|
||||
pivot.pivoted_session.kill('Died')
|
||||
client.remove_pivot(session_guid)
|
||||
end
|
||||
end
|
||||
true
|
||||
end
|
||||
end
|
||||
|
||||
def Pivot.get_listeners(client)
|
||||
client.pivot_listeners
|
||||
end
|
||||
|
||||
def Pivot.remove_listener(client, listener_id)
|
||||
if client.find_pivot_listener(listener_id)
|
||||
request = Packet.create_request('core_pivot_remove')
|
||||
request.add_tlv(TLV_TYPE_PIVOT_ID, listener_id)
|
||||
client.send_request(request)
|
||||
client.remove_pivot_listener(listener_id)
|
||||
end
|
||||
end
|
||||
|
||||
def Pivot.create_named_pipe_listener(client, opts={})
|
||||
STDERR.puts("Create listener: #{opts}\n")
|
||||
request = Packet.create_request('core_pivot_add')
|
||||
request.add_tlv(TLV_TYPE_PIVOT_NAMED_PIPE_NAME, opts[:pipe_name])
|
||||
|
||||
|
@ -63,25 +81,15 @@ class Pivot
|
|||
when 'windows'
|
||||
# Include the appropriate reflective dll injection module for the target process architecture...
|
||||
if opts[:arch] == ARCH_X86
|
||||
STDERR.puts("Including Meterp Loader x86\n")
|
||||
c.include(::Msf::Payload::Windows::MeterpreterLoader)
|
||||
elsif opts[:arch] == ARCH_X64
|
||||
STDERR.puts("Including Meterp Loader x64\n")
|
||||
c.include(::Msf::Payload::Windows::MeterpreterLoader_x64)
|
||||
else
|
||||
STDERR.puts("Not including a loader for #{opts[:arch]}\n")
|
||||
STDERR.puts("Not including a loader for '#{opts[:arch]}'\n")
|
||||
end
|
||||
end
|
||||
|
||||
# TODO: should we be generating configuration inside Meterpreter like
|
||||
# we do for migration, or just having a fixed one like we are now?
|
||||
#uuid = Msf::Payload::UUID.new({
|
||||
# arch: opts[:arch],
|
||||
# platform: opts[:platform]
|
||||
#})
|
||||
|
||||
stage_opts = {
|
||||
#uuid: uuid,
|
||||
arch: opts[:arch],
|
||||
force_write_handle: true,
|
||||
null_session_guid: true,
|
||||
|
@ -100,11 +108,10 @@ class Pivot
|
|||
stager = c.new()
|
||||
|
||||
stage_opts[:transport_config] = [stager.transport_config_reverse_named_pipe(stage_opts)]
|
||||
#STDERR.puts("Stager: #{stager.inspect}\n")
|
||||
stage = stager.stage_payload(stage_opts)
|
||||
#STDERR.puts("Stage: #{stage.inspect}\n")
|
||||
|
||||
pivot_listener = PivotListener.new(::Msf::Sessions::Meterpreter_x86_Win)
|
||||
display_name = "pipe://#{opts[:pipe_host]}/#{opts[:pipe_name]} (#{opts[:arch]}/#{opts[:platform]})"
|
||||
pivot_listener = PivotListener.new(::Msf::Sessions::Meterpreter_x86_Win, display_name)
|
||||
|
||||
request.add_tlv(TLV_TYPE_PIVOT_STAGE_DATA, stage)
|
||||
request.add_tlv(TLV_TYPE_PIVOT_STAGE_DATA_SIZE, stage.length)
|
||||
|
@ -113,11 +120,12 @@ class Pivot
|
|||
client.send_request(request)
|
||||
|
||||
client.add_pivot_listener(pivot_listener)
|
||||
|
||||
pivot_listener
|
||||
end
|
||||
|
||||
def initialize(client, session_guid, listener_id)
|
||||
self.client = client
|
||||
self.pivot_session_guid = session_guid
|
||||
|
||||
opts = {
|
||||
pivot_session: client,
|
||||
|
@ -127,17 +135,9 @@ class Pivot
|
|||
listener = client.find_pivot_listener(listener_id)
|
||||
self.pivoted_session = listener.session_class.new(nil, opts)
|
||||
|
||||
STDERR.puts("pivoted session instance created: #{self.pivoted_session.inspect}\n")
|
||||
|
||||
self.client.add_pivot(self)
|
||||
|
||||
STDERR.puts("Setting the framework instance\n")
|
||||
self.pivoted_session.framework = self.client.framework
|
||||
STDERR.puts("Invoking the on_session method\n")
|
||||
self.pivoted_session.bootstrap({'AutoVerifySessionTimeout' => 30})
|
||||
STDERR.puts("Registering the session with the framework\n")
|
||||
self.client.framework.sessions.register(self.pivoted_session)
|
||||
STDERR.puts("done!\n")
|
||||
end
|
||||
|
||||
protected
|
||||
|
|
|
@ -25,7 +25,7 @@ module PivotContainer
|
|||
# session guid.
|
||||
#
|
||||
def add_pivot(pivot)
|
||||
self.pivots[pivot.pivot_session_guid] = pivot
|
||||
self.pivots[pivot.pivoted_session.session_guid] = pivot
|
||||
end
|
||||
|
||||
def add_pivot_listener(listener)
|
||||
|
@ -50,6 +50,10 @@ module PivotContainer
|
|||
return self.pivots.delete(pivot_session_guid)
|
||||
end
|
||||
|
||||
def remove_pivot_listener(listener_id)
|
||||
return self.pivot_listeners.delete(listener_id)
|
||||
end
|
||||
|
||||
#
|
||||
# The hash of pivots.
|
||||
#
|
||||
|
|
|
@ -903,7 +903,6 @@ class Console::CommandDispatcher::Stdapi::Sys
|
|||
)
|
||||
|
||||
privs = client.sys.config.getprivs
|
||||
STDERR.puts(privs.inspect+"\n")
|
||||
client.sys.config.getprivs.each do |priv|
|
||||
table << [priv[:priv], priv[:enabled].to_s]
|
||||
end
|
||||
|
|
|
@ -70,7 +70,7 @@ Gem::Specification.new do |spec|
|
|||
# are needed when there's no database
|
||||
spec.add_runtime_dependency 'metasploit-model'
|
||||
# Needed for Meterpreter
|
||||
spec.add_runtime_dependency 'metasploit-payloads', '1.3.1'
|
||||
spec.add_runtime_dependency 'metasploit-payloads', '1.3.3'
|
||||
# Needed for the next-generation POSIX Meterpreter
|
||||
spec.add_runtime_dependency 'metasploit_payloads-mettle', '0.2.2'
|
||||
# Needed by msfgui and other rpc components
|
||||
|
|
Loading…
Reference in New Issue