Class: Msf::PayloadSet

Inherits:
ModuleSet show all
Defined in:
lib/msf/core/payload_set.rb

Overview

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!

Instance Attribute Summary collapse

Attributes inherited from ModuleSet

#module_type

Attributes included from Framework::Offspring

#framework

Instance Method Summary collapse

Methods inherited from ModuleSet

#[], #create, #each, #each_module, #each_module_ranked, #force_load_set, #valid?

Constructor Details

#initializePayloadSet

Creates an instance of a payload set which is just a specialized module set class that has custom handling for payloads.


23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
# File 'lib/msf/core/payload_set.rb', line 23

def initialize
  super(MODULE_PAYLOAD)

  # 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 = {}

  # Initializes an empty blob cache
  @blob_cache = {}
end

Instance Attribute Details

#singlesObject

The list of singles that have been loaded.


378
379
380
# File 'lib/msf/core/payload_set.rb', line 378

def singles
  @singles
end

#sizesObject

The sizes of all the built payloads thus far.


382
383
384
# File 'lib/msf/core/payload_set.rb', line 382

def sizes
  @sizes
end

#stagesObject

The list of stages that have been loaded.


374
375
376
# File 'lib/msf/core/payload_set.rb', line 374

def stages
  @stages
end

Instance Method Details

#add_blob_cache(key, blob, offsets) ⇒ Object

Adds a blob to the blob cache so that the payload does not have to be recompiled in the future


352
353
354
# File 'lib/msf/core/payload_set.rb', line 352

def add_blob_cache(key, blob, offsets)
  @blob_cache[key] = [ blob, offsets ]
end

#add_module(payload_module, reference_name, modinfo = {}) ⇒ void

This method returns an undefined value.

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.

Parameters:

  • payload_module (::Module)

    The module name.

  • reference_name (String)

    The module reference name.

  • modinfo (Hash{String => Array}) (defaults to: {})

    additional information about the module.

Options Hash (modinfo):

  • 'files' (Array<String>)

    List of paths to the ruby source files where class_or_module is defined.

  • 'paths' (Array<String>)

    List of module reference names.

  • 'type' (String)

    The module type, should match positional type argument.


187
188
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
# File 'lib/msf/core/payload_set.rb', line 187

