Class: Rex::Parser::NmapXMLStreamParser

Inherits:
Object
  • Object
show all
Defined in:
lib/rex/parser/nmap_xml.rb

Overview

Stream parser for nmap -oX xml output

Yields a hash representing each host found in the xml stream. Each host will look something like the following: { “status” => “up”, “addrs” => { “ipv4” => “192.168.0.1”, “mac” => “00:0d:87:a1:df:72” }, “ports” => [ { “portid” => “22”, “state” => “closed”, … }, { “portid” => “80”, “state” => “open”, … }, … ] }

Usage:

parser = NmapXMLStreamParser.new { |host|
  # do stuff with the host
}
REXML::Document.parse_stream(File.new(nmap_xml), parser)

– or –

parser = NmapXMLStreamParser.new
parser.on_found_host = Proc.new { |host|
  # do stuff with the host
}
REXML::Document.parse_stream(File.new(nmap_xml), parser)

This parser does not maintain state as well as a tree parser, so malformed xml will trip it up. Nmap shouldn’t ever output malformed xml, so it’s not a big deal.

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(&block) ⇒ NmapXMLStreamParser

Create a new stream parser for NMAP XML output

If given a block, it will be stored in on_found_host, otherwise you need to set it explicitly, e.g.:

parser = NmapXMLStreamParser.new
parser.on_found_host = Proc.new { |host|
  # do stuff with the host
}
REXML::Document.parse_stream(File.new(nmap_xml), parser)


56
57
58
59
# File 'lib/rex/parser/nmap_xml.rb', line 56

def initialize(&block)
	reset_state
	on_found_host = block if block
end

Instance Attribute Details

#on_found_hostObject

Callback for processing each found host



43
44
45
# File 'lib/rex/parser/nmap_xml.rb', line 43

def on_found_host
  @on_found_host
end

Instance Method Details

#attlistObject

:nodoc:



139
140
# File 'lib/rex/parser/nmap_xml.rb', line 139

def attlist # :nodoc:
end

#cdataObject

:nodoc:



133
134
# File 'lib/rex/parser/nmap_xml.rb', line 133

def cdata # :nodoc:
end

#comment(str) ⇒ Object

:nodoc:



135
136
# File 'lib/rex/parser/nmap_xml.rb', line 135

def comment(str) # :nodoc:
end

#instruction(name, instruction) ⇒ Object

:nodoc:



137
138
# File 'lib/rex/parser/nmap_xml.rb', line 137

def instruction(name, instruction) # :nodoc:
end

#reset_stateObject



61
62
63
# File 'lib/rex/parser/nmap_xml.rb', line 61

def reset_state
	@host = { "status" => nil, "addrs" => {}, "ports" => [] }
end

#tag_end(name) ⇒ Object



120
121
122
123
124
125
126
# File 'lib/rex/parser/nmap_xml.rb', line 120

def tag_end(name)
	case name
	when "host"
		on_found_host.call(@host) if on_found_host
		reset_state
	end
end

#tag_start(name, attributes) ⇒ Object



65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
# File 'lib/rex/parser/nmap_xml.rb', line 65

def tag_start(name, attributes)
	case name
	when "address"
		@host["addrs"][attributes["addrtype"]] = attributes["addr"]
		if (attributes["addrtype"] =~ /ipv[46]/)
			@host["addr"] = attributes["addr"]
		end
	when "osclass"
		# If there is more than one, take the highest accuracy.  In case of
		# a tie, this will have the effect of taking the last one in the
		# list.  Last is really no better than first but nmap appears to
		# put OSes in chronological order, at least for Windows.
		# Accordingly, this will report XP instead of 2000, 7 instead of
		# Vista, etc, when each has the same accuracy.
		if (@host["os_accuracy"].to_i <= attributes["accuracy"].to_i)
			@host["os_vendor"]   = attributes["vendor"]
			@host["os_family"]   = attributes["osfamily"]
			@host["os_version"]  = attributes["osgen"]
			@host["os_accuracy"] = attributes["accuracy"]
		end
	when "osmatch"
		if(attributes["accuracy"].to_i == 100)
			@host["os_match"] = attributes["name"]
		end
	when "uptime"
		@host["last_boot"]   = attributes["lastboot"]
	when "hostname"
		if(attributes["type"] == "PTR")
			@host["reverse_dns"] = attributes["name"]
		end
	when "status"
		# <status> refers to the liveness of the host; values are "up" or "down"
		@host["status"] = attributes["state"]
		@host["status_reason"] = attributes["reason"]
	when "port"
		@host["ports"].push(attributes)
	when "state"
		# <state> refers to the state of a port; values are "open", "closed", or "filtered"
		@host["ports"].last["state"] = attributes["state"]
	when "service"
		# Store any service and script info with the associated port.  There shouldn't
		# be any collisions on attribute names here, so just merge them.
		@host["ports"].last.merge!(attributes)
	when "script"
		@host["ports"].last["scripts"] ||= {}
		@host["ports"].last["scripts"][attributes["id"]] = attributes["output"]
	when "trace"
		@host["trace"] = {"port" => attributes["port"], "proto" => attributes["proto"], "hops" => [] }
	when "hop"
		if @host["trace"]
			@host["trace"]["hops"].push(attributes)
		end
	end
end

#text(str) ⇒ Object

We don’t need these methods, but they’re necessary to keep REXML happy



129
130
# File 'lib/rex/parser/nmap_xml.rb', line 129

def text(str) # :nodoc:
end

#xmldecl(version, encoding, standalone) ⇒ Object

:nodoc:



131
132
# File 'lib/rex/parser/nmap_xml.rb', line 131

def xmldecl(version, encoding, standalone) # :nodoc:
end