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

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

Overview

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

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 90

#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 99

#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 105

#payload_typeString?

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

Returns:



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

#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 84

#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 118

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.



233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
# File 'lib/metasploit/model/module/class.rb', line 233

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.



257
258
259
260
261
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
# File 'lib/metasploit/model/module/class.rb', line 257

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.



301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
# File 'lib/metasploit/model/module/class.rb', line 301

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:



139
140
141
142
143
144
145
146
147
148
149
150
151
152
# File 'lib/metasploit/model/module/class.rb', line 139

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:



163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
# File 'lib/metasploit/model/module/class.rb', line 163

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:



196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
# File 'lib/metasploit/model/module/class.rb', line 196

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:



329
330
331
332
333
334
335
336
337
338
339
340
341
# File 'lib/metasploit/model/module/class.rb', line 329

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:



353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
# File 'lib/metasploit/model/module/class.rb', line 353

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:



219
220
221
222
223
224
225
# File 'lib/metasploit/model/module/class.rb', line 219

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:



389
390
391
392
393
394
395
396
397
398
399
400
401
402
# File 'lib/metasploit/model/module/class.rb', line 389

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