def add_module(payload_module, reference_name, modinfo={})

  if (md = reference_name.match(/^(singles|stagers|stages)#{File::SEPARATOR}(.*)$/))
    ptype = md[1]
    reference_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(payload_module).new

  # Create an array of information about this payload module
  pinfo =
    [
      payload_module,
      instance.handler_klass,
      instance.platform,
      instance.arch,
      instance,
      modinfo
    ]

  # Use the module's preferred alias if it has one
  reference_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][reference_name] = pinfo
end

#add_single(p, name, modinfo) ⇒ Object

This method adds a single payload to the set and adds it to the singles hash.


279
280
281
282
283
284
285
286
287
288
289
290
291
# File 'lib/msf/core/payload_set.rb', line 279

def add_single(p, name, modinfo)
  p.framework = framework
  p.refname = name
  p.file_path = modinfo['files'][0]

  # Associate this class with the single payload's name
  self[name] = p

  # Add the singles hash
  singles[name] = p

  dlog("Built single payload #{name}.", 'core', LEV_2)
end

#add_stage(p, full_name, stage_name, handler_type, modinfo) ⇒ Object

This method adds a stage payload to the set and adds it to the stages hash using the supplied handler type.


297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
# File 'lib/msf/core/payload_set.rb', line 297

def add_stage(p, full_name, stage_name, handler_type, modinfo)
  p.framework = framework
  p.refname = full_name
  p.file_path = modinfo['files'][0]

  # Associate this stage's full name with the payload class in the set
  self[full_name] = p

  # 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_2)
end

#check_blob_cache(key) ⇒ Object

Checks to see if a payload has a blob cache entry. If it does, the blob is returned to the caller.


360
361
362
# File 'lib/msf/core/payload_set.rb', line 360

def check_blob_cache(key)
  @blob_cache[key]
end

#each_module_filter(opts, name, mod) ⇒ Object

Performs custom filtering during each_module enumeration. This allows us to filter out certain stagers as necessary.


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

def each_module_filter(opts, name, mod)
  return false
end

#find_payload(platform, arch, handler, session, payload_type) ⇒ Object

Looks for a payload that matches the specified requirements and returns an instance of that payload.


224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
# File 'lib/msf/core/payload_set.rb', line 224

def find_payload(platform, arch, handler, session, payload_type)
  # Pre-filter based on platform and architecture.
  each_module(
    'Platform' => platform,
    'Arch'     => arch) { |name, mod|

    p = mod.new

    # We can't substitute one generic with another one.
    next if (p.kind_of?(Msf::Payload::Generic))

    # Check to see if the handler classes match.
    next if (handler and not p.handler_klass.ancestors.include?(handler))

    # Check to see if the session classes match.
    next if (session and not p.session.ancestors.include?(session))

    # Check for matching payload types
    next if (payload_type and p.payload_type != payload_type)

    return p
  }

  return nil
end

#find_payload_from_set(set, platform, arch, handler, session, payload_type) ⇒ Object

Looks for a payload from a given set that matches the specified requirements and returns an instance of that payload.


254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
# File 'lib/msf/core/payload_set.rb', line 254

def find_payload_from_set(set, platform, arch, handler, session, payload_type)
  set.each do |name, mod|
    p = mod.new

    # We can't substitute one generic with another one.
    next if (p.kind_of?(Msf::Payload::Generic))

    # Check to see if the handler classes match.
    next if (handler and p.handler_klass != handler)

    # Check to see if the session classes match.
    next if (session and p.session != session)

    # Check for matching payload types
    next if (payload_type and p.payload_type != payload_type)

    return p
  end
  return nil
end

#flush_blob_cacheObject

Flushes all entries from the blob cache


367
368
369
# File 'lib/msf/core/payload_set.rb', line 367

def flush_blob_cache
  @blob_cache.clear
end

#instance(name) ⇒ Object

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.


321
322
323
324
325
326
327
# File 'lib/msf/core/payload_set.rb', line 321

def instance(name)
  if (self._instances[name] == nil)
    self._instances[name] = create(name)
  end

  self._instances[name]
end

#on_module_reload(mod) ⇒ Object

When a payload module is reloaded, the blob cache entry associated with it must be removed (if one exists)


340
341
342
343
344
345
346
# File 'lib/msf/core/payload_set.rb', line 340

def on_module_reload(mod)
  @blob_cache.each_key do |key|
    if key.start_with? mod.refname
      @blob_cache.delete(key)
    end
  end
end

#recalculateObject

This method builds the hash of alias names based on all the permutations of singles, stagers, and stages.


73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
# File 'lib/msf/core/payload_set.rb', line 73

def recalculate
  old_keys = self.keys
  new_keys = []

  # Recalculate single payloads
  _singles.each_pair { |name, op|
    mod, handler = op

    # Build the payload dupe using the determined handler
    # and module
    p = build_payload(handler, mod)

    # Add it to the set
    add_single(p, name, op[5])
    new_keys.push name

    # Cache the payload's size
    begin
      sizes[name] = p.new.size
    # Don't cache generic payload sizes.
    rescue NoCompatiblePayloadError
    end
  }

  # Recalculate staged payloads
  _stagers.each_pair { |stager_name, op|
    stager_mod, handler, stager_platform, stager_arch, stager_inst = op

    # Walk the array of stages
    _stages.each_pair { |stage_name, ip|
      stage_mod, _, stage_platform, stage_arch, stage_inst = ip

      # 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_2)
        next
      end

      # 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_2)
        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_2)
        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, {
        'files' => op[5]['files'] + ip[5]['files'],
        'paths' => op[5]['paths'] + ip[5]['paths'],
        'type'  => op[5]['type']})
      new_keys.push combined

      # Cache the payload's size
      sizes[combined] = p.new.size
    }
  }

  # Blow away anything that was cached but didn't exist during the
  # recalculation
  self.delete_if do |k, v|
    next if v == SymbolicModule
    !!(old_keys.include?(k) and not new_keys.include?(k))
  end

  flush_blob_cache
end

#stagersObject

Returns the hash of payload stagers that have been loaded.


332
333
334
# File 'lib/msf/core/payload_set.rb', line 332

def stagers
  _stagers
end