diff --git a/web/api/serializers.py b/web/api/serializers.py index bf4d1a28..6bf15711 100644 --- a/web/api/serializers.py +++ b/web/api/serializers.py @@ -7,6 +7,19 @@ from scanEngine.models import * from django.db.models import F, JSONField, Value +class OnlySubdomainNameSerializer(serializers.ModelSerializer): + class Meta: + model = Subdomain + fields = ['name', 'id'] + + +class ScanHistorySerializer(serializers.ModelSerializer): + class Meta: + model = ScanHistory + fields = '__all__' + depth = 1 + + class OrganizationSerializer(serializers.ModelSerializer): class Meta: diff --git a/web/api/urls.py b/web/api/urls.py index ce7d9f07..8baa76ce 100644 --- a/web/api/urls.py +++ b/web/api/urls.py @@ -6,9 +6,9 @@ from .views import * app_name = 'api' router = routers.DefaultRouter() -router.register(r'scanHistory', SubdomainViewset) +router.register(r'listDatatableSubdomain', SubdomainDatatableViewSet) -router.register(r'listSubdomains', ListSubdomainsViewSet) +router.register(r'listSubdomains', SubdomainsViewSet) router.register(r'listEndpoints', EndPointViewSet) @@ -44,6 +44,7 @@ urlpatterns = [ path('queryTargetsInOrganization/', ListTargetsInOrganization.as_view(), name='queryTargetsInOrganization'), path('listOrganizations/', ListOrganizations.as_view(), name='listOrganizations'), path('listEngines/', ListEngines.as_view(), name='listEngines'), + path('listScanHistory/', ListScanHistory.as_view(), name='listScanHistory'), ] urlpatterns += router.urls diff --git a/web/api/views.py b/web/api/views.py index ce3da009..a9941470 100644 --- a/web/api/views.py +++ b/web/api/views.py @@ -17,6 +17,14 @@ from startScan.models import * from targetApp.models import * +class ListScanHistory(APIView): + def get(self, request, format=None): + req = self.request + scan_history = ScanHistory.objects.all().order_by('-start_scan_date') + scan_history = ScanHistorySerializer(scan_history, many=True) + return Response({'scan_histories': scan_history.data}) + + class ListEngines(APIView): def get(self, request, format=None): req = self.request @@ -207,27 +215,25 @@ class ListSubdomains(APIView): ip_address = req.query_params.get('ip_address') port = req.query_params.get('port') tech = req.query_params.get('tech') - if scan_id and ip_address: - subdomain = Subdomain.objects.filter( - ip_addresses__address=ip_address).filter( - scan_history__id=scan_id) - serializer = SubdomainSerializer(subdomain, many=True) - return Response({"subdomains": serializer.data}) - elif scan_id and tech: - subdomain = Subdomain.objects.filter( - technologies__name=tech).filter( - scan_history__id=scan_id) - serializer = SubdomainSerializer(subdomain, many=True) - return Response({"subdomains": serializer.data}) - elif scan_id and port: - subdomain = Subdomain.objects.filter( + subdomain_query = Subdomain.objects.filter(scan_history__id=scan_id) + + if ip_address: + subdomain_query = subdomain_query.filter(ip_addresses__address=ip_address) + + if tech: + subdomain_query = subdomain_query.filter(technologies__name=tech) + + if port: + subdomain_query = subdomain_query.filter( ip_addresses__in=IpAddress.objects.filter( ports__in=Port.objects.filter( - number=port))).filter( - scan_history=scan_id) - serializer = SubdomainSerializer(subdomain, many=True) - return Response({"subdomains": serializer.data}) + number=port))) + if 'no_lookup_interesting' in req.query_params: + serializer = OnlySubdomainNameSerializer(subdomain_query, many=True) + else: + serializer = SubdomainSerializer(subdomain_query, many=True) + return Response({"subdomains": serializer.data}) class ListOsintUsers(APIView): def get(self, request, format=None): @@ -299,7 +305,7 @@ class IpAddressViewSet(viewsets.ModelViewSet): queryset, self.request, view=self) -class ListSubdomainsViewSet(viewsets.ModelViewSet): +class SubdomainsViewSet(viewsets.ModelViewSet): queryset = Subdomain.objects.none() serializer_class = SubdomainSerializer @@ -500,7 +506,7 @@ class InterestingEndpointViewSet(viewsets.ModelViewSet): queryset, self.request, view=self) -class SubdomainViewset(viewsets.ModelViewSet): +class SubdomainDatatableViewSet(viewsets.ModelViewSet): queryset = Subdomain.objects.none() serializer_class = SubdomainSerializer diff --git a/web/reNgine/settings.py b/web/reNgine/settings.py index e4bcbab4..92364eaf 100644 --- a/web/reNgine/settings.py +++ b/web/reNgine/settings.py @@ -33,6 +33,7 @@ INSTALLED_APPS = [ 'targetApp.apps.TargetappConfig', 'scanEngine.apps.ScanengineConfig', 'startScan.apps.StartscanConfig', + 'recon_note.apps.ReconNoteConfig', 'django_ace', 'django_celery_beat', 'mathfilters', diff --git a/web/recon_note/__init__.py b/web/recon_note/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/web/recon_note/admin.py b/web/recon_note/admin.py new file mode 100644 index 00000000..5f4b6b71 --- /dev/null +++ b/web/recon_note/admin.py @@ -0,0 +1,4 @@ +from django.contrib import admin +from recon_note.models import * + +admin.site.register(TodoNote) diff --git a/web/recon_note/apps.py b/web/recon_note/apps.py new file mode 100644 index 00000000..951dfce1 --- /dev/null +++ b/web/recon_note/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class ReconNoteConfig(AppConfig): + default_auto_field = 'django.db.models.BigAutoField' + name = 'recon_note' diff --git a/web/recon_note/migrations/0001_initial.py b/web/recon_note/migrations/0001_initial.py new file mode 100644 index 00000000..06a4c4cf --- /dev/null +++ b/web/recon_note/migrations/0001_initial.py @@ -0,0 +1,26 @@ +# Generated by Django 3.2.4 on 2021-07-22 06:09 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ('startScan', '0058_delete_todonote'), + ] + + operations = [ + migrations.CreateModel( + name='TodoNote', + fields=[ + ('id', models.AutoField(primary_key=True, serialize=False)), + ('title', models.CharField(blank=True, max_length=1000, null=True)), + ('description', models.TextField(blank=True, null=True)), + ('scan_history', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='startScan.scanhistory')), + ('subdomain', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='startScan.subdomain')), + ], + ), + ] diff --git a/web/recon_note/migrations/0002_auto_20210722_0611.py b/web/recon_note/migrations/0002_auto_20210722_0611.py new file mode 100644 index 00000000..ed83df47 --- /dev/null +++ b/web/recon_note/migrations/0002_auto_20210722_0611.py @@ -0,0 +1,23 @@ +# Generated by Django 3.2.4 on 2021-07-22 06:11 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('recon_note', '0001_initial'), + ] + + operations = [ + migrations.AddField( + model_name='todonote', + name='is_done', + field=models.BooleanField(default=False), + ), + migrations.AddField( + model_name='todonote', + name='is_important', + field=models.BooleanField(default=False), + ), + ] diff --git a/web/recon_note/migrations/__init__.py b/web/recon_note/migrations/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/web/recon_note/models.py b/web/recon_note/models.py new file mode 100644 index 00000000..f80f63f2 --- /dev/null +++ b/web/recon_note/models.py @@ -0,0 +1,21 @@ +from django.db import models +from startScan.models import * + +class TodoNote(models.Model): + id = models.AutoField(primary_key=True) + title = models.CharField(max_length=1000, null=True, blank=True) + description = models.TextField(null=True, blank=True) + scan_history = models.ForeignKey( + ScanHistory, + on_delete=models.CASCADE, + null=True, + blank=True + ) + subdomain = models.ForeignKey( + Subdomain, + on_delete=models.CASCADE, + null=True, + blank=True + ) + is_done = models.BooleanField(default=False) + is_important = models.BooleanField(default=False) diff --git a/web/recon_note/tests.py b/web/recon_note/tests.py new file mode 100644 index 00000000..7ce503c2 --- /dev/null +++ b/web/recon_note/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/web/recon_note/views.py b/web/recon_note/views.py new file mode 100644 index 00000000..91ea44a2 --- /dev/null +++ b/web/recon_note/views.py @@ -0,0 +1,3 @@ +from django.shortcuts import render + +# Create your views here. diff --git a/web/scanEngine/models.py b/web/scanEngine/models.py index c5c46800..d6f58d6e 100644 --- a/web/scanEngine/models.py +++ b/web/scanEngine/models.py @@ -1,6 +1,5 @@ from django.db import models - class EngineType(models.Model): id = models.AutoField(primary_key=True) engine_name = models.CharField(max_length=200) diff --git a/web/startScan/migrations/0057_todonote.py b/web/startScan/migrations/0057_todonote.py new file mode 100644 index 00000000..763178d0 --- /dev/null +++ b/web/startScan/migrations/0057_todonote.py @@ -0,0 +1,24 @@ +# Generated by Django 3.2.4 on 2021-07-22 05:59 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('startScan', '0056_email_password'), + ] + + operations = [ + migrations.CreateModel( + name='TodoNote', + fields=[ + ('id', models.AutoField(primary_key=True, serialize=False)), + ('title', models.CharField(blank=True, max_length=1000, null=True)), + ('description', models.TextField(blank=True, null=True)), + ('scan_history', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='startScan.scanhistory')), + ('subdomain', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='startScan.subdomain')), + ], + ), + ] diff --git a/web/startScan/migrations/0058_delete_todonote.py b/web/startScan/migrations/0058_delete_todonote.py new file mode 100644 index 00000000..64317842 --- /dev/null +++ b/web/startScan/migrations/0058_delete_todonote.py @@ -0,0 +1,16 @@ +# Generated by Django 3.2.4 on 2021-07-22 06:04 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('startScan', '0057_todonote'), + ] + + operations = [ + migrations.DeleteModel( + name='TodoNote', + ), + ] diff --git a/web/startScan/templates/startScan/detail_scan.html b/web/startScan/templates/startScan/detail_scan.html index 05028dae..d250691f 100644 --- a/web/startScan/templates/startScan/detail_scan.html +++ b/web/startScan/templates/startScan/detail_scan.html @@ -879,7 +879,7 @@ Detailed Scan Results for {{history.domain.name}} "lengthMenu": [20, 50, 100, 500, 1000], "pageLength": 20, 'serverSide': true, - "ajax": '/api/scanHistory/?scan_id={{scan_history_id}}&format=datatables', + "ajax": '/api/listDatatableSubdomain/?scan_id={{scan_history_id}}&format=datatables', "order": [[ 8, "desc" ]], "columns": [ {'data': 'id'}, diff --git a/web/targetApp/static/targetApp/js/todo.js b/web/targetApp/static/targetApp/js/todo.js index 5cffec8a..6d515ebc 100644 --- a/web/targetApp/static/targetApp/js/todo.js +++ b/web/targetApp/static/targetApp/js/todo.js @@ -107,6 +107,9 @@ $('#addTask').on('click', function(event) { const ps = new PerfectScrollbar('.todo-box-scroll', { suppressScrollX : true }); + + populateScanHistory(); + }); const ps = new PerfectScrollbar('.todo-box-scroll', { suppressScrollX : true @@ -143,55 +146,18 @@ function importantDropdown() { }); } -function priorityDropdown() { - $('.priority-dropdown .dropdown-menu .dropdown-item').on('click', function(event) { - - var getClass = $(this).attr('class').split(' ')[1]; - var getDropdownClass = $(this).parents('.p-dropdown').children('.dropdown-toggle').attr('class').split(' ')[1]; - $(this).parents('.p-dropdown').children('.dropdown-toggle').removeClass(getDropdownClass); - - $(this).parents('.p-dropdown').children('.dropdown-toggle').addClass(getClass); - }) -} - -function editDropdown() { - $('.action-dropdown .dropdown-menu .edit.dropdown-item').click(function() { - - event.preventDefault(); - - var $_outerThis = $(this); - - $('.add-tsk').hide(); - $('.edit-tsk').show(); - - var $_taskTitle = $_outerThis.parents('.todo-item').children().find('.todo-heading').text(); - var $_taskText = $_outerThis.parents('.todo-item').children().find('.todo-text').text(); - - $('#task').val($_taskTitle); - $('#taskdescription').val($_taskText); - - $('.edit-tsk').off('click').on('click', function(event) { - var $_innerThis = $(this); - var $_task = document.getElementById('task').value; - var $_taskDescription = document.getElementById('taskdescription').value; - var $_taskDescriptionText = document.getElementById('taskdescription').value; - var $_taskEditedTitle = $_outerThis.parents('.todo-item').children().find('.todo-heading').html($_task); - var $_taskEditedText = $_outerThis.parents('.todo-item').children().find('.todo-text').html($_taskDescriptionText); - $('#addTaskModal').modal('hide'); - }) - $('#addTaskModal').modal('show'); - }) -} - function todoItem() { $('.todo-item .todo-content').on('click', function(event) { event.preventDefault(); var $_taskTitle = $(this).find('.todo-heading').text(); + + var $_taskTarget = $(this).find('.target').text(); + var $todoDescription = $(this).find('.todo-text').text(); $('.task-heading').text($_taskTitle); - $('.task-text').html($todoDescription); + $('.task-text').html(`${$_taskTarget}
` + $todoDescription); $('#todoShowListItem').modal('show'); }); @@ -210,8 +176,6 @@ var $btns = $('.list-actions').click(function() { checkCheckbox(); importantDropdown(); -priorityDropdown(); -editDropdown(); todoItem(); $(".add-tsk").click(function(){ @@ -220,6 +184,20 @@ $(".add-tsk").click(function(){ var $_taskDescriptionText = document.getElementById('taskdescription').value; + var $_taskScanHistory = $("#scanHistoryIDropdown option:selected").text(); + + var $_taskSubdomain = $("#subdomainDropdown option:selected").text(); + + var $_targetText = ''; + + if ($_taskScanHistory != 'Choose Scan History...') { + $_targetText = $_taskScanHistory; + } + + if ($_taskSubdomain != 'Choose Subdomain...') { + $_targetText += ' Subdomain : ' + $_taskSubdomain; + } + $html = '
'+ '
'+ '
'+ @@ -231,6 +209,7 @@ $(".add-tsk").click(function(){ '
'+ '
'+$_task+'
'+ + '

'+$_targetText+''+ "

"+$_taskDescriptionText+"

"+ '
'+ @@ -257,8 +236,6 @@ $(".add-tsk").click(function(){ $("#ct").prepend($html); $('#addTaskModal').modal('hide'); checkCheckbox(); - editDropdown(); - priorityDropdown(); todoItem(); importantDropdown(); new dynamicBadgeNotification('allList'); @@ -269,3 +246,17 @@ $('.tab-title .nav-pills a.nav-link').on('click', function(event) { $(this).parents('.mail-box-container').find('.tab-title').removeClass('mail-menu-show') $(this).parents('.mail-box-container').find('.mail-overlay').removeClass('mail-overlay-show') }) + + +function populateScanHistory() { + scan_history_select = document.getElementById('scanHistoryIDropdown'); + $.getJSON(`/api/listScanHistory?format=json`, function(data) { + for (var history in data['scan_histories']){ + history_object = data['scan_histories'][history]; + var option = document.createElement('option'); + option.value = history_object['id']; + option.innerHTML = history_object['domain']['name'] + ' - Scanned ' + moment.utc(history_object['start_scan_date']).fromNow(); + scan_history_select.appendChild(option); + } + }); +} diff --git a/web/targetApp/templates/note/index.html b/web/targetApp/templates/note/index.html index 34ec01a0..994181b3 100644 --- a/web/targetApp/templates/note/index.html +++ b/web/targetApp/templates/note/index.html @@ -10,6 +10,7 @@ Recon Todo {% block custom_js_css_link %} + {% endblock custom_js_css_link %} {% block breadcrumb_title %} @@ -69,7 +70,7 @@ Recon Todo
Perform Directory Bruteforce
-

Target: example.com

+

Domain: example.com, Scanned 2 hours ago, Subdomain: hello.ns.hackerone.com

Need to identify documentation page, use xyz wordlist and start dir bruteforce.

@@ -80,7 +81,6 @@ Recon Todo @@ -108,7 +108,7 @@ Recon Todo