require 'msf/core' require 'msf/core/module_manager' module Msf ### # # PayloadSet # ---------- # # This class is a special case of the generic module set class because # payloads are generated in terms of combinations between various # components, such as a stager and a stage. As such, the payload set # needs to be built on the fly and cannot be simply matched one-to-one # with a payload module. Yeah, the term module is kind of overloaded # here, but eat it! # ### class PayloadSet < ModuleSet def initialize(manager) super(MODULE_PAYLOAD) # A reference to the ModuleManager instance self.manager = manager # A hash of each of the payload types that holds an array # for all of the associated modules self.payload_type_modules = {} # Initialize the hash entry for each type to an empty list [ Payload::Type::Single, Payload::Type::Stager, Payload::Type::Stage ].each { |type| self.payload_type_modules[type] = {} } # Initialize hashes for each of the stages and singles. Stagers # never exist independent. The stages hash will have entries that # point to another hash that point to the per-stager implementation # payload class. For instance: # # ['windows/shell']['reverse_tcp'] # # Singles will simply point to the single payload class. self.stages = {} self.singles = {} # Hash that caches the sizes of payloads self.sizes = {} # Single instance cache of modules for use with doing quick referencing # of attributes that would require an instance. self._instances = {} end # # Performs custom filtering during each_module enumeration. This allows us # to filter out certain stagers as necessary. # def each_module_filter(opts, name, mod) return false end # # This method builds the hash of alias names based on all the permutations # of singles, stagers, and stages. # def recalculate # Reset the current hash associations self.each_key { |key| manager.delete(key) } self.clear # Recalculate single payloads _singles.each_pair { |name, p| mod, handler = p # Build the payload dupe using the determined handler # and module p = build_payload(handler, mod) # Sets the modules derived name p.refname = name # Add it to the set add_single(p, name) # Cache the payload's size sizes[name] = p.new.size } # Recalculate stagers and stages _stagers.each_pair { |stager_name, p| stager_mod, handler, stager_platform, stager_arch, stager_inst = p # Walk the array of stages _stages.each_pair { |stage_name, p| stage_mod, junk, stage_platform, stage_arch, stage_inst = p # No intersection between architectures on the payloads? if ((stager_arch) and (stage_arch) and ((stager_arch & stage_arch).empty?)) dlog("Stager #{stager_name} and stage #{stage_name} have incompatible architectures: #{stager_arch.join} - #{stage_arch.join}", 'core', LEV_3) next end # No intersection between platforms on the payloads? if ((stager_platform) and (stage_platform) and (stager_platform & stage_platform).empty?) dlog("Stager #{stager_name} and stage #{stage_name} have incompatible platforms: #{stager_platform.names} - #{stage_platform.names}", 'core', LEV_3) next end # If the stage has a convention, make sure it's compatible with # the stager's if ((stage_inst) and (stage_inst.compatible?(stager_inst) == false)) dlog("Stager #{stager_name} and stage #{stage_name} are incompatible.", 'core', LEV_3) next end # Build the payload dupe using the handler, stager, # and stage p = build_payload(handler, stager_mod, stage_mod) # If the stager has an alias for the handler type (such as is the # case for ordinal based stagers), use it in preference of the # handler's actual type. if (stager_mod.respond_to?('handler_type_alias') == true) handler_type = stager_mod.handler_type_alias else handler_type = handler.handler_type end # Associate the name as a combination of the stager and stage combined = stage_name # If a valid handler exists for this stager, then combine it combined += '/' + handler_type # Sets the modules derived name p.refname = combined # Add the stage add_stage(p, combined, stage_name, handler_type) # Cache the payload's size sizes[combined] = p.new.size } } end # # This method is called when a new payload module class is loaded up. For # the payload set we simply create an instance of the class and do some # magic to figure out if it's a single, stager, or stage. Depending on # which it is, we add it to the appropriate list. # def add_module(pmodule, name, file_path = nil) if (md = name.match(/^(singles|stagers|stages)#{File::SEPARATOR}(.*)$/)) name = md[2] end # Duplicate the Payload base class and extend it with the module # class that is passed in. This allows us to inspect the actual # module to see what type it is, and to grab other information for # our own evil purposes. instance = build_payload(pmodule).new # Create an array of information about this payload module pinfo = [ pmodule, instance.handler_klass, instance.platform, instance.arch, instance, file_path ] # Use the module's preferred alias if it has one name = instance.alias if (instance.alias) # Store the module and alias name for this payload. We # also convey other information about the module, such as # the platforms and architectures it supports payload_type_modules[instance.payload_type][name] = pinfo # If the payload happens to be a single, but has no defined # connection, then it can also be staged. Insert it into # the staged list. if ((instance.payload_type == Payload::Type::Single) and ((instance.handler_klass == Msf::Handler::None) or (instance.handler_klass == nil))) payload_type_modules[Payload::Type::Stage][name] = pinfo end end # # This method adds a single payload to the set and adds it to the singles # hash. # def add_single(p, name) p.framework = framework # Associate this class with the single payload's name self[name] = p # Add the singles hash singles[name] = p # Add it to the global module set manager.add_module(p, name) dlog("Built single payload #{name}.", 'core', LEV_1) end # # This method adds a stage payload to the set and adds it to the stages # hash using the supplied handler type. # def add_stage(p, full_name, stage_name, handler_type) p.framework = framework # Associate this stage's full name with the payload class in the set self[full_name] = p # Add the full name association in the global module set manager.add_module(p, full_name) # Create the hash entry for this stage and then create # the associated entry for the handler type stages[stage_name] = {} if (!stages[stage_name]) # Add it to this stage's stager hash stages[stage_name][handler_type] = p dlog("Built staged payload #{full_name}.", 'core', LEV_1) end # # Returns a single read-only instance of the supplied payload name such # that specific attributes, like compatibility, can be evaluated. The # payload instance returned should NOT be used for anything other than # reading. # def instance(name) if (self._instances[name] == nil) self._instances[name] = create(name) end self._instances[name] end # # The list of stages that have been loaded. # attr_reader :stages # # The list of stagers that have been loaded. # attr_reader :singles # # The sizes of all the built payloads thus far. # attr_reader :sizes protected # # Return the hash of single payloads # def _singles return payload_type_modules[Payload::Type::Single] || {} end # # Return the hash of stager payloads # def _stagers return payload_type_modules[Payload::Type::Stager] || {} end # # Return the hash of stage payloads # def _stages return payload_type_modules[Payload::Type::Stage] || {} end # # Builds a duplicate, extended version of the Payload base # class using the supplied modules. # def build_payload(*modules) klass = Class.new(Payload) # Remove nil modules modules.delete_if { |x| x == nil } # Include the modules supplied to us with the mad skillz # spoonfu style klass.include(*modules.reverse) return klass end attr_accessor :manager, :payload_type_modules # :nodoc: attr_writer :stages, :singles, :sizes # :nodoc: attr_accessor :_instances # :nodoc: end end