├── .gitignore ├── README.md ├── __init__.py ├── admin.py ├── api.py ├── apps.py ├── functions.py ├── migrations └── __init__.py ├── models.py ├── pdf.py ├── static ├── async.js ├── custom.css ├── logo.png ├── logoblack.png ├── logomin.png └── report.css ├── templates └── nmapreport │ ├── index.html │ └── report.html ├── tests.py ├── urls.py └── views.py /.gitignore: -------------------------------------------------------------------------------- 1 | # Django Recommended 2 | *.egg-info 3 | *.pot 4 | *.py[co] 5 | __pycache__ 6 | MANIFEST 7 | dist/ 8 | docs/_build/ 9 | docs/locale/ 10 | node_modules/ 11 | tests/coverage_html/ 12 | tests/.coverage 13 | build/ 14 | tests/report/ 15 | 16 | # Sensitive Data 17 | credentials.py 18 | credentials.pyc 19 | 20 | # Byte-compiled / optimized / DLL files 21 | __pycache__/ 22 | *.py[cod] 23 | *$py.class 24 | 25 | # C extensions 26 | *.so 27 | 28 | # Distribution / packaging 29 | django-env/ 30 | .Python 31 | env/ 32 | build/ 33 | develop-eggs/ 34 | dist/ 35 | downloads/ 36 | eggs/ 37 | .eggs/ 38 | lib/ 39 | lib64/ 40 | parts/ 41 | sdist/ 42 | var/ 43 | *.egg-info/ 44 | .installed.cfg 45 | *.egg 46 | assets/ # The directory set as STATIC_ROOT where Apache creates new static files on live web servers. 47 | 48 | # PyInstaller 49 | # Usually these files are written by a python script from a template 50 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 51 | *.manifest 52 | *.spec 53 | 54 | # Installer logs 55 | pip-log.txt 56 | pip-delete-this-directory.txt 57 | 58 | # Unit test / coverage reports 59 | htmlcov/ 60 | .tox/ 61 | .coverage 62 | .coverage.* 63 | .cache 64 | nosetests.xml 65 | coverage.xml 66 | *,cover 67 | .hypothesis/ 68 | 69 | # Translations 70 | *.mo 71 | *.pot 72 | 73 | # Django 74 | *.log 75 | 76 | # Sphinx documentation 77 | docs/_build/ 78 | 79 | # PyBuilder 80 | target/ 81 | 82 | #Ipython Notebook 83 | .ipynb_checkpoints 84 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 |
3 | A Web Dashbord for Nmap XML Report 4 |

