The London Perl and Raku Workshop takes place on 26th Oct 2024. If your company depends on Perl, please consider sponsoring and/or attending.

NAME

Parse::Nmap::XML - parser for nmap xml scan data using perl.

SYNOPSIS

  use Parse::Nmap::XML;

        #PARSING
  my $p = new Parse::Nmap::XML;
  $p->parse($fh); #filehandle or nmap xml output string
  #or $p->parsefile('nmap_output.xml') for files

        #GETTING SCAN INFORMATION
  print "Scan Information:\n";
  $si = $p->get_scaninfo();
  #Now I can get scan information by calling methods
  print
  'Number of services scanned: '.$si->num_of_services()."\n",
  'Start Time: '.$si->start_time()."\n",
  'Scan Types: ',(join ' ',$si->scan_types())."\n";

        #GETTING HOST INFORMATION
   print "Hosts scanned:\n";
   for my $ip ($p->get_host_list()){
   $host_obj = Parse::Nmap::XML->get_host($ip);
   print
  'Hostname: '.($host_obj->hostnames())[0],"\n",
  'Address: '.$host_obj->addr()."\n",
  'OS matches: '.(join ',', $host_obj->os_matches())."\n",
  'Last Reboot: '.($host_obj->uptime_lastboot,"\n";
        #... you get the idea...
   }

  print "\n\nUnix Flavor Machines:\n";
  for ($p->filter_by_osfamily('linux','solaris','unix')){print;}

  print "\n\nAnd for those who like Windows:\n";
  for ($p->filter_by_osfamily('win')){print;}

  $p->clean(); #frees memory

DESCRIPTION

This is an XML parser for nmap XML reports. This uses the XML::Twig library which is fast and more memory efficient than using the XML::SAX::PurePerl that comes with Nmap::Scanner::Scanner. This module, in the authors opinion, is easier to use for basic information gathering of hosts.

This module is meant to be a balance of easy of use and efficiency. (more ease of use). If you need more information from an nmap xml-output that is not available in the release, please send your request. (see below).

OVERVIEW

Using this module is very simple. (hopefully).

Set your Options

You first set any filters you want on the information you will parse. This is optional, but if you wish the parser to be more efficient, don't parse information you don't need. Other options (os_family) can be set also. (See Pre-Parse methods)

Example, if you only want to retain the information of the hosts that nmap found to be up (active), then set the filter:

 $obj->parse_filters({only_active => 1});

Usually you won't have much information about hosts that are down from nmap anyways.

Parse the Info

Parse the info. You use $obj->parse() or $obj->parsefile(), to parse the nmap xml information. This information is parsed and constructed internally.

Get the Scan Info

Use the $si = $obj->get_scaninfo() to obtain the Parse::Nmap::XML::ScanInfo object. Then you can call any of the ScanInfo methods on this object to retrieve the information. See Parse::Nmap::XML::ScanInfo below.

Get the Host Info

Use the $obj->get_host($addr) to obtain the Parse::Nmap::XML::Host object of the current address. Using this object you can call any methods in the Host object to retrieve the information that nmap obtained from this scan.

 $obj->get_host($ip_addr);

You can use any of the other methods to filter or obtain different lists.

        #returns all ip addresses that were scanned
 $obj->get_host_list()

        #returns all ip addresses that have osfamily = $os
 $obj->filter_by_osfamily($os)
         #See get_os_list() and set_os_list()
         #etc. (see other methods)

        #returns all host objects from the information parsed.
        #All are Parse::Nmap::XML::Host objects
 $obj->get_host_objects()
Clean up

This is semi-optional. When files are not that long, this is optional. If you are in a situation with memory constraints and are dealing with large nmap xml-output files, this little effort helps. After you are done with everything, you should do a $obj->clean() to free up the memory used by maintaining the scan and hosts information from the scan. A much more efficient way to do is, once you are done using a host object, delete it.

                #Getting all IP addresses parsed
 for my $host ($obj->get_host_list())
        {       #Getting the host object for that address
        my $h = $obj->get_host($host);
                #Calling methods on that object
        print "Addr: $host  OS: ".(join ',',$h->os_matches())."\n";
        $obj->del_host($host); #frees memory
        }

        #Or when you are done with everything use $obj->clean()
Or you could skip the $obj->del_host(), and after you are done, perform a
$obj->clean() which resets all the internal trees. Of course there are much
better ways to clean-up (using perl idioms).

METHODS

Pre-Parsing Methods

new()

Creates a new Parse::Nmap::XML object with default handlers and default osfamily list.

set_osfamily_list($hashref)

Decides what is the osfamily name of the given system.

Takes in a hash refernce that referes to pairs of osfamily names to their keyword list. Shown here is the default. Calling this method will overwrite the whole list, not append to it. Use get_osfamily_list() first to get the current listing.

  $obj->set_osfamily_list({
        solaris => [qw(solaris sparc sunos)],
        linux   => [qw(linux mandrake redhat slackware)],
        unix    => [qw(unix hp-ux hpux bsd immunix aix)],
        win     => [qw(win microsoft)],
        mac     => [qw(mac osx)],
        switch  => [qw(ethernet cisco netscout router switch)],
            });

example: osfamily_name = solaris if the os string being matched matches (solaris, sparc or sunos) keywords

The reason for having this seprately that relying on the 'osclass' tag in the xml output is that the 'osclass' tag is not generated all the time. Usually new versions of nmap will generate the 'osclass' tags. These will be available through the Parse::Nmap::XML::Host methods. (See below).

get_osfamily_list()

Returns a hashre containing the current osfaimly names (keys) and an arrayref pointing to the list of corresponding keywords (values). See set_osfamily_list() for an example.

parse_filters($hashref)

This function takes a hash reference that will set the corresponding filters when parsing the xml information. All filter names passed will be treated as case-insensitive.

 $obj->parse_filters({
        osfamily        => 1, #same as any variation. Ex: osfaMiLy
        only_active     => 0   #same here
                });
OSFAMILY

If set to true, (the default), it will match the OS guessed by nmap with a osfamily name that is given in the OS list. See set_osfamily_list(). If false, it will disable this matching (a bit of speed up in parsing).

ONLY_ACTIVE

If set to true, it will ignore hosts that nmap found to be in state 'down'. If set to perl-wise false, it will parse all the hosts. This is the default. Note that if you do not place this filter, it will parse and store (in memory) hosts that do not have much information. So calling a Parse::Nmap::XML::Host method on one of these hosts that were 'down', will return undef.

SEQUENCES

If set to true, parses the tcpsequence, ipidsequence and tcptssequence information. This is the default.

PORTINFO

If set to true, parses the port information. (You usually want this enabled). This is the default.

SCANINFO

If set to true, parses the scan information. This includes the 'scaninfo', 'nmaprun' and 'finished' tags. This is set to true by default. If you don't care about the scan information of the file, then turn this off to enhance speed and memory usage.

UPTIME

If set to true, parses the uptime information (lastboot, uptime-seconds..etc). This is the default.

reset_filters()

Resets the value of the filters to the default values:

 osfamily       => 1
 scaninfo       => 1
 only_active    => 0
 sequences      => 1
 portinfo       => 1
 scaninfo       => 1
 uptime         => 1

Parse Methods

parse($source [, opt => opt_value [...]])

Same as XML::Twig::parse().

This method is inherited from XML::Parser. The "SOURCE" parameter should either be a string containing the whole XML document, or it should be an open "IO::Handle". Constructor options to "XML::Parser::Expat" given as keyword-value pairs may follow the"SOURCE" parameter. These override, for this call, any options or attributes passed through from the XML::Parser instance.

A die call is thrown if a parse error occurs. Otherwise it will return the twig built by the parse. Use "safe_parse" if you want the parsing to return even when an error occurs.

parsefile($filename [, opt => opt_value [...]])

Same as XML::Twig::parsefile().

This method is inherited from XML::Parser. Open "$filename" for reading, then call "parse" with the open handle. The file is closed no matter how "parse" returns.

A die call is thrown if a parse error occurs. Otherwise it willreturn the twig built by the parse. Use "safe_parsefile" if you want the parsing to return even when an error occurs.

safe_parse($source [, opt => opt_value [...]])

Same as XML::Twig::safe_parse().

This method is similar to "parse" except that it wraps the parsing in an "eval" block. It returns the twig on success and 0 on failure (the twig object also contains the parsed twig). $@ contains the error message on failure.

Note that the parsing still stops as soon as an error is detected, there is no way to keep going after an error.

safe_parsefile($source [, opt => opt_value [...]])

Same as XML::Twig::safe_parsefile().

This method is similar to "parsefile" except that it wraps the parsing in an "eval" block. It returns the twig on success and 0 on failure (the twig object also contains the parsed twig) . $@ contains the error message on failure

Note that the parsing still stops as soon as an error is detected, there is no way to keep going after an error.

clean()

Frees up memory by cleaning the current tree hashes and purging the current information in the XML::Twig object. Returns the Parse::Nmap::XML object.

Post-Parse Methods

get_host_list([$status])

Returns all the ip addresses that were run in the nmap scan. $status is optional and can be either 'up' or 'down'. If $status is given, then only IP addresses that have that corresponding state will be returned. Example: setting $status = 'up', then will return all IP addresses that were found to be up. (network talk for active)

get_host($ip_addr)

Returns the complete host object of the corresponding IP address.

del_host($ip_addr)

Deletes the corresponding host object from the main tree. (Frees up memory of unwanted host structures).

get_host_objects()

Returns all the host objects of all the IP addresses that nmap had run against. See Parse::Nmap::XML::Host.

filter_by_osfamily(@osfamily_names)

This returns all the IP addresses that have match any of the keywords in @osfamily_names that is set in their osfamily_names field. See os_list() for example on osfamily_name. This makes it easier to sift through the lists of IP if you are trying to split up IP addresses depending on platform (window and unix machines for example).

filter_by_status($status)

This returns an array of hosts addresses that are in the $status state. $status can be either 'up' or 'down'. Default is 'up'.

get_scaninfo()

Returns the the current Parse::Nmap::XML::ScanInfo. Methods can be called on this object to retrieve information about the parsed scan. See Parse::Nmap::XML::ScanInfo below.

Parse::Nmap::XML::ScanInfo

The scaninfo object. This package contains methods to easily access all the parameters and values of the Nmap scan information ran by the currently parsed xml file or filehandle.

 $si = $obj->get_scaninfo();
 print  'Nmap Version: '.$si->nmap_version()."\n",
        'Num of Scan Types: '.(join ',', $si->scan_types() )."\n",
        'Total time: '.($si->finish_time() - $si->start_time()).' seconds';
        #... you get the idea...
num_of_services([$scan_type]);

If given a corresponding scan type, it returns the number of services that was scan by nmap for that scan type. If $scan_type is omitted, then num_of_services() returns the total number of services scan by all scan_types.

start_time()

Returns the start time of the nmap scan.

finish_time()

Returns the finish time of the nmap scan.

nmap_version()

Returns the version of nmap that ran.

args()

Returns the command line parameters that were run with nmap

scan_types()

In list context, returns an array containing the names of the scan types that were selected. In scalar context, returns the total number of scan types that were selected.

proto_of_scan_type($scan_type)

Returns the protocol of the specific scan type.

Parse::Nmap::XML::Host

The host object. This package contains methods to easily access the information of a host that was scanned.

  $host_obj = Parse::Nmap::XML->get_host($ip_addr);
   #Now I can get information about this host whose ip = $ip_addr
   print
  'Hostname: '.$host_obj->hostnames(1),"\n",
  'Address: '.$host_obj->addr()."\n",
  'OS matches: '.(join ',', $host_obj->os_matches())."\n",
  'Last Reboot: '.($host_obj->uptime_lastboot,"\n";
  #... you get the idea...

If you would like for me to add more advanced information (such as TCP Sequences), let me know.

status()

Returns the status of the host system. Either 'up' or 'down'

addr()

Returns the IP address of the system

addrtype()

Returns the address type of the IP address returned by addr(). Ex. 'ipv4'

hostnames($number)

If $number is omitted (or false), returns an array containing all of the host names. If $number is given, then returns the host name in that particular slot. (order). The slot order starts at 1.

 $host_obj->hostnames(0); #returns an array containing the hostnames found
 $host_obj->hostnames();  #same thing
 $host_obj->hostnames(1); #returns the 1st hostname found
 $host_obj->hostnames(4); #returns the 4th. (you get the idea..)
tcp_ports()

In a list context, returns an array containing the open tcp ports on the system. In a scalar context, a hash reference of the tree branch is returned.

udp_ports()

In a list context, returns an array containing the open udp ports on the system. In a scalar context, a hash reference of the tree branch is returned.

tcp_service_name($port)

Returns the name of the service running on the given tcp $port. (if any)

udp_service_name($port)

Returns the name of the service running on the given udp $port. (if any)

os_matches([$number])

If $number is omitted, returns an array of possible matching os names. If $number is given, then returns that slot entry of possible os names. The slot order starts at 1.

 $host_obj->os_matches(0); #returns an array containing the os names found
 $host_obj->os_matches();  #same thing
 $host_obj->os_matches(1); #returns the 1st os name found
 $host_obj->os_matches(5); #returns the 5th. (you get the idea...)
os_port_used($state)

Returns the port number that was used in determining the OS of the system. If $state is set to 'open', then the port id that was used in state open is returned. If $state is set to 'closed', then the port id that was used in state closed is returned. (no kidding...). Default, the open port number is returned.

os_family()

Returns the osfamily_name that was matched to the given host. This osfamily value is determined by the list given in the *_osfamily_list() functions.

Note: see set_osfamily_list()

os_class([$number]) Experimental - interface might change in future releases

Returns the os_family, os_generation and os_type that was guessed by nmap. The os_class tag does not always appear in all nmap OS fingerprinting scans. This appears in newer nmap versions. You should check to see if there are values to this. If you want a customized (and sure) way of determining an os_family value use the *_osfamily_list() functions to set them. These will determine what os_family value to give depending on the osmatches recovered from the scan.

 ($os_family,$os_gen,$os_type) = $host_obj->os_class(); #returns the first set

There can be more than one os_class (different kernels of Linux for example). In order to access these extra os_class information, you can pass an index number to the function. If not number is given, the first os_class information is returned. The slot order starts at 1.

  #returns the first set (same as passing no arguments)
 ($os_family,$os_gen,$os_vendor,$os_type) = $host_obj->os_class(1);

  #returns os_gen value only. Example: '2.4.x' if is a Linux 2.4.x kernel.
  $os_gen                      = ($host_obj->os_class())[2];# os_gen only

You can play with perl to get the values you want easily. Also, if argument 'total' is passed, it will return the total number os_class tags parsed for this host.

Note: This tag is usually available in new versions of nmap. You can define your own os_family customizing the os_family lists using the Parse::Nmap::XML functions: set_osfamily_list() and get_osfamily_list().

tcpsequence()

Returns the tcpsequence information in the format:

 ($class,$values,$index) = $host_obj->tcpsequence();
ipidsequence()

Returns the ipidsequence information in the format:

 ($class,$values) = $host_obj->ipidsequence();
tcptssequence()

Returns the tcptssequence information in the format:

 ($class,$values) = $host_obj->tcptssequence();
uptime_seconds()

Returns the number of seconds the host has been up (since boot).

uptime_lastboot()

Returns the time and date the given host was last rebooted.

AUTHOR

Anthony G Persaud <ironstar@iastate.edu>

SEE ALSO

nmap(1), XML::Twig(3)

  http://www.insecure.org/nmap/
  http://www.xmltwig.com

COPYRIGHT

This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.

See http://www.perl.com/perl/misc/Artistic.html

7 POD Errors

The following errors were encountered while parsing the POD:

Around line 500:

'=item' outside of any '=over'

Around line 574:

You forgot a '=back' before '=head1'

Around line 676:

=back doesn't take any parameters, but you said =back 4

Around line 737:

=back doesn't take any parameters, but you said =back 4

Around line 784:

=back doesn't take any parameters, but you said =back 4

Around line 835:

=back doesn't take any parameters, but you said =back 4

Around line 986:

=back doesn't take any parameters, but you said =back 4