Class: Msf::PayloadGenerator

Inherits:
Object
  • Object
show all
Defined in:
lib/msf/core/payload_generator.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(opts = {}) ⇒ PayloadGenerator

Returns a new instance of PayloadGenerator

Parameters:

  • opts (Hash) (defaults to: {})

    The options hash

Options Hash (opts):

  • :payload (String) — default: see #payload
  • :format (String) — default: see #format
  • :encoder (String) — default: see #encoder
  • :iterations (Fixnum) — default: see #iterations
  • :arch (String) — default: see #arch
  • :platform (String) — default: see #platform
  • :badchars (String) — default: see #badchars
  • :template (String) — default: see #template
  • :space (Fixnum) — default: see #space
  • :nops (Fixnum) — default: see #nops
  • :add_code (String) — default: see #add_code
  • :keep (Boolean) — default: see #keep
  • :datastore (Hash) — default: see #datastore
  • :framework (Msf::Framework) — default: see #framework
  • :cli (Boolean) — default: see #cli

Raises:

  • (KeyError)

    if framework is not provided in the options hash


91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
# File 'lib/msf/core/payload_generator.rb', line 91

def initialize(opts={})
  @add_code   = opts.fetch(:add_code, '')
  @arch       = opts.fetch(:arch, '')
  @badchars   = opts.fetch(:badchars, '')
  @cli        = opts.fetch(:cli, false)
  @datastore  = opts.fetch(:datastore, {})
  @encoder    = opts.fetch(:encoder, '')
  @format     = opts.fetch(:format, 'raw')
  @iterations = opts.fetch(:iterations, 1)
  @keep       = opts.fetch(:keep, false)
  @nops       = opts.fetch(:nops, 0)
  @payload    = opts.fetch(:payload, '')
  @platform   = opts.fetch(:platform, '')
  @space      = opts.fetch(:space, 1.gigabyte)
  @stdin      = opts.fetch(:stdin, nil)
  @template   = opts.fetch(:template, '')

  @framework  = opts.fetch(:framework)

  raise ArgumentError, "Invalid Payload Selected" unless payload_is_valid?
  raise ArgumentError, "Invalid Format Selected" unless format_is_valid?
end

Instance Attribute Details

#add_codeString

Returns The path to a shellcode file to execute in a seperate thread

Returns:

  • (String)

    The path to a shellcode file to execute in a seperate thread


26
27
28
# File 'lib/msf/core/payload_generator.rb', line 26

def add_code
  @add_code
end

#archString

Returns The CPU architecture to build the payload for

Returns:

  • (String)

    The CPU architecture to build the payload for


29
30
31
# File 'lib/msf/core/payload_generator.rb', line 29

def arch
  @arch
end

#badcharsString

Returns The bad characters that can't be in the payload

Returns:

  • (String)

    The bad characters that can't be in the payload


32
33
34
# File 'lib/msf/core/payload_generator.rb', line 32

def badchars
  @badchars
end

#cliBoolean

Returns Whether this is being run by a CLI script

Returns:

  • (Boolean)

    Whether this is being run by a CLI script


35
36
37
# File 'lib/msf/core/payload_generator.rb', line 35

def cli
  @cli
end

#datastoreHash

Returns The datastore to apply to the payload module

Returns:

  • (Hash)

    The datastore to apply to the payload module


38
39
40
# File 'lib/msf/core/payload_generator.rb', line 38

def datastore
  @datastore
end

#encoderString

Returns The encoder(s) you want applied to the payload

Returns:

  • (String)

    The encoder(s) you want applied to the payload


41
42
43
# File 'lib/msf/core/payload_generator.rb', line 41

def encoder
  @encoder
end

#formatString

Returns The format you want the payload returned in

Returns:

  • (String)

    The format you want the payload returned in


44
45
46
# File 'lib/msf/core/payload_generator.rb', line 44

def format
  @format
end

#frameworkMsf::Framework

Returns The framework instance to use for generation

Returns:


47
48
49
# File 'lib/msf/core/payload_generator.rb', line 47

def framework
  @framework
end

#iterationsFixnum

Returns The number of iterations to run the encoder

Returns:

  • (Fixnum)

    The number of iterations to run the encoder


