Module: Msf::PostMixin
- Includes:
- Auxiliary::Report, Module::HasActions, Msf::Post::Common
- Defined in:
- lib/msf/core/post_mixin.rb
Overview
A mixin used for providing Modules with post-exploitation options and helper methods
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.
-
#sysinfo ⇒ Hash?
Cached sysinfo, returns nil for non-meterpreter sessions.
Methods included from Msf::Post::Common
#clear_screen, #cmd_exec, #cmd_exec_get_pid, #cmd_exec_with_result, #command_exists?, #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
231 232 233 |
# File 'lib/msf/core/post_mixin.rb', line 231 def passive @passive end |
#session_types ⇒ Array
A list of compatible session types
237 238 239 |
# File 'lib/msf/core/post_mixin.rb', line 237 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.
71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 |
# File 'lib/msf/core/post_mixin.rb', line 71 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
90 91 |
# File 'lib/msf/core/post_mixin.rb', line 90 def cleanup end |
#command_names_for(command_ids) ⇒ Object (protected)
327 328 329 |
# File 'lib/msf/core/post_mixin.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
147 148 149 150 151 152 153 |
# File 'lib/msf/core/post_mixin.rb', line 147 def compatible_sessions sessions = [] framework.sessions.each do |sid, s| sessions << sid if session_compatible?(s) end sessions end |
#initialize(info = {}) ⇒ Object
12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
# File 'lib/msf/core/post_mixin.rb', line 12 def initialize(info = {}) super( update_info( info, 'Compat' => { 'Meterpreter' => { 'Commands' => %w[ stdapi_sys_config_sysinfo ] } } ) ) ( [ Msf::OptInt.new('SESSION', [ true, 'The session to run this module on' ]) ] , Msf::Post) # 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/post_mixin.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
139 140 141 |
# File 'lib/msf/core/post_mixin.rb', line 139 def passive? self.passive end |
#post_commands ⇒ Object
Can be overridden by individual modules to add new commands
134 135 136 |
# File 'lib/msf/core/post_mixin.rb', line 134 def post_commands {} end |
#session ⇒ Msf::Session? Also known as: client
Return the associated session or nil if there isn't one
99 100 101 102 103 104 105 106 107 108 109 110 |
# File 'lib/msf/core/post_mixin.rb', line 99 def session # Try the cached one return @session if @session and not 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/post_mixin.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
.
175 176 177 |
# File 'lib/msf/core/post_mixin.rb', line 175 def session_compatible?(sess_or_sid) session_incompatibility_reasons(sess_or_sid).empty? end |
#session_display_info ⇒ Object
112 113 114 |
# File 'lib/msf/core/post_mixin.rb', line 112 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.
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 |
# File 'lib/msf/core/post_mixin.rb', line 183 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 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 issues << "incompatible session type: #{s.type}" unless session_types.include?(s.type) end # Check to make sure architectures match mod_arch = self.module_info['Arch'] if mod_arch mod_arch = [mod_arch] unless mod_arch.kind_of?(Array) # 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 # Arch is okay, now check the platform. if self.platform && self.platform.kind_of?(Msf::Module::PlatformList) issues << "incompatible session platform: #{s.platform}" unless self.platform.supports?(Msf::Module::PlatformList.transform(s.platform)) 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. Initializes user input and output on the session.
40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 |
# File 'lib/msf/core/post_mixin.rb', line 40 def setup alert_user unless session || (datastore['SESSION'].blank? && !['SESSION']&.required) raise Msf::OptionValidateError.new(['SESSION']) end if 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 end # Msf::Exploit#setup for exploits, NoMethodError for post modules super rescue NoMethodError @session.init_ui(self.user_input, self.user_output) if @session @sysinfo = nil end |
#sysinfo ⇒ Hash?
Cached sysinfo, returns nil for non-meterpreter sessions
122 123 124 125 126 127 128 129 |
# File 'lib/msf/core/post_mixin.rb', line 122 def sysinfo begin @sysinfo ||= session.sys.config.sysinfo rescue NoMethodError @sysinfo = nil end @sysinfo end |