PoshCode Logo PowerShell Code Repository

Parse nmap XML output (modification of post by view diff)
embed code: <script type="text/javascript" src="http://PoshCode.org/embed/1179"></script>download | new post

A PowerShell script into which one or more nmap XML output file objects can be piped, then the script emits synthetic objects representing port-scanned hosts from the XML file(s). Get Windows and Linux versions of the nmap scanner for free from http://nmap.org. Get a sample nmap XML file to play with and see some examples of using the script at https://blogs.sans.org/windows-security/2009/06/11/powershell-script-to-parse-nmap-xml-output/

  1. ####################################################################################
  2. #  Script: parse-nmap.ps1
  3. # Purpose: Parse the XML output file of the nmap port scanner and emit custom
  4. #          objects with properties containing data from XML file.
  5. # Example: dir *.xml | .\parse-nmap.ps1
  6. #  Author: Jason Fossen (www.EnclaveConsulting.com)
  7. # Version: 2.0
  8. # Updated: 15.Jun.2009
  9. #   Notes: Pipe one or more file objects into script.  Don't pipe file contents.
  10. #          Script is slow, e.g., a 20MB XML log with 65k entries requires 103 seconds
  11. #          to process on a 2.16GHz T2600 Intel CPU (about 630 entries per second).
  12. #   LEGAL: PUBLIC DOMAIN.  SCRIPT PROVIDED "AS IS" WITH NO WARRANTIES OR GUARANTEES OF
  13. #          ANY KIND, INCLUDING BUT NOT LIMITED TO MERCHANTABILITY AND/OR FITNESS FOR
  14. #          A PARTICULAR PURPOSE.  ALL RISKS OF DAMAGE REMAINS WITH THE USER, EVEN IF
  15. #          THE AUTHOR, SUPPLIER OR DISTRIBUTOR HAS BEEN ADVISED OF THE POSSIBILITY OF
  16. #          ANY SUCH DAMAGE.  IF YOUR STATE DOES NOT PERMIT THE COMPLETE LIMITATION OF
  17. #          LIABILITY, THEN DELETE THIS FILE SINCE YOU ARE NOW PROHIBITED TO HAVE IT.
  18. ####################################################################################
  19.  
  20.  
  21. if ($args -ne $null) {
  22.     "`nThis script takes no arguments, please pipe one or more files into it."
  23.     "Example: dir *.xml | .\parse-nmap.ps1 | export-csv -path c:\file.csv`n"
  24.     exit
  25. }
  26.  
  27. # Set $ShowProgress to $false if you do not want progress info sent to StdErr.
  28. $ShowProgress = $true
  29.  
  30.  
  31.  
  32. ForEach ($file in $input) {
  33.     If ($ShowProgress) { [Console]::Error.WriteLine("[" + (get-date).ToLongTimeString() + "] Starting $file" ) }
  34.  
  35.     $xmldoc = new-object System.XML.XMLdocument
  36.     $xmldoc.Load($file)
  37.     $i = 1  #Counter for <host> nodes processed.
  38.  
  39.     # Process each of the <host> nodes from the nmap report.
  40.     $xmldoc.nmaprun.host | foreach-object {
  41.         $hostnode = $_   # $hostnode is a <host> node in the XML.
  42.  
  43.         # Init variables, with $entry being the custom object for each <host>.
  44.         $service = " " #service needs to be a single space.
  45.         $entry = ($entry = " " | select-object FQDN, HostName, Status, IPv4, IPv6, MAC, Ports, OS)
  46.  
  47.         # Extract state element of status:
  48.         $entry.Status = $hostnode.status.state.Trim()
  49.         if ($entry.Status.length -eq 0) { $entry.Status = "<no-status>" }
  50.  
  51.         # Extract fully-qualified domain name(s).
  52.         $hostnode.hostnames | foreach-object { $entry.FQDN += $_.hostname.name + " " }
  53.         $entry.FQDN = $entry.FQDN.Trim()
  54.         if ($entry.FQDN.Length -eq 0) { $entry.FQDN = "<no-fullname>" }
  55.  
  56.         # Note that this code cheats, it only gets the hostname of the first FQDN if there are multiple FQDNs.
  57.         if ($entry.FQDN.Contains(".")) { $entry.HostName = $entry.FQDN.Substring(0,$entry.FQDN.IndexOf(".")) }
  58.         elseif ($entry.FQDN -eq "<no-fullname>") { $entry.HostName = "<no-hostname>" }
  59.         else { $entry.HostName = $entry.FQDN }
  60.  
  61.         # Process each of the <address> nodes, extracting by type.
  62.         $hostnode.address | foreach-object {
  63.             if ($_.addrtype -eq "ipv4") { $entry.IPv4 += $_.addr + " "}
  64.             if ($_.addrtype -eq "ipv6") { $entry.IPv6 += $_.addr + " "}
  65.             if ($_.addrtype -eq "mac")  { $entry.MAC  += $_.addr + " "}
  66.         }        
  67.         if ($entry.IPv4 -eq $null) { $entry.IPv4 = "<no-ipv4>" } else { $entry.IPv4 = $entry.IPv4.Trim()}
  68.         if ($entry.IPv6 -eq $null) { $entry.IPv6 = "<no-ipv6>" } else { $entry.IPv6 = $entry.IPv6.Trim()}
  69.         if ($entry.MAC  -eq $null) { $entry.MAC  = "<no-mac>" }  else { $entry.MAC  = $entry.MAC.Trim() }
  70.  
  71.  
  72.         # Process all ports from <ports><port>, and note that <port> does not contain an array if it only has one item in it.
  73.         if ($hostnode.ports.port -eq $null) { $entry.Ports = "<no-ports>" }
  74.         else
  75.         {
  76.             $hostnode.ports.port | foreach-object {
  77.                 If ($_.service.name -eq $null) { $service = "unknown" } else { $service = $_.service.name }
  78.                 $entry.Ports += $_.state.state + ":" + $_.protocol + ":" + $_.portid + ":" + $service + " "
  79.             }
  80.             $entry.Ports = $entry.Ports.Trim()
  81.         }
  82.  
  83.  
  84.         # Extract fingerprinted OS type and percent of accuracy.
  85.         $hostnode.os.osmatch | foreach-object {$entry.OS += $_.name + " <" + ([String] $_.accuracy) + "%-accuracy> "}
  86.         $entry.OS = $entry.OS.Trim()
  87.         if ($entry.OS -eq "<%-accuracy>") { $entry.OS = "<no-os>" }
  88.  
  89.  
  90.         # Emit custom object from script.
  91.         $entry
  92.         $i++  #Progress counter...
  93.     }
  94.  
  95. If ($ShowProgress) { [Console]::Error.WriteLine("[" + (get-date).ToLongTimeString() + "] Finished $file, processed $i entries." ) }
  96. }

Submit a correction or amendment below (
click here to make a fresh posting)
After submitting an amendment, you'll be able to view the differences between the old and new posts easily.

Syntax highlighting:


Remember me