Module: Metasploit::Model::Module::Class

Extended by:
ActiveModel::Naming, ActiveSupport::Autoload, ActiveSupport::Concern
Includes:
Translation
Defined in:
lib/metasploit/model/module/class.rb

Overview

Code shared between Mdm::Module::Class and Metasploit::Framework::Module::Class.

Defined Under Namespace

Modules: Spec

Constant Summary collapse

PAYLOAD_TYPES =

Valid values for #payload_type when #payload? is true.

[
    'single',
    'staged'
]
STAGED_ANCESTOR_PAYLOAD_TYPES =

The Ancestor#payload_type when #payload_type is 'staged'.

[
    'stage',
    'stager'
]

Instance Attribute Summary collapse

Instance Method Summary collapse

Instance Attribute Details

#ancestorsArray<Metasploit::Model::Module::Ancestor> (readonly)

The Class or Modules that were loaded to make this module Class.



# File 'lib/metasploit/model/module/class.rb', line 95

#full_nameString

The full name (type + reference) for the ClassMsf::Module. This is merely a denormalized cache of "#{{#module_type}}/#{{#reference_name}}" as full_name is used in numerous queries and reports.

Returns:

  • (String)


# File 'lib/metasploit/model/module/class.rb', line 104

#module_typeString

A denormalized cache of the ancestors' module_types, which must all be the same. This cache exists so that queries for modules of a given type don't need include the #ancestors.

Returns:

  • (String)


# File 'lib/metasploit/model/module/class.rb', line 110

#payload_typeString?

For payload modules, the type of payload, either 'single' or 'staged'.

Returns:



# File 'lib/metasploit/model/module/class.rb', line 117

#rankMetasploit::Model::Module::Rank

The reliability of the module and likelyhood that the module won't knock over the service or host being exploited. Bigger values is better.



# File 'lib/metasploit/model/module/class.rb', line 89

#reference_nameString

The reference name for the ClassMsf::Module. For non-payloads, this will just be Ancestor#reference_name for the only element in #ancestors. For payloads composed of a stage and stager, the reference name will be derived from the Ancestor#reference_name of each element #ancestors or an alias defined in those Modules.

Returns:

  • (String)


# File 'lib/metasploit/model/module/class.rb', line 123

Instance Method Details

#ancestor_module_typesvoid (private)

This method returns an undefined value.

Validates that #ancestors all have the same Ancestor#module_type as #module_type.



238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
# File 'lib/metasploit/model/module/class.rb', line 238

def ancestor_module_types
  ancestor_module_type_set = Set.new

  ancestors.each do |ancestor|
    if module_type and ancestor.module_type != module_type
      errors[:ancestors] << "can contain ancestors only with same module_type (#{module_type}); " \
                      "#{ancestor.full_name} cannot be an ancestor due to its module_type " \
                      "(#{ancestor.module_type})"
    end

    ancestor_module_type_set.add ancestor.module_type
  end

  if ancestor_module_type_set.length > 1
    ancestor_module_type_sentence = ancestor_module_type_set.sort.to_sentence
    errors[:ancestors] << "can only contain ancestors with one module_type, " \
                    "but contains multiple module_types (#{ancestor_module_type_sentence})"
  end
end

#ancestor_payload_typesvoid (private)

This method returns an undefined value.

Validates that #ancestors have correct payload_types for the #module_type and #payload_type.



262
263
264
265
266
267
268
269
270
271
272
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
301
# File 'lib/metasploit/model/module/class.rb', line 262

def ancestor_payload_types
  if payload?
    case payload_type
      when 'single'
        ancestors.each do |ancestor|
          unless ancestor.payload_type == 'single'
            errors[:ancestors] << "cannot have an ancestor (#{ancestor.full_name}) " \
                            "with payload_type (#{ancestor.payload_type}) " \
                            "for class payload_type (#{payload_type})"
          end
        end
      when 'staged'
        ancestors_by_payload_type = ancestors.group_by(&:payload_type)

        STAGED_ANCESTOR_PAYLOAD_TYPES.each do |ancestor_payload_type|
          staged_payload_type_count(ancestors_by_payload_type, ancestor_payload_type)
        end

        ancestors_by_payload_type.each do |ancestor_payload_type, ancestors|
          unless STAGED_ANCESTOR_PAYLOAD_TYPES.include? ancestor_payload_type
            full_names = ancestors.map(&:full_name)
            full_name_sentence = full_names.to_sentence

            errors[:ancestors] << "cannot have ancestors (#{full_name_sentence}) " \
                            "with payload_type (#{ancestor_payload_type}) " \
                            "for class payload_type (#{payload_type}); " \
                            "only one stage and one stager ancestor is allowed"
          end
        end
    end
  else
    ancestors.each do |ancestor|
      if ancestor.payload_type
        errors[:ancestors] << "cannot have an ancestor (#{ancestor.full_name}) " \
                        "with a payload_type (#{ancestor.payload_type}) " \
                        "for class module_type (#{module_type})"
      end
    end
  end
end

#ancestors_sizevoid (private)

This method returns an undefined value.

Validates that number of #ancestors is correct for the #module_type.