50
51
52
# File 'lib/msf/core/payload_generator.rb', line 50

def iterations
  @iterations
end

#keepBoolean

Returns Whether or not to preserve the original functionality of the template

Returns:

  • (Boolean)

    Whether or not to preserve the original functionality of the template


53
54
55
# File 'lib/msf/core/payload_generator.rb', line 53

def keep
  @keep
end

#nopsFixnum

Returns The size in bytes of NOP sled to prepend the payload with

Returns:

  • (Fixnum)

    The size in bytes of NOP sled to prepend the payload with


56
57
58
# File 'lib/msf/core/payload_generator.rb', line 56

def nops
  @nops
end

#payloadString

Returns The refname of the payload to generate

Returns:

  • (String)

    The refname of the payload to generate


59
60
61
# File 'lib/msf/core/payload_generator.rb', line 59

def payload
  @payload
end

#platformString

Returns The platform to build the payload for

Returns:

  • (String)

    The platform to build the payload for


62
63
64
# File 'lib/msf/core/payload_generator.rb', line 62

def platform
  @platform
end

#spaceFixnum

Returns The maximum size in bytes of the payload

Returns:

  • (Fixnum)

    The maximum size in bytes of the payload


65
66
67
# File 'lib/msf/core/payload_generator.rb', line 65

def space
  @space
end

#stdinString

Returns The raw bytes of a payload taken from STDIN

Returns:

  • (String)

    The raw bytes of a payload taken from STDIN


68
69
70
# File 'lib/msf/core/payload_generator.rb', line 68

def stdin
  @stdin
end

#templateString

Returns The path to an executable template to use

Returns:

  • (String)

    The path to an executable template to use


71
72
73
# File 'lib/msf/core/payload_generator.rb', line 71

def template
  @template
end

Instance Method Details

#add_shellcode(shellcode) ⇒ String

This method takes the shellcode generated so far and adds shellcode from a supplied file. The added shellcode is executed in a seperate thread from the main payload.

Parameters:

  • shellcode (String)

    The shellcode to add to

Returns:

  • (String)

    the combined shellcode which executes the added code in a seperate thread


119
120
121
122
123
124
125
126
127
128
129
130
131
# File 'lib/msf/core/payload_generator.rb', line 119

def add_shellcode(shellcode)
  if add_code.present? and platform_list.platforms.include? Msf::Module::Platform::Windows and arch == "x86"
    cli_print "Adding shellcode from #{add_code} to the payload"
    shellcode_file = File.open(add_code)
    shellcode_file.binmode
    added_code = shellcode_file.read
    shellcode_file.close
    shellcode = ::Msf::Util::EXE.win32_rwx_exec_thread(shellcode,0,'end')
    shellcode << added_code
  else
    shellcode.dup
  end
end

#choose_arch(mod) ⇒ String, Nil

This method takes a payload module and tries to reconcile a chosen arch with the arches supported by the module.

Parameters:

  • mod (Msf::Payload)

    The module class to choose an arch for

Returns:

  • (String)

    String form of the Arch if a valid arch found

  • (Nil)

    if no valid arch found


138
139
140
141
142
143
144
145
146
147
148
# File 'lib/msf/core/payload_generator.rb', line 138

def choose_arch(mod)
  if arch.blank?
    @arch = mod.arch.first
    cli_print "No Arch selected, selecting Arch: #{arch} from the payload"
    return mod.arch.first
  elsif mod.arch.include? arch
    return arch
  else
    return nil
  end
end

#choose_platform(mod) ⇒ Msf::Module::PlatformList

This method takes a payload module and tries to reconcile a chosen platform with the platforms supported by the module.

Parameters:

  • mod (Msf::Payload)

    The module class to choose a platform for

Returns:


154
155
156
157
158
159
160
161
162
163
164
# File 'lib/msf/core/payload_generator.rb', line 154

def choose_platform(mod)
  chosen_platform = platform_list
  if chosen_platform.platforms.empty?
    chosen_platform = mod.platform
    cli_print "No platform was selected, choosing #{chosen_platform.platforms.first} from the payload"
    @platform = mod.platform.platforms.first.to_s.split("::").last
  elsif (chosen_platform & mod.platform).empty?
    chosen_platform = Msf::Module::PlatformList.new
  end
  chosen_platform
