Module: Rex::Post::Sql::Ui::Console::CommandDispatcher::Client

Overview

Core Generic SQL client commands

Constant Summary collapse

@@query_opts =
Rex::Parser::Arguments.new(
['-h', '--help'] => [false, 'Help menu.'],
['-i', '--interact'] => [false,  'Enter an interactive prompt for running multiple SQL queries'],
)

Instance Attribute Summary

Attributes included from Ui::Text::DispatcherShell::CommandDispatcher

#shell, #tab_complete_items

Instance Method Summary collapse

Methods included from Rex::Post::Sql::Ui::Console::CommandDispatcher

#client, #filter_commands, #msf_loaded?, #session, #unknown_command

Methods included from Msf::Ui::Console::CommandDispatcher::Session

#cmd_background, #cmd_background_help, #cmd_exit, #cmd_irb, #cmd_irb_help, #cmd_irb_tabs, #cmd_pry, #cmd_pry_help, #cmd_resource, #cmd_resource_help, #cmd_resource_tabs, #cmd_sessions, #cmd_sessions_help

Methods included from Ui::Text::DispatcherShell::CommandDispatcher

#cmd_help, #cmd_help_help, #cmd_help_tabs, #deprecated_cmd, #deprecated_commands, #deprecated_help, #docs_dir, #help_to_s, included, #print, #print_error, #print_good, #print_line, #print_status, #print_warning, #tab_complete_directory, #tab_complete_filenames, #tab_complete_generic, #tab_complete_source_address, #unknown_command, #update_prompt

Instance Method Details

#cmd_query(*args) ⇒ Object



136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
# File 'lib/rex/post/sql/ui/console/command_dispatcher/client.rb', line 136

def cmd_query(*args)
  show_help = false
  call_interactive = false

  @@query_opts.parse(args) do |opt, _idx, val|
    case opt
    when nil
      show_help = true if val == 'help'
      break
    when '-h', '--help'
      show_help = true
      break
    when '-i', '--interact'
      call_interactive = true
      break
    end
  end

  if args.empty? || show_help
    cmd_query_help
    return
  end

  if call_interactive
    cmd_query_interactive
    return
  end

  result = run_query(args.join(' '))
  case result[:status]
  when :success
    # When changing a database in MySQL, we get a nil result back.
    if result[:result].nil?
      print_status 'Query executed successfully'
      return
    end

    normalised_result = normalise_sql_result(result[:result])

    # MSSQL returns :success, even if the query failed due to wrong syntax.
    if normalised_result[:errors].any?
      print_error "Query has failed. Reasons: #{normalised_result[:errors].join(', ')}"
      return
    end

    # When changing a database in MSSQL, we get a result, but it doesn't contain colnames or rows.
    if normalised_result[:rows].nil? || normalised_result[:columns].nil?
      print_status 'Query executed successfully'
      return
    end

    formatted_result = format_result(normalised_result)
    print_line(formatted_result.to_s)
  when :error
    print_error "Query has failed. Reasons: #{result[:result][:errors].join(', ')}"
    result[:result][:errors].each do |error|
      handle_error(error)
    end
  else
    elog "Unknown query status: #{result[:status]}"
    print_error "Unknown query status: #{result[:status]}"
  end
end

#cmd_query_helpObject

Raises:

  • (::NotImplementedError)


116
117
118
# File 'lib/rex/post/sql/ui/console/command_dispatcher/client.rb', line 116

def cmd_query_help
  raise ::NotImplementedError
end

#cmd_query_interactive(*args) ⇒ Object



79
80
81
82
83
84
85
86
87
88
# File 'lib/rex/post/sql/ui/console/command_dispatcher/client.rb', line 79

def cmd_query_interactive(*args)
  if help_args?(args)
    cmd_query_interactive_help
    return
  end

  console = shell
  # Pass in self so that we can call cmd_query in subsequent calls
  console.interact_with_client(client_dispatcher: self)
end

#cmd_query_interactive_helpObject



62
63
64
65
66
67
68
# File 'lib/rex/post/sql/ui/console/command_dispatcher/client.rb', line 62

def cmd_query_interactive_help
  print_line 'Usage: query_interactive'
  print_line
  print_line 'Go into an interactive SQL shell where SQL queries can be executed.'
  print_line "To exit, type 'exit', 'quit', 'end' or 'stop'."
  print_line
end

