rapid7/metasploit-framework

View on GitHub
lib/rex/post/sql/ui/console.rb

Summary

Maintainability
A
55 mins
Test Coverage
require 'rex/post/sql/ui/console/command_dispatcher'
require 'rex/post/sql/ui/console/interactive_sql_client'
require 'rex/post/session_compatible_modules'

module Rex
  module Post
    module Sql
      module Ui

        #
        # Base console class for Generic SQL consoles
        #
        module Console

          include Rex::Ui::Text::DispatcherShell
          include Rex::Post::SessionCompatibleModules

          # Called when someone wants to interact with an SQL client.  It's
          # assumed that init_ui has been called prior.
          #
          # @param [Proc] block
          # @return [Integer]
          def interact(&block)
            # Run queued commands
            commands.delete_if do |ent|
              run_single(ent)
              true
            end

            # Run the interactive loop
            run do |line|
              # Run the command
              run_single(line)

              # If a block was supplied, call it, otherwise return false
              if block
                block.call
              else
                false
              end
            end
          end

          # Queues a command to be run when the interactive loop is entered.
          #
          # @param [Object] cmd
          # @return [Object]
          def queue_cmd(cmd)
            self.commands << cmd
          end

          # Runs the specified command wrapper in something to catch meterpreter
          # exceptions.
          #
          # @param [Object] dispatcher
          # @param [Object] method
          # @param [Object] arguments
          # @return [FalseClass]
          def run_command(dispatcher, method, arguments)
            begin
              super
            rescue ::Timeout::Error
              log_error('Operation timed out.')
            rescue ::Rex::InvalidDestination => e
              log_error(e.message)
            rescue ::Errno::EPIPE, ::OpenSSL::SSL::SSLError, ::IOError
              self.session.kill
            rescue ::StandardError => e
              log_error("Error running command #{method}: #{e.class} #{e}")
              elog(e)
            end
          end

          # @param [Hash] opts
          # @return [String]
          def help_to_s(opts = {})
            super + format_session_compatible_modules
          end

          #
          # Notification to display when initially interacting with the client via the query_interactive command
          #
          # @return [String]
          def interact_with_client_notification
            print_status("Starting interactive SQL shell for #{sql_prompt}")
            print_status('SQL commands ending with ; will be executed on the remote server. Use the %grnexit%clr command to exit.')
            print_line
          end

          #
          # Create prompt via client and session data
          #
          # @return [String]
          def sql_prompt
            "#{session.type} @ #{client.peerinfo}#{current_database.blank? ? '' : " (#{current_database})"}"
          end

          #
          # Interacts with the supplied client.
          #
          def interact_with_client(client_dispatcher: nil)
            return unless client_dispatcher

            interact_with_client_notification
            client.extend(InteractiveSqlClient) unless client.is_a?(InteractiveSqlClient)
            client.on_command_proc = self.on_command_proc if self.on_command_proc && client.respond_to?(:on_command_proc)
            client.on_print_proc   = self.on_print_proc if self.on_print_proc && client.respond_to?(:on_print_proc)
            client.on_log_proc = method(:log_output) if self.respond_to?(:log_output, true) && client.respond_to?(:on_log_proc)
            client.client_dispatcher = client_dispatcher

            client.interact(input, output)
            client.reset_ui
          end

          # @param [Object] val
          # @return [String]
          def format_prompt(val)
            substitute_colors("%und#{sql_prompt}%clr > ", true)
          end

          #
          # Log that an error occurred.
          #
          def log_error(msg)
            print_error(msg)

            elog(msg, session.type)

            dlog("Call stack:\n#{$ERROR_POSITION.join("\n")}", session.type)
          end

          def current_database
            client.current_database
          end
        end
      end
    end
  end
end