306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
# File 'lib/metasploit/model/module/class.rb', line 306

def ancestors_size
  if payload?
    case payload_type
      when 'single'
        unless ancestors.size == 1
          errors[:ancestors] << 'must have exactly one ancestor for single payload module class'
        end
      when 'staged'
        unless ancestors.size == 2
          errors[:ancestors] << 'must have exactly two ancestors (stager + stage) for staged payload module class'
        end
      # other (invalid) types are handled by validation on payload_type
    end
  else
    unless ancestors.size == 1
      errors[:ancestors] << 'must have exactly one ancestor as a non-payload module class'
    end
  end
end

#derived_module_typeString?

Derives #module_type from the consensus of ancestors' module_types.

Returns:



144
145
146
147
148
149
150
151
152
153
154
155
156
157
# File 'lib/metasploit/model/module/class.rb', line 144

def derived_module_type
  module_type_consensus = nil
  module_type_set = Set.new

  ancestors.each do |ancestor|
    module_type_set.add ancestor.module_type
  end

  if module_type_set.length == 1
    module_type_consensus = module_type_set.to_a.first
  end

  module_type_consensus
end

#derived_payload_type'single', ...

Returns:



168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
# File 'lib/metasploit/model/module/class.rb', line 168

def derived_payload_type
  derived = nil

  if payload?
    case ancestors.length
      when 1
        if ancestors.first.payload_type == 'single'
          derived = 'single'
        end
      when 2
        payload_type_set = Set.new

        ancestors.each do |ancestor|
          payload_type_set.add ancestor.payload_type
        end

        if payload_type_set.include? 'stager' and payload_type_set.include? 'stage'
          derived = 'staged'
        end
    end
  end

  derived
end

#derived_reference_nameString?

Derives #reference_name from #ancestors.

Returns:



201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
# File 'lib/metasploit/model/module/class.rb', line 201

def derived_reference_name
  derived = nil

  if payload?
    case payload_type
      when 'single'
        derived = derived_single_payload_reference_name
      when 'staged'
        derived = derived_staged_payload_reference_name
    end
  else
    if ancestors.length == 1
      derived = ancestors.first.reference_name
    end
  end

  derived
end

#derived_single_payload_reference_nameString? (private)

Note:

Caller should check that #payload? is true and #payload_type is 'single' before calling #derived_single_payload_reference_name.

Derives #reference_name for single payload.

Returns:



334
335
336
337
338
339
340
341
342
343
344
345
346
# File 'lib/metasploit/model/module/class.rb', line 334

def derived_single_payload_reference_name
  derived = nil

  if ancestors.length == 1
    ancestor = ancestors.first

    if ancestor.payload_type == 'single'
      derived = ancestor.payload_name
    end
  end

  derived
end

#derived_staged_payload_reference_nameString? (private)

Note:

Caller should check that #payload? is true and #payload_type is 'staged' before calling #derived_staged_payload_reference_name.

Derives #reference_name for staged payload.

Returns:



358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
# File 'lib/metasploit/model/module/class.rb', line 358

def derived_staged_payload_reference_name
  derived = nil

  if ancestors.length == 2
    ancestors_by_payload_type = ancestors.group_by(&:payload_type)
    stage_ancestors = ancestors_by_payload_type.fetch('stage', [])

    # length can be 0..2
    if stage_ancestors.length == 1
      stage_ancestor = stage_ancestors.first

      if stage_ancestor.payload_name
        stager_ancestors = ancestors_by_payload_type.fetch('stager', [])

        # length can be 0..1
        if stager_ancestors.length == 1
          stager_ancestor = stager_ancestors.first

          if stager_ancestor.payload_name
            derived = "#{stage_ancestor.payload_name}/#{stager_ancestor.payload_name}"
          end
        end
      end
    end
  end

  derived
end

#payload?true, false

Returns whether this represents a ClassMsf::Payload.

Returns:



224
225
226
227
228
229
230
# File 'lib/metasploit/model/module/class.rb', line 224

def payload?
  if module_type == 'payload'
    true
  else
    false
  end
end

#staged_payload_type_count(ancestors_by_payload_type, ancestor_payload_type) ⇒ void (private)

This method returns an undefined value.

Validates that only 1 ancestor with the given payload_type exists.

Parameters:



394
395
396
397
398
399
400
401
402
403
404
405
406
407
# File 'lib/metasploit/model/module/class.rb', line 394

def staged_payload_type_count(ancestors_by_payload_type, ancestor_payload_type)
  payload_type_ancestors = ancestors_by_payload_type.fetch(ancestor_payload_type, [])
  payload_type_ancestor_count = payload_type_ancestors.length

  if payload_type_ancestor_count < 1
    errors[:ancestors] << "needs exactly one ancestor with payload_type (#{ancestor_payload_type}), " \
                    "but there are none."
  elsif payload_type_ancestor_count > 1
    full_names = payload_type_ancestors.map(&:full_name).sort
    full_name_sentence = full_names.to_sentence
    errors[:ancestors] << "needs exactly one ancestor with payload_type (#{ancestor_payload_type}), " \
                    "but there are #{payload_type_ancestor_count} (#{full_name_sentence})"
  end
end