cve_xref.rb 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252
  1. #!/usr/bin/env ruby
  2. msfbase = __FILE__
  3. while File.symlink?(msfbase)
  4. msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase))
  5. end
  6. $:.unshift(File.expand_path(File.join(File.dirname(msfbase), '..', '..', 'lib')))
  7. $:.unshift(ENV['MSF_LOCAL_LIB']) if ENV['MSF_LOCAL_LIB']
  8. require 'nokogiri'
  9. module CVE
  10. class XRefTable
  11. attr_reader :module_full_name_ref
  12. attr_reader :edb_ref
  13. attr_reader :bid_ref
  14. attr_reader :osvdb_ref
  15. attr_reader :msb_ref
  16. attr_reader :zdi_ref
  17. attr_reader :url_refs
  18. def initialize(refs)
  19. @module_full_name_ref = refs['fullname']
  20. @edb_ref = refs['EDB']
  21. @bid_ref = refs['BID']
  22. @osvdb_ref = refs['OSVDB']
  23. @msb_ref = refs['MSB']
  24. @zdi_ref = refs['ZDI']
  25. @url_refs = refs['URL']
  26. end
  27. def has_match?(ref_match)
  28. if (
  29. (module_full_name_ref && ref_match.match(/#{module_full_name_ref}/)) ||
  30. (edb_ref && ref_match.match(/EXPLOIT\-DB:#{edb_ref}$/)) ||
  31. (osvdb_ref && ref_match.match(/OSVDB:#{osvdb_ref}$/)) ||
  32. (bid_ref && ref_match.match(/BID:#{bid_ref}$/)) ||
  33. (msb_ref && ref_match.match(/http:\/\/technet\.microsoft\.com\/security\/bulletin\/#{msb_ref}$/)) ||
  34. (zdi_ref && ref_match.match(/zerodayinitiative\.com\/advisories\/ZDI\-#{zdi_ref}/)) ||
  35. (url_refs_match?(ref_match))
  36. )
  37. return true
  38. end
  39. false
  40. end
  41. private
  42. def url_refs_match?(ref_match)
  43. return false unless url_refs
  44. return false unless ref_match.match(/^http/)
  45. url_refs.each do |url|
  46. return true if url == ref_match
  47. end
  48. false
  49. end
  50. end
  51. class Database
  52. attr_reader :database
  53. def initialize(db_path)
  54. @database = normalize(db_path)
  55. end
  56. def cross_reference(reference_matches)
  57. return nil if reference_matches.empty?
  58. xref_table = XRefTable.new(reference_matches)
  59. database.each_pair do |cve_name, references|
  60. references.each do |cve_ref|
  61. if xref_table.has_match?(cve_ref)
  62. return cve_name
  63. end
  64. end
  65. end
  66. nil
  67. end
  68. private
  69. def normalize(db_path)
  70. html = load_cve_html_file(db_path)
  71. normalize_html_to_hash(html)
  72. end
  73. def load_cve_html_file(db_path)
  74. puts "[*] Loading database..."
  75. raw_data = File.read(db_path)
  76. Nokogiri::HTML(raw_data)
  77. end
  78. def normalize_html_to_hash(html)
  79. puts "[*] Normalizing database..."
  80. db = {}
  81. current_cve = nil
  82. metasploit_refs = []
  83. html.traverse do |element|
  84. if current_cve
  85. if element.text =~ /(https*:\/\/.*metasploit.+)/
  86. metasploit_refs << $1
  87. elsif element.text =~ /(http:\/\/www\.exploit\-db\.com\/.+)/
  88. metasploit_refs << $1
  89. elsif element.text =~ /(BID:\d+)/
  90. metasploit_refs << $1
  91. elsif element.text =~ /(OSVDB:\d+)/
  92. metasploit_refs << $1
  93. elsif element.text =~ /http:\/\/technet\.microsoft\.com\/security\/bulletin\/(MS\d+\-\d+)$/
  94. metasploit_refs << $1
  95. elsif element.text =~ /zerodayinitiative\.com\/advisories\/(ZDI\-\d+\-\d+)/
  96. metasploit_refs << $1
  97. elsif element.text =~ /URL:(http.+)/
  98. metasploit_refs << $1
  99. end
  100. end
  101. if element.text =~ /^Name: (CVE\-\d+\-\d+)$/
  102. current_cve = $1
  103. elsif element.text =~ /^Votes:/
  104. unless metasploit_refs.empty?
  105. db[current_cve] = metasploit_refs
  106. end
  107. current_cve = nil
  108. metasploit_refs = []
  109. end
  110. end
  111. db
  112. end
  113. end
  114. end
  115. class Utility
  116. def self.ignore_module?(module_full_name)
  117. [
  118. 'exploit/multi/handler'
  119. ].include?(module_full_name)
  120. end
  121. def self.collect_references_from_module!(module_references, ref_ids, mod)
  122. if ref_ids.include?('EDB')
  123. edb_ref = mod.references.select { |r| r.ctx_id == 'EDB' }.first.ctx_val
  124. module_references['EDB'] = edb_ref
  125. end
  126. if ref_ids.include?('BID')
  127. bid_ref = mod.references.select { |r| r.ctx_id == 'BID' }.first.ctx_val
  128. module_references['BID'] = bid_ref
  129. end
  130. if ref_ids.include?('OSVDB')
  131. osvdb_ref = mod.references.select { |r| r.ctx_id == 'OSVDB' }.first.ctx_val
  132. module_references['OSVDB'] = osvdb_ref
  133. end
  134. if ref_ids.include?('MSB')
  135. msb_ref = mod.references.select { |r| r.ctx_id == 'MSB' }.first.ctx_val
  136. module_references['MSB'] = msb_ref
  137. end
  138. if ref_ids.include?('ZDI')
  139. zdi_ref = mod.references.select { |r| r.ctx_id == 'ZDI' }.first.ctx_val
  140. module_references['ZDI'] = zdi_ref
  141. end
  142. if ref_ids.include?('URL')
  143. url_refs = mod.references.select { |r| r.ctx_id == 'URL' }.collect { |r| r.ctx_val if r }
  144. module_references['URL'] = url_refs
  145. end
  146. end
  147. end
  148. require 'msfenv'
  149. def main
  150. filter = 'All'
  151. filters = ['all','exploit','payload','post','nop','encoder','auxiliary']
  152. type = 'CVE'
  153. db_path = nil
  154. opts = Rex::Parser::Arguments.new(
  155. "-h" => [ false, 'Help menu.' ],
  156. "-f" => [ true, 'Filter based on Module Type [All,Exploit,Payload,Post,NOP,Encoder,Auxiliary] (Default = ALL).'],
  157. "-d" => [ true, 'Source of CVE database in HTML (allitems.html)'],
  158. )
  159. opts.parse(ARGV) { |opt, idx, val|
  160. case opt
  161. when "-h"
  162. puts "\nMetasploit script for finding CVEs from other references."
  163. puts "=========================================================="
  164. puts opts.usage
  165. exit
  166. when "-f"
  167. unless filters.include?(val.downcase)
  168. puts "Invalid Filter Supplied: #{val}"
  169. puts "Please use one of these: #{filters.map{|f|f.capitalize}.join(", ")}"
  170. exit
  171. end
  172. filter = val
  173. when "-d"
  174. unless File.exist?(val.to_s)
  175. raise RuntimeError, "#{val} not found"
  176. end
  177. db_path = val
  178. end
  179. }
  180. framework_opts = { 'DisableDatabase' => true }
  181. framework_opts[:module_types] = [ filter.downcase ] if filter.downcase != 'all'
  182. $framework = Msf::Simple::Framework.create(framework_opts)
  183. cve_database = CVE::Database.new(db_path)
  184. puts "[*] Going through Metasploit modules for missing references..."
  185. $framework.modules.each { |name, mod|
  186. if mod.nil?
  187. elog("Unable to load #{name}")
  188. next
  189. end
  190. elog "Loading #{name}"
  191. m = mod.new
  192. next if Utility.ignore_module?(m.fullname)
  193. ref_ids = m.references.collect { |r| r.ctx_id }
  194. next if ref_ids.include?(type)
  195. elog "Checking references for #{m.fullname}"
  196. module_references = {}
  197. module_references['fullname'] = m.fullname
  198. Utility.collect_references_from_module!(module_references, ref_ids, m)
  199. cve_match = cve_database.cross_reference(module_references)
  200. if cve_match
  201. puts "[*] #{m.fullname}: Found #{cve_match}"
  202. end
  203. }
  204. end
  205. if __FILE__ == $PROGRAM_NAME
  206. main
  207. end