├── .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 |
'
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 += ''+\
173 | ''
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 | '
Protocol / Port Port State Product / Version '+\
196 | hostdetails_html_tr+\
197 | '
'+\
198 | ''+\
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 | '
'
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('
Reload ');
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('
Save ');
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 += '
none none '
103 | }
104 |
105 | $('#modal1').css('background-color','#3e3e3e');
106 |
107 | $('#modalbody').html('
');
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 |
20 |
21 |
22 |
23 |
24 |
28 |
29 |
30 |
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////', api.label, name='api_label'),
12 | path('api/rmlabel///', api.rmlabel, name='api_rmlabel'),
13 | path('api/pdf/', api.genPDF, name='genPDF'),
14 | path('api/savenotes/', api.saveNotes, name='genPDF'),
15 | path('api/rmnotes//', api.rmNotes, name='api_rmnotes'),
16 | path('api///', api.port_details, name='api_port'),
17 | path('view/pdf/', pdf.reportPDFView, name='reportPDFView')
18 | ]
19 |
--------------------------------------------------------------------------------
/views.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 |
7 | def setscanfile(request, scanfile):
8 | xmlfiles = os.listdir('/opt/xml')
9 |
10 | for i in xmlfiles:
11 | if i == scanfile:
12 | request.session['scanfile'] = i
13 | break
14 |
15 | if scanfile == 'unset':
16 | if 'scanfile' in request.session:
17 | del(request.session['scanfile'])
18 |
19 | return render(request, 'nmapreport/index.html', { 'out': '', 'table': '', 'scaninfo': '', 'scandetails': '', 'trhost': '' })
20 |
21 |
22 | def port(request, port):
23 | return render(request, 'nmapreport/index.html', { 'out': '', 'table': '', 'scaninfo': '', 'scandetails': '', 'trhost': '' })
24 |
25 | def details(request, address):
26 | r = {}
27 | oo = xmltodict.parse(open('/opt/xml/'+request.session['scanfile'], 'r').read())
28 | r['out2'] = json.dumps(oo['nmaprun'], indent=4)
29 | o = json.loads(r['out2'])
30 |
31 | r['trhost'] = ''
32 | v,e,z,h = '','','',''
33 | pc,po,pf=0,0,0
34 |
35 | scanmd5 = hashlib.md5(str(request.session['scanfile']).encode('utf-8')).hexdigest()
36 | addressmd5 = hashlib.md5(str(address).encode('utf-8')).hexdigest()
37 |
38 | # collect all labels in labelhost dict
39 | labelhost = {}
40 | labelfiles = os.listdir('/opt/notes')
41 | for lf in labelfiles:
42 | m = re.match('^('+scanmd5+')_([a-z0-9]{32,32})\.host\.label$', lf)
43 | if m is not None:
44 | if m.group(1) not in labelhost:
45 | labelhost[m.group(1)] = {}
46 | labelhost[m.group(1)][m.group(2)] = open('/opt/notes/'+lf, 'r').read()
47 |
48 | # collect all notes in noteshost dict
49 | noteshost = {}
50 | notesfiles = os.listdir('/opt/notes')
51 | for nf in notesfiles:
52 | m = re.match('^('+scanmd5+')_([a-z0-9]{32,32})\.notes$', nf)
53 | if m is not None:
54 | if m.group(1) not in noteshost:
55 | noteshost[m.group(1)] = {}
56 | noteshost[m.group(1)][m.group(2)] = open('/opt/notes/'+nf, 'r').read()
57 |
58 |
59 | r['trhead'] = 'Port Product / Version Extra Info '
60 | pel=0
61 | for ik in o['host']:
62 | # this fix single host report
63 | if type(ik) is dict:
64 | i = ik
65 | else:
66 | i = o['host']
67 |
68 | if '@addr' in i['address']:
69 | saddress = i['address']['@addr']
70 | elif type(i['address']) is list:
71 | for ai in i['address']:
72 | if ai['@addrtype'] == 'ipv4':
73 | saddress = ai['@addr']
74 |
75 |
76 | if str(saddress) == address:
77 | #r['out'] = json.dumps(i, indent=4)
78 | h = 'No Hostname '
79 | if 'hostnames' in i:
80 | if type(i['hostnames']) is dict and 'hostname' in i['hostnames']:
81 | if '@name' in i['hostnames']['hostname']:
82 | h = ''+i['hostnames']['hostname']['@name']+' '
83 |
84 | labelout = ' '
85 | if scanmd5 in labelhost:
86 | if addressmd5 in labelhost[scanmd5]:
87 | labelcolor = labelToColor(labelhost[scanmd5][addressmd5])
88 | labelmargin = labelToMargin(labelhost[scanmd5][addressmd5])
89 | labelout = ''+html.escape(labelhost[scanmd5][addressmd5])+' '
90 |
91 | r['scaninfo'] = ''+\
92 | '
Host Details:
'+html.escape(address)+' '+h+labelout+''+\
93 | '
'+\
94 | '
'+\
95 | '
'+\
96 | '
'
97 |
98 | rmdupl = {}
99 | for pobj in i['ports']['port']:
100 | if type(pobj) is dict:
101 | p = pobj
102 | else:
103 | p = i['ports']['port']
104 |
105 | if p['@portid'] in rmdupl:
106 | continue
107 |
108 | rmdupl[p['@portid']] = 1
109 |
110 | if p['state']['@state'] == 'closed':
111 | pc = (pc + 1)
112 | elif p['state']['@state'] == 'open':
113 | po = (po + 1)
114 | elif p['state']['@state'] == 'filtered':
115 | pf = (pf + 1)
116 |
117 | pel = (pel + 1)
118 | oshtml = ''
119 | if '@ostype' in p['service']:
120 | oshtml = 'Operating System '+html.escape(p['service']['@ostype'])+'
'
121 |
122 | so = ''
123 | if 'script' in p:
124 | if '@id' in p['script']:
125 | if p['script']['@id'] != 'fingerprint-strings':
126 | so += 'script output '+html.escape(p['script']['@id'])+' '+html.escape(p['script']['@output'])+'
'
127 | else:
128 | for sosc in p['script']:
129 | if '@id' in sosc:
130 | if sosc['@id'] != 'fingerprint-strings':
131 | so += 'script output '+html.escape(sosc['@id'])+' '+html.escape(sosc['@output'])+'
'
132 |
133 | v,z,e = '','','N/A '
134 | if p['state']['@state'] == 'open':
135 | if '@version' in p['service']:
136 | v = p['service']['@version']
137 | else:
138 | v = 'No Version '
139 |
140 | if '@product' in p['service']:
141 | z = p['service']['@product']
142 | else:
143 | z = 'No Product '
144 |
145 | if '@extrainfo' in p['service']:
146 | e = p['service']['@extrainfo']
147 |
148 | cpe = ''
149 | if 'cpe' in p['service']:
150 | if type(p['service']['cpe']) is list:
151 | for cpei in p['service']['cpe']:
152 | cpe += ''+html.escape(cpei)+' '
153 | else:
154 | cpe = ''+html.escape(p['service']['cpe'])+' '
155 |
156 |
157 | r['trhost'] += ''+\
158 | ''+p['service']['@name']+' '+\
159 | ''+p['@protocol']+' / '+p['@portid']+' '+\
160 | ' '+\
161 | ''+z+' / '+v+'State: '+p['state']['@state']+' Reason: '+p['state']['@reason']+' '+\
162 | ''+e+' '+cpe+' '+\
163 | 'arrow_drop_down '+\
168 | 'receipt '+\
169 | ' '
170 | elif p['state']['@state'] == 'filtered':
171 | r['trhost'] += ''+p['@protocol']+' / '+p['@portid']+' '+\
172 | ''+p['service']['@name']+' '+\
173 | 'State: filtered Reason: '+p['state']['@reason']+' '+\
174 | 'receipt '
175 | else:
176 | r['trhost'] += ''+p['@protocol']+' / '+p['@portid']+' '+\
177 | ''+p['service']['@name']+' '+\
178 | 'State: '+p['state']['@state']+' Reason: '+p['state']['@reason']+' '+\
179 | 'receipt '
180 |
181 | # this fix single host report
182 | if type(ik) is not dict:
183 | break;
184 |
185 | notesout,notesb64,removenotes = '','',''
186 | if scanmd5 in noteshost:
187 | if addressmd5 in noteshost[scanmd5]:
188 | notesb64 = noteshost[scanmd5][addressmd5]
189 | r['table'] = ''+\
190 | '
Notes '+\
191 | ' '+base64.b64decode(urllib.parse.unquote(notesb64)).decode('ascii')+\
192 | ' '+\
193 | '
'
194 |
195 | #notesout = ' contains notes '
196 | #removenotes = 'Remove notes '
197 |
198 |
199 |
200 |
201 | r['pretable'] = ''
214 |
215 | return render(request, 'nmapreport/index.html', r)
216 |
217 | def index(request, filterservice="", filterportid=""):
218 | r = {}
219 |
220 | if 'scanfile' in request.session:
221 | oo = xmltodict.parse(open('/opt/xml/'+request.session['scanfile'], 'r').read())
222 | r['out2'] = json.dumps(oo['nmaprun'], indent=4)
223 | o = json.loads(r['out2'])
224 | else:
225 | # no file selected
226 | xmlfiles = os.listdir('/opt/xml')
227 |
228 |
229 | r['table'] = ''+\
230 | ' Put your Nmap XML files in
/opt/xml/ directory, example:
'+\
231 | '
nmap -A -T4 -oX myscan.xml 192.168.1.0/24 '+\
232 | ' mv myscan.xml <docker webmap xml dir> '+\
233 | ' # or you can copy myscan.xml to the webmap container: '+\
234 | ' docker cp myscan.xml webmap:/opt/xml/
'+\
235 | '
'+\
236 | ''
237 |
238 | r['table'] += ''+\
239 | '
'+\
240 | '
'+\
241 | '
Made with by Andrea theMiddle Menin '+\
242 | '
'+\
243 | '
'+\
248 | '
Follow me: '+\
249 | '
'+\
250 | '
'+\
251 | '
'+\
252 | '
'
253 |
254 |
255 | r['scaninfo'] = 'Select a Nmap XML file Nmap XML files: '+ str(len(xmlfiles)) +'
'
256 |
257 | r['trhost'] = ''
258 | r['trhead'] = 'Filename Scan Start Time Hosts '
259 |
260 | for i in xmlfiles:
261 | oo = xmltodict.parse(open('/opt/xml/'+i, 'r').read())
262 | r['out2'] = json.dumps(oo['nmaprun'], indent=4)
263 | o = json.loads(r['out2'])
264 |
265 | if type(o['host']) is not dict:
266 | hostnum = str(len(o['host']))
267 | else:
268 | hostnum = '1'
269 |
270 | r['trhost'] += ''+\
271 | ' '+html.escape(i)+' '+\
272 | ' '+html.escape(o['@startstr'])+' '+\
273 | ' '+hostnum+' '+\
274 | ' view '+\
275 | ' '
276 |
277 | # r['out'] = os.listdir('/opt/xml')
278 | return render(request, 'nmapreport/index.html', r)
279 |
280 | scanmd5 = hashlib.md5(str(request.session['scanfile']).encode('utf-8')).hexdigest()
281 |
282 | r['topcontainer'] = ''
291 |
292 | # collect all labels in labelhost dict
293 | labelhost = {}
294 | labelfiles = os.listdir('/opt/notes')
295 | for lf in labelfiles:
296 | m = re.match('^('+scanmd5+')_([a-z0-9]{32,32})\.host\.label$', lf)
297 | if m is not None:
298 | if m.group(1) not in labelhost:
299 | labelhost[m.group(1)] = {}
300 | labelhost[m.group(1)][m.group(2)] = open('/opt/notes/'+lf, 'r').read()
301 |
302 | # collect all notes in noteshost dict
303 | noteshost = {}
304 | notesfiles = os.listdir('/opt/notes')
305 | for nf in notesfiles:
306 | m = re.match('^('+scanmd5+')_([a-z0-9]{32,32})\.notes$', nf)
307 | if m is not None:
308 | if m.group(1) not in noteshost:
309 | noteshost[m.group(1)] = {}
310 | noteshost[m.group(1)][m.group(2)] = open('/opt/notes/'+nf, 'r').read()
311 |
312 | tableout = ''
313 | hostsup = 0
314 | ports = { 'open': 0, 'closed': 0, 'filtered': 0 }
315 | allostypelist = {}
316 | sscount = {}
317 | picount = {}
318 | hostindex = 1
319 |
320 | r['trhost'] = ''
321 | r['trhead'] = 'Host Port State Tot Ports Services Ports '
322 |
323 | for ik in o['host']:
324 |
325 | # this fix single host report
326 | if type(ik) is dict:
327 | i = ik
328 | else:
329 | i = o['host']
330 |
331 | hostname = ''
332 |
333 | if 'hostnames' in i and type(i['hostnames']) is dict:
334 | hostname += ''+str(i['hostnames']['hostname']['@name'])+' '
335 |
336 | if i['status']['@state'] == 'up':
337 | hostsup = (hostsup + 1)
338 |
339 | po,pc,pf = 0,0,0
340 | ss,pp,ost = {},{},{}
341 | lastportid = 0
342 |
343 | striggered = False
344 | if 'ports' in i and 'port' in i['ports']:
345 | for pobj in i['ports']['port']:
346 | if type(pobj) is dict:
347 | p = pobj
348 | else:
349 | p = i['ports']['port']
350 |
351 | if lastportid == p['@portid']:
352 | continue
353 | else:
354 | lastportid = p['@portid']
355 |
356 | if filterservice != "" and p['service']['@name'] == filterservice:
357 | striggered = True
358 |
359 | if filterportid != "" and p['@portid'] == filterportid:
360 | striggered = True
361 |
362 | ss[p['service']['@name']] = p['service']['@name']
363 | pp[p['@portid']] = p['@portid']
364 |
365 | if '@ostype' in p['service']:
366 | if p['service']['@ostype'] in allostypelist:
367 | allostypelist[p['service']['@ostype']] = (allostypelist[p['service']['@ostype']] +1)
368 | else:
369 | allostypelist[p['service']['@ostype']] = 1;
370 |
371 | ost[p['service']['@ostype']] = p['service']['@ostype']
372 |
373 | if p['service']['@name'] in sscount:
374 | sscount[p['service']['@name']] = (sscount[p['service']['@name']] + 1)
375 | else:
376 | sscount[p['service']['@name']] = 1
377 |
378 | if p['@portid'] in picount:
379 | picount[p['@portid']] = (picount[p['@portid']] + 1)
380 | else:
381 | picount[p['@portid']] = 1
382 |
383 |
384 | if p['state']['@state'] == 'closed':
385 | ports['closed'] = (ports['closed'] + 1)
386 | pc = (pc + 1)
387 | elif p['state']['@state'] == 'open':
388 | ports['open'] = (ports['open'] + 1)
389 | po = (po + 1)
390 | elif p['state']['@state'] == 'filtered':
391 | ports['filtered'] = (ports['filtered'] + 1)
392 | pf = (pf + 1)
393 |
394 | services = ''
395 | for s in ss:
396 | if filterservice != ss[s]:
397 | services += ''+ss[s]+' , '
398 | else:
399 | services += ''+ss[s]+' , '
400 |
401 | ostype = ''
402 | for oty in ost:
403 | ostype += ' '+ost[oty].lower()+' '
404 |
405 | tdports = ''
406 | for kp in pp:
407 | if filterportid != pp[kp]:
408 | tdports += ''+pp[kp]+' , '
409 | else:
410 | tdports += ''+pp[kp]+' , '
411 |
412 | poclass = ''
413 | if po == 0:
414 | poclass = 'zeroportopen'
415 |
416 | if '@addr' in i['address']:
417 | address = i['address']['@addr']
418 | elif type(i['address']) is list:
419 | for ai in i['address']:
420 | if ai['@addrtype'] == 'ipv4':
421 | address = ai['@addr']
422 |
423 | addressmd5 = hashlib.md5(str(address).encode('utf-8')).hexdigest()
424 | labelout = ' '
425 | if scanmd5 in labelhost:
426 | if addressmd5 in labelhost[scanmd5]:
427 | labelcolor = labelToColor(labelhost[scanmd5][addressmd5])
428 | labelmargin = labelToMargin(labelhost[scanmd5][addressmd5])
429 | labelout = ''+html.escape(labelhost[scanmd5][addressmd5])+' '
430 |
431 | notesout,notesb64,removenotes = '','',''
432 | if scanmd5 in noteshost:
433 | if addressmd5 in noteshost[scanmd5]:
434 | notesb64 = noteshost[scanmd5][addressmd5]
435 | notesout = ' contains notes '
436 | removenotes = 'Remove notes '
437 |
438 | if (filterservice != "" and striggered is True) or (filterportid != "" and striggered is True) or (filterservice == "" and filterportid == ""):
439 | portstateout = ' '
442 |
443 | if (filterservice != "" and striggered is True):
444 | portstateout = ' '
447 |
448 | r['trhost'] += ''+\
449 | ' '+str(hostindex)+' '+\
450 | ' '+ostype+' '+\
451 | ' '+str(address)+' '+hostname+''+\
452 | notesout+\
453 | ' '+\
454 | portstateout+\
455 | ' '+str((po + pf + pc))+' '+\
456 | ' '+str(services[0:-2])+' '+\
457 | ' '+str(tdports[0:-2])+' '+\
458 | ' '+\
459 | ' '+\
460 | ' Vulnerable '+\
461 | ' Critical '+\
462 | ' Warning '+\
463 | ' Checked '+\
464 | ' Remove label '+\
465 | ' '+\
466 | ' Insert notes '+\
467 | ' '+removenotes+\
468 | ' '+\
469 | labelout+\
470 | ' arrow_drop_down '+\
471 | ' '+\
472 | ' '
473 | hostindex = (hostindex + 1)
474 |
475 | # this fix single host report
476 | if type(ik) is not dict:
477 | break;
478 |
479 | totports = (ports['open']+ports['closed']+ports['filtered'])
480 | if filterservice == "" and filterportid == "":
481 | scaninfobox2 = ' '
482 | scaninfobox3 = ' '
483 | else:
484 | scaninfobox2 = ''+\
485 | ' Filter port / service: '+html.escape(filterportid+filterservice)+' '+\
486 | ' Total Ports: '+str(totports)+' '+\
487 | ' Open Ports: '+str(ports['open'])+' '+\
488 | ' Closed Ports: '+str(ports['closed'])+' '+\
489 | ' Filtered Ports: '+str(ports['filtered'])+' '
490 | scaninfobox3 = '
'
491 |
492 | r['scaninfo'] = ''+\
493 | ''+\
494 | '
Scan Information
'+\
495 | '
Ports Status
'+\
496 | '
Top Ports / Services
'+\
497 | '
'+\
498 | ''+\
499 | '
'+\
500 | '
Start: '+o['@startstr']+'
'+\
501 | '
Scan Type: '+o['scaninfo']['@type']+'
'+\
502 | '
Scan Protocol: '+o['scaninfo']['@protocol']+'
'+\
503 | '
Nmap Command: view details '+\
504 | '
'+\
505 | '
'+\
506 | scaninfobox2+\
507 | '
'+\
508 | '
'+\
509 | scaninfobox3+\
510 | '
'+\
511 | '
'
512 |
513 | r['scandetails'] = ''+\
514 | '
'+o['@args']+'
'+\
515 | '
'+\
516 | ' version: '+o['@version']+' '+\
517 | ' xmloutputversion: '+o['@xmloutputversion']+' '+\
518 | '
'+\
519 | '
'
520 |
521 |
522 | allss = ''
523 | allsslabels = ''
524 | allssdata = ''
525 | for i in sorted(sscount, key=sscount.__getitem__, reverse=True):
526 | if filterservice != i:
527 | allss += ''+html.escape(i)+'('+str(sscount[i])+') , '
528 | else:
529 | allss += ''+html.escape(i)+' , '
530 |
531 | allsslabels += '"'+html.escape(i)+'", '
532 | allssdata += ''+str(sscount[i])+','
533 |
534 | allpilabels = ''
535 | allpidata = ''
536 | allpilinks = ''
537 | allpic = 1
538 | for i in sorted(picount, key=picount.__getitem__, reverse=True):
539 | allpilinks += ''+str(i)+' , '
540 | if allpic <= 5:
541 | allpilabels += '"'+html.escape(i)+'", '
542 | allpidata += ''+str(picount[i])+','
543 | allpic = (allpic + 1)
544 |
545 | allostypelinks = ''
546 | for i in sorted(allostypelist, key=allostypelist.__getitem__, reverse=True):
547 | allostypelinks += ''+str(i)+' , '
548 |
549 |
550 | r['pretable'] = ''
551 | if filterservice == "" and filterportid == "":
552 | r['pretable'] += ''+\
553 | '
'+\
554 | '
'+str(ports['open'])+' OPEN PORTS '+\
555 | '
'+str(ports['closed'])+' CLOSED PORTS '+\
556 | '
'+str(ports['filtered'])+' FILTERED PORTS '+\
557 | '
'+\
558 | ''+\
559 | '
'+\
560 | '
'+\
561 | '
Services:
Services:
'+\
562 | '
'+\
563 | ' '+allss[0:-2]+' '+\
564 | ' Top 10 Ports: '+allpilinks[0:-2]+' '+\
565 | ' OS Type List: '+allostypelinks[0:-2]+' '+\
566 | '
'+\
567 | '
'+\
568 | '
'+\
569 | '
'+\
570 | '
'
571 |
572 | r['pretable'] += ''
588 |
589 | r['pretable'] += ''
603 | #r['pretable'] += 'Hide/Show hosts with no open ports '+\
604 | #' PDF '+\
605 | #' '
606 |
607 | return render(request, 'nmapreport/index.html', r)
608 |
609 |
610 |
--------------------------------------------------------------------------------