require 'msf/core/module' module Msf class Evasion < Msf::Module include Msf::Auxiliary::Report class Complete < RuntimeError ; end class Failed < RuntimeError ; end def initialize(info={}) if (info['Payload'] and info['Payload']['Compat']) info['Compat'] = Hash.new if (info['Compat'] == nil) info['Compat']['Payload'] = Hash.new if (info['Compat']['Payload'] == nil) info['Compat']['Payload'].update(info['Payload']['Compat']) end super(info) self.payload_info = info['Payload'] || {} self.targets = Rex::Transformer.transform(info['Targets'], Array, [ Target ], 'Targets') if info.key? 'DefaultTarget' self.default_target = info['DefaultTarget'] else self.default_target = 0 # Add an auto-target to the evasion if it doesn't have one if info['Targets'] && info['Targets'].count > 1 && !has_auto_target?(info['Targets']) # Finally, only add the target if there is a remote host option if self.respond_to?(:rhost) && self.respond_to?(:auto_targeted_index) auto = ["Automatic", {'AutoGenerated' => true}.merge(info['Targets'][self.default_target][1])] info['Targets'].unshift(auto) end end end if (info['Payload'] and info['Payload']['ActiveTimeout']) self.active_timeout = info['Payload']['ActiveTimeout'].to_i end register_options([ OptString.new( 'FILENAME', [ true, 'Filename for the evasive file (default: random)', "#{Rex::Text.rand_text_alpha(3..10)}.exe" ]) ], self.class) end def self.type Msf::MODULE_EVASION end def type Msf::MODULE_EVASION end def setup end def file_format_filename datastore['FILENAME'] end def file_create(data) fname = file_format_filename ltype = "evasion.fileformat.#{self.shortname}" full_path = store_local(ltype, nil, data, fname) print_good "#{fname} stored at #{full_path}" end # # Returns the target's platform, or the one assigned to the module itself. # def target_platform (target and target.platform) ? target.platform : platform end # # Returns the target's architecture, or the one assigned to the module # itself. # def target_arch (target and target.arch) ? target.arch : (arch == []) ? nil : arch end def normalize_platform_arch c_platform = (target && target.platform) ? target.platform : platform c_arch = (target && target.arch) ? target.arch : (arch == []) ? nil : arch c_arch ||= [ ARCH_X86 ] return c_platform, c_arch end # Returns whether the requested payload is compatible with the module # # @param [String] name The payload name # @param [TrueClass] Payload is compatible. # @param [FlaseClass] Payload is not compatible. def is_payload_compatible?(name) p = framework.payloads[name] pi = p.new # Are we compatible in terms of conventions and connections and # what not? return false if !compatible?(pi) # If the payload is privileged but the evasion does not give # privileged access, then fail it. return false if !self.privileged && pi.privileged return true end # Returns a list of compatible payloads based on platform, architecture, # and size requirements. def compatible_payloads payloads = [] c_platform, c_arch = normalize_platform_arch framework.payloads.each_module( 'Arch' => c_arch, 'Platform' => c_platform) { |name, mod| payloads << [ name, mod ] if is_payload_compatible?(name) } return payloads end def run raise NotImplementedError end def cleanup end def fail_with(reason, msg=nil) raise Msf::Evasion::Failed, "#{reason}: #{msg}" end def evasion_commands {} end def stance 'passive' end def passive? true end def aggressive? false end # Generates the encoded version of the supplied payload using the payload # requirements specific to this evasion module. The encoded instance is returned # to the caller. This method is exposed in the manner that it is such that passive # evasions and re-generate an encoded payload on the fly rather than having to use # the pre-generated one. def generate_payload(pinst = nil) # Set the encoded payload to the result of the encoding process self.payload = generate_single_payload(pinst) # Save the payload instance self.payload_instance = (pinst) ? pinst : self.payload_instance return self.payload end def generate_single_payload(pinst = nil, platform = nil, arch = nil, explicit_target = nil) explicit_target ||= target # If a payload instance was supplied, use it, otherwise # use the active payload instance real_payload = (pinst) ? pinst : self.payload_instance if (real_payload == nil) raise MissingPayloadError, "No payload has been selected.", caller end # If this is a generic payload, then we should specify the platform # and architecture so that it knows how to pass things on. if real_payload.kind_of?(Msf::Payload::Generic) # Convert the architecture specified into an array. if arch and arch.kind_of?(String) arch = [ arch ] end # Define the explicit platform and architecture information only if # it's been specified. if platform real_payload.explicit_platform = Msf::Module::PlatformList.transform(platform) end if arch real_payload.explicit_arch = arch end # Force it to reset so that it will find updated information. real_payload.reset end # Duplicate the evasion payload requirements reqs = self.payload_info.dup # Pass save register requirements to the NOP generator reqs['Space'] = payload_info['Space'].to_i reqs['SaveRegisters'] = module_info['SaveRegisters'] reqs['Prepend'] = payload_info['Prepend'] reqs['PrependEncoder'] = payload_info['PrependEncoder'] reqs['BadChars'] = payload_info['BadChars'] reqs['Append'] = payload_info['Append'] reqs['AppendEncoder'] = payload_info['AppendEncoder'] reqs['MaxNops'] = payload_info['MaxNops'] reqs['MinNops'] = payload_info['MinNops'] reqs['Encoder'] = datastore['ENCODER'] || payload_info['Encoder'] reqs['Nop'] = datastore['NOP'] || payload_info['Nop'] reqs['EncoderType'] = payload_info['EncoderType'] reqs['EncoderOptions'] = payload_info['EncoderOptions'] reqs['ExtendedOptions'] = payload_info['ExtendedOptions'] reqs['Evasion'] = self # Pass along the encoder don't fall through flag reqs['EncoderDontFallThrough'] = datastore['EncoderDontFallThrough'] # Incorporate any context encoding requirements that are needed define_context_encoding_reqs(reqs) # Call the encode begin routine. encode_begin(real_payload, reqs) # Generate the encoded payload. encoded = EncodedPayload.create(real_payload, reqs) # Call the encode end routine which is expected to return the actual # encoded payload instance. return encode_end(real_payload, reqs, encoded) end def define_context_encoding_reqs(reqs) return unless datastore['EnableContextEncoding'] # At present, we don't support any automatic methods of obtaining # context information. In the future, we might support obtaining # temporal information remotely. # Pass along the information specified in our evasion datastore as # encoder options reqs['EncoderOptions'] = {} if reqs['EncoderOptions'].nil? reqs['EncoderOptions']['EnableContextEncoding'] = datastore['EnableContextEncoding'] reqs['EncoderOptions']['ContextInformationFile'] = datastore['ContextInformationFile'] end def encode_begin(real_payload, reqs) end def encode_end(real_payload, reqs, encoded) encoded end def target if self.respond_to?(:auto_targeted_index) if auto_target? auto_idx = auto_targeted_index if auto_idx.present? datastore['TARGET'] = auto_idx else # If our inserted Automatic Target was selected but we failed to # find a suitable target, we just grab the original first target. datastore['TARGET'] = 1 end end end target_idx = target_index return (target_idx) ? targets[target_idx.to_i] : nil end def target_index target_idx = Integer(datastore['TARGET']) rescue datastore['TARGET'] default_idx = default_target || 0 # Use the default target if one was not supplied. if (target_idx == nil and default_idx and default_idx >= 0) target_idx = default_idx elsif target_idx.is_a?(String) target_idx = targets.index { |target| target.name == target_idx } end return (target_idx) ? target_idx.to_i : nil end def has_auto_target?(targets=[]) target_names = targets.collect { |target| target.first} target_names.each do |target| return true if target =~ /Automatic/ end return false end attr_accessor :default_target attr_accessor :targets attr_reader :payload_info attr_accessor :payload_info attr_accessor :payload_instance attr_accessor :payload end end