Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Path: blob/master/lib/msf/core/encoded_payload.rb
Views: 11779
# -*- coding: binary -*-123module Msf45###6#7# This class wrappers an encoded payload buffer and the means used to create8# one.9#10###11class EncodedPayload1213include Framework::Offspring1415#16# This method creates an encoded payload instance and returns it to the17# caller.18#19def self.create(pinst, reqs = {})20# Create the encoded payload instance21p = EncodedPayload.new(pinst.framework, pinst, reqs)2223p.generate(reqs['Raw'])2425return p26end2728#29# Creates an instance of an EncodedPayload.30#31def initialize(framework, pinst, reqs)32self.framework = framework33self.pinst = pinst34self.reqs = reqs35self.space = reqs['Space']36end3738#39# This method generates the full encoded payload and returns the encoded40# payload buffer.41#42# @return [String] The encoded payload.43def generate(raw = nil)44self.raw = raw45self.encoded = nil46self.nop_sled_size = 047self.nop_sled = nil48self.encoder = nil49self.nop = nil5051# Increase thread priority as necessary. This is done52# to ensure that the encoding and sled generation get53# enough time slices from the ruby thread scheduler.54priority = Thread.current.priority5556if (priority == 0)57Thread.current.priority = 158end5960begin61# First, validate62pinst.validate()6364# Propagate space information when set65unless self.space.nil?66# Tell the payload how much space is available67pinst.available_space = self.space68# Reserve 10% of the available space if encoding is required69pinst.available_space -= (self.space * 0.1).ceil if needs_encoding70end7172# Generate the raw version of the payload first73generate_raw() if self.raw.nil?7475# If encoder is set, it could be an encoders list76# The form is "<encoder>:<iteration>, <encoder2>:<iteration>"...77unless reqs['Encoder'].blank?78encoder_str = reqs['Encoder']79encoder_str.scan(/([^:, ]+):?([^,]+)?/).map do |encoder_opt|80reqs['Encoder'] = encoder_opt[0]8182self.iterations = (encoder_opt[1] || reqs['Iterations']).to_i83self.iterations = 1 if self.iterations < 18485# Encode the payload with every encoders in the list86encode()87# Encoded payload is now the raw payload to be encoded by the next encoder88self.raw = self.encoded89end90else91self.iterations = reqs['Iterations'].to_i92self.iterations = 1 if self.iterations < 193# No specified encoder, let BadChars or ForceEncode do their job94encode()95end9697# Build the NOP sled98generate_sled()99100# Finally, set the complete payload definition101self.encoded = (self.nop_sled || '') + self.encoded102ensure103# Restore the thread priority104Thread.current.priority = priority105end106107# Return the complete payload108return encoded109end110111#112# Generates the raw payload from the payload instance. This populates the113# {#raw} attribute.114#115# @return [String] The raw, unencoded payload.116def generate_raw117self.raw = (reqs['Prepend'] || '') + pinst.generate_complete + (reqs['Append'] || '')118119# If an encapsulation routine was supplied, then we should call it so120# that we can get the real raw payload.121if reqs['EncapsulationRoutine']122self.raw = reqs['EncapsulationRoutine'].call(reqs, raw)123end124end125126#127# Scans for a compatible encoder using ranked precedence and populates the128# encoded attribute.129#130def encode131# Get the minimum number of nops to use132min = (reqs['MinNops'] || 0).to_i133min = 0 if reqs['DisableNops']134135# If the exploit needs the payload to be encoded, we need to run the list of136# encoders in ranked precedence and try to encode with them.137if needs_encoding138# Make sure the encoder name from the user has the same String#encoding139# as the framework's list of encoder names so we can compare them later.140# This is important for when we get input from RPC.141if reqs['Encoder']142reqs['Encoder'] = reqs['Encoder'].encode(framework.encoders.module_refnames[0].encoding)143end144145# If the caller had a preferred encoder, use this encoder only146if ((reqs['Encoder']) and (preferred = framework.encoders[reqs['Encoder']]))147encoders = [ [reqs['Encoder'], preferred] ]148elsif (reqs['Encoder'])149wlog("#{pinst.refname}: Failed to find preferred encoder #{reqs['Encoder']}")150raise NoEncodersSucceededError, "Failed to find preferred encoder #{reqs['Encoder']}"151else152encoders = compatible_encoders153end154155encoders.each { |encname, encmod|156self.encoder = encmod.new157self.encoded = nil158159# If the encoding is requested by an exploit check compatibility160# options first of all. For the 'generic/none' encoder compatibility161# options don't apply.162if (reqs['Exploit'] &&163!reqs['Exploit'].compatible?(self.encoder) &&164encname !~ /generic\/none/)165wlog("#{pinst.refname}: Encoder #{encoder.refname} doesn't match the exploit Compat options",166'core', LEV_1)167next168end169170# If there is an encoder type restriction, check to see if this171# encoder matches with what we're searching for.172if ((reqs['EncoderType']) and173(self.encoder.encoder_type.split(/\s+/).include?(reqs['EncoderType']) == false))174wlog("#{pinst.refname}: Encoder #{encoder.refname} is not a compatible encoder type: #{reqs['EncoderType']} != #{self.encoder.encoder_type}",175'core', LEV_1)176next177end178179# If the exploit did not explicitly request a kind of encoder and180# the current encoder has a manual ranking, then it should not be181# considered as a valid encoder. A manual ranking tells the182# framework that an encoder must be explicitly defined as the183# encoder of choice for an exploit.184if ((reqs['EncoderType'].nil?) and185(reqs['Encoder'].nil?) and186(self.encoder.rank == ManualRanking))187wlog("#{pinst.refname}: Encoder #{encoder.refname} is manual ranked and was not defined as a preferred encoder.",188'core', LEV_1)189next190end191192# If the caller explicitly requires register preservation, make sure193# that the module in question can handle it. This is mostly used by194# the stage encoder path.195if (reqs['ForceSaveRegisters'] and196reqs['EncoderOptions'] and197(reqs['EncoderOptions']['SaveRegisters'].to_s.length > 0) and198(! self.encoder.can_preserve_registers?))199wlog("#{pinst.refname}: Encoder #{encoder.refname} does not preserve registers and the caller needs #{reqs['EncoderOptions']['SaveRegisters']} preserved.",200'core', LEV_1)201next202end203204# Import the datastore from payload (and likely exploit by proxy)205self.encoder.share_datastore(pinst.datastore)206207# If we have any encoder options, import them into the datastore208# of the encoder.209if (reqs['EncoderOptions'])210self.encoder.datastore.import_options_from_hash(reqs['EncoderOptions'])211end212213# Validate the encoder to make sure it's properly initialized.214begin215self.encoder.validate216rescue ::Exception217wlog("#{pinst.refname}: Failed to validate encoder #{encoder.refname}: #{$!}",218'core', LEV_1)219next220end221222# Tell the encoder how much space is available223self.encoder.available_space = self.space224225eout = self.raw.dup226227next_encoder = false228229# Try encoding with the current encoder230#231# NOTE: Using more than one iteration may cause successive iterations to switch232# to using a different encoder.233#2341.upto(self.iterations) do |iter|235err_start = "#{pinst.refname}: iteration #{iter}"236237begin238eout = self.encoder.encode(eout, reqs['BadChars'], nil, pinst.platform)239rescue EncodingError => e240wlog("#{err_start}: Encoder #{encoder.refname} failed: #{e}", 'core', LEV_1)241dlog("#{err_start}: Call stack\n#{e.backtrace}", 'core', LEV_3)242next_encoder = true243break244245rescue ::Exception => e246elog("Broken encoder #{encoder.refname}", error: e)247next_encoder = true248break249end250251# Check to see if we have enough room for the minimum requirements252if ((reqs['Space']) and (reqs['Space'] < eout.length + min))253wlog("#{err_start}: Encoded payload version is too large (#{eout.length} bytes) with encoder #{encoder.refname}",254'core', LEV_1)255next_encoder = true256break257end258259ilog("#{err_start}: Successfully encoded with encoder #{encoder.refname} (size is #{eout.length})",260'core', LEV_0)261end262263next if next_encoder264265self.encoded = eout266break267}268269# If the encoded payload is nil, raise an exception saying that we270# suck at life.271if (self.encoded == nil)272self.encoder = nil273raise NoEncodersSucceededError,274"#{pinst.refname}: All encoders failed to encode.",275caller276end277278# If there are no bad characters, then the raw is the same as the279# encoded280else281# NOTE: BadChars can contain whitespace, so don't use String#blank?282unless reqs['BadChars'].nil? || reqs['BadChars'].empty?283ilog("#{pinst.refname}: payload contains no badchars, skipping automatic encoding", 'core', LEV_0)284end285286# Space = 0 is a special value used by msfvenom to generate the smallest287# payload possible. In that case do not raise an exception indicating288# that the payload is too large.289if reqs['Space'] && reqs['Space'] > 0 && reqs['Space'] < raw.length + min290wlog("#{pinst.refname}: Raw (unencoded) payload is too large (#{raw.length} bytes)", 'core', LEV_1)291raise PayloadSpaceViolation, 'The payload exceeds the specified space', caller292end293294self.encoded = raw295end296297# Prefix the prepend encoder value298self.encoded = (reqs['PrependEncoder'] || '') + self.encoded299self.encoded << (reqs['AppendEncoder'] || '')300end301302#303# Construct a NOP sled if necessary304#305def generate_sled306min = reqs['MinNops'] || 0307space = reqs['Space']308pad_nops = reqs['PadNops']309310self.nop_sled_size = min311312# Calculate the number of NOPs to pad out the buffer with based on the313# requirements. If there was a space requirement, check to see if314# there's any room at all left for a sled.315if ((space) and316(space > encoded.length))317self.nop_sled_size = reqs['Space'] - self.encoded.length318end319320# If the maximum number of NOPs has been exceeded, wrap it back down.321if ((reqs['MaxNops']) and322(reqs['MaxNops'] < self.nop_sled_size))323self.nop_sled_size = reqs['MaxNops']324end325326# Check for the DisableNops setting327self.nop_sled_size = 0 if reqs['DisableNops']328329# Check for the PadNops setting330self.nop_sled_size = (pad_nops - self.encoded.length) if reqs['PadNops']331332# Now construct the actual sled333if (self.nop_sled_size > 0)334nops = pinst.compatible_nops335336# If the caller had a preferred nop, try to find it and prefix it337if ((reqs['Nop']) and338(preferred = framework.nops[reqs['Nop']]))339nops.unshift([reqs['Nop'], preferred ])340elsif (reqs['Nop'])341wlog("#{pinst.refname}: Failed to find preferred nop #{reqs['Nop']}")342end343344nops.each { |nopname, nopmod|345# Create an instance of the nop module346self.nop = nopmod.new347348# Propagate options from the payload and possibly exploit349self.nop.share_datastore(pinst.datastore)350351# The list of save registers352save_regs = (reqs['SaveRegisters'] || []) + (pinst.save_registers || [])353354if (save_regs.empty? == true)355save_regs = nil356end357358begin359nop.copy_ui(pinst)360self.nop_sled = nop.generate_sled(self.nop_sled_size,361'BadChars' => reqs['BadChars'],362'SaveRegisters' => save_regs)363364if nop_sled && nop_sled.length == nop_sled_size365break366else367dlog("#{pinst.refname}: Nop generator #{nop.refname} failed to generate sled for payload", 'core', LEV_1)368end369rescue370dlog("#{pinst.refname}: Nop generator #{nop.refname} failed to generate sled for payload: #{$!}",371'core', LEV_1)372373self.nop = nil374end375}376377if (self.nop_sled == nil)378raise NoNopsSucceededError,379"#{pinst.refname}: All NOP generators failed to construct sled for.",380caller381end382else383self.nop_sled = ''384end385386return self.nop_sled387end388389390#391# Convert the payload to an executable appropriate for its arch and392# platform.393#394# +opts+ are passed directly to +Msf::Util::EXE.to_executable+395#396# see +Msf::Exploit::EXE+397#398def encoded_exe(opts={})399# Ensure arch and platform are in the format that to_executable expects400if opts[:arch] and not opts[:arch].kind_of? Array401opts[:arch] = [ opts[:arch] ]402end403if (opts[:platform].kind_of? Msf::Module::PlatformList)404opts[:platform] = opts[:platform].platforms405end406407emod = pinst.assoc_exploit if pinst.respond_to? :assoc_exploit408409if emod410if (emod.datastore["EXE::Custom"] and emod.respond_to? :get_custom_exe)411return emod.get_custom_exe412end413# This is a little ghetto, grabbing datastore options from the414# associated exploit, but it doesn't really make sense for the415# payload to have exe options if the exploit doesn't need an exe.416# Msf::Util::EXE chooses reasonable defaults if these aren't given,417# so it's not that big of an issue.418opts.merge!({419:template_path => emod.datastore['EXE::Path'],420:template => emod.datastore['EXE::Template'],421:inject => emod.datastore['EXE::Inject'],422:fallback => emod.datastore['EXE::FallBack'],423:sub_method => emod.datastore['EXE::OldMethod']424})425# Prefer the target's platform/architecture information, but use426# the exploit module's if no target specific information exists.427opts[:platform] ||= emod.target_platform if emod.respond_to? :target_platform428opts[:platform] ||= emod.platform if emod.respond_to? :platform429opts[:arch] ||= emod.target_arch if emod.respond_to? :target_arch430opts[:arch] ||= emod.arch if emod.respond_to? :arch431end432# Lastly, try the payload's. This always happens if we don't have an433# associated exploit module.434opts[:platform] ||= pinst.platform if pinst.respond_to? :platform435opts[:arch] ||= pinst.arch if pinst.respond_to? :arch436437Msf::Util::EXE.to_executable(framework, opts[:arch], opts[:platform], encoded, opts)438end439440#441# Generate a jar file containing the encoded payload.442#443# Uses the payload's +generate_jar+ method if it is implemented (Java444# payloads should all have it). Otherwise, converts the payload to an445# executable and uses Msf::Util::EXE.to_jar to create a jar file that dumps446# the exe out to a random file name in the system's temporary directory and447# executes it.448#449def encoded_jar(opts={})450return pinst.generate_jar(opts) if pinst.respond_to? :generate_jar451452opts[:spawn] ||= pinst.datastore["Spawn"]453454Msf::Util::EXE.to_jar(encoded_exe(opts), opts)455end456457#458# Similar to +encoded_jar+ but builds a web archive for use in servlet459# containers such as Tomcat.460#461def encoded_war(opts={})462return pinst.generate_war(opts) if pinst.respond_to? :generate_war463464Msf::Util::EXE.to_jsp_war(encoded_exe(opts), opts)465end466467#468# An array containing the architecture(s) that this payload was made to run on469#470def arch471if pinst472pinst.arch473end474end475476#477# An array containing the platform(s) that this payload was made to run on478#479def platform480if pinst481pinst.platform482end483end484485#486# The raw version of the payload487#488attr_reader :raw489#490# The encoded version of the raw payload plus the NOP sled491# if one was generated.492#493attr_reader :encoded494#495# The size of the NOP sled496#497attr_reader :nop_sled_size498#499# The NOP sled itself500#501attr_reader :nop_sled502#503# The encoder that was used504#505attr_reader :encoder506#507# The NOP generator that was used508#509attr_reader :nop510#511# The number of encoding iterations used512#513attr_reader :iterations514#515# The maximum number of bytes acceptable for the encoded payload516#517attr_reader :space518protected519520attr_writer :raw # :nodoc:521attr_writer :encoded # :nodoc:522attr_writer :nop_sled_size # :nodoc:523attr_writer :nop_sled # :nodoc:524attr_writer :payload # :nodoc:525attr_writer :encoder # :nodoc:526attr_writer :nop # :nodoc:527attr_writer :iterations # :nodoc:528attr_writer :space # :nodoc529530#531# The payload instance used to generate the payload532#533attr_accessor :pinst534#535# The requirements used for generation536#537attr_accessor :reqs538539def needs_encoding540!reqs['Encoder'].blank? || reqs['ForceEncode'] || has_chars?(reqs['BadChars'])541end542543def has_chars?(chars)544# NOTE: BadChars can contain whitespace, so don't use String#blank?545if chars.nil? || chars.empty?546return false547end548549# payload hasn't been set yet but we have bad characters so assume they'll need to be encoded550return true if self.raw.nil?551552return false if self.raw.empty?553554chars.each_byte do |bad|555return true if self.raw.index(bad.chr(::Encoding::ASCII_8BIT))556end557558false559end560561def compatible_encoders562arch = reqs['Arch'] || pinst.arch563platform = reqs['Platform'] || pinst.platform564565encoders = []566567framework.encoders.each_module_ranked(568'Arch' => arch, 'Platform' => platform) { |name, mod|569encoders << [ name, mod ]570}571572encoders573end574end575576end577578579