Skip to content

Commit 6e51d84

Browse files
committed
Land rapid7#4138, @wchen-r7's reference cheking for module_reference.rb
* Fixes rapid7#4039
2 parents f7e308c + 8da6e0b commit 6e51d84

File tree

2 files changed

+147
-28
lines changed

2 files changed

+147
-28
lines changed

lib/msf/core/module/reference.rb

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,9 @@ def self.from_a(ary)
8787

8888
#
8989
# Initialize the site reference.
90+
# If you're updating the references, please also update:
91+
# * tools/module_reference.rb
92+
# * https://github.com/rapid7/metasploit-framework/wiki/Metasploit-module-reference-identifiers
9093
#
9194
def initialize(in_ctx_id = 'Unknown', in_ctx_val = '')
9295
self.ctx_id = in_ctx_id

tools/module_reference.rb

Lines changed: 144 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,7 @@
11
#!/usr/bin/env ruby
22
#
3-
# $Id$
4-
#
53
# This script lists each module with its references
64
#
7-
# $Revision$
8-
#
95

106
msfbase = __FILE__
117
while File.symlink?(msfbase)
@@ -20,64 +16,151 @@
2016
require 'rex'
2117
require 'msf/ui'
2218
require 'msf/base'
19+
require 'uri'
20+
21+
22+
# See lib/msf/core/module/reference.rb
23+
# We gsub '#{in_ctx_val}' with the actual value
24+
def types
25+
{
26+
'ALL' => '',
27+
'OSVDB' => 'http://www.osvdb.org/#{in_ctx_val}',
28+
'CVE' => 'http://cvedetails.com/cve/#{in_ctx_val}/',
29+
'CWE' => 'http://cwe.mitre.org/data/definitions/#{in_ctx_val}.html',
30+
'BID' => 'http://www.securityfocus.com/bid/#{in_ctx_val}',
31+
'MSB' => 'http://technet.microsoft.com/en-us/security/bulletin/#{in_ctx_val}',
32+
'EDB' => 'http://www.exploit-db.com/exploits/#{in_ctx_val}',
33+
'US-CERT-VU' => 'http://www.kb.cert.org/vuls/id/#{in_ctx_val}',
34+
'ZDI' => 'http://www.zerodayinitiative.com/advisories/ZDI-#{in_ctx_val}',
35+
'WPVDB' => 'https://wpvulndb.com/vulnerabilities/#{in_ctx_val}',
36+
'URL' => '#{in_ctx_val}'
37+
}
38+
end
2339

40+
STATUS_ALIVE = 'Alive'
41+
STATUS_DOWN = 'Down'
42+
STATUS_UNSUPPORTED = 'Unsupported'
2443

25-
sort=0
26-
filter= 'All'
44+
sort = 0
45+
filter = 'All'
2746
filters = ['all','exploit','payload','post','nop','encoder','auxiliary']
28-
types = ['All','URL','CVE','OSVDB','BID','MSB','NSS','US-CERT-VU']
29-
type='All'
30-
match= nil
47+
type ='ALL'
48+
match = nil
49+
check = false
50+
save = nil
3151

3252
opts = Rex::Parser::Arguments.new(
3353
"-h" => [ false, "Help menu." ],
54+
"-c" => [ false, "Check reference status"],
3455
"-s" => [ false, "Sort by Reference instead of Module Type."],
3556
"-r" => [ false, "Reverse Sort"],
36-
"-f" => [ true, "Filter based on Module Type [All,Exploit,Payload,Post,NOP,Encoder,Auxiliary] (Default = All)."],
37-
"-t" => [ true, "Type of Reference to sort by [All,URL,CVE,OSVDB,BID,MSB,NSS,US-CERT-VU]"],
38-
"-x" => [ true, "String or RegEx to try and match against the Reference Field"]
39-
57+
"-f" => [ true, "Filter based on Module Type [All,Exploit,Payload,Post,NOP,Encoder,Auxiliary] (Default = ALL)."],
58+
"-t" => [ true, "Type of Reference to sort by #{types.keys}"],
59+
"-x" => [ true, "String or RegEx to try and match against the Reference Field"],
60+
"-o" => [ true, "Save the results to a file"]
4061
)
4162

63+
flags = []
64+
4265
opts.parse(ARGV) { |opt, idx, val|
4366
case opt
4467
when "-h"
4568
puts "\nMetasploit Script for Displaying Module Reference information."
4669
puts "=========================================================="
4770
puts opts.usage
4871
exit
72+
when "-c"
73+
flags << "URI Check: Yes"
74+
check = true
4975
when "-s"
50-
puts "Sorting by License"
76+
flags << "Order: Sorting by License"
5177
sort = 1
5278
when "-r"
53-
puts "Reverse Sorting"
79+
flags << "Order: Reverse Sorting"
5480
sort = 2
5581
when "-f"
5682
unless filters.include?(val.downcase)
5783
puts "Invalid Filter Supplied: #{val}"
5884
puts "Please use one of these: #{filters.map{|f|f.capitalize}.join(", ")}"
5985
exit
6086
end
61-
puts "Module Filter: #{val}"
87+
flags << "Module Filter: #{val}"
6288
filter = val
6389
when "-t"
64-
unless types.include?(val)
90+
val = (val || '').upcase
91+
unless types.has_key(val)
6592
puts "Invalid Type Supplied: #{val}"
66-
puts "Please use one of these: [All,URL,CVE,OSVDB,BID,MSB,NSS,US-CERT-VU]"
93+
puts "Please use one of these: #{types.keys.inspect}"
6794
exit
6895
end
69-
puts "Type: #{val}"
7096
type = val
7197
when "-x"
72-
puts "Regex: #{val}"
98+
flags << "Regex: #{val}"
7399
match = Regexp.new(val)
100+
when "-o"
101+
flags << "Output to file: Yes"
102+
save = val
74103
end
75-
76104
}
77105

