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
237 238 239 |
# File 'lib/msf/core/post_mixin.rb', line 237 def passive @passive end |
#session_types ⇒ Array
A list of compatible session types
243 244 245 |
# File 'lib/msf/core/post_mixin.rb', line 243 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.
77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 |
# File 'lib/msf/core/post_mixin.rb', line 77 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
96 97 |
# File 'lib/msf/core/post_mixin.rb', line 96 def cleanup end |
#command_names_for(command_ids) ⇒ Object (protected)
333 334 335 |
# File 'lib/msf/core/post_mixin.rb', line 333 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
153 154 155 156 157 158 159 |
# File 'lib/msf/core/post_mixin.rb', line 153 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
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 326 327 328 329 330 331 |
# File 'lib/msf/core/post_mixin.rb', line 266 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
145 146 147 |
# File 'lib/msf/core/post_mixin.rb', line 145 def passive? self.passive end |
#post_commands ⇒ Object
Can be overridden by individual modules to add new commands
140 141 142 |
# File 'lib/msf/core/post_mixin.rb', line 140 def post_commands {} end |
#session ⇒ Msf::Session? Also known as: client
Return the associated session or nil if there isn't one
105 106 107 108 109 110 111 112 113 114 115 116 |
# File 'lib/msf/core/post_mixin.rb', line 105 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)
250 251 252 253 254 255 256 257 258 259 |
# File 'lib/msf/core/post_mixin.rb', line 250 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
.
181 182 183 |
# File 'lib/msf/core/post_mixin.rb', line 181 def session_compatible?(sess_or_sid) session_incompatibility_reasons(sess_or_sid).empty? end |
#session_display_info ⇒ Object
118 119 120 |
# File 'lib/msf/core/post_mixin.rb', line 118 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.
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 226 227 228 229 230 |
# File 'lib/msf/core/post_mixin.rb', line 189 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 67 68 69 70 71 72 |
# 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" if session.type.ends_with?(':winpty') raise Msf::OptionValidateError.new({ 'SESSION' => 'Session does not support post modules.' }) end 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
128 129 130 131 132 133 134 135 |
# File 'lib/msf/core/post_mixin.rb', line 128 def sysinfo begin @sysinfo ||= session.sys.config.sysinfo rescue NoMethodError @sysinfo = nil end @sysinfo end |