Skip to content

Commit 6b8366d

Browse files
committed
Add parser for nmap XML reports
1 parent 5eedc34 commit 6b8366d

File tree

5 files changed

+65
-1
lines changed

5 files changed

+65
-1
lines changed

dojo/forms.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -228,7 +228,7 @@ class Meta:
228228

229229

230230
class ImportScanForm(forms.Form):
231-
SCAN_TYPE_CHOICES = (("Burp Scan", "Burp Scan"), ("Nessus Scan", "Nessus Scan"), ("Nexpose Scan", "Nexpose Scan"),
231+
SCAN_TYPE_CHOICES = (("Burp Scan", "Burp Scan"), ("Nessus Scan", "Nessus Scan"), ("nmap Scan", "nmap Scan"), ("Nexpose Scan", "Nexpose Scan"),
232232
("AppSpider Scan", "AppSpider Scan"), ("Veracode Scan", "Veracode Scan"),
233233
("Checkmarx Scan", "Checkmarx Scan"), ("ZAP Scan", "ZAP Scan"),
234234
("Arachni Scan", "Arachni Scan"), ("VCG Scan", "VCG Scan"),

dojo/templates/dojo/import_scan_results.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
<li><b>Burp XML</b> - When the Burp report is generated, we recommend selecting the option to Base64 encode both the request and
1919
response fields. These fields will be processed and made available in the Finding view page.</li>
2020
<li><b>Tenable Nessus</b> - Reports can be imported in the CSV, and .nessus (XML) report formats.</li>
21+
<li><b>nmap</b> - XML output (use -oX)</li>
2122
<li><b>Rapid7 Nexpose XML 2.0</b> - Use the full XML export template from Nexpose.</li>
2223
<li><b>Rapid7 AppSpider</b> - Use the VulnerabilitiesSummary.xml file found in the zipped report download.</li>
2324
<li><b>Veracode Detailed XML Report</b></li>

dojo/tools/factory.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from dojo.tools.burp.parser import BurpXmlParser
22
from dojo.tools.nessus.parser import NessusCSVParser, NessusXMLParser
3+
from dojo.tools.nmap.parser import NmapXMLParser
34
from dojo.tools.nexpose.parser import NexposeFullXmlParser
45
from dojo.tools.veracode.parser import VeracodeXMLParser
56
from dojo.tools.zap.parser import ZapXmlParser
@@ -27,6 +28,8 @@ def import_parser_factory(file, test):
2728
parser = NessusCSVParser(file, test)
2829
elif filename.endswith("xml") or filename.endswith("nessus"):
2930
parser = NessusXMLParser(file, test)
31+
elif scan_type == "nmap Scan":
32+
parser = NmapXMLParser(file, test)
3033
elif scan_type == "Nexpose Scan":
3134
parser = NexposeFullXmlParser(file, test)
3235
elif scan_type == "Veracode Scan":

dojo/tools/nmap/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
__author__ = 'patriknordlen'

dojo/tools/nmap/parser.py

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
from xml.dom import NamespaceErr
2+
import lxml.etree as le
3+
import os
4+
import csv
5+
import re
6+
from dojo.models import Endpoint, Finding
7+
from pprint import pprint
8+
9+
__author__ = 'patriknordlen'
10+
11+
class NmapXMLParser(object):
12+
def __init__(self, file, test):
13+
nscan = le.parse(file)
14+
root = nscan.getroot()
15+
16+
if 'nmaprun' not in root.tag:
17+
raise NamespaceErr("This doesn't seem to be a valid nmap xml file.")
18+
dupes = {}
19+
for host in root.iter("host"):
20+
ip = host.find("address[@addrtype='ipv4']").attrib['addr']
21+
fqdn = host.find("hostnames/hostname[@type='PTR']").attrib['name'] if host.find("hostnames/hostname[@type='PTR']") is not None else None
22+
23+
for portelem in host.xpath("ports/port[state/@state='open']"):
24+
port = portelem.attrib['portid']
25+
protocol = portelem.attrib['protocol']
26+
27+
title = "Open port: %s/%s" % (port, protocol)
28+
29+
description = "%s:%s A service was found to be listening on this port." % (ip, port)
30+
31+
if portelem.find('service'):
32+
if hasattr(portelem.find('service'),'product'):
33+
serviceinfo = " (%s%s)" % (portelem.find('service').attrib['product'], " "+portelem.find('service').attrib['version'] if hasattr(portelem.find('service'),'version') else "")
34+
else:
35+
serviceinfo = ""
36+
description += " It was identified as '%s%s'." % (portelem.find('service').attrib['name'], serviceinfo)
37+
description += '\n\n'
38+
39+
severity = "Info"
40+
41+
dupe_key = port
42+
43+
if dupe_key in dupes:
44+
find = dupes[dupe_key]
45+
if description is not None:
46+
find.description += description
47+
else:
48+
find = Finding(title=title,
49+
test=test,
50+
active=False,
51+
verified=False,
52+
description=description,
53+
severity=severity,
54+
numerical_severity=Finding.get_numerical_severity(severity))
55+
find.unsaved_endpoints = list()
56+
dupes[dupe_key] = find
57+
58+
find.unsaved_endpoints.append(Endpoint(host=ip, fqdn=fqdn, port=port, protocol=protocol))
59+
self.items = dupes.values()

0 commit comments

Comments
 (0)