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_xml.rb
Views: 11623
# -*- coding: binary -*-12require 'rexml/document'34module Rex5module Parser67#8# Stream parser for nmap -oX xml output9#10# Yields a hash representing each host found in the xml stream. Each host11# will look something like the following:12# {13# "status" => "up",14# "addrs" => { "ipv4" => "192.168.0.1", "mac" => "00:0d:87:a1:df:72" },15# "ports" => [16# { "portid" => "22", "state" => "closed", ... },17# { "portid" => "80", "state" => "open", ... },18# ...19# ]20# }21#22# Usage:23# parser = NmapXMLStreamParser.new { |host|24# # do stuff with the host25# }26# REXML::Document.parse_stream(File.new(nmap_xml), parser)27# -- or --28# parser = NmapXMLStreamParser.new29# parser.on_found_host = Proc.new { |host|30# # do stuff with the host31# }32# REXML::Document.parse_stream(File.new(nmap_xml), parser)33#34# This parser does not maintain state as well as a tree parser, so malformed35# xml will trip it up. Nmap shouldn't ever output malformed xml, so it's not36# a big deal.37#38class NmapXMLStreamParser3940#41# Callback for processing each found host42#43attr_accessor :on_found_host4445#46# Create a new stream parser for NMAP XML output47#48# If given a block, it will be stored in +on_found_host+, otherwise you49# need to set it explicitly, e.g.:50# parser = NmapXMLStreamParser.new51# parser.on_found_host = Proc.new { |host|52# # do stuff with the host53# }54# REXML::Document.parse_stream(File.new(nmap_xml), parser)55#56def initialize(&block)57reset_state58on_found_host = block if block59end6061def reset_state62@host = { "status" => nil, "addrs" => {}, "ports" => [], "scripts" => {} }63@state = nil64end6566def tag_start(name, attributes)67begin68case name69when "address"70@host["addrs"][attributes["addrtype"]] = attributes["addr"]71if (attributes["addrtype"] =~ /ipv[46]/)72@host["addr"] = attributes["addr"]73end74when "osclass"75# If there is more than one, take the highest accuracy. In case of76# a tie, this will have the effect of taking the last one in the77# list. Last is really no better than first but nmap appears to78# put OSes in chronological order, at least for Windows.79# Accordingly, this will report XP instead of 2000, 7 instead of80# Vista, etc, when each has the same accuracy.81if (@host["os_accuracy"].to_i <= attributes["accuracy"].to_i)82@host["os_vendor"] = attributes["vendor"]83@host["os_family"] = attributes["osfamily"]84@host["os_version"] = attributes["osgen"]85@host["os_accuracy"] = attributes["accuracy"]86end87when "osmatch"88if(attributes["accuracy"].to_i == 100)89@host["os_match"] = attributes["name"]90end91when "uptime"92@host["last_boot"] = attributes["lastboot"]93when "hostname"94if(attributes["type"] == "PTR")95@host["reverse_dns"] = attributes["name"]96end97when "status"98# <status> refers to the liveness of the host; values are "up" or "down"99@host["status"] = attributes["state"]100@host["status_reason"] = attributes["reason"]101when "port"102@host["ports"].push(attributes)103when "state"104# <state> refers to the state of a port; values are "open", "closed", or "filtered"105@host["ports"].last["state"] = attributes["state"]106when "service"107# Store any service and script info with the associated port. There shouldn't108# be any collisions on attribute names here, so just merge them.109@host["ports"].last.merge!(attributes)110when "script"111# Associate scripts under a port tag with the appropriate port.112# Other scripts from <hostscript> tags can only be associated with113# the host and scripts from <postscript> tags don't really belong114# to anything, so ignore them115if @state == :in_port_tag116@host["ports"].last["scripts"] ||= {}117@host["ports"].last["scripts"][attributes["id"]] = attributes["output"]118elsif @host119@host["scripts"] ||= {}120@host["scripts"][attributes["id"]] = attributes["output"]121else122# post scripts are used for things like comparing all the found123# ssh keys to see if multiple hosts have the same key124# fingerprint. Ignore them.125end126when "trace"127@host["trace"] = {"port" => attributes["port"], "proto" => attributes["proto"], "hops" => [] }128when "hop"129if @host["trace"]130@host["trace"]["hops"].push(attributes)131end132end133rescue NoMethodError => err134raise err unless err.message =~ /NilClass/135end136end137138def tag_end(name)139case name140when "port"141@state = nil142when "host"143on_found_host.call(@host) if on_found_host144reset_state145end146end147148# We don't need these methods, but they're necessary to keep REXML happy149def text(str) # :nodoc:150end151def xmldecl(version, encoding, standalone) # :nodoc:152end153def cdata # :nodoc:154end155def comment(str) # :nodoc:156end157def instruction(name, instruction) # :nodoc:158end159def attlist # :nodoc:160end161end162163end164end165166167168