end

#encode_payload(shellcode) ⇒ String

This method takes the shellcode generated so far and iterates through the chosen or compatible encoders. It attempts to encode the payload with each encoder until it finds one that works.

Parameters:

  • shellcode (String)

    The shellcode to encode

Returns:

  • (String)

    The encoded shellcode


171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
# File 'lib/msf/core/payload_generator.rb', line 171

def encode_payload(shellcode)
  shellcode = shellcode.dup
  encoder_list = get_encoders
  cli_print "Found #{encoder_list.count} compatible encoders"
  if encoder_list.empty?
    shellcode
  else
    encoder_list.each do |encoder_mod|
      cli_print "Attempting to encode payload with #{iterations} iterations of #{encoder_mod.refname}"
      begin
        return run_encoder(encoder_mod, shellcode.dup)
      rescue ::Msf::EncoderSpaceViolation => e
        cli_print "#{encoder_mod.refname} failed with #{e.message}"
        next
      rescue ::Msf::EncodingError => e
        cli_print "#{encoder_mod.refname} failed with #{e.message}"
        next
      end
    end
    raise ::Msf::EncodingError, "No Encoder Succeeded"
  end
end

#exe_optionsHash

This returns a hash for the exe format generation of payloads

Returns:

  • (Hash)

    The hash needed for generating an executable format


196
197
198
199
200
201
202
203
# File 'lib/msf/core/payload_generator.rb', line 196

def exe_options
  opts = { inject: keep }
  unless template.blank?
    opts[:template_path] = File.dirname(template)
    opts[:template]      = File.basename(template)
  end
  opts
end

#format_payload(shellcode) ⇒ String

This method takes the payload shellcode and formats it appropriately based on the selected output format.

Parameters:

  • shellcode (String)

    the processed shellcode to be formatted

Returns:

  • (String)

    The final formatted form of the payload


209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
# File 'lib/msf/core/payload_generator.rb', line 209

def format_payload(shellcode)
  case format.downcase
    when "js_be"
      if Rex::Arch.endian(arch) != ENDIAN_BIG
        raise IncompatibleEndianess, "Big endian format selected for a non big endian payload"
      else
        ::Msf::Simple::Buffer.transform(shellcode, format)
      end
    when *::Msf::Simple::Buffer.transform_formats
      ::Msf::Simple::Buffer.transform(shellcode, format)
    when *::Msf::Util::EXE.to_executable_fmt_formats
      ::Msf::Util::EXE.to_executable_fmt(framework, arch, platform_list, shellcode, format, exe_options)
    else
      raise InvalidFormat, "you have selected an invalid payload format"
  end
end

#generate_java_payloadString

This method generates Java payloads which are a special case. They can be generated in raw or war formats, which respectively produce a JAR or WAR file for the java payload.

Returns:

  • (String)

    Java payload as a JAR or WAR file


230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
# File 'lib/msf/core/payload_generator.rb', line 230

def generate_java_payload
  payload_module = framework.payloads.create(payload)
  payload_module.datastore.merge!(datastore)
  case format
    when "raw", "jar"
      if payload_module.respond_to? :generate_jar
        payload_module.generate_jar.pack
      else
        payload_module.generate
      end
    when "war"
      if payload_module.respond_to? :generate_war
        payload_module.generate_war.pack
      else
        raise InvalidFormat, "#{payload} is not a Java payload"
      end
    else
      raise InvalidFormat, "#{format} is not a valid format for Java payloads"
  end
end

#generate_payloadString

This method is a wrapper around all of the other methods. It calls the correct methods in order based on the supplied options and returns the finished payload.

Returns:

  • (String)

    A string containing the bytes of the payload in the format selected


254
255
256
257
258
259
260
261
262
263
264
# File 'lib/msf/core/payload_generator.rb', line 254

def generate_payload
  if platform == "java" or arch == "java" or payload.start_with? "java/"
    generate_java_payload
  else
    raw_payload = generate_raw_payload
    raw_payload = add_shellcode(raw_payload)
    encoded_payload = encode_payload(raw_payload)
    encoded_payload = prepend_nops(encoded_payload)
    format_payload(encoded_payload)
  end
