Module: Msf::Ui::Console::ModuleCommandDispatcher

Overview

Module-specific command dispatcher.

Constant Summary collapse

@@reload_opts =
Rex::Parser::Arguments.new(
'-k' => [ false,  'Stop the current job before reloading.' ],
'-h' => [ false,  'Help banner.' ])

Instance Attribute Summary

Attributes included from CommandDispatcher

#driver

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

#shell, #tab_complete_items

Instance Method Summary collapse

Methods included from CommandDispatcher

#active_module, #active_module=, #active_session, #active_session=, #defanged?, #framework, #initialize, #log_error

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

#cmd_help, #cmd_help_help, #cmd_help_tabs, #deprecated_cmd, #deprecated_commands, #deprecated_help, #help_to_s, #initialize, #print, #print_error, #print_good, #print_line, #print_status, #print_warning, #tab_complete_filenames, #update_prompt

Instance Method Details

#check_multiple(hosts) ⇒ Object


53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
# File 'lib/msf/ui/console/module_command_dispatcher.rb', line 53

def check_multiple(hosts)
  # This part of the code is mostly from scanner.rb
  @show_progress = framework.datastore['ShowProgress'] || mod.datastore['ShowProgress'] || false
  @show_percent  = ( framework.datastore['ShowProgressPercent'] || mod.datastore['ShowProgressPercent'] ).to_i

  @range_count   = hosts.length || 0
  @range_done    = 0
  @range_percent = 0

  # Set the default thread to 1. The same behavior as before.
  threads_max = (framework.datastore['THREADS'] || mod.datastore['THREADS'] || 1).to_i
  @tl = []


  if Rex::Compat.is_windows
    if threads_max > 16
      print_warning("Thread count has been adjusted to 16")
      threads_max = 16
    end
  end

  if Rex::Compat.is_cygwin
    if threads_max > 200
      print_warning("Thread count has been adjusted to 200")
      threads_max = 200
    end
  end

  loop do
    while (@tl.length < threads_max)
      ip = hosts.next_ip
      break unless ip

      @tl << framework.threads.spawn("CheckHost-#{ip}", false, ip.dup) { |tip|
        # Make sure this is thread-safe when assigning an IP to the RHOST
        # datastore option
        instance = mod.replicant
        instance.datastore['RHOST'] = tip.dup
        Msf::Simple::Framework.simplify_module(instance, false)
        check_simple(instance)
      }
    end

    break if @tl.length == 0

    tla = @tl.length

    # This exception handling is necessary, the first thread with errors can kill the
    # whole check_multiple and leave the rest of the threads running in background and
    # only accessible with the threads command (Thread.list)
    begin
      @tl.first.join
    rescue ::Exception => exception
      if exception.kind_of?(::Interrupt)
        raise exception
      else
        elog("#{exception} #{exception.class}:\n#{exception.backtrace.join("\n")}")
      end
    end

    @tl.delete_if { |t| not t.alive? }
    tlb = @tl.length

    @range_done += (tla - tlb)
    check_show_progress if @show_progress
  end
end

#check_progressObject


39
40
41
42
# File 'lib/msf/ui/console/module_command_dispatcher.rb', line 39

def check_progress
  return 0 unless @range_done and @range_count
  pct = (@range_done / @range_count.to_f) * 100
end

#check_show_progressObject


44
45
46
47
48
49
50
51
# File 'lib/msf/ui/console/module_command_dispatcher.rb', line 44

