Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Path: blob/master/lib/rex/parser/mbsa_document.rb
Views: 11778
# -*- coding: binary -*-1require "rex/parser/nokogiri_doc_mixin"23module Rex4module Parser56# If Nokogiri is available, define Template document class.7load_nokogiri && class MbsaDocument < Nokogiri::XML::SAX::Document89include NokogiriDocMixin1011# Triggered every time a new element is encountered. We keep state12# ourselves with the @state variable, turning things on when we13# get here (and turning things off when we exit in end_element()).14def start_element(name=nil,attrs=[])15attrs = normalize_attrs(attrs)16block = @block17@state[:current_tag][name] = true18case name19when "SecScan"20record_host(attrs)21when "IP" # TODO: Check to see if IPList/IP is useful to import22when "Check" # A list of MBSA checks. They have an ID and a Name.23record_check(attrs)24when "Advice" # Check advice. Free form text about the check25@state[:has_text] = true26when "Detail" # Check/Detail is where missing fixes are.27record_detail(attrs)28when "UpdateData" # Info about installed/missing hotfixes29record_updatedata(attrs)30when "Title" # MSB Title31@state[:has_text] = true32when "InformationURL" # Only use this if we don't have a Bulletin ID33@state[:has_text] = true34end35end3637# This breaks xml-encoded characters, so need to append38def characters(text)39return unless @state[:has_text]40@text ||= ""41@text << text42end4344# When we exit a tag, this is triggered.45def end_element(name=nil)46block = @block47case name48when "SecScan" # Wrap it up49collect_host_data50host_object = report_host &block51if host_object52db.report_import_note(@args[:workspace],host_object)53report_fingerprint(host_object)54report_vulns(host_object,&block)55end56# Reset the state once we close a host57@state.delete_if {|k| k != :current_tag}58when "Check"59collect_check_data60when "Advice"61@state[:has_text] = false62collect_advice_data63when "Detail"64collect_detail_data65when "UpdateData"66collect_updatedata67when "Title"68@state[:has_text] = false69collect_title70when "InformationURL"71collect_url72@state[:has_text] = false73end74@state[:current_tag].delete name75end7677def report_fingerprint(host_object)78return unless host_object.kind_of? ::Mdm::Host79return unless @report_data[:os_fingerprint]80fp_note = @report_data[:os_fingerprint].merge(81{82:workspace => host_object.workspace,83:host => host_object84})85db_report(:note, fp_note)86end8788def collect_url89return unless in_tag("References")90return unless in_tag("UpdateData")91return unless in_tag("Detail")92return unless in_tag("Check")93@state[:update][:url] = @text.to_s.strip94@text = nil95end9697def report_vulns(host_object, &block)98return unless host_object.kind_of? ::Mdm::Host99return unless @report_data[:vulns]100return if @report_data[:vulns].empty?101@report_data[:vulns].each do |vuln|102next unless vuln[:refs]103if vuln[:refs].empty?104next105end106if block107db.emit(:vuln, ["Missing #{vuln[:name]}",1], &block) if block108end109db_report(:vuln, vuln.merge(:host => host_object))110end111end112113def collect_title114return unless in_tag("SecScan")115return unless in_tag("Check")116collect_bulletin_title117@text = nil118end119120def collect_bulletin_title121return unless @state[:check_state]["ID"] == 500.to_s122return unless in_tag("UpdateData")123return unless @state[:update]124return if @text.to_s.strip.empty?125@state[:update]["Title"] = @text.to_s.strip126end127128def collect_updatedata129return unless in_tag("SecScan")130return unless in_tag("Check")131return unless in_tag("Detail")132collect_missing_update133@state[:updates] = {}134end135136def collect_missing_update137return unless @state[:check_state]["ID"] == 500.to_s138return if @state[:update]["IsInstalled"] == "true"139@report_data[:missing_updates] ||= []140this_update = {}141this_update[:name] = @state[:update]["Title"].to_s.strip142this_update[:refs] = []143if @state[:update]["BulletinID"].empty?144this_update[:refs] << "URL-#{@state[:update][:url]}"145else146this_update[:refs] << "MSB-#{@state[:update]["BulletinID"]}"147end148@report_data[:missing_updates] << this_update149end150151# So far, just care about Host OS152# There is assuredly more interesting things going on in here.153def collect_advice_data154return unless in_tag("SecScan")155return unless in_tag("Check")156collect_os_name157@text = nil158end159160def collect_os_name161return unless @state[:check_state]["ID"] == 10101.to_s162return unless @text163return if @text.strip.empty?164os_match = @text.match(/Computer is running (.*)/)165return unless os_match166os_info = os_match[1]167os_vendor = os_info[/Microsoft/]168os_family = os_info[/Windows/]169os_version = os_info[/(XP|2000 Advanced Server|2000|2003|2008|SBS|Vista|7 .* Edition|7)/]170if os_info171@report_data[:os_fingerprint] = {}172@report_data[:os_fingerprint][:type] = "host.os.mbsa_fingerprint"173@report_data[:os_fingerprint][:data] = {174:os_vendor => os_vendor,175:os_family => os_family,176:os_version => os_version,177:os_accuracy => 100,178:os_match => os_info.gsub(/\x2e$/n,"")179}180end181end182183def collect_detail_data184return unless in_tag("SecScan")185return unless in_tag("Check")186if @report_data[:missing_updates]187@report_data[:vulns] = @report_data[:missing_updates]188end189end190191def collect_check_data192return unless in_tag("SecScan")193@state[:check_state] = {}194end195196def collect_host_data197return unless @state[:address]198return if @state[:address].strip.empty?199@report_data[:host] = @state[:address].strip200if @state[:hostname] && !@state[:hostname].empty?201@report_data[:name] = @state[:hostname]202end203@report_data[:state] = Msf::HostState::Alive204end205206def report_host(&block)207if host_is_okay208db.emit(:address,@report_data[:host],&block) if block209host_info = @report_data.merge(:workspace => @args[:workspace])210db_report(:host, host_info)211end212end213214def record_updatedata(attrs)215return unless in_tag("SecScan")216return unless in_tag("Check")217return unless in_tag("Detail")218update_attrs = attr_hash(attrs)219@state[:update] = attr_hash(attrs)220end221222def record_host(attrs)223host_attrs = attr_hash(attrs)224@state[:address] = host_attrs["IP"]225@state[:hostname] = host_attrs["Machine"]226end227228def record_check(attrs)229return unless in_tag("SecScan")230@state[:check_state] = attr_hash(attrs)231end232233def record_detail(attrs)234return unless in_tag("SecScan")235return unless in_tag("Check")236@state[:detail_state] = attr_hash(attrs)237end238239# We need to override the usual host_is_okay because MBSA apparently240# doesn't report on open ports at all.241def host_is_okay242return false unless @report_data[:host]243return false unless valid_ip(@report_data[:host])244return false unless @report_data[:state] == Msf::HostState::Alive245if @args[:blacklist]246return false if @args[:blacklist].include?(@report_data[:host])247end248return true249end250251end252253end254end255256257258