Module: Msf::SessionCompatibility
- Includes:
- Auxiliary::Report, Module::HasActions, Post::Common
- Included in:
- OptionalSession, PostMixin
- Defined in:
- lib/msf/core/session_compatibility.rb
Instance Attribute Summary collapse
-
#passive ⇒ Boolean
True when this module is passive, false when active.
-
#session_types ⇒ Array
A list of compatible session types.
Attributes included from Module::HasActions
#actions, #default_action, #passive_actions
Instance Method Summary collapse
-
#check_for_session_readiness(tries = 6) ⇒ Object
Meterpreter sometimes needs a little bit of extra time to actually be responsive for post modules.
-
#cleanup ⇒ Object
Default cleanup handler does nothing.
- #command_names_for(command_ids) ⇒ Object protected
-
#compatible_sessions ⇒ Array
Return a (possibly empty) list of all compatible sessions.
- #initialize(info = {}) ⇒ Object
-
#meterpreter_session_incompatibility_reasons(session) ⇒ Object
protected
Return the reasons why a meterpreter session is incompatible.
-
#passive? ⇒ Boolean
Whether this module’s Exploit::Stance is passive.
-
#post_commands ⇒ Object
Can be overridden by individual modules to add new commands.
-
#session ⇒ Msf::Session?
(also: #client)
Return the associated session or nil if there isn’t one.
- #session_changed? ⇒ Boolean protected
-
#session_compatible?(sess_or_sid) ⇒ Boolean
Return false if the given session is not compatible with this module.
- #session_display_info ⇒ Object
-
#session_incompatibility_reasons(sess_or_sid) ⇒ Object
Return the reasons why a session is incompatible.
-
#setup ⇒ Object
Grabs a session object from the framework or raises OptionValidateError if one doesn’t exist and is required.
-
#sysinfo ⇒ Hash?
Cached sysinfo, returns nil for non-meterpreter sessions.
Methods included from Post::Common
#clear_screen, #cmd_exec, #cmd_exec_get_pid, #cmd_exec_with_result, #command_exists?, #create_process, #get_env, #get_envs, #peer, #report_virtualization, #rhost, #rport
Methods included from Module::HasActions
#action, #find_action, #passive_action?
Methods included from Auxiliary::Report
#active_db?, #create_cracked_credential, #create_credential, #create_credential_and_login, #create_credential_login, #db, #db_warning_given?, #get_client, #get_host, #inside_workspace_boundary?, #invalidate_login, #mytask, #myworkspace, #myworkspace_id, #report_auth_info, #report_client, #report_exploit, #report_host, #report_loot, #report_note, #report_service, #report_vuln, #report_web_form, #report_web_page, #report_web_site, #report_web_vuln, #store_cred, #store_local, #store_loot
Methods included from Metasploit::Framework::Require
optionally, optionally_active_record_railtie, optionally_include_metasploit_credential_creation, #optionally_include_metasploit_credential_creation, optionally_require_metasploit_db_gem_engines
Instance Attribute Details
#passive ⇒ Boolean
True when this module is passive, false when active
232 233 234 |
# File 'lib/msf/core/session_compatibility.rb', line 232 def passive @passive end |
#session_types ⇒ Array
A list of compatible session types
238 239 240 |
# File 'lib/msf/core/session_compatibility.rb', line 238 def session_types @session_types end |
Instance Method Details
#check_for_session_readiness(tries = 6) ⇒ Object
Meterpreter sometimes needs a little bit of extra time to actually be responsive for post modules. Default tries and retries for 5 seconds.
58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 |
# File 'lib/msf/core/session_compatibility.rb', line 58 def check_for_session_readiness(tries=6) session_ready_count = 0 session_ready = false until session.sys or session_ready_count > tries session_ready_count += 1 back_off_period = (session_ready_count**2)/10.0 select(nil,nil,nil,back_off_period) end session_ready = !!session.sys unless session_ready raise "The stdapi extension has not been loaded yet." unless session.tlv_enc_key.nil? raise "Could not get a hold of the session." end return session_ready end |
#cleanup ⇒ Object
Default cleanup handler does nothing
77 78 79 |
# File 'lib/msf/core/session_compatibility.rb', line 77 def cleanup super if defined?(super) end |
#command_names_for(command_ids) ⇒ Object (protected)
327 328 329 |
# File 'lib/msf/core/session_compatibility.rb', line 327 def command_names_for(command_ids) command_ids.map { |id| Rex::Post::Meterpreter::CommandMapper.get_command_name(id) }.join(', ') end |
#compatible_sessions ⇒ Array
Return a (possibly empty) list of all compatible sessions
135 136 137 138 139 140 141 |
# File 'lib/msf/core/session_compatibility.rb', line 135 def compatible_sessions sessions = [] framework.sessions.each do |sid, s| sessions << sid if session_compatible?(s) end sessions end |
#initialize(info = {}) ⇒ Object
10 11 12 13 14 15 16 |
# File 'lib/msf/core/session_compatibility.rb', line 10 def initialize(info = {}) super # Default stance is active self.passive = info['Passive'] || false self.session_types = info['SessionTypes'] || [] end |
#meterpreter_session_incompatibility_reasons(session) ⇒ Object (protected)
Return the reasons why a meterpreter session is incompatible. Checks all specified meterpreter commands are provided by the remote session
260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 |
# File 'lib/msf/core/session_compatibility.rb', line 260 def meterpreter_session_incompatibility_reasons(session) cmd_name_wildcards = module_info.dig('Compat', 'Meterpreter', 'Commands') || [] cmd_names = Rex::Post::Meterpreter::CommandMapper.get_command_names.select do |cmd_name| cmd_name_wildcards.any? { |cmd_name_wildcard| ::File.fnmatch(cmd_name_wildcard, cmd_name) } end unmatched_wildcards = cmd_name_wildcards.select { |cmd_name_wildcard| cmd_names.none? { |cmd_name| ::File.fnmatch(cmd_name_wildcard, cmd_name) } } unless unmatched_wildcards.empty? # This implies that there was a typo in one of the wildcards because it didn't match anything. This is a developer mistake. wlog("The #{fullname} module specified the following Meterpreter command wildcards that did not match anything: #{ unmatched_wildcards.join(', ') }") end cmd_ids = cmd_names.map { |name| Rex::Post::Meterpreter::CommandMapper.get_command_id(name) } # XXX: Remove this condition once the payloads gem has had another major version bump from 2.x to 3.x and # rapid7/metasploit-payloads#451 has been landed to correct the `enumextcmd` behavior on Windows. Until then, skip # proactive validation of Windows core commands. This is not the only instance of this workaround. if session.base_platform == 'windows' cmd_ids = cmd_ids.select do |cmd_id| !cmd_id.between?( Rex::Post::Meterpreter::ClientCore.extension_id, Rex::Post::Meterpreter::ClientCore.extension_id + Rex::Post::Meterpreter::COMMAND_ID_RANGE - 1 ) end end # Windows does not support chmod, but will be defined by default in the file mixin if session.base_platform == 'windows' cmd_ids -= [Rex::Post::Meterpreter::Extensions::Stdapi::COMMAND_ID_STDAPI_FS_CHMOD] end missing_cmd_ids = (cmd_ids - session.commands) unless missing_cmd_ids.empty? # If there are missing commands, try to load the necessary extension. # If core_loadlib isn't supported, then extensions can't be loaded return ['missing Meterpreter features: core can not be extended'] unless session.commands.include?(Rex::Post::Meterpreter::COMMAND_ID_CORE_LOADLIB) # Since core is already loaded, if the missing command is a core command then it's truly missing missing_core_cmd_ids = missing_cmd_ids.select do |cmd_id| cmd_id.between?( Rex::Post::Meterpreter::ClientCore.extension_id, Rex::Post::Meterpreter::ClientCore.extension_id + Rex::Post::Meterpreter::COMMAND_ID_RANGE - 1 ) end if missing_core_cmd_ids.any? return ["missing Meterpreter features: #{command_names_for(missing_core_cmd_ids)}"] end missing_extensions = missing_cmd_ids.map { |cmd_id| Rex::Post::Meterpreter::ExtensionMapper.get_extension_name(cmd_id) }.uniq missing_extensions.each do |ext_name| # If the extension is already loaded, the command is truly missing return ["missing Meterpreter features: #{command_names_for(missing_cmd_ids)}"] if session.ext.aliases.include?(ext_name) begin session.core.use(ext_name) rescue RuntimeError return ["unloadable Meterpreter extension: #{ext_name}"] end end end missing_cmd_ids -= session.commands return ["missing Meterpreter features: #{command_names_for(missing_cmd_ids)}"] unless missing_cmd_ids.empty? [] end |
#passive? ⇒ Boolean
Whether this module’s Exploit::Stance is passive
127 128 129 |
# File 'lib/msf/core/session_compatibility.rb', line 127 def passive? passive end |
#post_commands ⇒ Object
Can be overridden by individual modules to add new commands
122 123 124 |
# File 'lib/msf/core/session_compatibility.rb', line 122 def post_commands {} end |
#session ⇒ Msf::Session? Also known as: client
Return the associated session or nil if there isn’t one
87 88 89 90 91 92 93 94 95 96 97 98 |
# File 'lib/msf/core/session_compatibility.rb', line 87 def session # Try the cached one return @session if @session && !session_changed? if datastore['SESSION'] @session = framework.sessions.get(datastore['SESSION'].to_i) else @session = nil end @session end |
#session_changed? ⇒ Boolean (protected)
244 245 246 247 248 249 250 251 252 253 |
# File 'lib/msf/core/session_compatibility.rb', line 244 def session_changed? @ds_session ||= datastore['SESSION'] if (@ds_session != datastore['SESSION']) @ds_session = nil return true else return false end end |
#session_compatible?(sess_or_sid) ⇒ Boolean
Because it errs on the side of compatibility, a true return value from this method does not guarantee the module will work with the session. For example, ARCH_CMD modules can work on a variety of platforms and archs and thus return true in this check.
Return false if the given session is not compatible with this module
Checks the session’s type against this module’s module_info["SessionTypes"]
as well as examining platform and arch compatibility.
sess_or_sid
can be a Session object, Integer, or String. In the latter cases it should be a key in framework.sessions
.
163 164 165 |
# File 'lib/msf/core/session_compatibility.rb', line 163 def session_compatible?(sess_or_sid) session_incompatibility_reasons(sess_or_sid).empty? end |
#session_display_info ⇒ Object
100 101 102 |
# File 'lib/msf/core/session_compatibility.rb', line 100 def session_display_info "Session: #{session.sid} (#{session.session_host})" end |
#session_incompatibility_reasons(sess_or_sid) ⇒ Object
Return the reasons why a session is incompatible.
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 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 |
# File 'lib/msf/core/session_compatibility.rb', line 171 def session_incompatibility_reasons(sess_or_sid) # Normalize the argument to an actual Session case sess_or_sid when ::Integer, ::String s = framework.sessions[sess_or_sid.to_i] when ::Msf::Session s = sess_or_sid when nil # No session provided return [] end issues = [] # Can't do anything without a session unless s issues << ['invalid session'] return issues end # Can't be compatible if it's the wrong type if session_types && !session_types.include?(s.type) issues << "incompatible session type: #{s.type}. This module works with: #{session_types.join(', ')}." end # Check to make sure architectures match mod_arch = module_info['Arch'] if mod_arch if s.arch.blank? issues << 'Unknown session arch' else mod_arch = Array.wrap(mod_arch) # Assume ARCH_CMD modules can work on supported SessionTypes since both shell and meterpreter types can execute commands issues << "incompatible session architecture: #{s.arch}" unless mod_arch.include?(s.arch) || mod_arch.include?(ARCH_CMD) end end # Arch is okay, now check the platform. if platform && platform.is_a?(Msf::Module::PlatformList) && !platform.empty? if s.platform.blank? issues << "Unknown session platform. This module works with: #{platform.names.join(', ')}." elsif !platform.supports?(Msf::Module::PlatformList.transform(s.platform)) issues << "incompatible session platform: #{s.platform}. This module works with: #{platform ? platform.names.join(', ') : platform.inspect}." end end # Check all specified meterpreter commands are provided by the remote session if s.type == 'meterpreter' issues += meterpreter_session_incompatibility_reasons(s) end issues end |
#setup ⇒ Object
Grabs a session object from the framework or raises OptionValidateError if one doesn’t exist and is required. Initializes user input and output on the session.
23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 |
# File 'lib/msf/core/session_compatibility.rb', line 23 def setup alert_user if ['SESSION']&.required && session.blank? raise Msf::OptionValidateError, ['SESSION'] end if datastore['SESSION'] && session.nil? raise Msf::OptionValidateError, ['SESSION'] end # Msf::Exploit#setup for exploits, NoMethodError for post modules super rescue NoMethodError return unless session # Check session readiness before compatibility so the session can be queried # for its platform, capabilities, etc. check_for_session_readiness if session.type == "meterpreter" incompatibility_reasons = session_incompatibility_reasons(session) if incompatibility_reasons.any? print_warning('SESSION may not be compatible with this module:') incompatibility_reasons.each do |reason| print_warning(" * #{reason}") end end @session.init_ui(user_input, user_output) if @session @sysinfo = nil end |
#sysinfo ⇒ Hash?
Cached sysinfo, returns nil for non-meterpreter sessions
110 111 112 113 114 115 116 117 |
# File 'lib/msf/core/session_compatibility.rb', line 110 def sysinfo begin @sysinfo ||= session.sys.config.sysinfo rescue NoMethodError @sysinfo = nil end @sysinfo end |