Parent

Nmap::Parser

What Is This Library For?

This library provides a Ruby interface to the Nmap Security Scanner and its XML formatted scan data. It can run Nmap and parse its XML output directly from the scan, parse a file or string of XML scan data, or parse XML scan data from an object via its read() method. This information is presented in an easy-to-use and intuitive fashion for further storage and manipulation.

Note that while Anthony Persaud’s Perl Nmap::Parser was certainly an inspiration when designing this library, there are a number of distinguishing characteristics. Very briefly, this library contains more classes, many more methods, and has blocks extensively available.

The Nmap Security Scanner is an awesome utility written and maintained by Fyodor (fyodor(a)insecure.org). Its main function is port scanning, but it also has service and operating system detection, its own scripting engine and a whole lot more. One of its many available output formats is XML, which allows machines to handle all of the information instead of us slowly sifting through tons of output.

Conventions

Depending on the data type, unavailable information is presented differently:

All information available as arrays are presented via methods. These methods not only return the array, but they also yield each element to a block if one is given.

Module Hierarchy

  Nmap::Parser
  |
  + Session           <- Scan session information
  |
  + Host              <- General host information
    |
    + ExtraPorts      <- Ports consolidated in an "ignored" state
    |
    + Port            <- General port information
    | |
    | + Service       <- Port Service information
    |
    + Script          <- NSE Script information (both host and port)
    |
    + Times           <- Timimg information (round-trip time, etc)
    |
    + Traceroute      <- General Traceroute information
    | |
    | + Hop           <- Individual Hop information
    |
    + OS              <- OS Detection information
      |
      + OSClass       <- OS Class information
      |
      + OSMatch       <- OS Match information

Examples

There are two ways to go about getting a new Parser object and actually parsing Nmap’s XML output:

Parsing XML Data Already Available as a String

This method is not limited to only String objects, but rather any object which responsds to to_str().

        require 'nmap/parser'

        parser = Nmap::Parser.new
        parser.parsestring(xml)

or

        parser = Nmap::Parser.parsestring(xml)

Reading and Parsing From a File

        require 'nmap/parser'

        parser = Nmap::Parser.parsefile("log.xml")

Reading and Parsing From an Object

This method can read from any object responding to a read() method that returns a String (or something else responding to to_str())

        require 'nmap/parser'

        parser = Nmap::Parser.parseread($stdin)

Scanning and Parsing

This is the only Parser method that requires Nmap to be available.

        require 'nmap/parser'

        parser = Nmap::Parser.parsescan("sudo nmap", "-sVC 192.168.1.0/24")

Registering a Callback

To use a callback you create a new Parser object and register a proc or method to call each time a new host is parsed, as soon as it’s parsed. The callback is then run in a new thread and is passed the newly created Nmap::Parser::Host object.

        require 'nmap/parser'

        callback = proc do |host|
                return if host.status != "up"
                puts "Found #{host.addr}"
        end

        parser = Nmap::Parser.new(:callback => callback)
        parser.parsefile("nmaplog.xml")

        # Found 192.168.10.1
        # Found 192.168.10.2
        # Found 192.168.10.7
        # [...]

Doing a Bit More

After printing a little session information, this example will cycle through all of the up hosts, printing state and service information on the open TCP and UDP ports. See the examples directory that comes with this library for more examples.

        puts "Nmap args: #{parser.session.scan_args}"
        puts "Runtime: #{parser.session.scan_time} seconds"
        puts

        parser.hosts("up") do |host|
                puts "#{host.addr} is up:"
                puts

                [:tcp, :udp].each do |type|
                        host.getports(type, "open") do |port|
                                srv = port.service

                                puts "Port ##{port.num}/#{port.proto} is open (#{port.reason})"
                                puts "\tService: #{srv.name}" if srv.name
                                puts "\tProduct: #{srv.product}" if srv.product
                                puts "\tVersion: #{srv.version}" if srv.version
                                puts
                        end
                end

                puts
        end

Credits

Author & Maintainer:

Contributors (in chronological order of first contribution):

Thanks a lot for taking the time and helping out, everybody!

For information on what each contributor actually did, please take a look at the project’s ChangeLog and Subversion logs.

Constants

Major

Major version number

Minor

Minor version number

Teeny

Teeny version number

Stage

Development stage (currently “dev” or “release”)

Version

Pre-built version string

Attributes

rawxml[R]

Raw XML output from the scan

session[R]

Session object for the scan

Public Class Methods

new(opts = {}) click to toggle source

Creates a fresh Parser object, taking a hash of options as an optional argument. Use the instance parsing methods to read in the XML and parse the data into the available classes.

Returns the new Nmap::Parser object and yields it to a block if one is given

# File lib/nmap/parser.rb, line 538
        def initialize(opts = {}) # :yields: parser
                @hosts = []
                @fresh = true

                opts.keys.each do |key|
                        begin
                                send("option_#{key}", opts[key])
                        rescue NoMethodError
                        end
                end

                yield self if block_given?
        end

Public Instance Methods

+(pa) click to toggle source

Returns a new Parser object with the following characteristics:

 * rawxml = nil
 * session = nil
 * contains hosts from both operands.  If any of the hosts in the
   first operand are also in the second (as determined by comparing
   host.addr information), the duplicate hosts from the second one
   are not available.
# File lib/nmap/parser.rb, line 446
        def +(pa)
                return nil unless self.class == pa.class
                n = Nmap::Parser.new
                n.rawxml = nil
                n.session = nil
                [ self.hosts, pa.hosts ].each do |h|
                        n.addhosts(h)
                end
                n
        end
