tools/missing-payload-tests.rb

MSP-11145

**NOTE: Failing specs**

Add a tool for reading `log/untested-payload.log` and
`framework.payloads` to determine `context`s to add
`spec/modules/payloads_spec.rb` to test the untested payloads.
bug/bundler_fix
Luke Imhoff 2014-10-27 13:03:31 -05:00
parent 1df82ea273
commit c84febea5f
No known key found for this signature in database
GPG Key ID: 5B1FB01FB33356F8
3 changed files with 159 additions and 75 deletions

View File

@ -44,13 +44,12 @@ module Metasploit
untested_payloads_pathname = Pathname.new 'log/untested-payloads.log'
if untested_payloads_pathname.exist?
$stderr.puts "Untested payload detected. Add tests to spec/modules/payload_spec.rb for payloads classes composed of the following payload modules:"
tool_path = 'tools/missing-payload-tests.rb'
untested_payloads_pathname.open do |f|
f.each_line do |line|
$stderr.write " #{line}"
end
end
$stderr.puts "Untested payload detected. Running `#{tool_path}` to see contexts to add to " \
"`spec/modules/payloads_spec.rb` to test those payload ancestor reference names."
system(tool_path)
exit 1
end

View File

@ -31,75 +31,75 @@ describe 'modules/payloads' do
modules_pathname: modules_pathname,
reference_name: 'aix/ppc/shell_interact'
end
context 'aix/ppc/shell_reverse_tcp' do
it_should_behave_like 'payload can be instantiated',
ancestor_reference_names: [
'singles/aix/ppc/shell_reverse_tcp'
],
modules_pathname: modules_pathname,
reference_name: 'aix/ppc/shell_reverse_tcp'
end
context 'android/meterpreter/reverse_http' do
it_should_behave_like 'payload can be instantiated',
ancestor_reference_names: [
'stagers/android/reverse_http',
'stages/android/meterpreter'
],
modules_pathname: modules_pathname,
reference_name: 'android/meterpreter/reverse_http'
end
context 'android/meterpreter/reverse_https' do
it_should_behave_like 'payload can be instantiated',
ancestor_reference_names: [
'stagers/android/reverse_https',
'stages/android/meterpreter'
],
modules_pathname: modules_pathname,
reference_name: 'android/meterpreter/reverse_https'
end
context 'android/meterpreter/reverse_tcp' do
it_should_behave_like 'payload can be instantiated',
ancestor_reference_names: [
'stagers/android/reverse_tcp',
'stages/android/meterpreter'
],
modules_pathname: modules_pathname,
reference_name: 'android/meterpreter/reverse_tcp'
end
context 'android/shell/reverse_http' do
it_should_behave_like 'payload can be instantiated',
ancestor_reference_names: [
'stagers/android/reverse_http',
'stages/android/shell'
],
modules_pathname: modules_pathname,
reference_name: 'android/shell/reverse_http'
end
context 'android/shell/reverse_https' do
it_should_behave_like 'payload can be instantiated',
ancestor_reference_names: [
'stagers/android/reverse_https',
'stages/android/shell'
],
modules_pathname: modules_pathname,
reference_name: 'android/shell/reverse_https'
end
context 'android/shell/reverse_tcp' do
it_should_behave_like 'payload can be instantiated',
ancestor_reference_names: [
'stagers/android/reverse_tcp',
'stages/android/shell'
],
modules_pathname: modules_pathname,
reference_name: 'android/shell/reverse_tcp'
end
#
# context 'aix/ppc/shell_reverse_tcp' do
# it_should_behave_like 'payload can be instantiated',
# ancestor_reference_names: [
# 'singles/aix/ppc/shell_reverse_tcp'
# ],
# modules_pathname: modules_pathname,
# reference_name: 'aix/ppc/shell_reverse_tcp'
# end
#
# context 'android/meterpreter/reverse_http' do
# it_should_behave_like 'payload can be instantiated',
# ancestor_reference_names: [
# 'stagers/android/reverse_http',
# 'stages/android/meterpreter'
# ],
# modules_pathname: modules_pathname,
# reference_name: 'android/meterpreter/reverse_http'
# end
#
# context 'android/meterpreter/reverse_https' do
# it_should_behave_like 'payload can be instantiated',
# ancestor_reference_names: [
# 'stagers/android/reverse_https',
# 'stages/android/meterpreter'
# ],
# modules_pathname: modules_pathname,
# reference_name: 'android/meterpreter/reverse_https'
# end
#
# context 'android/meterpreter/reverse_tcp' do
# it_should_behave_like 'payload can be instantiated',
# ancestor_reference_names: [
# 'stagers/android/reverse_tcp',
# 'stages/android/meterpreter'
# ],
# modules_pathname: modules_pathname,
# reference_name: 'android/meterpreter/reverse_tcp'
# end
#
# context 'android/shell/reverse_http' do
# it_should_behave_like 'payload can be instantiated',
# ancestor_reference_names: [
# 'stagers/android/reverse_http',
# 'stages/android/shell'
# ],
# modules_pathname: modules_pathname,
# reference_name: 'android/shell/reverse_http'
# end
#
# context 'android/shell/reverse_https' do
# it_should_behave_like 'payload can be instantiated',
# ancestor_reference_names: [
# 'stagers/android/reverse_https',
# 'stages/android/shell'
# ],
# modules_pathname: modules_pathname,
# reference_name: 'android/shell/reverse_https'
# end
#
# context 'android/shell/reverse_tcp' do
# it_should_behave_like 'payload can be instantiated',
# ancestor_reference_names: [
# 'stagers/android/reverse_tcp',
# 'stages/android/shell'
# ],
# modules_pathname: modules_pathname,
# reference_name: 'android/shell/reverse_tcp'
# end
context 'bsd/sparc/shell_bind_tcp' do
it_should_behave_like 'payload can be instantiated',