78-
puts "Type: #{type}"
106+
flags << "Type: #{type}"
107+
108+
puts flags * " | "
109+
110+
def get_ipv4_addr(hostname)
111+
Rex::Socket::getaddresses(hostname, false)[0]
112+
end
113+
114+
def is_url_alive?(uri)
115+
#puts "URI: #{uri}"
116+
117+
begin
118+
uri = URI(uri)
119+
rhost = get_ipv4_addr(uri.host)
120+
rescue SocketError, URI::InvalidURIError => e
121+
#puts "Return false 1: #{e.message}"
122+
return false
123+
end
124+
125+
rport = uri.port || 80
126+
path = uri.path.blank? ? '/' : uri.path
127+
vhost = rport == 80 ? uri.host : "#{uri.host}:#{rport}"
128+
if uri.scheme == 'https'
129+
cli = ::Rex::Proto::Http::Client.new(rhost, 443, {}, true, 'TLS1')
130+
else
131+
cli = ::Rex::Proto::Http::Client.new(rhost, rport)
132+
end
133+
134+
begin
135+
cli.connect
136+
req = cli.request_raw('uri'=>path, 'vhost'=>vhost)
137+
res = cli.send_recv(req)
138+
rescue Errno::ECONNRESET, Rex::ConnectionError, Rex::ConnectionRefused, Rex::HostUnreachable, Rex::ConnectionTimeout, Rex::UnsupportedProtocol, ::Timeout::Error, Errno::ETIMEDOUT => e
139+
#puts "Return false 2: #{e.message}"
140+
return false
141+
ensure
142+
cli.close
143+
end
144+
145+
if res.nil? || res.code == 404 || res.body =~ /<title>.*not found<\/title>/i
146+
#puts "Return false 3: HTTP #{res.code}"
147+
#puts req.to_s
148+
return false
149+
end
79150

80-
Indent = ' '
151+
true
152+
end
153+
154+
def save_results(path, results)
155+
begin
156+
File.open(path, 'wb') do |f|
157+
f.write(results)
158+
end
159+
puts "Results saved to: #{path}"
160+
rescue Exception => e
161+
puts "Failed to save the file: #{e.message}"
162+
end
163+
end
81164

82165
# Always disable the database (we never need it just to list module
83166
# information).
@@ -91,21 +174,50 @@
91174
# Initialize the simplified framework instance.
92175
$framework = Msf::Simple::Framework.create(framework_opts)
93176

177+
if check
178+
columns = [ 'Module', 'Status', 'Reference' ]
179+
else
180+
columns = [ 'Module', 'Reference' ]
181+
end
94182

95183
tbl = Rex::Ui::Text::Table.new(
96184
'Header' => 'Module References',
97-
'Indent' => Indent.length,
98-
'Columns' => [ 'Module', 'Reference' ]
185+
'Indent' => 2,
186+
'Columns' => columns
99187
)
100188

189+
bad_refs_count = 0
190+
101191
$framework.modules.each { |name, mod|
102192
next if match and not name =~ match
103193

104194
x = mod.new
105195
x.references.each do |r|
106-
if type=='All' or type==r.ctx_id
196+
ctx_id = r.ctx_id.upcase
197+
if type == 'ALL' || type == ctx_id
198+
199+
if check
200+
if types.has_key?(ctx_id)
201+
uri = types[r.ctx_id.upcase].gsub(/\#{in_ctx_val}/, r.ctx_val)
202+
if is_url_alive?(uri)
203+
status = STATUS_ALIVE
204+
else
205+
bad_refs_count += 1
206+
status = STATUS_DOWN
207+
end
208+
else
209+
# The reference ID isn't supported so we don't know how to check this
210+
bad_refs_count += 1
211+
status = STATUS_UNSUPPORTED
212+
end
213+
end
214+
107215
ref = "#{r.ctx_id}-#{r.ctx_val}"
108-
tbl << [ x.fullname, ref ]
216+
new_column = []
217+
new_column << x.fullname
218+
new_column << status if check
219+
new_column << ref
220+
tbl << new_column
109221
end
110222
end
111223
}
@@ -120,5 +232,9 @@
120232
tbl.rows.reverse
121233
end
122234

123-
235+
puts
124236
puts tbl.to_s
237+
puts
238+
239+
puts "Number of bad references found: #{bad_refs_count}" if check
240+
save_results(save, tbl.to_s) if save

0 commit comments

Comments
 (0)