Path: blob/master/lib/msf/base/sessions/ldap.rb
19778 views
# -*- coding: binary -*-12require 'rex/post/ldap'34class Msf::Sessions::LDAP5#6# This interface supports basic interaction.7#8include Msf::Session::Basic9include Msf::Sessions::Scriptable1011# @return [Rex::Post::LDAP::Ui::Console] The interactive console12attr_accessor :console13# @return [Rex::Proto::LDAP::Client] The LDAP client14attr_accessor :client1516attr_accessor :keep_alive_thread1718# @return [Integer] Seconds between keepalive requests19attr_accessor :keepalive_seconds2021attr_accessor :platform, :arch22attr_reader :framework2324# @param[Rex::IO::Stream] rstream25# @param [Hash] opts26# @option opts [Rex::Proto::LDAP::Client] :client27# @option opts [Integer] :keepalive28def initialize(rstream, opts = {})29@client = opts.fetch(:client)30@keepalive_seconds = opts.fetch(:keepalive_seconds)31self.console = Rex::Post::LDAP::Ui::Console.new(self)32super(rstream, opts)33end3435def cleanup36stop_keep_alive_loop37super38end3940def bootstrap(datastore = {}, handler = nil)41session = self42session.init_ui(user_input, user_output)4344username = datastore['USERNAME']45if username.blank?46begin47whoami = client.ldapwhoami48rescue Net::LDAP::Error => e49ilog('ldap session opened with no username and the target does not support the LDAP whoami extension')50else51username = whoami.delete_prefix('u:').split('\\').last52end53end54@info = "LDAP #{username} @ #{@peer_info}"55end5657def execute_file(full_path, args)58if File.extname(full_path) == '.rb'59Rex::Script::Shell.new(self, full_path).run(args)60else61console.load_resource(full_path)62end63end6465def process_autoruns(datastore)66['InitialAutoRunScript', 'AutoRunScript'].each do |key|67next if datastore[key].nil? || datastore[key].empty?6869args = Shellwords.shellwords(datastore[key])70print_status("Session ID #{sid} (#{tunnel_to_s}) processing #{key} '#{datastore[key]}'")71execute_script(args.shift, *args)72end73end7475def type76self.class.type77end7879# Returns the type of session.80#81def self.type82'ldap'83end8485def self.can_cleanup_files86false87end8889#90# Returns the session description.91#92def desc93'LDAP'94end9596def address97@address ||= client.peerhost98end99100def port101@port ||= client.peerport102end103104##105# :category: Msf::Session::Interactive implementors106#107# Initializes the console's I/O handles.108#109def init_ui(input, output)110self.user_input = input111self.user_output = output112console.init_ui(input, output)113console.set_log_source(log_source)114115super116end117118##119# :category: Msf::Session::Interactive implementors120#121# Resets the console's I/O handles.122#123def reset_ui124console.unset_log_source125console.reset_ui126end127128def exit129console.stop130end131132##133# :category: Msf::Session::Interactive implementors134#135# Override the basic session interaction to use shell_read and136# shell_write instead of operating on rstream directly.137def _interact138framework.events.on_session_interact(self)139framework.history_manager.with_context(name: type.to_sym) do140_interact_stream141end142end143144##145# :category: Msf::Session::Interactive implementors146#147def _interact_stream148framework.events.on_session_interact(self)149150console.framework = framework151# Call the console interaction of the ldap client and152# pass it a block that returns whether or not we should still be153# interacting. This will allow the shell to abort if interaction is154# canceled.155console.interact { interacting != true }156console.framework = nil157158# If the stop flag has been set, then that means the user exited. Raise159# the EOFError so we can drop this handle like a bad habit.160raise EOFError if (console.stopped? == true)161end162163def on_registered164start_keep_alive_loop165end166167# Start a background thread for regularly sending a no-op command to keep the connection alive168def start_keep_alive_loop169self.keep_alive_thread = framework.threads.spawn("LDAP-shell-keepalive-#{sid}", false) do170loop do171if client.last_interaction.nil?172remaining_sleep = @keepalive_seconds173else174remaining_sleep = @keepalive_seconds - (Process.clock_gettime(Process::CLOCK_MONOTONIC) - client.last_interaction)175end176sleep(remaining_sleep)177if (Process.clock_gettime(Process::CLOCK_MONOTONIC) - client.last_interaction) > @keepalive_seconds178client.search_root_dse179end180# This should have moved last_interaction forwards181fail if (Process.clock_gettime(Process::CLOCK_MONOTONIC) - client.last_interaction) > @keepalive_seconds182end183end184end185186# Stop the background thread187def stop_keep_alive_loop188keep_alive_thread.kill189end190end191192193