85
tools/missing-payload-tests.rb Executable file
View File

@ -0,0 +1,85 @@
#!/usr/bin/env ruby
# Reads untest payload modules from log/untested-payloads.log (which can be produced by running `rake spec`) and prints
# the statements that need to be added to `spec/modules/payloads_spec.rb`. **Note: this script depends on the payload
# being loadable, so if module is not loadable, then the developer must manually determine which single needs to be tested
# or which combinations of stages and stagers need to be tested.**
msfbase = __FILE__
while File.symlink?(msfbase)
msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase))
end
$:.unshift(File.expand_path(File.join(File.dirname(msfbase), '..', 'lib')))
require 'msfenv'
require 'msf/core'
require 'msf/base'
framework = Msf::Simple::Framework.create()
options_set_by_ancestor_reference_name = Hash.new { |hash, ancestor_reference_name|
hash[ancestor_reference_name] = Set.new
}
framework.payloads.each { |reference_name, payload_class|
module_ancestors = payload_class.ancestors.select { |ancestor|
# need to use try because name may be nil for anonymous Modules
ancestor.name.try(:start_with?, 'Msf::Modules::')
}
ancestor_reference_names = module_ancestors.map { |module_ancestor|
unpacked_module_ancestor_full_name = module_ancestor.name.sub(/^Msf::Modules::Mod/, '')
.sub(/::Metasploit\d+/, '')
module_ancestor_full_name = [unpacked_module_ancestor_full_name].pack("H*")
module_ancestor_full_name.sub(%r{^payload/}, '')
}
options = {
reference_name: reference_name,
ancestor_reference_names: ancestor_reference_names
}
# record for both ancestor_reference_names as which is untested is not known here
ancestor_reference_names.each do |ancestor_reference_name|
options_set_by_ancestor_reference_name[ancestor_reference_name].add options
end
}
tested_options = Set.new
$stderr.puts "Add the following context to `spec/modules/payloads_spec.rb` by inserting them in lexical order between the pre-existing contexts:"
File.open('log/untested-payloads.log') { |f|
f.each_line do |line|
ancestor_reference_name = line.strip
options_set = options_set_by_ancestor_reference_name[ancestor_reference_name]
options_set.each do |options|
# don't print a needed test twice
unless tested_options.include? options
reference_name = options[:reference_name]
$stderr.puts
$stderr.puts " context '#{reference_name}' do\n" \
" it_should_behave_like 'payload can be instantiated',\n" \
" ancestor_reference_name: ["
ancestor_reference_names = options[:ancestor_reference_names]
if ancestor_reference_names.length == 1
$stderr.puts " '#{ancestor_reference_names[0]}'"
else
$stderr.puts " '#{ancestor_reference_names[0]}',"
$stderr.puts " '#{ancestor_reference_names[1]}'"
end
$stderr.puts " ],\n" \
" modules_pathname: modules_pathname,\n" \
" reference_name: '#{reference_name}'\n" \
" end"
tested_options.add options
end
end
end
}