diff --git a/spec/tools/msu_finder_spec.rb b/spec/tools/msu_finder_spec.rb index f6f2b70fae..d9ef3770ba 100644 --- a/spec/tools/msu_finder_spec.rb +++ b/spec/tools/msu_finder_spec.rb @@ -3,7 +3,7 @@ load Metasploit::Framework.root.join('tools/msu_finder.rb').to_path require 'nokogiri' require 'uri' -describe MicrosoftPatch do +describe MicrosoftPatchFinder do before(:each) do cli = Rex::Proto::Http::Client.new('127.0.0.1') @@ -14,18 +14,18 @@ describe MicrosoftPatch do end let(:technet) do - MicrosoftPatch::SiteInfo::TECHNET + MicrosoftPatchFinder::SiteInfo::TECHNET end let(:microsoft) do - MicrosoftPatch::SiteInfo::MICROSOFT + MicrosoftPatchFinder::SiteInfo::MICROSOFT end let(:googleapis) do - MicrosoftPatch::SiteInfo::GOOGLEAPIS + MicrosoftPatchFinder::SiteInfo::GOOGLEAPIS end - describe MicrosoftPatch::SiteInfo do + describe MicrosoftPatchFinder::SiteInfo do context 'Constants' do context 'TECHNET' do it 'returns 157.56.148.23 as the IP' do @@ -59,7 +59,7 @@ describe MicrosoftPatch do end end - describe MicrosoftPatch::Base do + describe MicrosoftPatchFinder::Helper do def get_stdout(&block) out = $stdout @@ -84,7 +84,9 @@ describe MicrosoftPatch do end subject do - MicrosoftPatch::Base.new + mod = Object.new + mod.extend MicrosoftPatchFinder::Helper + mod end describe '#print_debug' do @@ -119,14 +121,14 @@ describe MicrosoftPatch do describe '#send_http_request' do it 'returns a Rex::Proto::Http::Response object' do allow(subject).to receive(:print_debug) - res = subject.send_http_request(MicrosoftPatch::SiteInfo::TECHNET) + res = subject.send_http_request(MicrosoftPatchFinder::SiteInfo::TECHNET) expect(res).to be_kind_of(Rex::Proto::Http::Response) end end end - describe MicrosoftPatch::PatchLinkCollector do + describe MicrosoftPatchFinder::PatchLinkCollector do let(:ms15_100_html) do %Q| @@ -199,7 +201,7 @@ describe MicrosoftPatch do end subject do - MicrosoftPatch::PatchLinkCollector.new + MicrosoftPatchFinder::PatchLinkCollector.new end before(:each) do @@ -361,15 +363,15 @@ describe MicrosoftPatch do end - describe MicrosoftPatch::TechnetMsbSearch do + describe MicrosoftPatchFinder::TechnetMsbSearch do subject do - MicrosoftPatch::TechnetMsbSearch.new + MicrosoftPatchFinder::TechnetMsbSearch.new end before(:each) do - allow_any_instance_of(MicrosoftPatch::Base).to receive(:print_debug) - allow_any_instance_of(MicrosoftPatch::Base).to receive(:send_http_request) { |info_obj, info_opts, opts| + allow_any_instance_of(MicrosoftPatchFinder::TechnetMsbSearch).to receive(:print_debug) + allow_any_instance_of(MicrosoftPatchFinder::TechnetMsbSearch).to receive(:send_http_request) { |info_obj, info_opts, opts| case opts['uri'] when /\/en\-us\/security\/bulletin\/dn602597\.aspx/ html = %Q| @@ -465,10 +467,10 @@ describe MicrosoftPatch do end - describe MicrosoftPatch::GoogleMsbSearch do + describe MicrosoftPatchFinder::GoogleMsbSearch do subject do - MicrosoftPatch::GoogleMsbSearch.new + MicrosoftPatchFinder::GoogleMsbSearch.new end let(:json_data) do @@ -555,8 +557,8 @@ describe MicrosoftPatch do end before(:each) do - allow_any_instance_of(MicrosoftPatch::Base).to receive(:print_debug) - allow_any_instance_of(MicrosoftPatch::Base).to receive(:send_http_request) { |info_obj, info_opts, opts| + allow_any_instance_of(MicrosoftPatchFinder::GoogleMsbSearch).to receive(:print_debug) + allow_any_instance_of(MicrosoftPatchFinder::GoogleMsbSearch).to receive(:send_http_request) { |info_obj, info_opts, opts| res = Rex::Proto::Http::Response.new allow(res).to receive(:body).and_return(json_data) res @@ -608,7 +610,7 @@ describe MicrosoftPatch do end - describe MicrosoftPatch::Module do + describe MicrosoftPatchFinder::Driver do let(:msb) do 'ms15-100' @@ -620,17 +622,19 @@ describe MicrosoftPatch do before(:each) do opts = { keyword: msb } - allow(MicrosoftPatch::OptsConsole).to receive(:get_parsed_options).and_return(opts) - allow_any_instance_of(MicrosoftPatch::PatchLinkCollector).to receive(:download_advisory).and_return(Rex::Proto::Http::Response.new) - allow_any_instance_of(MicrosoftPatch::PatchLinkCollector).to receive(:get_details_aspx).and_return([expected_link]) - allow_any_instance_of(MicrosoftPatch::PatchLinkCollector).to receive(:get_download_page).and_return(Rex::Proto::Http::Response.new) - allow_any_instance_of(MicrosoftPatch::PatchLinkCollector).to receive(:get_download_links).and_return([expected_link]) - allow_any_instance_of(MicrosoftPatch::Base).to receive(:print_debug) - allow_any_instance_of(MicrosoftPatch::Base).to receive(:print_error) + allow(MicrosoftPatchFinder::OptsConsole).to receive(:get_parsed_options).and_return(opts) + allow_any_instance_of(MicrosoftPatchFinder::PatchLinkCollector).to receive(:download_advisory).and_return(Rex::Proto::Http::Response.new) + allow_any_instance_of(MicrosoftPatchFinder::PatchLinkCollector).to receive(:get_details_aspx).and_return([expected_link]) + allow_any_instance_of(MicrosoftPatchFinder::PatchLinkCollector).to receive(:get_download_page).and_return(Rex::Proto::Http::Response.new) + allow_any_instance_of(MicrosoftPatchFinder::PatchLinkCollector).to receive(:get_download_links).and_return([expected_link]) + allow_any_instance_of(MicrosoftPatchFinder::Driver).to receive(:print_debug) + allow_any_instance_of(MicrosoftPatchFinder::Driver).to receive(:print_error) + allow_any_instance_of(MicrosoftPatchFinder::PatchLinkCollector).to receive(:print_debug) + allow_any_instance_of(MicrosoftPatchFinder::PatchLinkCollector).to receive(:print_error) end subject do - MicrosoftPatch::Module.new + MicrosoftPatchFinder::Driver.new end describe '#get_download_links' do @@ -643,13 +647,13 @@ describe MicrosoftPatch do describe '#google_search' do it 'returns search results' do - skip('See rspec for MicrosoftPatch::GoogleMsbSearch#find_msb_numbers') + skip('See rspec for MicrosoftPatchFinder::GoogleMsbSearch#find_msb_numbers') end end describe '#technet_search' do it 'returns search results' do - skip('See rspec for MicrosoftPatch::TechnetMsbSearch#find_msb_numbers') + skip('See rspec for MicrosoftPatchFinder::TechnetMsbSearch#find_msb_numbers') end end diff --git a/tools/msu_finder.rb b/tools/msu_finder.rb old mode 100644 new mode 100755 index adc514afa5..b17588bcda --- a/tools/msu_finder.rb +++ b/tools/msu_finder.rb @@ -21,7 +21,7 @@ require 'uri' require 'json' require 'optparse' -module MicrosoftPatch +module MicrosoftPatchFinder module SiteInfo TECHNET = { @@ -40,8 +40,8 @@ module MicrosoftPatch } end - # This class provides whatever other classes need. - class Base + # This provides whatever other classes need. + module Helper # Prints a debug message. # @@ -77,7 +77,7 @@ module MicrosoftPatch # Sends an HTTP request with Rex. # - # @param rhost [Hash] Information about the target host. Use MicrosoftPatch::SiteInfo. + # @param rhost [Hash] Information about the target host. Use MicrosoftPatchFinder::SiteInfo. # @option rhost [String] :vhost # @option rhost [String] :ip IPv4 address # @param opts [Hash] Information about the Rex request. @@ -114,7 +114,8 @@ module MicrosoftPatch # Collects MSU download links from Technet. - class PatchLinkCollector < Base + class PatchLinkCollector + include MicrosoftPatchFinder::Helper # Returns a response of an advisory page. # @@ -197,7 +198,7 @@ module MicrosoftPatch # Returns the redirected page. # - # @param rhost [Hash] From MicrosoftPatch::SiteInfo + # @param rhost [Hash] From MicrosoftPatchFinder::SiteInfo # @param res [Rex::Proto::Http::Response] # @return [Rex::Proto::Http::Response] def follow_redirect(rhost, res) @@ -278,7 +279,9 @@ module MicrosoftPatch # A class that searches advisories from Technet. - class TechnetMsbSearch < Base + class TechnetMsbSearch + include MicrosoftPatchFinder::Helper + def initialize opts = { 'method' => 'GET', @@ -387,7 +390,9 @@ module MicrosoftPatch attr_reader :firstpage end - class GoogleMsbSearch < Base + class GoogleMsbSearch + include MicrosoftPatchFinder::Helper + # API Doc: # https://developers.google.com/custom-search/json-api/v1/using_rest # Known bug: @@ -430,7 +435,7 @@ module MicrosoftPatch end rescue RuntimeError => e print_error(e.message) - return msb_numbers + return msb_numbers.uniq end msb_numbers.uniq @@ -634,11 +639,12 @@ module MicrosoftPatch end end - class Module < Base + class Driver + include MicrosoftPatchFinder::Helper def initialize begin - @args = MicrosoftPatch::OptsConsole.get_parsed_options + @args = MicrosoftPatchFinder::OptsConsole.get_parsed_options rescue OptionParser::InvalidOption, OptionParser::MissingArgument => e print_error(e.message) exit @@ -651,7 +657,7 @@ module MicrosoftPatch # @param regex [String] The regex pattern to use to collect specific download URLs. # @return [Array] Download links def get_download_links(msb, regex=nil) - msft = MicrosoftPatch::PatchLinkCollector.new + msft = MicrosoftPatchFinder::PatchLinkCollector.new unless msft.is_valid_msb?(msb) print_error "Not a valid MSB format." @@ -695,9 +701,9 @@ module MicrosoftPatch # @param keyword [String] The keyword to search # @param api_key [String] Google API key # @param cx [String] Google Search Engine Key - # @return [Array] See MicrosoftPatch::GoogleMsbSearch#find_msb_numbers + # @return [Array] See MicrosoftPatchFinder::GoogleMsbSearch#find_msb_numbers def google_search(keyword, api_key, cx) - search = MicrosoftPatch::GoogleMsbSearch.new(api_key: api_key, search_engine_id: cx) + search = MicrosoftPatchFinder::GoogleMsbSearch.new(api_key: api_key, search_engine_id: cx) search.find_msb_numbers(keyword) end @@ -705,9 +711,9 @@ module MicrosoftPatch # Performs a search via Technet # # @param keyword [String] The keyword to search - # @return [Array] See MicrosoftPatch::TechnetMsbSearch#find_msb_numbers + # @return [Array] See MicrosoftPatchFinder::TechnetMsbSearch#find_msb_numbers def technet_search(keyword) - search = MicrosoftPatch::TechnetMsbSearch.new + search = MicrosoftPatchFinder::TechnetMsbSearch.new search.find_msb_numbers(keyword) end @@ -750,7 +756,7 @@ end if __FILE__ == $PROGRAM_NAME - mod = MicrosoftPatch::Module.new + mod = MicrosoftPatchFinder::Driver.new begin mod.run rescue Interrupt @@ -758,3 +764,10 @@ if __FILE__ == $PROGRAM_NAME $stdout.puts "Good bye" end end + +=begin +TODO: + * Make a gem + * Make it generic in order to manage different kind of patches and providers + * Multithreading +=end \ No newline at end of file