Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Path: blob/master/plugins/aggregator.rb
Views: 11705
module Msf1Aggregator_yaml = "#{Msf::Config.config_directory}/aggregator.yaml".freeze # location of the aggregator.yml containing saved aggregator creds23# This plugin provides management and interaction with an external session aggregator.4class Plugin::Aggregator < Msf::Plugin5class AggregatorCommandDispatcher6include Msf::Ui::Console::CommandDispatcher78@response_queue = []910def name11'Aggregator'12end1314def commands15{16'aggregator_connect' => 'Connect to a running Aggregator instance ( host[:port] )',17'aggregator_save' => 'Save connection details to an Aggregator instance',18'aggregator_disconnect' => 'Disconnect from an active Aggregator instance',19'aggregator_addresses' => 'List all remote ip addresses available for ingress',20'aggregator_cables' => 'List all remote listeners for sessions',21'aggregator_cable_add' => 'Setup remote https listener for sessions',22'aggregator_cable_remove' => 'Stop remote listener for sessions',23'aggregator_default_forward' => 'forward a unlisted/unhandled sessions to a specified listener',24'aggregator_sessions' => 'List all remote sessions currently available from the Aggregator instance',25'aggregator_session_forward' => 'forward a session to a specified listener',26'aggregator_session_park' => 'Park an existing session on the Aggregator instance'27}28end2930def aggregator_verify31if !@aggregator32print_error("No active Aggregator instance has been configured, please use 'aggregator_connect'")33return false34end3536true37end3839def usage(*lines)40print_status('Usage: ')41lines.each do |line|42print_status(" #{line}")43end44end4546def usage_save47usage('aggregator_save')48end4950def usage_connect51usage('aggregator_connect host[:port]',52' -OR- ',53'aggregator_connect host port')54end5556def usage_cable_add57usage('aggregator_cable_add host:port [certificate]',58' -OR- ',59'aggregator_cable_add host port [certificate]')60end6162def usage_cable_remove63usage('aggregator_cable_remove host:port',64' -OR- ',65'aggregator_cable_remove host port')66end6768def usage_session_forward69usage('aggregator_session_forward remote_id')70end7172def usage_default_forward73usage('aggregator_session_forward')74end7576def show_session(details, _target, local_id)77status = pad_space(" #{local_id}", 4)78status += " #{details['ID']}"79status = pad_space(status, 15)80status += ' meterpreter '81status += "#{guess_target_platform(details['OS'])} "82status = pad_space(status, 43)83status += "#{details['USER']} @ #{details['HOSTNAME']} "84status = pad_space(status, 64)85status += "#{details['LOCAL_SOCKET']} -> #{details['REMOTE_SOCKET']}"86print_status status87end8889def show_session_detailed(details, target, local_id)90print_status "\t Remote ID: #{details['ID']}"91print_status "\t Type: meterpreter #{guess_target_platform(details['OS'])}"92print_status "\t Info: #{details['USER']} @ #{details['HOSTNAME']}"93print_status "\t Tunnel: #{details['LOCAL_SOCKET']} -> #{details['REMOTE_SOCKET']}"94print_status "\t Via: exploit/multi/handler"95print_status "\t UUID: #{details['UUID']}"96print_status "\t MachineID: #{details['MachineID']}"97print_status "\t CheckIn: #{details['LAST_SEEN'].to_i}s ago" unless details['LAST_SEEN'].nil?98print_status "\tRegistered: Not Yet Implemented"99print_status "\t Forward: #{target}"100print_status "\tSession ID: #{local_id}" unless local_id.nil?101print_status ''102end103104def cmd_aggregator_save(*args)105# if we are logged in, save session details to aggregator.yaml106if !args.empty? || args[0] == '-h'107usage_save108return109end110111if args[0]112usage_save113return114end115116group = 'default'117118if (@host && !@host.empty?) && (@port && !@port.empty? && @port.to_i > 0)119config = { group.to_s => { 'server' => @host, 'port' => @port } }120::File.open(Aggregator_yaml.to_s, 'wb') { |f| f.puts YAML.dump(config) }121print_good("#{Aggregator_yaml} created.")122else123print_error('Missing server/port - reconnect and then try again.')124return125end126end127128def cmd_aggregator_connect(*args)129if !args[0] && ::File.readable?(Aggregator_yaml.to_s)130lconfig = YAML.load_file(Aggregator_yaml.to_s)131@host = lconfig['default']['server']132@port = lconfig['default']['port']133aggregator_login134return135end136137if args.empty? || args[0].empty? || args[0] == '-h'138usage_connect139return140end141142@host = @port = @sslv = nil143144case args.length145when 1146@host, @port = args[0].split(':', 2)147@port ||= '2447'148when 2149@host, @port = args150else151usage_connect152return153end154aggregator_login155end156157def cmd_aggregator_sessions(*args)158case args.length159when 0160is_detailed = false161when 1162unless args[0] == '-v'163usage_sessions164return165end166is_detailed = true167else168usage_sessions169return170end171return unless aggregator_verify172173sessions_list = @aggregator.sessions174return if sessions_list.nil?175176session_map = {}177178# get details for each session and print in format of sessions -v179sessions_list.each do |session|180session_id, target = session181details = @aggregator.session_details(session_id)182local_id = nil183framework.sessions.each_pair do |key, value|184next unless value.conn_id == session_id185186local_id = key187end188# filter session that do not have details as forwarding options (this may change later)189next unless details && details['ID']190191session_map[details['ID']] = [details, target, local_id]192end193194print_status('Remote sessions')195print_status('===============')196print_status('')197if session_map.empty?198print_status('No remote sessions.')199else200unless is_detailed201print_status(' Id Remote Id Type Information Connection')202print_status(' -- --------- ---- ----------- ----------')203end204session_map.keys.sort.each do |key|205details, target, local_id = session_map[key]206if is_detailed207show_session_detailed(details, target, local_id)208else209show_session(details, target, local_id)210end211end212end213end214215def cmd_aggregator_addresses(*_args)216return if !aggregator_verify217218address_list = @aggregator.available_addresses219return if address_list.nil?220221print_status('Remote addresses found:')222address_list.each do |addr|223print_status(" #{addr}")224end225end226227def cmd_aggregator_cable_add(*args)228host, port, certificate = nil229case args.length230when 1231host, port = args[0].split(':', 2)232when 2233host, port = args[0].split(':', 2)234if port.nil?235port = args[1]236else237certificate = args[1]238end239when 3240host, port, certificate = args241else242usage_cable_add243return244end245246if !aggregator_verify || args.empty? || args[0] == '-h' || \247port.nil? || port.to_i <= 0248usage_cable_add249return250end251252certificate = File.new(certificate).read if certificate && File.exist?(certificate)253254@aggregator.add_cable(Metasploit::Aggregator::Cable::HTTPS, host, port, certificate)255end256257def cmd_aggregator_cables(*_args)258return if !aggregator_verify259260res = @aggregator.cables261print_status('Remote Cables:')262res.each do |k|263print_status(" #{k}")264end265end266267def cmd_aggregator_cable_remove(*args)268case args.length269when 1270host, port = args[0].split(':', 2)271when 2272host, port = args273end274if !aggregator_verify || args.empty? || args[0] == '-h' || host.nil?275usage_cable_remove276return277end278@aggregator.remove_cable(host, port)279end280281def cmd_aggregator_session_park(*args)282return if !aggregator_verify283284case args.length285when 1286session_id = args[0]287s = framework.sessions.get(session_id)288if s.nil?289print_status("#{session_id} is not a valid session.")290elsif @aggregator.sessions.keys.include? s.conn_id291@aggregator.release_session(s.conn_id)292framework.sessions.deregister(s)293else294# TODO: determine if we can add a transport and route with the295# aggregator. For now, just report action not taken.296print_status("#{session_id} does not originate from the aggregator connection.")297end298else299usage('aggregator_session_park session_id')300return301end302end303304def cmd_aggregator_default_forward(*_args)305return if !aggregator_verify306307@aggregator.register_default(@aggregator.uuid, nil)308end309310def cmd_aggregator_session_forward(*args)311return if !aggregator_verify312313remote_id = nil314case args.length315when 1316remote_id = args[0]317else318usage_session_forward319return320end321# find session with ID matching request322@aggregator.sessions.each do |session|323session_uri, _target = session324details = @aggregator.session_details(session_uri)325next unless details['ID'] == remote_id326327return @aggregator.obtain_session(session_uri, @aggregator.uuid)328end329print_error("#{remote_id} was not found.")330end331332def cmd_aggregator_disconnect(*_args)333if @aggregator && @aggregator.available?334# check if this connection is the default forward335@aggregator.register_default(nil, nil) if @aggregator.default == @aggregator.uuid336337# now check for any specifically forwarded sessions338local_sessions_by_id = {}339framework.sessions.each_pair do |_id, s|340local_sessions_by_id[s.conn_id] = s341end342343sessions = @aggregator.sessions344unless sessions.nil?345sessions.each_pair do |session, console|346next unless local_sessions_by_id.keys.include?(session)347348if console == @aggregator.uuid349# park each session locally addressed350cmd_aggregator_session_park(framework.sessions.key(local_sessions_by_id[session]))351else352# simple disconnect session that were from the default forward353framework.sessions.deregister(local_sessions_by_id[session])354end355end356end357end358@aggregator.stop if @aggregator359if @payload_job_ids360@payload_job_ids.each do |id|361framework.jobs.stop_job(id)362end363@payload_job_ids = nil364end365@aggregator = nil366end367368def aggregator_login369if !((@host && !@host.empty?) && (@port && !@port.empty? && @port.to_i > 0))370usage_connect371return372end373374if (@host != 'localhost') && (@host != '127.0.0.1')375print_error('Warning: SSL connections are not verified in this release, it is possible for an attacker')376print_error(' with the ability to man-in-the-middle the Aggregator traffic to capture the Aggregator')377print_error(' traffic, if you are running this on an untrusted network.')378return379end380381# Wrap this so a duplicate session does not prevent access382begin383cmd_aggregator_disconnect384rescue ::Interrupt => e385raise e386rescue ::Exception387end388389begin390print_status("Connecting to Aggregator instance at #{@host}:#{@port}...")391@aggregator = Metasploit::Aggregator::ServerProxy.new(@host, @port)392end393394aggregator_compatibility_check395396unless @payload_job_ids397@payload_job_ids = []398@my_io = local_handler399end400401@aggregator.register_response_channel(@my_io)402@aggregator403end404405def aggregator_compatibility_check406false if @aggregator.nil?407unless @aggregator.available?408print_error("Connection to aggregator @ #{@host}:#{@port} is unavailable.")409cmd_aggregator_disconnect410end411end412413def local_handler414# get a random ephemeral port415server = TCPServer.new('127.0.0.1', 0)416port = server.addr[1]417server.close418419multi_handler = framework.exploits.create('multi/handler')420421multi_handler.datastore['LHOST'] = '127.0.0.1'422# multi_handler.datastore['PAYLOAD'] = "multi/meterpreter/reverse_https"423multi_handler.datastore['PAYLOAD'] = 'multi/meterpreter/reverse_http'424multi_handler.datastore['LPORT'] = port.to_s425426# %w(DebugOptions PrependMigrate PrependMigrateProc427# InitialAutoRunScript AutoRunScript CAMPAIGN_ID HandlerSSLCert428# StagerVerifySSLCert PayloadUUIDTracking PayloadUUIDName429# IgnoreUnknownPayloads SessionRetryTotal SessionRetryWait430# SessionExpirationTimeout SessionCommunicationTimeout).each do |opt|431# multi_handler.datastore[opt] = datastore[opt] if datastore[opt]432# end433434multi_handler.datastore['ExitOnSession'] = false435multi_handler.datastore['EXITFUNC'] = 'thread'436437multi_handler.exploit_simple(438'LocalInput' => nil,439'LocalOutput' => nil,440'Payload' => multi_handler.datastore['PAYLOAD'],441'RunAsJob' => true442)443@payload_job_ids << multi_handler.job_id444# requester = Metasploit::Aggregator::Http::SslRequester.new(multi_handler.datastore['LHOST'], multi_handler.datastore['LPORT'])445requester = Metasploit::Aggregator::Http::Requester.new(multi_handler.datastore['LHOST'], multi_handler.datastore['LPORT'])446requester447end448449# borrowed from Msf::Sessions::Meterpreter for now450def guess_target_platform(os)451case os452when /windows/i453Msf::Module::Platform::Windows.realname.downcase454when /darwin/i455Msf::Module::Platform::OSX.realname.downcase456when /mac os ?x/i457# this happens with java on OSX (for real!)458Msf::Module::Platform::OSX.realname.downcase459when /freebsd/i460Msf::Module::Platform::FreeBSD.realname.downcase461when /openbsd/i, /netbsd/i462Msf::Module::Platform::BSD.realname.downcase463else464Msf::Module::Platform::Linux.realname.downcase465end466end467468def pad_space(status, length)469status << ' ' while status.length < length470status471end472473private :guess_target_platform474private :aggregator_login475private :aggregator_compatibility_check476private :aggregator_verify477private :local_handler478private :pad_space479private :show_session480private :show_session_detailed481end482483#484# Plugin initialization485#486487def initialize(framework, opts)488super489490#491# Require the metasploit/aggregator gem, but fail nicely if it's not there.492#493begin494require 'metasploit/aggregator'495rescue LoadError496raise 'WARNING: metasploit/aggregator is not available for now.'497end498499add_console_dispatcher(AggregatorCommandDispatcher)500print_status('Aggregator interaction has been enabled')501end502503def cleanup504remove_console_dispatcher('Aggregator')505end506507def name508'aggregator'509end510511def desc512'Interacts with the external Session Aggregator'513end514end515end516517518