# -*- coding: binary -*- require "rex/parser/nokogiri_doc_mixin" module Rex module Parser load_nokogiri && class Outpost24Document < Nokogiri::XML::SAX::Document include NokogiriDocMixin def start_element(name, attrs) @state[:current_tag][name] = true case name when "description", "information" return unless in_tag("detaillist") return unless in_tag("detail") record_text when "detail" return unless in_tag("detaillist") record_vuln when "detaillist" record_vulns when "host" return unless in_tag("hostlist") record_host when "hostlist" record_hosts when "id" return unless in_tag("detaillist") return unless in_tag("detail") return unless in_tag("cve") record_text when "name" return unless in_tag("hostlist") || in_tag("detaillist") return unless in_tag("host") || in_tag("detail") record_text when "platform" return unless in_tag("hostlist") return unless in_tag("host") record_text when "portinfo" return unless in_tag("portlist") return unless in_tag("portlist-host") record_service when "portlist" record_services when "portnumber", "protocol", "service" return unless in_tag("portlist") return unless in_tag("portlist-host") return unless in_tag("portinfo") record_text when "report", "ip" record_text end end def end_element(name) case name when "description", "information" return unless in_tag("detaillist") return unless in_tag("detail") collect_vuln_data(name) when "detail" return unless in_tag("detaillist") collect_vuln when "detaillist" report_vulns when "host" return unless in_tag("hostlist") collect_host when "hostlist" report_hosts when "id" return unless in_tag("detaillist") return unless in_tag("detail") return unless in_tag("cve") collect_vuln_data(name) when "ip" collect_ip when "name" if in_tag("hostlist") && in_tag("host") collect_host_data(name) elsif in_tag("detaillist") && in_tag("detail") collect_vuln_data(name) end when "platform" return unless in_tag("hostlist") return unless in_tag("host") collect_host_data(name) when "portinfo" return unless in_tag("portlist") return unless in_tag("portlist-host") collect_service when "portlist" report_services when "portnumber", "protocol", "service" return unless in_tag("portlist") return unless in_tag("portlist-host") return unless in_tag("portinfo") collect_service_data(name) when "report" collect_product end @state[:current_tag].delete(name) end def record_hosts @report_data[:hosts] = [] end def record_services @report_data[:services] = [] end def record_vulns @report_data[:vulns] = [] end def record_host @host = {} end def record_service @service = {} end def record_vuln @vuln = {} @refs = [] end def record_text @state[:has_text] = true end def collect_host @host[:host] = @state[:host] @host[:name] = @state[:hname] @host[:os_name] = @state[:os_name] @host[:info] = @state[:pinfo] @report_data[:hosts] << @host end def collect_service @service[:host] = @state[:host] @service[:port] = @state[:port] @service[:proto] = @state[:proto] @service[:name] = @state[:sname] @service[:info] = @state[:pinfo] @report_data[:services] << @service end def collect_vuln @vuln[:host] = @state[:host] @vuln[:name] = @state[:vname] @vuln[:info] = @state[:vinfo] @vuln[:refs] = @refs @report_data[:vulns] << @vuln end def collect_product @state[:has_text] = false @state[:pinfo] = @text.strip if @text @text = nil end def collect_ip @state[:has_text] = false @state[:host] = @text.strip if @text @text = nil end def collect_host_data(name) @state[:has_text] = false if name == "name" @state[:hname] = @text.strip if @text elsif name == "platform" if @text @state[:os_name] = @text.strip else @state[:os_name] = Msf::OperatingSystems::UNKNOWN end end @text = nil end def collect_service_data(name) @state[:has_text] = false if name == "portnumber" @state[:port] = @text.strip if @text elsif name == "protocol" @state[:proto] = @text.strip.downcase if @text elsif name == "service" @state[:sname] = @text.strip if @text end @text = nil end def collect_vuln_data(name) @state[:has_text] = false if name == "name" @state[:vname] = @text.strip if @text elsif name == "description" @state[:vinfo] = @text.strip if @text elsif name == "information" @state[:vinfo] << " #{@text.strip if @text}" elsif name == "id" @state[:ref] = @text.strip if @text @refs << normalize_ref("CVE", @state[:ref]) end @text = nil end def report_hosts block = @block @report_data[:hosts].each do |h| db.emit(:address, h[:host], &block) if block db_report(:host, h) end end def report_services block = @block @report_data[:services].each do |s| db.emit(:service, "#{s[:host]}:#{s[:port]}/#{s[:proto]}", &block) if block db_report(:service, s) end end def report_vulns block = @block @report_data[:vulns].each do |v| db.emit(:vuln, ["#{v[:name]} (#{v[:host]})", 1], &block) if block db_report(:vuln, v) end end end end end