==(pa) click to toggle source

This operator simply compares the rawxml members

# File lib/nmap/parser.rb, line 435
        def ==(pa)
                @rawxml == pa.rawxml
        end
combination?() click to toggle source

Returns a boolean value depending on whether this object is just a combination of others (e.g. using +)

# File lib/nmap/parser.rb, line 459
        def combination?
                rawxml.nil? and session.nil? and not @fresh
        end
del_host(hostip) click to toggle source

Deletes host with the specified IP address or hostname hostip

Calling this method from inside of a block given to a method like hosts() or get_ips() may lead to adverse effects.

# File lib/nmap/parser.rb, line 409
        def del_host(hostip)
                @hosts.delete_if do |host|
                        host.addr == hostip or host.hostname == hostip
                end
        end
Also aliased as: delete_host
delete_host(hostip) click to toggle source

Alias for del_host

get_host(hostip) click to toggle source

Alias for host

get_ips(status = "") click to toggle source

Returns an array of IPs scanned and yields them each to a block if one is given

If an argument is given, only hosts matching status are given

NOTE: Calling parser.get_ips(status).size can be very different than running parser.session.numhosts(status) because the information there and here are coming from different places in the XML. Nmap will not typically list individual hosts which it doesn’t know or assume are “up”.

# File lib/nmap/parser.rb, line 427
        def get_ips(status = "") # :yields: host.addr
                hosts(status).map do |host|
                        yield host.addr if block_given?
                        host.addr
                end
        end
host(hostip) click to toggle source

Returns a Host object for the host with the specified IP address or hostname hostip

# File lib/nmap/parser.rb, line 397
        def host(hostip)
                @hosts.find do |host|
                        host.addr == hostip or host.hostname == hostip
                end
        end
Also aliased as: get_host
hosts(status = "") click to toggle source

Returns an array of Host objects and yields them each to a block if one is given

If an argument is given, only hosts matching status are given

NOTE: Calling parser.hosts(status).size can be very different than running parser.session.numhosts(status) because the information there and here are coming from different places in the XML. Nmap will not typically list individual hosts which it doesn’t know or assume are “up”.

# File lib/nmap/parser.rb, line 386
        def hosts(status = "") # :yields: host
                @hosts.map { |host|
                        if status.empty? or host.status == status
                                yield host if block_given?
                                host
                        end
                }.compact
        end
parsefile(filename) click to toggle source

Read and parse the contents of the Nmap XML file filename

# File lib/nmap/parser.rb, line 320
        def parsefile(filename)
                File.open(filename) { |f| parseread(f) }
        rescue
                raise $!.class, "Error parsing \"#{filename}\": #{$!}"
        end
parseread(obj) click to toggle source

Read and parse XML from obj. obj can be any object responding to a read() method that returns a String (or something else responding to to_str()). IO and File are just a couple of examples.

# File lib/nmap/parser.rb, line 311
        def parseread(obj)
                if not obj.respond_to?(:read)
                        raise TypeError, "Passed object must respond to read()"
                end

                parsestring(obj.read)
        end
parsescan(nmap, args, targets = []) click to toggle source

Essentially runs “nmap -d args targets

nmap is here to allow you to do things like:

parser.parsescan(“sudo ./nmap”, arguments, targets)

and still make it easy for me to inject the options for XML output and debugging.

args can’t contain arguments like -oA, -oX, etc. as these could interfere with Parser’s processing. If you need that other output, you could run Nmap yourself and just pass the -oX output to Parser. Or you could use rawxml to grab the XML from the scan and write it to a file, for example.

targets is an optional array of target specifications. It’s here only for convenience because you can also put any targets you want scanned in args (which is what I tend to do unless I happen to already have a collection of targets as an array).

# File lib/nmap/parser.rb, line 355
        def parsescan(nmap, args, targets = [])
                if args =~ /\s-o|^-o/
                        raise ArgumentError, "Output option (-o*) passed to parsescan()"
                end

                # Enable debugging and XML; pass args and targets
                command = "#{nmap} -d -oX - #{args} #{targets.join(" ")}"

                begin
                        # First try popen3() if it loaded successfully..
                        Open3.popen3(command) do |sin, sout, serr|
                                parseread(sout)
                        end
                rescue NameError
                        # ..but fall back to popen() if not
                        IO.popen(command) do |io|
                                parseread(io)
                        end
                end
        end
parsestring(str) click to toggle source

Read and parse a String (or something else responding to to_str()) of XML

# File lib/nmap/parser.rb, line 328
        def parsestring(str)
                if not str.respond_to?(:to_str)
                        raise TypeError, "XML data should be a String, or must respond to to_str()"
                end

                parse(str.to_str)
        end
self.parsefile(filename) click to toggle source

Wrapper around the instance method’s functionality

Returns a new Nmap::Parser object and yields it to a block if one is given

# File lib/nmap/parser.rb, line 277
        
self.parseread(obj) click to toggle source

Wrapper around the instance method’s functionality

Returns a new Nmap::Parser object and yields it to a block if one is given

# File lib/nmap/parser.rb, line 283
        
self.parsescan(nmap,args,targets=[]) click to toggle source

Wrapper around the instance method’s functionality

Returns a new Nmap::Parser object and yields it to a block if one is given

# File lib/nmap/parser.rb, line 289
        
self.parsestring(str) click to toggle source

Wrapper around the instance method’s functionality

Returns a new Nmap::Parser object and yields it to a block if one is given

# File lib/nmap/parser.rb, line 295
        

Disabled; run with --debug to generate this.

[Validate]

Generated with the Darkfish Rdoc Generator 1.1.6.