140 lines
4.3 KiB
Ruby
140 lines
4.3 KiB
Ruby
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
|