diff --git a/README.md b/README.md index 61c136f..86ee775 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ Full Tutorial is available here - [Nmap Dashboard using Grafana](https://hackertarget.com/nmap-dashboard-with-grafana/) ![Grafana Dashboard](https://hackertarget.com/images/nmap-grafana-dashboard.webp) - +ss ## Overview The project consists of two main components: @@ -61,6 +61,17 @@ Once the container is up and running, access the Grafana dashboard through your http://localhost:3000 ``` +5. **Auto-Nmap Usage** + +Once the container is up and running, access the Grafana dashboard through your web browser: + +``` +cd data +source nmap_auto.sh +nmap {target} +``` + + Use the default Grafana credentials (admin/admin) unless changed in the configuration. The Nmap dashboard should be loaded with the data from your Nmap scans. Multiple scans can be reviewed within the DB and the Nmap Dashboard time filters can be used to the view the scan information based on the time stamps from the scans. diff --git a/data/nmap-auto.sh b/data/nmap-auto.sh new file mode 100644 index 0000000..21440d4 --- /dev/null +++ b/data/nmap-auto.sh @@ -0,0 +1,62 @@ +#!/bin/bash +# nmap_auto.sh + +original_nmap() { + local args=("$@") + local has_script=false + local has_sv=false + local has_xml=false + + for arg in "${args[@]}"; do + case "$arg" in + --script=*|--script) has_script=true ;; + -sV) has_sv=true ;; + -oX*) has_xml=true ;; + esac + done + + if [ "$has_script" = false ]; then + args+=(--script=vulners,http-title,ssl-cert) + echo "Auto-added: --script=vulners,http-title,ssl-cert" + fi + + if [ "$has_sv" = false ]; then + args+=(-sV) + echo "Auto-added: -sV" + fi + + if [ "$has_xml" = false ]; then + args+=(-oX output.xml) + echo "Auto-added: -oX output.xml" + fi + + echo "Running: nmap ${args[*]}" + command nmap "${args[@]}" + local exit_code=$? + + if [ $exit_code -eq 0 ]; then + echo "Scan was completed" + + if [ -f "nmap-to-sqlite.py" ]; then + if ls *.xml 1> /dev/null 2>&1; then + echo "Python script is running..." + xml_file=$(ls *.xml | head -n 1) + python3 nmap-to-sqlite.py "$xml_file" + echo 'Run successful!' + + rm *.xml + echo "XML files deleted" + else + echo "No XML output found" + fi + else + echo "nmap-to-sqlite.py can't find" + fi + else + echo "Nmap failed" + fi + + return $exit_code +} + +alias nmap='original_nmap' diff --git a/data/nmap-to-sqlite.py b/data/nmap-to-sqlite.py index b5db87c..b77062b 100644 --- a/data/nmap-to-sqlite.py +++ b/data/nmap-to-sqlite.py @@ -70,13 +70,31 @@ def parse_nmap_xml(xml_file): service_ostype = service.get('ostype', None) if service else None service_info = (service_product if service_product else '') + (' ' + service_version if service_version else '') http_title = None + http_headers = None ssl_common_name = None ssl_issuer = None scripts = port.findall('script') + exploit_info = [] for script in scripts: if script.get('id') == 'http-title': http_title = script.get('output') + elif script.get('id') == 'http-headers': + http_headers = script.get('output') + elif script.get('id') == 'vulners': + for i in script.findall('.//table'): + if i.find("elem[@key='type']") is None or i.find("elem[@key='type']").text != 'cve': + continue + else: + exc_dic = {} + for x in i.findall('elem'): + key = x.get('key') + val = x.text + if key and val: + exc_dic[key] = val + if exc_dic: + exploit_info.append(exc_dic) + elif script.get('id') == 'ssl-cert': for table in script.findall('table'): if table.get('key') == 'subject': @@ -88,6 +106,14 @@ def parse_nmap_xml(xml_file): if 'commonName' in issuer_elems: ssl_issuer = f"{issuer_elems.get('commonName')} {issuer_elems.get('organizationName', '')}".strip() + if service is not None: + cpes = [cpe.text for cpe in service.findall('cpe')] + cpes_str = ",".join(cpes) if cpes else "" + + exploit_str = None + if exploit_info: + exploit_str = "; ".join(", ".join(f"{k}={v}" for k, v in e.items()) for e in exploit_info) + if service_ostype and os == 'Unknown': os = service_ostype @@ -98,12 +124,15 @@ def parse_nmap_xml(xml_file): 'service_name': service_name, 'service_info': service_info, 'http_title': http_title, + 'http_headers': http_headers, 'ssl_common_name': ssl_common_name, - 'ssl_issuer': ssl_issuer + 'ssl_issuer': ssl_issuer, + 'cpes': cpes_str, + 'exploit': exploit_str }) extraports = ports_element.find('extraports') - if len(extraports): + if extraports is not None and len(extraports): extraports_count = int(extraports.get('count', '0')) extraports_state = extraports.get('state', '') if extraports_state == 'closed': @@ -178,8 +207,11 @@ def create_database(db_name): service_name TEXT, service_info TEXT, http_title TEXT, + http_headers TEXT, ssl_common_name TEXT, ssl_issuer TEXT, + cpes TEXT, + exploit TEXT, FOREIGN KEY (scan_id) REFERENCES scans (id), FOREIGN KEY (host_id) REFERENCES hosts (id))''') @@ -199,8 +231,8 @@ def insert_data(conn, scan, hosts): host_id = c.lastrowid for port in host['ports']: - c.execute("INSERT INTO ports (scan_id, host_id, port, protocol, state, service_name, service_info, http_title, ssl_common_name, ssl_issuer) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", - (scan_id, host_id, port['port'], port['protocol'], port['state'], port['service_name'], port['service_info'], port['http_title'], port['ssl_common_name'], port['ssl_issuer'])) + c.execute("INSERT INTO ports (scan_id, host_id, port, protocol, state, service_name, service_info, http_title, http_headers, cpes, exploit, ssl_common_name, ssl_issuer) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", + (scan_id, host_id, port['port'], port['protocol'], port['state'], port['service_name'], port['service_info'], port['http_title'], port['http_headers'], port['cpes'], port['exploit'], port['ssl_common_name'], port['ssl_issuer'])) conn.commit() diff --git a/grafana-docker/dashboards/nmap-dashboard.json b/grafana-docker/dashboards/nmap-dashboard.json index 9583f1f..63bc711 100644 --- a/grafana-docker/dashboards/nmap-dashboard.json +++ b/grafana-docker/dashboards/nmap-dashboard.json @@ -18,7 +18,7 @@ "editable": true, "fiscalYearStartMonth": 0, "graphTooltip": 0, - "id": 2, + "id": 1, "links": [], "panels": [ { @@ -37,7 +37,7 @@ "steps": [ { "color": "#4ecda7", - "value": null + "value": 0 }, { "color": "red", @@ -60,6 +60,7 @@ "graphMode": "area", "justifyMode": "auto", "orientation": "auto", + "percentChangeColorMode": "standard", "reduceOptions": { "calcs": [ "lastNotNull" @@ -71,7 +72,7 @@ "textMode": "auto", "wideLayout": true }, - "pluginVersion": "10.4.1", + "pluginVersion": "12.1.0", "targets": [ { "datasource": { @@ -139,11 +140,12 @@ "values": true }, "tooltip": { + "hideZeros": false, "mode": "single", "sort": "none" } }, - "pluginVersion": "10.4.1", + "pluginVersion": "12.1.0", "targets": [ { "datasource": { @@ -207,11 +209,12 @@ "values": true }, "tooltip": { + "hideZeros": false, "mode": "single", "sort": "none" } }, - "pluginVersion": "10.4.1", + "pluginVersion": "12.1.0", "targets": [ { "datasource": { @@ -254,7 +257,7 @@ "steps": [ { "color": "#4ecda7", - "value": null + "value": 0 }, { "color": "red", @@ -306,7 +309,7 @@ } ] }, - "pluginVersion": "10.4.1", + "pluginVersion": "12.1.0", "targets": [ { "datasource": { @@ -342,7 +345,7 @@ "steps": [ { "color": "#4ecda7", - "value": null + "value": 0 }, { "color": "light-purple", @@ -365,6 +368,7 @@ "graphMode": "area", "justifyMode": "auto", "orientation": "auto", + "percentChangeColorMode": "standard", "reduceOptions": { "calcs": [ "lastNotNull" @@ -376,7 +380,7 @@ "textMode": "auto", "wideLayout": true }, - "pluginVersion": "10.4.1", + "pluginVersion": "12.1.0", "targets": [ { "datasource": { @@ -412,7 +416,7 @@ "steps": [ { "color": "#4ecda7", - "value": null + "value": 0 }, { "color": "light-purple", @@ -435,6 +439,7 @@ "graphMode": "area", "justifyMode": "auto", "orientation": "auto", + "percentChangeColorMode": "standard", "reduceOptions": { "calcs": [ "lastNotNull" @@ -446,7 +451,7 @@ "textMode": "auto", "wideLayout": true }, - "pluginVersion": "10.4.1", + "pluginVersion": "12.1.0", "targets": [ { "datasource": { @@ -503,7 +508,7 @@ "steps": [ { "color": "#4ecda7", - "value": null + "value": 0 }, { "color": "red", @@ -537,6 +542,7 @@ "showValue": "auto", "stacking": "none", "tooltip": { + "hideZeros": false, "mode": "single", "sort": "none" }, @@ -544,7 +550,7 @@ "xTickLabelRotation": -45, "xTickLabelSpacing": 0 }, - "pluginVersion": "10.4.1", + "pluginVersion": "12.1.0", "targets": [ { "datasource": { @@ -580,7 +586,7 @@ "steps": [ { "color": "#4ecda7", - "value": null + "value": 0 }, { "color": "light-purple", @@ -603,6 +609,7 @@ "graphMode": "area", "justifyMode": "auto", "orientation": "auto", + "percentChangeColorMode": "standard", "reduceOptions": { "calcs": [ "lastNotNull" @@ -614,7 +621,7 @@ "textMode": "auto", "wideLayout": true }, - "pluginVersion": "10.4.1", + "pluginVersion": "12.1.0", "targets": [ { "datasource": { @@ -658,7 +665,7 @@ "steps": [ { "color": "#4ecda7", - "value": null + "value": 0 }, { "color": "light-purple", @@ -798,7 +805,7 @@ "showHeader": true, "sortBy": [] }, - "pluginVersion": "10.4.1", + "pluginVersion": "12.1.0", "targets": [ { "datasource": { @@ -837,6 +844,7 @@ "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, + "barWidthFactor": 0.6, "drawStyle": "bars", "fillOpacity": 0, "gradientMode": "hue", @@ -868,7 +876,7 @@ "steps": [ { "color": "#4ecda7", - "value": null + "value": 0 }, { "color": "light-purple", @@ -907,11 +915,12 @@ "showLegend": true }, "tooltip": { + "hideZeros": false, "mode": "single", "sort": "none" } }, - "pluginVersion": "10.4.1", + "pluginVersion": "12.1.0", "targets": [ { "datasource": { @@ -948,7 +957,7 @@ "steps": [ { "color": "#4ecda7", - "value": null + "value": 0 }, { "color": "light-purple", @@ -968,6 +977,12 @@ "id": 15, "options": { "displayMode": "gradient", + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": false + }, "maxVizHeight": 300, "minVizHeight": 16, "minVizWidth": 8, @@ -982,7 +997,7 @@ "sizing": "auto", "valueMode": "color" }, - "pluginVersion": "10.4.1", + "pluginVersion": "12.1.0", "targets": [ { "datasource": { @@ -1009,92 +1024,26 @@ }, "fieldConfig": { "defaults": { - "color": { - "mode": "thresholds" - }, "custom": { "align": "auto", "cellOptions": { - "type": "auto" + "type": "color-text", + "wrapText": true }, - "inspect": false + "inspect": true }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { - "color": "green" - }, - { - "color": "red", - "value": 80 + "color": "green", + "value": 0 } ] } }, - "overrides": [ - { - "matcher": { - "id": "byName", - "options": "port" - }, - "properties": [ - { - "id": "custom.width", - "value": 85 - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "protocol" - }, - "properties": [ - { - "id": "custom.width", - "value": 115 - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "ip" - }, - "properties": [ - { - "id": "custom.width", - "value": 110 - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "http_title" - }, - "properties": [ - { - "id": "custom.width", - "value": 537 - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "service_info" - }, - "properties": [ - { - "id": "custom.width", - "value": 330 - } - ] - } - ] + "overrides": [] }, "gridPos": { "h": 16, @@ -1116,21 +1065,21 @@ "showHeader": true, "sortBy": [ { - "desc": false, - "displayName": "http_title" + "desc": true, + "displayName": "exploit" } ] }, - "pluginVersion": "10.4.1", + "pluginVersion": "12.1.0", "targets": [ { "datasource": { "type": "frser-sqlite-datasource", "uid": "P2D2EEF3E092AF52B" }, - "queryText": "SELECT h.ip, p.protocol, p.port, p.service_info, p.http_title, p.ssl_common_name, p.ssl_issuer\nFROM ports p\nJOIN hosts h ON p.host_id = h.id\nWHERE h.start_time >= $__from AND h.end_time < $__to\nAND p.state = 'open'\n", + "queryText": "SELECT h.ip, p.protocol, p.port, p.service_info, p.http_title, p.http_headers, p.cpes, p.exploit\nFROM ports p\nJOIN hosts h ON p.host_id = h.id\nWHERE h.start_time >= 1751492729108 AND h.end_time < 1754084729108\nAND p.state = 'open'\n", "queryType": "table", - "rawQueryText": "SELECT h.ip, p.protocol, p.port, p.service_info, p.http_title, p.ssl_common_name, p.ssl_issuer\nFROM ports p\nJOIN hosts h ON p.host_id = h.id\nWHERE h.start_time >= $__from AND h.end_time < $__to\nAND p.state = 'open'\n", + "rawQueryText": "SELECT h.ip, p.protocol, p.port, p.service_info, p.http_title, p.http_headers, p.cpes, p.exploit\nFROM ports p\nJOIN hosts h ON p.host_id = h.id\nWHERE h.start_time >= $__from AND h.end_time < $__to\nAND p.state = 'open'\n", "refId": "A", "timeColumns": [ "time", @@ -1139,6 +1088,7 @@ } ], "title": "Open Services", + "transparent": true, "type": "table" }, { @@ -1163,7 +1113,8 @@ "mode": "absolute", "steps": [ { - "color": "#4ecda7" + "color": "#4ecda7", + "value": 0 }, { "color": "light-purple", @@ -1279,7 +1230,7 @@ "showHeader": true, "sortBy": [] }, - "pluginVersion": "10.4.1", + "pluginVersion": "12.1.0", "targets": [ { "datasource": { @@ -1300,8 +1251,9 @@ "type": "table" } ], + "preload": false, "refresh": "", - "schemaVersion": 39, + "schemaVersion": 41, "tags": [], "templating": { "list": [] @@ -1314,6 +1266,5 @@ "timezone": "browser", "title": "Nmap Dashboard", "uid": "cdikljcadaf40c", - "version": 58, - "weekStart": "" -} \ No newline at end of file + "version": 6 +}