end

#generate_raw_payloadString

This method generates the raw form of the payload as generated by the payload module itself.

Returns:

  • (String)

    the raw bytes of the payload to be generated

Raises:


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
# File 'lib/msf/core/payload_generator.rb', line 273

def generate_raw_payload
  if payload == 'stdin'
    if arch.blank?
      raise IncompatibleArch, "You must select an arch for a custom payload"
    elsif platform.blank?
      raise IncompatiblePlatform, "You must select a platform for a custom payload"
    end
    stdin
  else
    payload_module = framework.payloads.create(payload)

    chosen_platform = choose_platform(payload_module)
    if chosen_platform.platforms.empty?
      raise IncompatiblePlatform, "The selected platform is incompatible with the payload"
    end

    chosen_arch = choose_arch(payload_module)
    unless chosen_arch
      raise IncompatibleArch, "The selected arch is incompatible with the payload"
    end

    payload_module.generate_simple(
        'Format'   => 'raw',
        'Options'  => datastore,
        'Encoder'  => nil
    )
  end
end

#get_encodersArray<Msf::Encoder>

This method returns an array of encoders that either match the encoders selected by the user, or match the arch selected.

Returns:

  • (Array<Msf::Encoder>)

    An array of potential encoders to use


305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
# File 'lib/msf/core/payload_generator.rb', line 305

def get_encoders
  encoders = []
  if encoder.present?
    # Allow comma seperated list of encoders so users can choose several
    encoder.split(',').each do |chosen_encoder|
      encoders << framework.encoders.create(chosen_encoder)
    end
    encoders.sort_by { |my_encoder| my_encoder.rank }.reverse
  elsif badchars.present?
    framework.encoders.each_module_ranked('Arch' => [arch]) do |name, mod|
      encoders << framework.encoders.create(name)
    end
    encoders.sort_by { |my_encoder| my_encoder.rank }.reverse
  else
    encoders
  end
end

#platform_listMsf::Module::PlatformList

Returns a PlatformList object based on the platform string given at creation.

Returns:


325
326
327
328
329
330
331
332
333
334
335
336
# File 'lib/msf/core/payload_generator.rb', line 325

def platform_list
  if platform.blank?
    list = Msf::Module::PlatformList.new
  else
    begin
      list = ::Msf::Module::PlatformList.transform(platform)
    rescue
      list = Msf::Module::PlatformList.new
    end
  end
  list
end

#prepend_nops(shellcode) ⇒ String

This method takes an encoded payload and prepends a NOP Sled to it with a size based on the nops value given to the generator.

Parameters:

  • shellcode (String)

    The shellcode to prepend the NOPs to

Returns:

  • (String)

    the shellcode with the appropriate nopsled affixed


342
343
344
345
346
347
348
349
350
351
352
353
354
355
# File 'lib/msf/core/payload_generator.rb', line 342

def prepend_nops(shellcode)
  if nops > 0
    framework.nops.each_module_ranked('Arch' => [arch]) do |name, mod|
      nop = framework.nops.create(name)
      raw = nop.generate_sled(nops, {'BadChars' => badchars, 'SaveRegisters' => [ 'esp', 'ebp', 'esi', 'edi' ] })
      if raw
        cli_print "Successfully added NOP sled from #{name}"
        return raw + shellcode
      end
    end
  else
    shellcode
  end
end

#run_encoder(encoder_module, shellcode) ⇒ String

This method runs a specified encoder, for a number of defined iterations against the shellcode.

Parameters:

  • encoder_module (Msf::Encoder)

    The Encoder to run against the shellcode

  • shellcode (String)

    The shellcode to be encoded

Returns:

  • (String)

    The encoded shellcode

Raises:


362
363
364
365
366
367
368
369
# File 'lib/msf/core/payload_generator.rb', line 362

def run_encoder(encoder_module, shellcode)
  iterations.times do |x|
    shellcode = encoder_module.encode(shellcode.dup, badchars, nil, platform_list)
    cli_print "#{encoder_module.refname} succeeded with size #{shellcode.length} (iteration=#{x})"
    raise EncoderSpaceViolation, "encoder has made a buffer that is too big" if shellcode.length > space
  end
  shellcode
end