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/nmap_document.rb
Views: 11780
# -*- coding: binary -*-1require "rex/parser/nokogiri_doc_mixin"23module Rex4module Parser56# If Nokogiri is available, define Nmap document class.7load_nokogiri && class NmapDocument < Nokogiri::XML::SAX::Document89include NokogiriDocMixin1011attr_accessor :result12def initialize(args, db, &block)13@result = Rex::Parser::ParsedResult.new14super15end1617def determine_port_state(v)18case v19when "open"20Msf::ServiceState::Open21when "closed"22Msf::ServiceState::Closed23when "filtered"24Msf::ServiceState::Filtered25else26Msf::ServiceState::Unknown27end28end2930# Compare OS fingerprinting data31def better_os_match(orig_hash,new_hash)32return false unless new_hash.has_key? "accuracy"33return true unless orig_hash.has_key? "accuracy"34new_hash["accuracy"].to_i > orig_hash["accuracy"].to_i35end3637# Triggered every time a new element is encountered. We keep state38# ourselves with the @state variable, turning things on when we39# get here (and turning things off when we exit in end_element()).40def start_element(name=nil,attrs=[])41attrs = normalize_attrs(attrs)42block = @block43@state[:current_tag][name] = true44case name45when "status"46record_host_status(attrs)47when "address"48record_address(attrs)49when "osclass"50record_host_osclass(attrs)51when "osmatch"52record_host_osmatch(attrs)53when "uptime"54record_host_uptime(attrs)55when "hostname"56record_hostname(attrs)57when "port"58record_port(attrs)59when "state"60record_port_state(attrs)61when "service"62record_port_service(attrs)63when "script"64record_port_script(attrs)65record_host_script(attrs)66record_vuln_script(attrs)67# Ignoring post scripts completely68when "table"69record_vuln_table(attrs)70when "elem"71record_vuln_values(attrs)72when "trace"73record_host_trace(attrs)74when "hop"75record_host_hop(attrs)76end77end7879# When we exit a tag, this is triggered.80def end_element(name=nil)81block = @block82case name83when "os"84collect_os_data85@state[:os] = {}86when "port"87collect_port_data88@state[:port] = {}89when "host" # Roll everything up now90collect_host_data91host_object = report_host &block92if host_object93db.report_import_note(@args[:workspace],host_object)94report_services(host_object,&block)95report_fingerprint(host_object)96report_uptime(host_object)97report_traceroute(host_object)98end99@state.delete_if {|k| k != :current_tag}100@report_data = {:workspace => @args[:workspace]}101when "script"102report_vulns103end104@state[:current_tag].delete name105end106107def characters(text)108@state[:add_characters] << text if @state[:add_characters]109@state.delete(:add_characters)110end111112# We can certainly get fancier with self.send() magic, but113# leaving this pretty simple for now.114115def record_host_hop(attrs)116return unless in_tag("host")117return unless in_tag("trace")118hops = attr_hash(attrs)119hops["name"] = hops.delete "host"120@state[:trace][:hops] << hops121end122123def record_host_trace(attrs)124return unless in_tag("host")125@state[:trace] = attr_hash(attrs)126@state[:trace][:hops] = []127end128129def record_host_uptime(attrs)130return unless in_tag("host")131@state[:uptime] = attr_hash(attrs)132end133134def record_host_osmatch(attrs)135return unless in_tag("host")136return unless in_tag("os")137temp_hash = attr_hash(attrs)138if temp_hash["accuracy"].to_i == 100139@state[:os] ||= {}140@state[:os]["osmatch"] = temp_hash["name"]141end142end143144def record_host_osclass(attrs)145return unless in_tag("host")146return unless in_tag("os")147@state[:os] ||= {}148temp_hash = attr_hash(attrs)149if better_os_match(@state[:os],temp_hash)150@state[:os] = temp_hash151end152end153154def record_hostname(attrs)155return unless in_tag("host")156if attr_hash(attrs)["type"] == "PTR"157@state[:hostname] = attr_hash(attrs)["name"]158end159end160161def record_host_script(attrs)162return unless in_tag("host")163return if in_tag("port")164temp_hash = attr_hash(attrs)165166if temp_hash["id"] and temp_hash["output"]167@state[:scripts] ||= []168@state[:scripts] << { temp_hash["id"] => temp_hash["output"] }169end170end171172def record_port_script(attrs)173return unless in_tag("host")174return unless in_tag("port")175temp_hash = attr_hash(attrs)176if temp_hash["id"] and temp_hash["output"]177@state[:port][:scripts] ||= []178@state[:port][:scripts] << { temp_hash["id"] => temp_hash["output"] }179end180end181182def record_port_service(attrs)183return unless in_tag("host")184return unless in_tag("port")185svc = attr_hash(attrs)186if svc["name"] && @args[:fix_services]187svc["name"] = db.nmap_msf_service_map(svc["name"])188end189@state[:port] = @state[:port].merge(svc)190end191192def record_port_state(attrs)193return unless in_tag("host")194return unless in_tag("port")195temp_hash = attr_hash(attrs)196@state[:port] = @state[:port].merge(temp_hash)197end198199def record_port(attrs)200return unless in_tag("host")201@state[:port] ||= {}202svc = attr_hash(attrs)203@state[:port] = @state[:port].merge(svc)204end205206def record_host_status(attrs)207return unless in_tag("host")208attrs.each do |k,v|209next unless k == "state"210if v == 'up'211@state[:host_alive] = true212else213@state[:host_alive] = false214end215end216end217218def record_address(attrs)219return unless in_tag("host")220@state[:addresses] ||= {}221address = nil222type = nil223attrs.each do |k,v|224if k == "addr"225address = v226elsif k == "addrtype"227type = v228end229end230@state[:addresses][type] = address231end232233def collect_os_data234return unless in_tag("host")235if @state[:os]236@report_data[:os_fingerprint] = {237:type => "host.os.nmap_fingerprint",238:data => {239:os_vendor => @state[:os]["vendor"],240:os_family => @state[:os]["osfamily"],241:os_version => @state[:os]["osgen"],242:os_accuracy => @state[:os]["accuracy"].to_i243}244}245if @state[:os].has_key? "osmatch"246@report_data[:os_fingerprint][:data][:os_match] = @state[:os]["osmatch"]247end248end249end250251def collect_host_data252if @state[:host_alive] == true253@report_data[:state] = Msf::HostState::Alive254elsif @state[:host_alive] == false255@report_data[:state] = Msf::HostState::Dead256# Default to alive if no host state available (masscan)257else258@report_data[:state] = Msf::HostState::Alive259end260if @state[:addresses]261if @state[:addresses].has_key? "ipv4"262@report_data[:host] = @state[:addresses]["ipv4"]263elsif @state[:addresses].has_key? "ipv6"264@report_data[:host] = @state[:addresses]["ipv6"]265end266end267if @state[:addresses] and @state[:addresses].has_key?("mac")268@report_data[:mac] = @state[:addresses]["mac"]269end270if @state[:hostname]271@report_data[:name] = @state[:hostname]272end273if @state[:uptime]274@report_data[:last_boot] = @state[:uptime]["lastboot"]275end276if @state[:trace] and @state[:trace].has_key?(:hops)277@report_data[:traceroute] = @state[:trace]278end279if @state[:scripts]280@report_data[:scripts] = @state[:scripts]281end282end283284def collect_port_data285return unless in_tag("host")286if @args[:fix_services]287if @state[:port]["state"] == "filtered"288return289end290end291@report_data[:ports] ||= []292port_hash = {}293extra = []294@state[:port].each do |k,v|295case k296when "protocol"297port_hash[:proto] = v298when "portid"299port_hash[:port] = v300when "state"301port_hash[:state] = determine_port_state(v)302when "name"303port_hash[:name] = v304when "tunnel"305port_hash[:name] = "#{v}/#{port_hash[:name] || 'unknown'}"306when "reason"307port_hash[:reason] = v308when "product"309extra[0] = v310when "version"311extra[1] = v312when "extrainfo"313extra[2] = v314when :scripts315port_hash[:scripts] = v316end317end318port_hash[:info] = extra.compact.join(" ") unless extra.empty?319# Skip localhost port results when they're unknown320if( port_hash[:reason] == "localhost-response" &&321port_hash[:state] == Msf::ServiceState::Unknown )322@report_data[:ports]323else324@report_data[:ports] << port_hash325end326end327328def report_traceroute(host_object)329return unless host_object.kind_of? ::Mdm::Host330return unless @report_data[:traceroute]331tr_note = {332:workspace => host_object.workspace,333:host => host_object,334:type => "host.nmap.traceroute",335:data => { 'port' => @report_data[:traceroute]["port"].to_i,336'proto' => @report_data[:traceroute]["proto"].to_s,337'hops' => @report_data[:traceroute][:hops] }338}339db_report(:note, tr_note)340end341342def report_uptime(host_object)343return unless host_object.kind_of? ::Mdm::Host344return unless @report_data[:last_boot]345up_note = {346:workspace => host_object.workspace,347:host => host_object,348:type => "host.last_boot",349:data => { :time => @report_data[:last_boot] }350}351db_report(:note, up_note)352end353354def report_fingerprint(host_object)355return unless host_object.kind_of? ::Mdm::Host356return unless @report_data[:os_fingerprint]357fp_note = @report_data[:os_fingerprint].merge(358{359:workspace => host_object.workspace,360:host => host_object361})362db_report(:note, fp_note)363end364365def report_host(&block)366if host_is_okay367scripts = @report_data.delete(:scripts) || []368host_object = db_report(:host, @report_data.merge( :workspace => @args[:workspace] ) )369db.emit(:address,@report_data[:host],&block) if block370371scripts.each do |script|372script.each_pair do |k,v|373ntype =374nse_note = {375:workspace => host_object.workspace,376:host => host_object,377:type => "nmap.nse.#{k}.host",378:data => { 'output' => v },379:update => :unique_data380}381db_report(:note, nse_note)382end383end384@result.record_host(host_object)385host_object386end387end388389def report_services(host_object,&block)390return unless host_object.kind_of? ::Mdm::Host391return unless @report_data[:ports]392return if @report_data[:ports].empty?393reported = []394@report_data[:ports].each do |svc|395scripts = svc.delete(:scripts) || []396wspace = db.workspaces({:id => host_object.workspace.id}).first397svc_obj = db_report(:service, svc.merge(:host => host_object, :workspace => wspace.name))398scripts.each do |script|399script.each_pair do |k,v|400ntype =401nse_note = {402:workspace => wspace,403:host => host_object,404:service => svc_obj,405:type => "nmap.nse.#{k}." + (svc[:proto] || "tcp") +".#{svc[:port]}",406:data => { 'output' => v },407:update => :unique_data408}409db_report(:note, nse_note)410end411end412reported << svc_obj413end414reported415end416417def report_vulns418if @state[:vulners]419vuln_info = {420:workspace => @args[:workspace],421:host => @state[:addresses]["ipv4"],422:port => @state[:port]["portid"],423:proto => @state[:port]["protocol"],424:name => @state[:vulners][:cpe],425:refs => @state[:vulners][:refs]426}427db_report(:vuln, vuln_info)428@state.delete :vulners429end430end431432def record_vuln_table(attrs)433if attrs.dig(0, 0) == 'key' && @state[:vulners] == {}434@state[:vulners][:cpe] = attrs[0][1]435@state[:vulners][:refs] = []436end437end438439def record_vuln_values(attrs)440if attrs.dig(0, 0) == 'key' && attrs.dig(0, 1) == 'id' && @state[:vulners]441@state[:add_characters] = @state[:vulners][:refs]442end443end444445def record_vuln_script(attrs)446if attrs[0][0] == 'id' && attrs[0][1] == 'vulners'447@state[:vulners] = {}448end449end450451end452453end454end455456457458