Land #10366, Add a script that can find CVEs based on other known references
commit
fa43a8ca27
|
@ -0,0 +1,252 @@
|
|||
#!/usr/bin/env ruby
|
||||
|
||||
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')))
|
||||
$:.unshift(ENV['MSF_LOCAL_LIB']) if ENV['MSF_LOCAL_LIB']
|
||||
|
||||
require 'nokogiri'
|
||||
|
||||
module CVE
|
||||
class XRefTable
|
||||
|
||||
attr_reader :module_full_name_ref
|
||||
attr_reader :edb_ref
|
||||
attr_reader :bid_ref
|
||||
attr_reader :osvdb_ref
|
||||
attr_reader :msb_ref
|
||||
attr_reader :zdi_ref
|
||||
attr_reader :url_refs
|
||||
|
||||
def initialize(refs)
|
||||
@module_full_name_ref = refs['fullname']
|
||||
@edb_ref = refs['EDB']
|
||||
@bid_ref = refs['BID']
|
||||
@osvdb_ref = refs['OSVDB']
|
||||
@msb_ref = refs['MSB']
|
||||
@zdi_ref = refs['ZDI']
|
||||
@url_refs = refs['URL']
|
||||
end
|
||||
|
||||
def has_match?(ref_match)
|
||||
if (
|
||||
(module_full_name_ref && ref_match.match(/#{module_full_name_ref}/)) ||
|
||||
(edb_ref && ref_match.match(/EXPLOIT\-DB:#{edb_ref}$/)) ||
|
||||
(osvdb_ref && ref_match.match(/OSVDB:#{osvdb_ref}$/)) ||
|
||||
(bid_ref && ref_match.match(/BID:#{bid_ref}$/)) ||
|
||||
(msb_ref && ref_match.match(/http:\/\/technet\.microsoft\.com\/security\/bulletin\/#{msb_ref}$/)) ||
|
||||
(zdi_ref && ref_match.match(/zerodayinitiative\.com\/advisories\/ZDI\-#{zdi_ref}/)) ||
|
||||
(url_refs_match?(ref_match))
|
||||
)
|
||||
return true
|
||||
end
|
||||
|
||||
false
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def url_refs_match?(ref_match)
|
||||
return false unless url_refs
|
||||
return false unless ref_match.match(/^http/)
|
||||
|
||||
url_refs.each do |url|
|
||||
return true if url == ref_match
|
||||
end
|
||||
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
class Database
|
||||
attr_reader :database
|
||||
|
||||
def initialize(db_path)
|
||||
@database = normalize(db_path)
|
||||
end
|
||||
|
||||
def cross_reference(reference_matches)
|
||||
return nil if reference_matches.empty?
|
||||
xref_table = XRefTable.new(reference_matches)
|
||||
|
||||
database.each_pair do |cve_name, references|
|
||||
references.each do |cve_ref|
|
||||
if xref_table.has_match?(cve_ref)
|
||||
return cve_name
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
nil
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def normalize(db_path)
|
||||
html = load_cve_html_file(db_path)
|
||||
normalize_html_to_hash(html)
|
||||
end
|
||||
|
||||
def load_cve_html_file(db_path)
|
||||
puts "[*] Loading database..."
|
||||
raw_data = File.read(db_path)
|
||||
Nokogiri::HTML(raw_data)
|
||||
end
|
||||
|
||||
def normalize_html_to_hash(html)
|
||||
puts "[*] Normalizing database..."
|
||||
|
||||
db = {}
|
||||
current_cve = nil
|
||||
metasploit_refs = []
|
||||
html.traverse do |element|
|
||||
if current_cve
|
||||
if element.text =~ /(https*:\/\/.*metasploit.+)/
|
||||
metasploit_refs << $1
|
||||
elsif element.text =~ /(http:\/\/www\.exploit\-db\.com\/.+)/
|
||||
metasploit_refs << $1
|
||||
elsif element.text =~ /(BID:\d+)/
|
||||
metasploit_refs << $1
|
||||
elsif element.text =~ /(OSVDB:\d+)/
|
||||
metasploit_refs << $1
|
||||
elsif element.text =~ /http:\/\/technet\.microsoft\.com\/security\/bulletin\/(MS\d+\-\d+)$/
|
||||
metasploit_refs << $1
|
||||
elsif element.text =~ /zerodayinitiative\.com\/advisories\/(ZDI\-\d+\-\d+)/
|
||||
metasploit_refs << $1
|
||||
elsif element.text =~ /URL:(http.+)/
|
||||
metasploit_refs << $1
|
||||
end
|
||||
end
|
||||
|
||||
if element.text =~ /^Name: (CVE\-\d+\-\d+)$/
|
||||
current_cve = $1
|
||||
elsif element.text =~ /^Votes:/
|
||||
unless metasploit_refs.empty?
|
||||
db[current_cve] = metasploit_refs
|
||||
end
|
||||
current_cve = nil
|
||||
metasploit_refs = []
|
||||
end
|
||||
end
|
||||
|
||||
db
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
class Utility
|
||||
def self.ignore_module?(module_full_name)
|
||||
[
|
||||
'exploit/multi/handler'
|
||||
].include?(module_full_name)
|
||||
end
|
||||
|
||||
def self.collect_references_from_module!(module_references, ref_ids, mod)
|
||||
if ref_ids.include?('EDB')
|
||||
edb_ref = mod.references.select { |r| r.ctx_id == 'EDB' }.first.ctx_val
|
||||
module_references['EDB'] = edb_ref
|
||||
end
|
||||
|
||||
if ref_ids.include?('BID')
|
||||
bid_ref = mod.references.select { |r| r.ctx_id == 'BID' }.first.ctx_val
|
||||
module_references['BID'] = bid_ref
|
||||
end
|
||||
|
||||
if ref_ids.include?('OSVDB')
|
||||
osvdb_ref = mod.references.select { |r| r.ctx_id == 'OSVDB' }.first.ctx_val
|
||||
module_references['OSVDB'] = osvdb_ref
|
||||
end
|
||||
|
||||
if ref_ids.include?('MSB')
|
||||
msb_ref = mod.references.select { |r| r.ctx_id == 'MSB' }.first.ctx_val
|
||||
module_references['MSB'] = msb_ref
|
||||
end
|
||||
|
||||
if ref_ids.include?('ZDI')
|
||||
zdi_ref = mod.references.select { |r| r.ctx_id == 'ZDI' }.first.ctx_val
|
||||
module_references['ZDI'] = zdi_ref
|
||||
end
|
||||
|
||||
if ref_ids.include?('URL')
|
||||
url_refs = mod.references.select { |r| r.ctx_id == 'URL' }.collect { |r| r.ctx_val if r }
|
||||
module_references['URL'] = url_refs
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
require 'msfenv'
|
||||
require 'msf/base'
|
||||
|
||||
def main
|
||||
filter = 'All'
|
||||
filters = ['all','exploit','payload','post','nop','encoder','auxiliary']
|
||||
type = 'CVE'
|
||||
db_path = nil
|
||||
|
||||
opts = Rex::Parser::Arguments.new(
|
||||
"-h" => [ false, 'Help menu.' ],
|
||||
"-f" => [ true, 'Filter based on Module Type [All,Exploit,Payload,Post,NOP,Encoder,Auxiliary] (Default = ALL).'],
|
||||
"-d" => [ true, 'Source of CVE database in HTML (allitems.html)'],
|
||||
)
|
||||
|
||||
opts.parse(ARGV) { |opt, idx, val|
|
||||
case opt
|
||||
when "-h"
|
||||
puts "\nMetasploit script for finding CVEs from other references."
|
||||
puts "=========================================================="
|
||||
puts opts.usage
|
||||
exit
|
||||
when "-f"
|
||||
unless filters.include?(val.downcase)
|
||||
puts "Invalid Filter Supplied: #{val}"
|
||||
puts "Please use one of these: #{filters.map{|f|f.capitalize}.join(", ")}"
|
||||
exit
|
||||
end
|
||||
filter = val
|
||||
when "-d"
|
||||
unless File.exists?(val.to_s)
|
||||
raise RuntimeError, "#{val} not found"
|
||||
end
|
||||
|
||||
db_path = val
|
||||
end
|
||||
}
|
||||
|
||||
framework_opts = { 'DisableDatabase' => true }
|
||||
framework_opts[:module_types] = [ filter.downcase ] if filter.downcase != 'all'
|
||||
$framework = Msf::Simple::Framework.create(framework_opts)
|
||||
cve_database = CVE::Database.new(db_path)
|
||||
|
||||
puts "[*] Going through Metasploit modules for missing references..."
|
||||
$framework.modules.each { |name, mod|
|
||||
if mod.nil?
|
||||
elog("Unable to load #{name}")
|
||||
next
|
||||
end
|
||||
|
||||
elog "Loading #{name}"
|
||||
m = mod.new
|
||||
next if Utility.ignore_module?(m.fullname)
|
||||
|
||||
ref_ids = m.references.collect { |r| r.ctx_id }
|
||||
next if ref_ids.include?(type)
|
||||
|
||||
elog "Checking references for #{m.fullname}"
|
||||
module_references = {}
|
||||
module_references['fullname'] = m.fullname
|
||||
Utility.collect_references_from_module!(module_references, ref_ids, m)
|
||||
cve_match = cve_database.cross_reference(module_references)
|
||||
if cve_match
|
||||
puts "[*] #{m.fullname}: Found #{cve_match}"
|
||||
end
|
||||
}
|
||||
end
|
||||
|
||||
if __FILE__ == $PROGRAM_NAME
|
||||
main
|
||||
end
|
||||
|
Loading…
Reference in New Issue