From d4a347667f9ef9305d0ce7eb47dfd49a1522c7c9 Mon Sep 17 00:00:00 2001 From: kenna-bmcdevitt Date: Tue, 27 Oct 2020 04:26:56 -0500 Subject: [PATCH] make msft security adv url generator if given cve ids --- microsoft_kb/microsoft_kb_checker.rb | 139 +++++++++++++++++++++++++++ 1 file changed, 139 insertions(+) create mode 100644 microsoft_kb/microsoft_kb_checker.rb diff --git a/microsoft_kb/microsoft_kb_checker.rb b/microsoft_kb/microsoft_kb_checker.rb new file mode 100644 index 0000000..252acbc --- /dev/null +++ b/microsoft_kb/microsoft_kb_checker.rb @@ -0,0 +1,139 @@ +# frozen_string_literal: true + +# This will check microsoft security portal for the kbs assosicated with an operating system for a given CVE. +# +# end goal usage: --cve CVE-2020-1234 --os "Windows Server 2012 R2" +# +require 'json' +require 'rest-client' +require 'pry' + +class MicrosoftKbChecker + attr_accessor :options + + # options hash {:cve_id => 'cve-2020-1234', :os_name => 'Windows Server 2012'} + # you can also pass in an advisory number as it uses the same API endpoint + # eg... {:cve_id => 'ADV180002', :os_name => "Windows Server 2012 R2"} + def initialize(options) + @options = options + end + + AVAILABLE_OS = [ + 'Windows 10 Version 2004 for 32-bit Systems', + 'Windows 10 Version 2004 for ARM64-based Systems', + 'Windows 10 Version 2004 for x64-based Systems', + 'Windows Server, version 2004 (Server Core installation)', + 'Windows 10 Version 1803 for 32-bit Systems', + 'Windows 10 Version 1803 for x64-based Systems', + 'Windows 10 Version 1803 for ARM64-based Systems', + 'Windows 10 Version 1809 for 32-bit Systems', + 'Windows 10 Version 1809 for x64-based Systems', + 'Windows 10 Version 1809 for ARM64-based Systems', + 'Windows Server 2019', + 'Windows Server 2019 (Server Core installation)', + 'Windows 10 Version 1909 for 32-bit Systems', + 'Windows 10 Version 1909 for x64-based Systems', + 'Windows 10 Version 1909 for ARM64-based Systems', + 'Windows Server, version 1909 (Server Core installation)', + 'Windows 10 Version 1709 for 32-bit Systems', + 'Windows 10 Version 1709 for x64-based Systems', + 'Windows 10 Version 1709 for ARM64-based Systems', + 'Windows 10 Version 1903 for 32-bit Systems', + 'Windows 10 Version 1903 for x64-based Systems', + 'Windows 10 Version 1903 for ARM64-based Systems', + 'Windows Server, version 1903 (Server Core installation)', + 'Windows 10 for 32-bit Systems', + 'Windows 10 for x64-based Systems', + 'Windows 10 Version 1607 for 32-bit Systems', + 'Windows 10 Version 1607 for x64-based Systems', + 'Windows Server 2016', + 'Windows Server 2016 (Server Core installation)', + 'Windows 7 for 32-bit Systems Service Pack 1', + 'Windows 7 for x64-based Systems Service Pack 1', + 'Windows 8.1 for 32-bit systems', + 'Windows 8.1 for x64-based systems', + 'Windows RT 8.1', + 'Windows Server 2008 for 32-bit Systems Service Pack 2', + 'Windows Server 2008 for 32-bit Systems Service Pack 2 (Server Core installation)', + 'Windows Server 2008 for x64-based Systems Service Pack 2', + 'Windows Server 2008 for x64-based Systems Service Pack 2 (Server Core installation)', + 'Windows Server 2008 R2 for x64-based Systems Service Pack 1', + 'Windows Server 2008 R2 for x64-based Systems Service Pack 1 (Server Core installation)', + 'Windows Server 2012', + 'Windows Server 2012 (Server Core installation)', + 'Windows Server 2012 R2', + 'Windows Server 2012 R2 (Server Core installation)' + ].freeze + + def query_cve + #TODO: handle error msg better + if os_available? + response = make_request + if response.code == 200 + json = parse_json(response) + os_results = select_os(json) + if os_results.nil? + 'Product not found in response data.' + else + kbs_for_os(os_results) + end + else + 'Problem with HTTP response data.' + end + else + 'Operating system not found.' + end + end + + private + + def cve_url + cve = options[:cve_id] + "https://portal.msrc.microsoft.com/api/security-guidance/en-US/CVE/#{cve}" + end + + def make_request + RestClient::Request.execute( + method: :get, + headers: { Host: 'portal.msrc.microsoft.com' }, + url: cve_url + ) + end + + def parse_json(response) + JSON.parse(response) + end + + def os_available? + AVAILABLE_OS.include?(options[:os_name]) + end + + def select_os(json) + json.dig('affectedProducts').select do |os| + os if os.values_at('name', 'platform').include? options[:os_name] + end.first + end + + def kbs_for_os(os_json) + supersedence = os_json.dig('supersedence') + articles = article_title_keys(os_json) + kbs = articles.map do |article_info| + num = article_info[:article_num] + os_json.values_at("articleTitle#{num}") + end.flatten.reject { |kb| kb == '' } + { + os_name: os_json['name'], + cve_id: options[:cve_id], + kbs: kbs, + supersedence_kb: supersedence + } + end + + def article_title_keys(json) + # let us know if we have a kb article in our json response data + # the json response is made up of 4 articleTitle keys + # i have this looping through 99 in case there are more articles from the ones ive manually looked at + results = (1..99).map { |index| { article_num: index, has_key?: json.key?("articleTitle#{index}") } } + results.select { |r| r if r[:has_key?] == true } + end +end