#commandsObject

List of supported commands.



38
39
40
41
42
43
44
45
46
47
# File 'lib/rex/post/sql/ui/console/command_dispatcher/client.rb', line 38

def commands
  cmds = {
    'query'   => 'Run a single SQL query',
    'query_interactive'   => 'Enter an interactive prompt for running multiple SQL queries',
  }

  reqs = {}

  filter_commands(cmds, reqs)
end

#format_result(result) ⇒ Object

Take in a normalised SQL result and print it. If there are any errors, print those instead.

Parameters:

  • result (Hash {Symbol => Array, Symbol => Array, Symbol => Array})

    A hash of 'rows', 'columns' and 'errors'



97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
# File 'lib/rex/post/sql/ui/console/command_dispatcher/client.rb', line 97

def format_result(result)
  if result[:errors].any?
    return "Query has failed. Reasons: #{result[:errors].join(', ')}"
  end

  number_column = ['#']
  columns = [number_column, result[:columns]].flatten
  rows = result[:rows].map.each_with_index do |row, i|
    [i, row].flatten
  end

  ::Rex::Text::Table.new(
    'Header' => 'Response',
    'Indent' => 4,
    'Columns' => columns,
    'Rows' => rows
  )
end

#handle_error(e) ⇒ Object

Handles special cased error for each protocol that are return when a session has died resulting in a session needing to be closed

Parameters:

  • e (Object)


211
212
213
214
215
216
# File 'lib/rex/post/sql/ui/console/command_dispatcher/client.rb', line 211

def handle_error(e)
  case e
  when EOFError
    _close_session
  end
end

#help_args?(args) ⇒ TrueClass, FalseClass

Returns True if the array contains '-h' or '–help', else false.

Parameters:

  • args (Array)

    An array of arguments passed in to a command

Returns:

  • (TrueClass, FalseClass)

    True if the array contains '-h' or '--help', else false.



56
57
58
59
60
# File 'lib/rex/post/sql/ui/console/command_dispatcher/client.rb', line 56

def help_args?(args)
  return false unless args.instance_of?(::Array)

  args.include?('-h') || args.include?('--help')
end

#initialize(console) ⇒ Object

Initializes an instance of the core command set using the supplied console for interactivity.

Parameters:

  • console

    The protocol-specific Rex Ui Console



29
30
31
32
33
# File 'lib/rex/post/sql/ui/console/command_dispatcher/client.rb', line 29

def initialize(console)
  super

  @db_search_results = []
end

#nameString

Returns The name of the client.

Returns:

  • (String)

    The name of the client

Raises:

  • (::NotImplementedError)


50
51
52
# File 'lib/rex/post/sql/ui/console/command_dispatcher/client.rb', line 50

def name
  raise ::NotImplementedError
end

#normalise_sql_result(result) ⇒ Object

Raises:

  • (::NotImplementedError)


90
91
92
# File 'lib/rex/post/sql/ui/console/command_dispatcher/client.rb', line 90

def normalise_sql_result(result)
  raise ::NotImplementedError
end

#process_query(query: '') ⇒ Object



200
201
202
203
204
# File 'lib/rex/post/sql/ui/console/command_dispatcher/client.rb', line 200

def process_query(query: '')
  return '' if query.empty?

  query.lines.each.map { |line| line.chomp.chomp('\\').strip }.reject(&:empty?).compact.join(' ')
end

#query_interactive_helpObject



70
71
72
73
74
75
76
77
# File 'lib/rex/post/sql/ui/console/command_dispatcher/client.rb', line 70

def query_interactive_help
  print_line 'Interactive SQL prompt'
  print_line
  print_line 'You are in an interactive SQL shell where SQL queries can be executed.'
  print_line 'SQL commands ending with ; will be executed on the remote server.'
  print_line "To exit, type 'exit', 'quit', 'end' or 'stop'."
  print_line
end

#run_query(query) ⇒ Object



120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
# File 'lib/rex/post/sql/ui/console/command_dispatcher/client.rb', line 120

def run_query(query)
  begin
    result = client.query(query)
  rescue => e
    elog("Running query '#{query}' failed on session #{self.inspect}", error: e)
    return { status: :error, result: { errors: [e] } }
  end

  if result.respond_to?(:cmd_tag) && result.cmd_tag
    print_status result.cmd_tag
    print_line
  end

  { status: :success, result: result }
end