5 | 6 | ![WebMap](https://i.imgur.com/U9S089v.png) 7 | 8 | ![WebMap](https://i.imgur.com/Ptijc67.png) 9 | 10 | ![WebMap](https://i.imgur.com/alWZix9.png) 11 | 12 | ## Table Of Contents 13 | - [Usage](#usage) 14 | - [Video](#video) 15 | - [Features](#features) 16 | - [XML Filenames](#xml-filenames) 17 | - [Third Parts](#third-parts) 18 | - [Security Issues](#security-issues) 19 | - [Contributors](#contributors) 20 | - [Contacts](#contacts) 21 | 22 | ## Usage 23 | You should use this with docker, just by sending this command: 24 | ```bash 25 | $ mkdir /tmp/webmap 26 | $ docker run -d \ 27 | --name webmap \ 28 | -h webmap \ 29 | -p 8000:8000 \ 30 | -v /tmp/webmap:/opt/xml \ 31 | rev3rse/webmap /run.sh 32 | 33 | $ # now you can run Nmap and save the XML Report on /tmp/webmap 34 | $ nmap -sT -A -T4 -oX /tmp/webmap/myscan.xml 192.168.1.0/24 35 | ``` 36 | Now point your browser to http://localhost:8000 37 | 38 | ## Video 39 | -- coming soon... 40 | 41 | ## Features 42 | - Import and parse Nmap XML files 43 | - Statistics and Charts on discovered services, ports, OS, etc... 44 | - Inspect a single host by clicking on its IP address 45 | - Attach labels on a host 46 | - Insert notes for a specific host 47 | - Create a PDF Report with charts, details, labels and notes 48 | - Copy to clipboard as Nikto, Curl or Telnet commands 49 | 50 | ## XML Filenames 51 | When creating the PDF version of the Nmap XML Report, the XML filename is used as document title on the first page. WebMap will replace some parts of the filename as following: 52 | 53 | - `_` will replaced by a space (` `) 54 | - `.xml` will be removed 55 | 56 | Example: `ACME_Ltd..xml`
57 | PDF title: `ACME Ltd.` 58 | 59 | ## Third Parts 60 | - [Django](https://www.djangoproject.com) 61 | - [Materialize CSS](https://materializecss.com) 62 | - [Clipboard.js](https://clipboardjs.com) 63 | - [Chart.js](https://www.chartjs.org) 64 | - [Wkhtmltopdf](https://wkhtmltopdf.org) 65 | 66 | ## Security Issues 67 | This app is not intended to be exposed on the internet. Please, **DO NOT expose** this app to the internet, use your localhost or, in case you can't do it, take care to filter who and what can access to WebMap with a firewall rule or something like that. Exposing this app to the whole internet could lead not only to a stored XSS but also to a leakage of sensitive/critical/private informations about your port scan. Please, be smart. 68 | 69 | ## Contributors 70 | This project is currently a beta, and I'm not super skilled on Django so, every type of contribution is appreciated. I'll mention all contributors in this section of the README file. 71 | 72 | ### Contributors List 73 | - s3th_0x [@adubaldo](https://github.com/adubaldo) (bug on single host report) 74 | 75 | ## Contacts 76 | Twitter: [@Menin_TheMiddle](https://twitter.com/Menin_TheMiddle)
77 | YouTube: [Rev3rseSecurity](https://www.youtube.com/rev3rsesecurity) 78 | -------------------------------------------------------------------------------- /__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/punitdarji/WebMap/ff553249f2b3f62d0e414f4c7f4991a4f2a65b16/__init__.py -------------------------------------------------------------------------------- /admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | 3 | # Register your models here. 4 | -------------------------------------------------------------------------------- /api.py: -------------------------------------------------------------------------------- 1 | from django.shortcuts import render 2 | from django.http import HttpResponse 3 | import xmltodict, json, html, os, hashlib, re 4 | from collections import OrderedDict 5 | 6 | def rmNotes(request, hashstr): 7 | scanfilemd5 = hashlib.md5(str(request.session['scanfile']).encode('utf-8')).hexdigest() 8 | if re.match('^[a-f0-9]{32,32}$', hashstr) is not None: 9 | os.remove('/opt/notes/'+scanfilemd5+'_'+hashstr+'.notes') 10 | res = {'ok':'notes removed'} 11 | else: 12 | res = {'error':'invalid format'} 13 | 14 | return HttpResponse(json.dumps(res), content_type="application/json") 15 | 16 | def saveNotes(request): 17 | if request.method == "POST": 18 | scanfilemd5 = hashlib.md5(str(request.session['scanfile']).encode('utf-8')).hexdigest() 19 | 20 | if re.match('^[a-f0-9]{32,32}$', request.POST['hashstr']) is not None: 21 | f = open('/opt/notes/'+scanfilemd5+'_'+request.POST['hashstr']+'.notes', 'w') 22 | f.write(request.POST['notes']) 23 | f.close() 24 | res = {'ok':'notes saved'} 25 | else: 26 | res = {'error': request.method } 27 | 28 | return HttpResponse(json.dumps(res), content_type="application/json") 29 | 30 | def rmlabel(request, objtype, hashstr): 31 | types = { 32 | 'host':True, 33 | 'port':True 34 | } 35 | 36 | scanfilemd5 = hashlib.md5(str(request.session['scanfile']).encode('utf-8')).hexdigest() 37 | 38 | if re.match('^[a-f0-9]{32,32}$', hashstr) is not None: 39 | os.remove('/opt/notes/'+scanfilemd5+'_'+hashstr+'.'+objtype+'.label') 40 | res = {'ok':'label removed'} 41 | return HttpResponse(json.dumps(res), content_type="application/json") 42 | 43 | def label(request, objtype, label, hashstr): 44 | labels = { 45 | 'Vulnerable':True, 46 | 'Critical':True, 47 | 'Warning':True, 48 | 'Checked':True 49 | } 50 | 51 | types = { 52 | 'host':True, 53 | 'port':True 54 | } 55 | 56 | scanfilemd5 = hashlib.md5(str(request.session['scanfile']).encode('utf-8')).hexdigest() 57 | 58 | if label in labels and objtype in types: 59 | if re.match('^[a-f0-9]{32,32}$', hashstr) is not None: 60 | f = open('/opt/notes/'+scanfilemd5+'_'+hashstr+'.'+objtype+'.label', 'w') 61 | f.write(label) 62 | f.close() 63 | res = {'ok':'label set', 'label':str(label)} 64 | return HttpResponse(json.dumps(res), content_type="application/json") 65 | 66 | def port_details(request, address, portid): 67 | r = {} 68 | oo = xmltodict.parse(open('/opt/xml/'+request.session['scanfile'], 'r').read()) 69 | r['out'] = json.dumps(oo['nmaprun'], indent=4) 70 | o = json.loads(r['out']) 71 | 72 | for ik in o['host']: 73 | 74 | # this fix single host report 75 | if type(ik) is dict: 76 | i = ik 77 | else: 78 | i = o['host'] 79 | 80 | if '@addr' in i['address']: 81 | saddress = i['address']['@addr'] 82 | elif type(i['address']) is list: 83 | for ai in i['address']: 84 | if ai['@addrtype'] == 'ipv4': 85 | saddress = ai['@addr'] 86 | 87 | if str(saddress) == address: 88 | for pobj in i['ports']['port']: 89 | if type(pobj) is dict: 90 | p = pobj 91 | else: 92 | p = i['ports']['port'] 93 | 94 | if p['@portid'] == portid: 95 | return HttpResponse(json.dumps(p, indent=4), content_type="application/json") 96 | 97 | def genPDF(request): 98 | if 'scanfile' in request.session: 99 | pdffile = hashlib.md5(str(request.session['scanfile']).encode('utf-8')).hexdigest() 100 | if os.path.exists('/opt/nmapdashboard/nmapreport/static/'+pdffile+'.pdf'): 101 | os.remove('/opt/nmapdashboard/nmapreport/static/'+pdffile+'.pdf') 102 | 103 | os.popen('/opt/wkhtmltox/bin/wkhtmltopdf --cookie sessionid '+request.session._session_key+' --enable-javascript --javascript-delay 6000 http://127.0.0.1:8000/view/pdf/ /opt/nmapdashboard/nmapreport/static/'+pdffile+'.pdf') 104 | res = {'ok':'PDF created', 'file':'/static/'+pdffile+'.pdf'} 105 | return HttpResponse(json.dumps(res), content_type="application/json") 106 | -------------------------------------------------------------------------------- /apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class NmapreportConfig(AppConfig): 5 | name = 'nmapreport' 6 | -------------------------------------------------------------------------------- /functions.py: -------------------------------------------------------------------------------- 1 | def labelToMargin(label): 2 | labels = { 3 | 'Vulnerable':'10px', 4 | 'Critical':'22px', 5 | 'Warning':'28px', 6 | 'Checked':'28px' 7 | } 8 | 9 | if label in labels: 10 | return labels[label] 11 | 12 | def labelToColor(label): 13 | labels = { 14 | 'Vulnerable':'red', 15 | 'Critical':'black', 16 | 'Warning':'orange', 17 | 'Checked':'blue' 18 | } 19 | 20 | if label in labels: 21 | return labels[label] 22 | 23 | def fromOSTypeToFontAwesome(ostype): 24 | icons = { 25 | 'windows':'fab fa-windows', 26 | 'solaris':'fab fa-linux', # there isn't a better icon on fontawesome :( 27 | 'unix':'fab fa-linux', # same here... 28 | 'linux':'fab fa-linux', 29 | } 30 | 31 | if ostype.lower() in icons: 32 | return str(icons[ostype.lower()]) 33 | else: 34 | return 'fas fa-question' 35 | -------------------------------------------------------------------------------- /migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/punitdarji/WebMap/ff553249f2b3f62d0e414f4c7f4991a4f2a65b16/migrations/__init__.py -------------------------------------------------------------------------------- /models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | 3 | # Create your models here. 4 | -------------------------------------------------------------------------------- /pdf.py: -------------------------------------------------------------------------------- 1 | from django.shortcuts import render 2 | from django.http import HttpResponse 3 | import xmltodict, json, html, os, hashlib, re, urllib.parse, base64 4 | from collections import OrderedDict 5 | from nmapreport.functions import * 6 | #from view import labelToColor 7 | 8 | def reportPDFView(request): 9 | r = { 'out':'' } 10 | filterscriptid = { 11 | } 12 | 13 | if 'scanfile' in request.session: 14 | oo = xmltodict.parse(open('/opt/xml/'+request.session['scanfile'], 'r').read()) 15 | r['out2'] = json.dumps(oo['nmaprun'], indent=4) 16 | o = json.loads(r['out2']) 17 | else: 18 | return HttpResponse('error: scan file not loaded', content_type="text/html") 19 | 20 | r['html'] = '' 21 | hostdetails = '' 22 | counters = {'po':0,'pc':0,'pf':0,'hostsup':0,'ostype':{},'pi':{},'ss':{}} 23 | 24 | scanmd5 = hashlib.md5(str(request.session['scanfile']).encode('utf-8')).hexdigest() 25 | for ik in o['host']: 26 | 27 | # this fix single host report 28 | if type(ik) is dict: 29 | i = ik 30 | else: 31 | i = o['host'] 32 | 33 | hostcounters = {'po':0,'pc':0,'pf':0,'ostype':{},'pi':{},'ss':{}} 34 | hostdetails_html = '' 35 | portsfound = False 36 | striggered = False 37 | lastportid = 0 38 | 39 | saddress = 'noaddress' 40 | 41 | if '@addr' in i['address']: 42 | saddress = i['address']['@addr'] 43 | elif type(i['address']) is list: 44 | for ai in i['address']: 45 | if ai['@addrtype'] == 'ipv4': 46 | saddress = ai['@addr'] 47 | 48 | addressmd5 = hashlib.md5(str(saddress).encode('utf-8')).hexdigest() 49 | 50 | # collect all labels in labelhost dict 51 | labelhost = {} 52 | labelfiles = os.listdir('/opt/notes') 53 | for lf in labelfiles: 54 | m = re.match('^('+scanmd5+')_([a-z0-9]{32,32})\.host\.label$', lf) 55 | if m is not None: 56 | if m.group(1) not in labelhost: 57 | labelhost[m.group(1)] = {} 58 | labelhost[m.group(1)][m.group(2)] = open('/opt/notes/'+lf, 'r').read() 59 | 60 | # collect all notes in noteshost dict 61 | noteshost = {} 62 | notesfiles = os.listdir('/opt/notes') 63 | for nf in notesfiles: 64 | m = re.match('^('+scanmd5+')_([a-z0-9]{32,32})\.notes$', nf) 65 | if m is not None: 66 | if m.group(1) not in noteshost: 67 | noteshost[m.group(1)] = {} 68 | noteshost[m.group(1)][m.group(2)] = open('/opt/notes/'+nf, 'r').read() 69 | 70 | if i['status']['@state'] == 'up': 71 | labelout = '' 72 | if scanmd5 in labelhost: 73 | if addressmd5 in labelhost[scanmd5]: 74 | labelcolor = labelToColor(labelhost[scanmd5][addressmd5]) 75 | # labelmargin = labelToMargin(labelhost[scanmd5][addressmd5]) 76 | labelout = ''+html.escape(labelhost[scanmd5][addressmd5])+'' 77 | 78 | hostdetails_html += '
' 79 | hostdetails_html += '

'+html.escape(saddress)+' '+labelout+'

' 80 | 81 | hostdetails_html += ' Status: '+html.escape(i['status']['@state'])+', ' 82 | hostdetails_html += 'Reason: '+html.escape(i['status']['@reason'])+', ' 83 | hostdetails_html += 'TTL: '+html.escape(i['status']['@reason_ttl'])+'' 84 | 85 | if 'hostsup' in counters: 86 | counters['hostsup'] = (counters['hostsup'] + 1) 87 | else: 88 | counters['hostsup'] = 1 89 | 90 | hostdetails_html_tr = '' 91 | portdetails_html_tr = '' 92 | if 'ports' in i and 'port' in i['ports']: 93 | for pobj in i['ports']['port']: 94 | if type(pobj) is dict: 95 | p = pobj 96 | else: 97 | p = i['ports']['port'] 98 | 99 | if p['@portid'] != lastportid: 100 | lastportid = p['@portid'] 101 | else: 102 | continue; 103 | 104 | hdhtml_stateico = '' 105 | if p['state']['@state'] == 'closed': 106 | hdhtml_stateico = '' 107 | counters['pc'] = (counters['pc'] + 1) 108 | hostcounters['pc'] = (hostcounters['pc'] + 1) 109 | elif p['state']['@state'] == 'open': 110 | hdhtml_stateico = '' 111 | counters['po'] = (counters['po'] + 1) 112 | hostcounters['po'] = (hostcounters['po'] + 1) 113 | elif p['state']['@state'] == 'filtered': 114 | hdhtml_stateico = '' 115 | counters['pf'] = (counters['pf'] + 1) 116 | hostcounters['pf'] = (hostcounters['pf'] + 1) 117 | 118 | if '@ostype' in p['service']: 119 | if p['service']['@ostype'] in counters['ostype']: 120 | counters['ostype'][p['service']['@ostype']] = (counters['ostype'][p['service']['@ostype']] +1) 121 | else: 122 | counters['ostype'][p['service']['@ostype']] = 1; 123 | 124 | if p['service']['@name'] in counters['ss']: 125 | counters['ss'][p['service']['@name']] = (counters['ss'][p['service']['@name']] + 1) 126 | else: 127 | counters['ss'][p['service']['@name']] = 1 128 | 129 | if p['@portid'] in counters['pi']: 130 | counters['pi'][p['@portid']] = (counters['pi'][p['@portid']] + 1) 131 | else: 132 | counters['pi'][p['@portid']] = 1 133 | 134 | hdhtml_product = '' 135 | if '@product' in p['service']: 136 | hdhtml_product = html.escape(p['service']['@product']) 137 | else: 138 | hdhtml_product = 'No Product' 139 | 140 | hdhtml_version = '' 141 | if '@version' in p['service']: 142 | hdhtml_version = html.escape(p['service']['@version']) 143 | else: 144 | hdhtml_version = 'No Version' 145 | 146 | hdhtml_protocolor = 'grey' 147 | if p['@protocol'] == 'tcp': 148 | hdhtml_protocolor = 'blue' 149 | elif p['@protocol'] == 'udp': 150 | hdhtml_protocolor = 'red' 151 | 152 | hostdetails_html_tr += ''+\ 153 | ' '+p['@protocol']+' / '+p['@portid']+'
'+p['service']['@name']+''+\ 154 | ' '+hdhtml_stateico+' '+p['state']['@state']+''+\ 155 | ' '+hdhtml_product+' / '+hdhtml_version+''+\ 156 | '' 157 | 158 | if 'script' in p: 159 | lastscript = '' 160 | for ii in p['script']: 161 | if type(ii) is dict: 162 | script = ii 163 | else: 164 | script = p['script'] 165 | 166 | if lastscript != script['@id']: 167 | lastscript = script['@id'] 168 | else: 169 | continue 170 | 171 | if script['@output'].replace('\n','') != '' and script['@id'] not in filterscriptid: 172 | portdetails_html_tr += '
'+html.escape(script['@id'])+' - Address: '+html.escape(saddress)+' - Port: '+p['@portid']+'
'+\ 173 | '
'+html.escape(script['@output']).replace('\n','
')+'
' 174 | 175 | portsfound = True 176 | 177 | notesout,notesb64 = '','' 178 | if scanmd5 in noteshost: 179 | if addressmd5 in noteshost[scanmd5]: 180 | notesb64 = noteshost[scanmd5][addressmd5] 181 | notesout = '
'+\ 182 | '

Notes for host '+saddress+'

'+\ 183 | ' '+base64.b64decode(urllib.parse.unquote(notesb64)).decode('ascii')+\ 184 | '
' 185 | 186 | 187 | 188 | if i['status']['@state'] == 'up': 189 | hostdetails_html += '
'+\ 190 | '

'+str(hostcounters['po']+hostcounters['pc']+hostcounters['pf'])+'

TOTAL PORT
'+\ 191 | '

'+str(hostcounters['po'])+'

OPEN PORT
'+\ 192 | '

'+str(hostcounters['pc'])+'

CLOSED PORT
'+\ 193 | '

'+str(hostcounters['pf'])+'

FILTERED PORT
'+\ 194 | '
'+\ 195 | ' '+\ 196 | hostdetails_html_tr+\ 197 | '
Protocol / PortPort StateProduct / Version
'+\ 198 | '
'+portdetails_html_tr+'
'+\ 199 | notesout 200 | 201 | if portsfound is True: 202 | # r['out'] += '1,' 203 | hostdetails += hostdetails_html 204 | #else: 205 | # r['out'] += '0,' 206 | 207 | # this fix single host report 208 | if type(ik) is not dict: 209 | break; 210 | 211 | 212 | html_ports = '' 213 | javascript_ports = '' 214 | for ii in counters['pi']: 215 | html_ports += ''+str(ii)+' ('+str(counters['pi'][ii])+'), ' 216 | javascript_ports += '["'+str(ii)+'", '+str(counters['pi'][ii])+'],' 217 | 218 | html_services = '' 219 | javascript_services = '' 220 | for ii in counters['ss']: 221 | html_services += ''+str(ii)+' ('+str(counters['ss'][ii])+'), ' 222 | javascript_services += '["'+str(ii)+'", '+str(counters['ss'][ii])+'],' 223 | 224 | 225 | r['html'] += ''+\ 226 | '
'+\ 227 | ' '+\ 228 | '

Port Scan Report

'+\ 229 | ' '+html.escape(request.session['scanfile'].replace('.xml','').replace('_',' '))+'
'+\ 230 | '
'+\ 231 | ' '+\ 232 | ' '+\ 233 | ' '+\ 234 | ' '+\ 235 | ' '+\ 236 | ' '+\ 237 | ' '+\ 238 | ' '+\ 239 | '
 
Arguments:'+html.escape(o['@args'])+'
Scan started at:'+html.escape(o['@startstr'])+'
Scan type:'+html.escape(o['scaninfo']['@type'])+'
Nmap version:'+html.escape(o['@version'])+'
'+\ 240 | '
The information contained in these documents is confidential, privileged and only for the information of the intended recipient and may not be used, published or redistributed.
'+\ 241 | '

'+\ 242 | '
'+\ 243 | '

Ports and Services

Ports status and services type
'+\ 244 | '
'+\ 245 | '
HOSTS UP
'+str(counters['hostsup'])+'
'+\ 246 | '
PORTS
'+str(counters['pc']+counters['po']+counters['pf'])+'
'+\ 247 | '
SERVICES
'+str(len(counters['ss'].keys()))+'
'+\ 248 | '
OS
'+str(len(counters['ostype'].keys()))+'
'+\ 249 | '
'+\ 250 | '
'+\ 251 | '
'+\ 252 | '
'+\ 253 | '
'+\ 254 | '
'+\ 255 | '
'+\ 256 | '
'+\ 257 | ' Total Ports
'+str(counters['pc']+counters['po']+counters['pf'])+'

'+\ 258 | ' Open Ports
'+str(counters['po'])+'

'+\ 259 | ' Closed Ports
'+str(counters['pc'])+'

'+\ 260 | ' Filtered Ports
'+str(counters['pf'])+'

'+\ 261 | ' Ports:
'+html_ports[0:-2]+'

'+\ 262 | ' Services:
'+html_services[0:-2]+''+\ 263 | '
'+\ 264 | '
' 265 | 266 | r['html'] += '' 331 | 332 | r['html'] += hostdetails 333 | 334 | r['html'] += ''+\ 335 | '
'+\ 336 | '
'+\ 337 | '
Generated with
'+\ 338 | '
'+\ 339 | ' https://github.com/Rev3rseSecurity/WebMap'+\ 340 | '
'+\ 341 | '
'+\ 342 | '
' 343 | 344 | return render(request, 'nmapreport/report.html', r) 345 | 346 | 347 | -------------------------------------------------------------------------------- /static/async.js: -------------------------------------------------------------------------------- 1 | $(document).ready(function() { 2 | // doc ready 3 | }); 4 | 5 | function genPDF(md5scan) { 6 | if(/^[a-f0-9]{32,32}$/.test(md5scan)) { 7 | $.get('/report/api/pdf/').done(function(data) { 8 | console.log(data); 9 | $('#modal1').css('background-color','#3e3e3e'); 10 | $('#modaltitle').html('Generating PDF Report'); 11 | 12 | $('#modalbody').html('Please wait a few seconds...
'+ 13 | '
'+ 14 | '

You\'ll be redirected to the PDF Report:
') 15 | $('#modal1').modal('open'); 16 | }); 17 | 18 | var pdfcheck = setInterval(function() { 19 | $.get('/static/'+md5scan+'.pdf') 20 | .done(function() { $('#modalbody').append('PDF ready! Please wait...
'); setTimeout(function() { location.href='/static/'+md5scan+'.pdf' }, 3000); }) 21 | .fail(function() { $('#modalbody').append('PDF not ready yet...
'); }); 22 | }, 2000); 23 | } 24 | } 25 | 26 | function removeNotes(hashstr, i) { 27 | $.get('/report/api/rmnotes/'+hashstr+'/').done(function(data) { 28 | if(data['ok'] == 'notes removed') { 29 | $('#noteshost'+i).remove(); 30 | } 31 | }); 32 | } 33 | 34 | function saveNotes() { 35 | nb64 = encodeURIComponent(btoa($('#notes').val())); 36 | csrftoken = $('input[name="csrfmiddlewaretoken"]').val(); 37 | hashstr = $('#hashstr').val(); 38 | console.log(hashstr); 39 | $.post('/report/api/savenotes/', { 40 | 'notes': nb64, 41 | 'csrfmiddlewaretoken': csrftoken, 42 | 'hashstr': hashstr 43 | }).done(function(d) { 44 | console.log(d); 45 | if(typeof(d['ok']) !== 'undefined' && d['ok'] == 'notes saved') { 46 | $('#modalbody').html('Notes successfully saved!
Now this page needs a reload. Please, click on the "Reload" button.'); 47 | $('#modalfooter').html(''); 48 | } 49 | }); 50 | } 51 | 52 | function openNotes(hashstr, notesb64) { 53 | if(/^[a-f0-9]{32,32}$/.test(hashstr)) { 54 | if(notesb64 != '') { 55 | savednotes = atob(decodeURIComponent(notesb64)); 56 | } else { 57 | savednotes = '' 58 | } 59 | $('#modal1').css('background-color','#3e3e3e'); 60 | $('#modaltitle').html('Save Notes'); 61 | $('#modalbody').html( 62 | 'In the text area below, you can insert notes that will appear on the PDF report. '+ 63 | 'All input here are intentionally not sanitized, so you can use HTML markup and JavaScript. '+ 64 | 'Please, keep in mind that you can break the PDF View HTML and, of course, this represents a stored XSS vector.

'+ 65 | ''+ 66 | '

'+ 67 | 'Tips:
'+ 68 | '<b>bold text</b> = bold text
'+ 69 | '<i>italic text</i> = italic text
'+ 70 | '<span class="label green">A green label</span> = A green label
'+ 71 | '<code>monospace font</code> = monospace font' 72 | ); 73 | $('#modalfooter').html(''); 74 | $('#modal1').modal('open'); 75 | } 76 | } 77 | 78 | function apiPortDetails(address, portid) { 79 | $.get('/report/api/'+address+'/'+portid+'/').done(function(data) { 80 | console.log(data); 81 | 82 | $('#modaltitle').html('Port Details: '+$('').text(data['@portid']).html()+' / '+$('').text(data['@protocol']).html()+''); 83 | 84 | tbody = '' 85 | ingorescriptid = { 86 | 'fingerprint-strings':true 87 | } 88 | 89 | console.log(typeof(data['script'])); 90 | if(typeof(data['script']) !== 'undefined') { 91 | if(typeof(data['script']) === 'object' && typeof(data['script']['@id']) === 'undefined') { 92 | for(sid in data['script']) { 93 | if(typeof(ingorescriptid[data['script'][sid]['@id']]) !== 'undefined') { continue; } 94 | tbody += ''+$('').text(data['script'][sid]['@id']).html()+''+$('').text(data['script'][sid]['@output']).html()+'' 95 | } 96 | } else { 97 | if(typeof(ingorescriptid[data['script']['@id']]) === 'undefined') { 98 | tbody += ''+$('').text(data['script']['@id']).html()+''+$('').text(data['script']['@output']).html()+'' 99 | } 100 | } 101 | } else { 102 | tbody += 'nonenone' 103 | } 104 | 105 | $('#modal1').css('background-color','#3e3e3e'); 106 | 107 | $('#modalbody').html(''+tbody+'
Script IDOutput
'); 108 | $('#modalbody').append('
Raw Output:
'+$('').text(JSON.stringify(data, null, 4)).html()+'
'); 109 | $('#modal1').modal('open'); 110 | }); 111 | } 112 | 113 | function removeLabel(type, hashstr, i) { 114 | $.get('/report/api/rmlabel/'+type+'/'+hashstr+'/').done(function(data) { 115 | if(data['ok'] == 'label removed') { 116 | $('#hostlabel'+i).attr("class","") 117 | $('#hostlabel'+i).html(""); 118 | } 119 | }); 120 | } 121 | 122 | function setLabel(type, label, hashstr, i) { 123 | $.get('/report/api/setlabel/'+type+'/'+label+'/'+hashstr+'/').done(function(data) { 124 | console.log(data); 125 | var res = data; 126 | var color = 'grey'; var margin = '10px'; 127 | if(res['ok'] == 'label set') { 128 | switch(res['label']) { 129 | case 'Vulnerable': color = 'red'; margin = '10px'; break; 130 | case 'Critical': color = 'black'; margin = '22px'; break; 131 | case 'Warning': color = 'orange'; margin = '28px'; break; 132 | case 'Checked': color = 'blue'; margin = '28px'; break; 133 | } 134 | $('#hostlabel'+i).css("margin-left", margin) 135 | $('#hostlabel'+i).attr("class","") 136 | $('#hostlabel'+i).addClass('rightlabel'); 137 | $('#hostlabel'+i).addClass(color); 138 | $('#hostlabel'+i).html(res['label']); 139 | } 140 | }); 141 | } 142 | -------------------------------------------------------------------------------- /static/custom.css: -------------------------------------------------------------------------------- 1 | body { 2 | background-color:#333333; 3 | color:#eeeeee; 4 | } 5 | 6 | .scantitle { 7 | padding-left:20px; 8 | } 9 | 10 | .rightlabel { 11 | border-radius:2px; 12 | font-size:10px; 13 | font-family:monospace; 14 | text-align:center; 15 | position:absolute; 16 | padding-left:8px; 17 | padding-right:8px; 18 | margin-left:10px; 19 | margin-top:-20px; 20 | } 21 | 22 | 23 | .leftlabel { 24 | border-radius:2px; 25 | font-size:10px; 26 | font-family:monospace; 27 | text-align:center; 28 | position:absolute; 29 | padding-left:8px; 30 | padding-right:8px; 31 | margin-left:-29px; 32 | margin-top:-14px; 33 | } 34 | 35 | .small { 36 | font-size:12px; 37 | } 38 | 39 | .tmlabel { 40 | font-family: monospace; 41 | font-size: 12px; 42 | background-color: #999; 43 | border-radius: 4px; 44 | padding: 2px; 45 | padding-left: 4px; 46 | padding-right: 4px; 47 | color: #fff; 48 | } 49 | 50 | .perco { 51 | border-radius: 2px; 52 | background-color:#090; 53 | font-size:10px; 54 | font-family:monospace; 55 | color:#eee; 56 | text-align:center; 57 | } 58 | 59 | .percf { 60 | border-radius: 2px; 61 | background-color:#999; 62 | font-size:10px; 63 | font-family:monospace; 64 | color:#eee; 65 | text-align:center; 66 | } 67 | 68 | .percc { 69 | border-radius: 2px; 70 | background-color:#f00; 71 | font-size:10px; 72 | font-family:monospace; 73 | color:#eee; 74 | text-align:center; 75 | } 76 | 77 | .label { 78 | padding:6px; 79 | border-radius:4px; 80 | font-size:12px; 81 | color:#fff; 82 | } 83 | -------------------------------------------------------------------------------- /static/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/punitdarji/WebMap/ff553249f2b3f62d0e414f4c7f4991a4f2a65b16/static/logo.png -------------------------------------------------------------------------------- /static/logoblack.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/punitdarji/WebMap/ff553249f2b3f62d0e414f4c7f4991a4f2a65b16/static/logoblack.png -------------------------------------------------------------------------------- /static/logomin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/punitdarji/WebMap/ff553249f2b3f62d0e414f4c7f4991a4f2a65b16/static/logomin.png -------------------------------------------------------------------------------- /static/report.css: -------------------------------------------------------------------------------- 1 | 2 | body { 3 | // font-weight:300; 4 | } 5 | 6 | b { 7 | font-weight:600; 8 | } 9 | 10 | h1 { 11 | font-weight:700; 12 | } 13 | 14 | .subtitle { 15 | margin-top:-20px; 16 | color:#999; 17 | font-size:24px; 18 | } 19 | 20 | .small { 21 | font-size:12px; 22 | color:#666; 23 | } 24 | 25 | .margintb { 26 | margin-top:60px; 27 | margin-bottom:60px; 28 | } 29 | 30 | .bleft { 31 | border-left:solid #999 1px; 32 | } 33 | 34 | .extrainfo { 35 | border-top: solid #ccc 1px; 36 | padding:10px; 37 | margin-bottom:10px; 38 | font-family:monospace; 39 | } 40 | 41 | .extratitle { 42 | font-size: 20px; 43 | margin-top: 40px; 44 | } 45 | 46 | .label { 47 | padding:6px; 48 | border-radius:4px; 49 | font-size:12px; 50 | color:#fff; 51 | } 52 | -------------------------------------------------------------------------------- /templates/nmapreport/index.html: -------------------------------------------------------------------------------- 1 | {% load static %} 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 31 | 32 | 33 |
34 | 35 | 36 | {{ topcontainer|safe }} 37 |
38 |
39 | {{ scaninfo|safe }} 40 |
41 |
42 | Scan Detailsclose 43 | {{ scandetails|safe }} 44 |
45 |
46 | 47 | 48 |
{{ pretable|safe }}
49 | 50 | 51 |
52 |
53 | 54 | 55 | {{ trhead|safe }} 56 | 57 | 58 | {{ trhost|safe }} 59 | 60 |
61 |
62 |
63 | 64 | {{ table|safe }} 65 | 66 | 67 |
 68 | 				{{ out|safe }}
 69 | 			
70 |
71 | 72 | 73 | 83 | 84 | 85 | 98 | 99 | 100 | 101 | -------------------------------------------------------------------------------- /templates/nmapreport/report.html: -------------------------------------------------------------------------------- 1 | {% load static %} 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | {{ html|safe }} 16 | 17 |
 {{ out|safe }}
18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /urls.py: -------------------------------------------------------------------------------- 1 | from django.urls import path 2 | from . import views, api, pdf 3 | 4 | urlpatterns = [ 5 | path('', views.index, name='index'), 6 | path('setscanfile/', views.setscanfile, name='setscanfile'), 7 | path('
/', views.details, name='details'), 8 | path('port//', views.port, name='port'), 9 | path('service//', views.index, name='service'), 10 | path('portid//', views.index, name='portid'), 11 | path('api/setlabel//