def check_show_progress
  pct = check_progress
  if(pct >= (@range_percent + @show_percent))
    @range_percent = @range_percent + @show_percent
    tdlen = @range_count.to_s.length
    print_status("Checked #{"%.#{tdlen}d" % @range_done} of #{@range_count} hosts (#{"%.3d" % pct.to_i}% complete)")
  end
end

#check_simple(instance = nil) ⇒ Object


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
# File 'lib/msf/ui/console/module_command_dispatcher.rb', line 161

def check_simple(instance=nil)
  unless instance
    instance = mod 
  end

  rhost = instance.rhost
  rport = instance.rport

  begin
    code = instance.check_simple(
      'LocalInput'  => driver.input,
      'LocalOutput' => driver.output)
    if (code and code.kind_of?(Array) and code.length > 1)
      if (code == Msf::Exploit::CheckCode::Vulnerable)
        print_good("#{rhost}:#{rport} - #{code[1]}")
      else
        print_status("#{rhost}:#{rport} - #{code[1]}")
      end
    else
      print_error("#{rhost}:#{rport} - Check failed: The state could not be determined.")
    end
  rescue ::Rex::ConnectionError, ::Rex::ConnectionProxyError, ::Errno::ECONNRESET, ::Errno::EINTR, ::Rex::TimeoutError, ::Timeout::Error
    # Connection issues while running check should be handled by the module
  rescue ::RuntimeError
    # Some modules raise RuntimeError but we don't necessarily care about those when we run check()
  rescue Msf::OptionValidateError => e
    print_error("Check failed: #{e.message}")
  rescue ::Exception => e
    print_error("#{rhost}:#{rport} - Check failed: #{e.class} #{e}")
  end
end

#cmd_check(*args) ⇒ Object

Checks to see if a target is vulnerable.


124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
# File 'lib/msf/ui/console/module_command_dispatcher.rb', line 124

def cmd_check(*args)
  defanged?

  ip_range_arg = args.shift || mod.datastore['RHOSTS'] || framework.datastore['RHOSTS'] || ''
  hosts = Rex::Socket::RangeWalker.new(ip_range_arg)

  begin
    if hosts.ranges.blank?
      # Check a single rhost
      check_simple
    else
      # Check multiple hosts
      last_rhost_opt = mod.rhost
      last_rhosts_opt = mod.datastore['RHOSTS']
      mod.datastore['RHOSTS'] = ip_range_arg
      begin
        check_multiple(hosts)
      ensure
        # Restore the original rhost if set
        mod.datastore['RHOST'] = last_rhost_opt
        mod.datastore['RHOSTS'] = last_rhosts_opt
        mod.cleanup
      end
    end
  rescue ::Interrupt
    # When the user sends interrupt trying to quit the task, some threads will still be active.
    # This means even though the console tells the user the task has aborted (or at least they
    # assume so), the checks are still running. Because of this, as soon as we detect interrupt,
    # we force the threads to die.
    if @tl
      @tl.each { |t| t.kill }
    end
    print_status("Caught interrupt from the console...")
    return
  end
end

#cmd_pry(*args) ⇒ Object


201
202
203
204
205
206
207
208
209
# File 'lib/msf/ui/console/module_command_dispatcher.rb', line 201

def cmd_pry(*args)
  begin
    require 'pry'
  rescue LoadError
    print_error("Failed to load pry, try 'gem install pry'")
    return
  end
  mod.pry
end

#cmd_pry_helpObject


193
194
195
196
197
198
199
# File 'lib/msf/ui/console/module_command_dispatcher.rb', line 193

def cmd_pry_help
  print_line "Usage: pry"
  print_line
  print_line "Open a pry session on the current module.  Be careful, you"
  print_line "can break things."
  print_line
end

#cmd_reload(*args) ⇒ Object

Reloads the active module


214
215
216
217
218
219
220
# File 'lib/msf/ui/console/module_command_dispatcher.rb', line 214

def cmd_reload(*args)
  begin
    reload
  rescue
    log_error("Failed to reload: #{$!}")
  end
end

#cmd_reload_helpObject


226
227
228
229
230
231
# File 'lib/msf/ui/console/module_command_dispatcher.rb', line 226

def cmd_reload_help
  print_line "Usage: reload [-k]"
  print_line
  print_line "Reloads the current module."
  print @@reload_opts.usage
end

#commandsObject


17
18
19
20
21
22
23
# File 'lib/msf/ui/console/module_command_dispatcher.rb', line 17

def commands
  {
    "pry"    => "Open a Pry session on the current module",
    "reload" => "Reload the current module from disk",
    "check"  => "Check to see if a target is vulnerable"
  }
end

#modObject

The active driver module, if any.


28
29
30
# File 'lib/msf/ui/console/module_command_dispatcher.rb', line 28

def mod
  return driver.active_module
end

#mod=(m) ⇒ Object

Sets the active driver module.


35
36
37
# File 'lib/msf/ui/console/module_command_dispatcher.rb', line 35

def mod=(m)
  self.driver.active_module = m
end

#reload(should_stop_job = false) ⇒ Object

Reload the current module, optionally stopping existing job


236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
# File 'lib/msf/ui/console/module_command_dispatcher.rb', line 236

def reload(should_stop_job=false)
  if should_stop_job and mod.job_id
    print_status('Stopping existing job...')

    framework.jobs.stop_job(mod.job_id)
    mod.job_id = nil
  end

  print_status('Reloading module...')

  original_mod = self.mod
  reloaded_mod = framework.modules.reload_module(original_mod)

  unless reloaded_mod
    error = framework.modules.module_load_error_by_path[original_mod.file_path]

    print_error("Failed to reload module: #{error}")

    self.mod = original_mod
  else
    self.mod = reloaded_mod

    self.mod.init_ui(driver.input, driver.output)
  end

  reloaded_mod
end