I fell down a rathole trying to debug something today, but at least now I can always find my way back with this. It parses metasploit modules to see if you're using all the datastore elements you declared and if you declared all your datastore elements. Don't try to get too fancy with it -- my metaprogramming is all driven by regex, so it'll be easy to trick me if you want to with funny-looking code with weird linebreaks.
git-svn-id: file:///home/svn/framework3/trunk@9758 4d416f70-5f16-0410-b530-b9f4589650daunstable
parent
3d01172af5
commit
d1726ddadf
|
@ -0,0 +1,114 @@
|
|||
#!/usr/bin/env ruby
|
||||
#
|
||||
# $Id$
|
||||
#
|
||||
# This script parses a Metasploit module's use of the datastore to
|
||||
# ensure that all datastore elements are both declared and used. Adding
|
||||
# arbitrary elements to the datastore without first declaring them won't
|
||||
# throw an error at runtime, but can occasionally be the cause of bugs and
|
||||
# make troubleshooting more difficult.
|
||||
#
|
||||
# This script could use more serious option parsing, and a batch mode beyond
|
||||
# bash's "for i in path/to/modules/*.rb; do verify_datastore.rb $i; done" Also,
|
||||
# it assumes Metasploit's msf/core is in the load path.
|
||||
#
|
||||
# $Revision$
|
||||
#
|
||||
|
||||
infile = ARGV[0]
|
||||
unless(infile && File.readable?(infile))
|
||||
puts "Usage: #{$0} /path/to/module.rb"
|
||||
exit(1)
|
||||
end
|
||||
|
||||
verbose = false
|
||||
|
||||
mod = File.open(infile, "rb") {|f| f.read(f.stat.size)}
|
||||
|
||||
regex = {}
|
||||
regex[:datastore] = /[^\x2e](datastore\x5b[\x22\x27]([^\x22\x27]+))/
|
||||
regex[:comment] = /^[\s]*#/
|
||||
regex[:opts] = /register_options/
|
||||
regex[:opts_end] = /^[\s]*def[\s]+/
|
||||
regex[:is_opt] = /^[\s]*(Opt[A-Z][^\x2e]+)\x2enew[\s]*\x28?[\x22\x27]([^\x22\x27]+)/
|
||||
regex[:mixin] = /^[\s]*include[\s]+([^\s]+)/
|
||||
regex[:class] = /^[\s]*class[\s]+Metasploit3[\s]*<[\s]*([A-Z][^\s]+)/
|
||||
# regex[:require] = /^[\s]*require[\s]+[\x22\x27]([^\x22\x27]+)[\x22\x27]/
|
||||
|
||||
referenced_datastores = []
|
||||
declared_datastores = {}
|
||||
undeclared_datastores = []
|
||||
unused_datastores = []
|
||||
|
||||
# Declared datastore finder
|
||||
mod.each_line do |line|
|
||||
next if line.match regex[:comment]
|
||||
datastores = line.scan regex[:datastore]
|
||||
datastores.each {|ds| referenced_datastores << ds[1]}
|
||||
end
|
||||
|
||||
# Referenced datastore finder
|
||||
in_opts = false
|
||||
mod.each_line do |line|
|
||||
in_opts = true if line.match regex[:opts]
|
||||
in_opts = false if line.match regex[:opts_end]
|
||||
next unless in_opts
|
||||
if line.match regex[:is_opt]
|
||||
# Assumes only one!
|
||||
declared_datastores[$2] ||= $1
|
||||
end
|
||||
end
|
||||
|
||||
# Class and Mixin finder
|
||||
$mixins = []
|
||||
$class = nil
|
||||
require 'msf/core' # Make sure this is in your path, or else all is for naught.
|
||||
|
||||
mod.each_line do |line|
|
||||
if line.match regex[:class]
|
||||
$class = ObjectSpace.class_eval($1)
|
||||
elsif line.match regex[:mixin]
|
||||
mixin = $1
|
||||
begin
|
||||
$mixins << ObjectSpace.module_eval(mixin)
|
||||
rescue
|
||||
puts "[-] Error including mixin: #{mixin}"
|
||||
next
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class Fakemod < $class
|
||||
$mixins.each {|m| include m}
|
||||
end
|
||||
fakemod = Fakemod.new
|
||||
inhereted_datastores = fakemod.options.keys
|
||||
|
||||
undeclared_datastores = referenced_datastores - (declared_datastores.keys + inhereted_datastores)
|
||||
|
||||
# It's common to not use some inhereted datastores, don't bother talking about them
|
||||
unused_datastores = declared_datastores.keys - referenced_datastores
|
||||
|
||||
if verbose
|
||||
puts "[+] --- Referenced datastore keys for #{infile}"
|
||||
referenced_datastores.uniq.sort.each {|ds| puts ds}
|
||||
puts "[+] --- Declared datastore keys for #{infile}"
|
||||
declared_datastores.keys.sort.each {|opt| puts "%-30s%s" % [opt, declared_datastores[opt]] }
|
||||
end
|
||||
|
||||
unless undeclared_datastores.empty?
|
||||
puts "[-] %-60s : fail (undeclared)" % [infile]
|
||||
puts "[-] The following datastore elements are undeclared" if verbose
|
||||
undeclared_datastores.uniq.sort.each {|opt| puts " \e[31m#{opt}\e[0m" }
|
||||
end
|
||||
|
||||
unless unused_datastores.empty?
|
||||
puts "[*] %-60s : warn (unused)" % [infile]
|
||||
puts "[*] The following datastore elements are unused" if verbose
|
||||
unused_datastores.uniq.sort.each {|opt| puts " \e[33m#{opt}\e[0m" }
|
||||
end
|
||||
|
||||
if undeclared_datastores.empty? && unused_datastores.empty?
|
||||
puts "[+] %-60s : okay" % [infile]
|
||||
end
|
||||
|
Loading…
Reference in New Issue