CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
rapid7

Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.

GitHub Repository: rapid7/metasploit-framework
Path: blob/master/tools/modules/verify_datastore.rb
Views: 11619
1
#!/usr/bin/env ruby
2
3
#
4
# This script parses a Metasploit module's use of the datastore to
5
# ensure that all datastore elements are both declared and used. Adding
6
# arbitrary elements to the datastore without first declaring them won't
7
# throw an error at runtime, but can occasionally be the cause of bugs and
8
# make troubleshooting more difficult.
9
#
10
# This script could use more serious option parsing, and a batch mode beyond
11
# bash's "for i in path/to/modules/*.rb; do verify_datastore.rb $i; done" Also,
12
# it assumes Metasploit's msf/core is in the load path.
13
#
14
15
infile = ARGV[0]
16
unless(infile && File.readable?(infile))
17
puts "Usage: #{$0} /path/to/module.rb"
18
exit(1)
19
end
20
21
verbose = false
22
23
mod = File.open(infile, "rb") {|f| f.read(f.stat.size)}
24
25
regex = {}
26
regex[:datastore] = /[^\x2e](datastore\x5b[\x22\x27]([^\x22\x27]+))/
27
regex[:comment] = /^[\s]*#/
28
regex[:opts] = /register_options/
29
regex[:opts_end] = /^[\s]*def[\s]+/
30
regex[:is_opt] = /^[\s]*(Opt[A-Z][^\x2e]+)\x2enew[\s]*\x28?[\x22\x27]([^\x22\x27]+)/
31
regex[:mixin] = /^[\s]*include[\s]+([^\s]+)/
32
regex[:class] = /^[\s]*class[\s]+Metasploit3[\s]*<[\s]*([A-Z][^\s]+)/
33
# regex[:require] = /^[\s]*require[\s]+[\x22\x27]([^\x22\x27]+)[\x22\x27]/
34
35
referenced_datastores = []
36
declared_datastores = {}
37
undeclared_datastores = []
38
unused_datastores = []
39
40
# Declared datastore finder
41
mod.each_line do |line|
42
next if line.match regex[:comment]
43
datastores = line.scan regex[:datastore]
44
datastores.each {|ds| referenced_datastores << ds[1]}
45
end
46
47
# Referenced datastore finder
48
in_opts = false
49
mod.each_line do |line|
50
in_opts = true if line.match regex[:opts]
51
in_opts = false if line.match regex[:opts_end]
52
next unless in_opts
53
if line.match regex[:is_opt]
54
# Assumes only one!
55
declared_datastores[$2] ||= $1
56
end
57
end
58
59
# Class and Mixin finder
60
$mixins = []
61
$class = nil
62
63
mod.each_line do |line|
64
if line.match regex[:class]
65
$class = ObjectSpace.class_eval($1)
66
elsif line.match regex[:mixin]
67
mixin = $1
68
begin
69
$mixins << ObjectSpace.module_eval(mixin)
70
rescue
71
puts "[-] Error including mixin: #{mixin}"
72
next
73
end
74
end
75
end
76
77
class Fakemod < $class
78
$mixins.each {|m| include m}
79
end
80
fakemod = Fakemod.new
81
inhereted_datastores = fakemod.options.keys
82
83
undeclared_datastores = referenced_datastores - (declared_datastores.keys + inhereted_datastores)
84
85
# It's common to not use some inhereted datastores, don't bother talking about them
86
unused_datastores = declared_datastores.keys - referenced_datastores
87
88
if verbose
89
puts "[+] --- Referenced datastore keys for #{infile}"
90
referenced_datastores.uniq.sort.each {|ds| puts ds}
91
puts "[+] --- Declared datastore keys for #{infile}"
92
declared_datastores.keys.sort.each {|opt| puts "%-30s%s" % [opt, declared_datastores[opt]] }
93
end
94
95
unless undeclared_datastores.empty?
96
puts "[-] %-60s : fail (undeclared)" % [infile]
97
puts "[-] The following datastore elements are undeclared" if verbose
98
undeclared_datastores.uniq.sort.each {|opt| puts " \e[31m#{opt}\e[0m" }
99
end
100
101
unless unused_datastores.empty?
102
puts "[*] %-60s : warn (unused)" % [infile]
103
puts "[*] The following datastore elements are unused" if verbose
104
unused_datastores.uniq.sort.each {|opt| puts " \e[33m#{opt}\e[0m" }
105
end
106
107
if undeclared_datastores.empty? && unused_datastores.empty?
108
puts "[+] %-60s : okay" % [infile]
109
end
110
111