From 2f539a70d13d8126ec67802cf2aff99ca2bdff9c Mon Sep 17 00:00:00 2001 From: Yogesh Ojha Date: Wed, 21 Aug 2024 21:11:08 +0530 Subject: [PATCH 1/5] refactor starting point url to starting point path --- web/reNgine/celery_custom_task.py | 2 +- web/reNgine/tasks.py | 37 +++++++++++-------- .../migrations/0002_auto_20240821_1518.py | 29 +++++++++++++++ ...url_scanhistory_cfg_starting_point_path.py | 18 +++++++++ web/startScan/models.py | 14 +++++++ .../_items/schedule_scan_wizard.html | 4 +- .../startScan/_items/start_scan_wizard.html | 4 +- web/startScan/views.py | 24 ++++++------ 8 files changed, 99 insertions(+), 33 deletions(-) create mode 100644 web/startScan/migrations/0002_auto_20240821_1518.py create mode 100644 web/startScan/migrations/0003_rename_cfg_starting_point_url_scanhistory_cfg_starting_point_path.py diff --git a/web/reNgine/celery_custom_task.py b/web/reNgine/celery_custom_task.py index 2c6b11f25..863f77169 100644 --- a/web/reNgine/celery_custom_task.py +++ b/web/reNgine/celery_custom_task.py @@ -67,7 +67,7 @@ def __call__(self, *args, **kwargs): self.subscan_id = ctx.get('subscan_id') self.engine_id = ctx.get('engine_id') self.filename = ctx.get('filename') - self.starting_point_url = ctx.get('starting_point_url', '') + self.starting_point_path = ctx.get('starting_point_path', '') self.excluded_paths = ctx.get('excluded_paths', []) self.results_dir = ctx.get('results_dir', RENGINE_RESULTS) self.yaml_configuration = ctx.get('yaml_configuration', {}) diff --git a/web/reNgine/tasks.py b/web/reNgine/tasks.py index 33ecbae04..d23086c91 100644 --- a/web/reNgine/tasks.py +++ b/web/reNgine/tasks.py @@ -57,7 +57,7 @@ def initiate_scan( imported_subdomains=[], out_of_scope_subdomains=[], initiated_by_id=None, - starting_point_url='', + starting_point_path='', excluded_paths=[], ): """Initiate a new scan. @@ -70,7 +70,7 @@ def initiate_scan( results_dir (str): Results directory. imported_subdomains (list): Imported subdomains. out_of_scope_subdomains (list): Out-of-scope subdomains. - starting_point_url (str): URL path. Default: '' Defined where to start the scan. + starting_point_path (str): URL path. Default: '' Defined where to start the scan. initiated_by (int): User ID initiating the scan. excluded_paths (list): Excluded paths. Default: [], url paths to exclude from scan. """ @@ -92,7 +92,7 @@ def initiate_scan( domain.save() # Get path filter - starting_point_url = starting_point_url.rstrip('/') + starting_point_path = starting_point_path.rstrip('/') # for live scan scan history id is passed as scan_history_id # and no need to create scan_history object @@ -114,6 +114,11 @@ def initiate_scan( scan.tasks = engine.tasks scan.results_dir = f'{results_dir}/{domain.name}_{scan.id}' add_gf_patterns = gf_patterns and 'fetch_url' in engine.tasks + # add configs to scan object, cfg_ prefix is used to avoid conflicts with other scan object fields + scan.cfg_starting_point_path = starting_point_path + scan.cfg_excluded_paths = excluded_paths + scan.cfg_out_of_scope_subdomains = out_of_scope_subdomains + if add_gf_patterns: scan.used_gf_patterns = ','.join(gf_patterns) scan.save() @@ -127,7 +132,7 @@ def initiate_scan( 'engine_id': engine_id, 'domain_id': domain.id, 'results_dir': scan.results_dir, - 'starting_point_url': starting_point_url, + 'starting_point_path': starting_point_path, 'excluded_paths': excluded_paths, 'yaml_configuration': config, 'out_of_scope_subdomains': out_of_scope_subdomains @@ -152,7 +157,7 @@ def initiate_scan( # If enable_http_crawl is set, create an initial root HTTP endpoint so that # HTTP crawling can start somewhere - http_url = f'{domain.name}{starting_point_url}' if starting_point_url else domain.name + http_url = f'{domain.name}{starting_point_path}' if starting_point_path else domain.name endpoint, _ = save_endpoint( http_url, ctx=ctx, @@ -228,7 +233,7 @@ def initiate_subscan( engine_id=None, scan_type=None, results_dir=RENGINE_RESULTS, - starting_point_url='', + starting_point_path='', excluded_paths=[], ): """Initiate a new subscan. @@ -239,7 +244,7 @@ def initiate_subscan( engine_id (int): Engine ID. scan_type (int): Scan type (periodic, live). results_dir (str): Results directory. - starting_point_url (str): URL path. Default: '' + starting_point_path (str): URL path. Default: '' excluded_paths (list): Excluded paths. Default: [], url paths to exclude from scan. """ @@ -298,13 +303,13 @@ def initiate_subscan( 'subdomain_id': subdomain.id, 'yaml_configuration': config, 'results_dir': results_dir, - 'starting_point_url': starting_point_url, + 'starting_point_path': starting_point_path, 'excluded_paths': excluded_paths, } # Create initial endpoints in DB: find domain HTTP endpoint so that HTTP # crawling can start somewhere - base_url = f'{subdomain.name}{starting_point_url}' if starting_point_url else subdomain.name + base_url = f'{subdomain.name}{starting_point_path}' if starting_point_path else subdomain.name endpoint, _ = save_endpoint( base_url, crawl=enable_http_crawl, @@ -406,8 +411,8 @@ def subdomain_discovery( if not host: host = self.subdomain.name if self.subdomain else self.domain.name - if self.starting_point_url: - logger.warning(f'Ignoring subdomains scan as an URL path filter was passed ({self.starting_point_url}).') + if self.starting_point_path: + logger.warning(f'Ignoring subdomains scan as an URL path filter was passed ({self.starting_point_path}).') return # Config @@ -1929,7 +1934,7 @@ def fetch_url(self, urls=[], ctx={}, description=None): if base_url and urlpath: subdomain = urlparse(base_url) - url = f'{subdomain.scheme}://{subdomain.netloc}{self.starting_point_url}' + url = f'{subdomain.scheme}://{subdomain.netloc}{self.starting_point_path}' if not validators.url(url): logger.warning(f'Invalid URL "{url}". Skipping.') @@ -1938,8 +1943,8 @@ def fetch_url(self, urls=[], ctx={}, description=None): all_urls.append(url) # Filter out URLs if a path filter was passed - if self.starting_point_url: - all_urls = [url for url in all_urls if self.starting_point_url in url] + if self.starting_point_path: + all_urls = [url for url in all_urls if self.starting_point_path in url] # if exclude_paths is found, then remove urls matching those paths if self.excluded_paths: @@ -2839,8 +2844,8 @@ def http_crawl( input_path = f'{self.results_dir}/httpx_input.txt' history_file = f'{self.results_dir}/commands.txt' if urls: # direct passing URLs to check - if self.starting_point_url: - urls = [u for u in urls if self.starting_point_url in u] + if self.starting_point_path: + urls = [u for u in urls if self.starting_point_path in u] with open(input_path, 'w') as f: f.write('\n'.join(urls)) diff --git a/web/startScan/migrations/0002_auto_20240821_1518.py b/web/startScan/migrations/0002_auto_20240821_1518.py new file mode 100644 index 000000000..03dd158b3 --- /dev/null +++ b/web/startScan/migrations/0002_auto_20240821_1518.py @@ -0,0 +1,29 @@ +# Generated by Django 3.2.23 on 2024-08-21 15:18 + +import django.contrib.postgres.fields +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('startScan', '0001_initial'), + ] + + operations = [ + migrations.AddField( + model_name='scanhistory', + name='cfg_excluded_paths', + field=django.contrib.postgres.fields.ArrayField(base_field=models.CharField(max_length=200), blank=True, default=list, null=True, size=None), + ), + migrations.AddField( + model_name='scanhistory', + name='cfg_out_of_scope_subdomains', + field=django.contrib.postgres.fields.ArrayField(base_field=models.CharField(max_length=200), blank=True, default=list, null=True, size=None), + ), + migrations.AddField( + model_name='scanhistory', + name='cfg_starting_point_url', + field=models.CharField(blank=True, max_length=200, null=True), + ), + ] diff --git a/web/startScan/migrations/0003_rename_cfg_starting_point_url_scanhistory_cfg_starting_point_path.py b/web/startScan/migrations/0003_rename_cfg_starting_point_url_scanhistory_cfg_starting_point_path.py new file mode 100644 index 000000000..e5f005bf1 --- /dev/null +++ b/web/startScan/migrations/0003_rename_cfg_starting_point_url_scanhistory_cfg_starting_point_path.py @@ -0,0 +1,18 @@ +# Generated by Django 3.2.23 on 2024-08-21 15:40 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('startScan', '0002_auto_20240821_1518'), + ] + + operations = [ + migrations.RenameField( + model_name='scanhistory', + old_name='cfg_starting_point_url', + new_name='cfg_starting_point_path', + ), + ] diff --git a/web/startScan/models.py b/web/startScan/models.py index 0d44638a9..b93a0645c 100644 --- a/web/startScan/models.py +++ b/web/startScan/models.py @@ -48,6 +48,20 @@ class ScanHistory(models.Model): dorks = models.ManyToManyField('Dork', related_name='dorks', blank=True) initiated_by = models.ForeignKey(User, on_delete=models.CASCADE, related_name='initiated_scans', blank=True, null=True) aborted_by = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, blank=True, related_name='aborted_scans') + # scan related configs, append config fields with cfg_ + cfg_out_of_scope_subdomains = ArrayField( + models.CharField(max_length=200), + blank=True, + null=True, + default=list + ) + cfg_starting_point_path = models.CharField(max_length=200, blank=True, null=True) + cfg_excluded_paths = ArrayField( + models.CharField(max_length=200), + blank=True, + null=True, + default=list + ) def __str__(self): diff --git a/web/startScan/templates/startScan/_items/schedule_scan_wizard.html b/web/startScan/templates/startScan/_items/schedule_scan_wizard.html index d2dbf8816..b4e43e0f6 100644 --- a/web/startScan/templates/startScan/_items/schedule_scan_wizard.html +++ b/web/startScan/templates/startScan/_items/schedule_scan_wizard.html @@ -88,8 +88,8 @@

Out of Scope Subdomains(Optional)

URL Scope and Exclusions

-

Starting Point URL (Optional)

- +

Starting Point Path (Optional)

+ Defines where the scan should begin. Leave blank to scan from the root (/) and include all subdomains.
diff --git a/web/startScan/templates/startScan/_items/start_scan_wizard.html b/web/startScan/templates/startScan/_items/start_scan_wizard.html index 1f2af3eda..1fb0934d7 100644 --- a/web/startScan/templates/startScan/_items/start_scan_wizard.html +++ b/web/startScan/templates/startScan/_items/start_scan_wizard.html @@ -51,8 +51,8 @@

Out of Scope Subdomains(Optional)

URL Scope and Exclusions

-

Starting Point URL (Optional)

- +

Starting Point Path (Optional)

+ Defines where the scan should begin. Leave blank to scan from the root (/) and include all subdomains.
diff --git a/web/startScan/views.py b/web/startScan/views.py index 94b9ef21f..d6908f9c6 100644 --- a/web/startScan/views.py +++ b/web/startScan/views.py @@ -259,7 +259,7 @@ def start_scan_ui(request, slug, domain_id): subdomains_in = [s.rstrip() for s in subdomains_in if s] subdomains_out = request.POST['outOfScopeSubdomainTextarea'].split() subdomains_out = [s.rstrip() for s in subdomains_out if s] - starting_point_url = request.POST['startingPointUrl'].strip() + starting_point_path = request.POST['startingPointPath'].strip() excluded_paths = request.POST['excludedPaths'] # string separated by , # split excluded paths by , excluded_paths = [path.strip() for path in excluded_paths.split(',')] @@ -284,7 +284,7 @@ def start_scan_ui(request, slug, domain_id): 'results_dir': '/usr/src/scan_results', 'imported_subdomains': subdomains_in, 'out_of_scope_subdomains': subdomains_out, - 'starting_point_url': starting_point_url, + 'starting_point_path': starting_point_path, 'excluded_paths': excluded_paths, 'initiated_by_id': request.user.id } @@ -329,7 +329,7 @@ def start_multiple_scan(request, slug): subdomains_in = [s.rstrip() for s in subdomains_in if s] subdomains_out = request.POST['outOfScopeSubdomainTextarea'].split() subdomains_out = [s.rstrip() for s in subdomains_out if s] - starting_point_url = request.POST['startingPointUrl'].strip() + starting_point_path = request.POST['startingPointPath'].strip() excluded_paths = request.POST['excludedPaths'] # string separated by , # split excluded paths by , excluded_paths = [path.strip() for path in excluded_paths.split(',')] @@ -354,7 +354,7 @@ def start_multiple_scan(request, slug): 'initiated_by_id': request.user.id, 'imported_subdomains': subdomains_in, 'out_of_scope_subdomains': subdomains_out, - 'starting_point_url': starting_point_url, + 'starting_point_path': starting_point_path, 'excluded_paths': excluded_paths, } @@ -562,7 +562,7 @@ def schedule_scan(request, host_id, slug): subdomains_in = [s.rstrip() for s in subdomains_in if s] subdomains_out = request.POST['outOfScopeSubdomainTextarea'].split() subdomains_out = [s.rstrip() for s in subdomains_out if s] - starting_point_url = request.POST['startingPointUrl'].strip() + starting_point_path = request.POST['startingPointPath'].strip() excluded_paths = request.POST['excludedPaths'] # string separated by , # split excluded paths by , excluded_paths = [path.strip() for path in excluded_paths.split(',')] @@ -596,7 +596,7 @@ def schedule_scan(request, host_id, slug): 'scan_type': SCHEDULED_SCAN, 'imported_subdomains': subdomains_in, 'out_of_scope_subdomains': subdomains_out, - 'starting_point_url': starting_point_url, + 'starting_point_path': starting_point_path, 'excluded_paths': excluded_paths, 'initiated_by_id': request.user.id } @@ -617,7 +617,7 @@ def schedule_scan(request, host_id, slug): 'scan_type': SCHEDULED_SCAN, 'imported_subdomains': subdomains_in, 'out_of_scope_subdomains': subdomains_out, - 'starting_point_url': starting_point_url, + 'starting_point_path': starting_point_path, 'excluded_paths': excluded_paths, 'initiated_by_id': request.user.id } @@ -745,7 +745,7 @@ def start_organization_scan(request, id, slug): subdomains_in = [s.rstrip() for s in subdomains_in if s] subdomains_out = request.POST['outOfScopeSubdomainTextarea'].split() subdomains_out = [s.rstrip() for s in subdomains_out if s] - starting_point_url = request.POST['startingPointUrl'].strip() + starting_point_path = request.POST['startingPointPath'].strip() excluded_paths = request.POST['excludedPaths'] # string separated by , # split excluded paths by , excluded_paths = [path.strip() for path in excluded_paths.split(',')] @@ -768,7 +768,7 @@ def start_organization_scan(request, id, slug): 'initiated_by_id': request.user.id, 'imported_subdomains': subdomains_in, 'out_of_scope_subdomains': subdomains_out, - 'starting_point_url': starting_point_url, + 'starting_point_path': starting_point_path, 'excluded_paths': excluded_paths, } initiate_scan.apply_async(kwargs=kwargs) @@ -814,7 +814,7 @@ def schedule_organization_scan(request, slug, id): subdomains_in = [s.rstrip() for s in subdomains_in if s] subdomains_out = request.POST['outOfScopeSubdomainTextarea'].split() subdomains_out = [s.rstrip() for s in subdomains_out if s] - starting_point_url = request.POST['startingPointUrl'].strip() + starting_point_path = request.POST['startingPointPath'].strip() excluded_paths = request.POST['excludedPaths'] # string separated by , # split excluded paths by , excluded_paths = [path.strip() for path in excluded_paths.split(',')] @@ -852,7 +852,7 @@ def schedule_organization_scan(request, slug, id): 'initiated_by_id': request.user.id, 'imported_subdomains': subdomains_in, 'out_of_scope_subdomains': subdomains_out, - 'starting_point_url': starting_point_url, + 'starting_point_path': starting_point_path, 'excluded_paths': excluded_paths, }) PeriodicTask.objects.create( @@ -876,7 +876,7 @@ def schedule_organization_scan(request, slug, id): 'initiated_by_id': request.user.id, 'imported_subdomains': subdomains_in, 'out_of_scope_subdomains': subdomains_out, - 'starting_point_url': starting_point_url, + 'starting_point_path': starting_point_path, 'excluded_paths': excluded_paths, }) PeriodicTask.objects.create(clocked=clock, From 4af8226098ca1c21f6052f4336166b45b9909e52 Mon Sep 17 00:00:00 2001 From: Yogesh Ojha Date: Thu, 22 Aug 2024 10:59:16 +0530 Subject: [PATCH 2/5] Add imported subdomains in scan config --- web/reNgine/tasks.py | 1 + web/startScan/models.py | 8 +++++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/web/reNgine/tasks.py b/web/reNgine/tasks.py index d23086c91..35f8aa85f 100644 --- a/web/reNgine/tasks.py +++ b/web/reNgine/tasks.py @@ -118,6 +118,7 @@ def initiate_scan( scan.cfg_starting_point_path = starting_point_path scan.cfg_excluded_paths = excluded_paths scan.cfg_out_of_scope_subdomains = out_of_scope_subdomains + scan.cfg_imported_subdomains = imported_subdomains if add_gf_patterns: scan.used_gf_patterns = ','.join(gf_patterns) diff --git a/web/startScan/models.py b/web/startScan/models.py index b93a0645c..fcfbafa20 100644 --- a/web/startScan/models.py +++ b/web/startScan/models.py @@ -48,7 +48,7 @@ class ScanHistory(models.Model): dorks = models.ManyToManyField('Dork', related_name='dorks', blank=True) initiated_by = models.ForeignKey(User, on_delete=models.CASCADE, related_name='initiated_scans', blank=True, null=True) aborted_by = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, blank=True, related_name='aborted_scans') - # scan related configs, append config fields with cfg_ + # scan related configs, prefix config fields with cfg_ cfg_out_of_scope_subdomains = ArrayField( models.CharField(max_length=200), blank=True, @@ -62,6 +62,12 @@ class ScanHistory(models.Model): null=True, default=list ) + cfg_imported_subdomains = ArrayField( + models.CharField(max_length=200), + blank=True, + null=True, + default=list + ) def __str__(self): From b5846c6199509ad4ff7df0f800380c78b54fe1db Mon Sep 17 00:00:00 2001 From: Yogesh Ojha Date: Thu, 22 Aug 2024 10:59:36 +0530 Subject: [PATCH 3/5] fix icon and log view as code --- web/startScan/static/startScan/js/detail_scan.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/web/startScan/static/startScan/js/detail_scan.js b/web/startScan/static/startScan/js/detail_scan.js index a7a972d7d..8e2da5b20 100644 --- a/web/startScan/static/startScan/js/detail_scan.js +++ b/web/startScan/static/startScan/js/detail_scan.js @@ -1039,12 +1039,12 @@ function create_log_element(log) { let logElement = document.createElement("p"); innerHTML = `

-

- ${log.command} +

+ ${log.command}

` if (log.output != ''){ - innerHTML += `
${log.output}
`; + innerHTML += `
${log.output}
`; } logElement.innerHTML = innerHTML; return logElement; From ce80595a0164e31b9eb11124c7e339fba433d296 Mon Sep 17 00:00:00 2001 From: Yogesh Ojha Date: Thu, 22 Aug 2024 11:00:31 +0530 Subject: [PATCH 4/5] show scan config in ui --- ...004_scanhistory_cfg_imported_subdomains.py | 19 +++++++ .../templates/startScan/detail_scan.html | 56 ++++++++++++++++--- web/static/custom/custom.js | 2 +- 3 files changed, 67 insertions(+), 10 deletions(-) create mode 100644 web/startScan/migrations/0004_scanhistory_cfg_imported_subdomains.py diff --git a/web/startScan/migrations/0004_scanhistory_cfg_imported_subdomains.py b/web/startScan/migrations/0004_scanhistory_cfg_imported_subdomains.py new file mode 100644 index 000000000..992199df5 --- /dev/null +++ b/web/startScan/migrations/0004_scanhistory_cfg_imported_subdomains.py @@ -0,0 +1,19 @@ +# Generated by Django 3.2.23 on 2024-08-22 04:26 + +import django.contrib.postgres.fields +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('startScan', '0003_rename_cfg_starting_point_url_scanhistory_cfg_starting_point_path'), + ] + + operations = [ + migrations.AddField( + model_name='scanhistory', + name='cfg_imported_subdomains', + field=django.contrib.postgres.fields.ArrayField(base_field=models.CharField(max_length=200), blank=True, default=list, null=True, size=None), + ), + ] diff --git a/web/startScan/templates/startScan/detail_scan.html b/web/startScan/templates/startScan/detail_scan.html index c7fad04d1..f704affd4 100644 --- a/web/startScan/templates/startScan/detail_scan.html +++ b/web/startScan/templates/startScan/detail_scan.html @@ -146,11 +146,51 @@

Scan failed due to ERROR: {{history.error_message}}

{% endif %} {% endif %} -
Scan Logs
- Logs -
Scan Engine
+
Scan Configurations
+ Starting Path: {% if history.cfg_starting_point_path %} {{history.cfg_starting_point_path}} {% else %}/ {% endif %}
+
+
+ +
+
    + {% for subdomain in history.cfg_imported_subdomains %} +
  • {{subdomain}}
  • + {% endfor %} +
+
+
+
+ +
+
    + {% for subdomain in history.cfg_out_of_scope_subdomains %} +
  • {{subdomain}}
  • + {% endfor %} +
+
+
+
+ +
+
    + {% for path in history.cfg_excluded_paths %} +
  • {{path}}
  • + {% endfor %} +
+
+
+
+
Scan Logs
+ Logs +
Scan Engine
{{history.scan_type.engine_name}} -
Scan Duration
+
Scan Duration
{% if history.scan_status == -1 %}

Scan not yet started.

{% elif history.scan_status == 0 %} @@ -166,9 +206,7 @@
Scan Duration
{% elif history.scan_status == 3 %} Aborted in {{ history.start_scan_date|timesince:history.stop_scan_date }} {% endif %} - - -

Scan Progress

+

Scan Progress

{% if history.scan_status == -1 %}
@@ -200,7 +238,7 @@

Tagged to Organization

{% endfor %} {% endif %} -
Scan Timeline
+
Timeline
    @@ -220,7 +258,7 @@
    {{activity.title}}

    Error: {{activity.error_message}}

    {% endif %} {% endif %} - Logs + Logs {% endfor %}
diff --git a/web/static/custom/custom.js b/web/static/custom/custom.js index b5f58bf1e..9dfd313ef 100644 --- a/web/static/custom/custom.js +++ b/web/static/custom/custom.js @@ -3328,4 +3328,4 @@ function handleHashInUrl(){ }, 100); } } -} +} \ No newline at end of file From 8b7b577fac97cc121c1736603822f977bd511da3 Mon Sep 17 00:00:00 2001 From: Yogesh Ojha Date: Thu, 22 Aug 2024 20:51:50 +0530 Subject: [PATCH 5/5] Add show config modal in scan history --- .../templates/startScan/history.html | 5 + web/static/custom/custom.css | 14 +- web/static/custom/custom.js | 246 +++++++++++------- 3 files changed, 165 insertions(+), 100 deletions(-) diff --git a/web/startScan/templates/startScan/history.html b/web/startScan/templates/startScan/history.html index ff9bfba1f..0f4fe1257 100644 --- a/web/startScan/templates/startScan/history.html +++ b/web/startScan/templates/startScan/history.html @@ -97,6 +97,10 @@

Filters

{{ scan_history.domain.name }}
+ {% if scan_history.cfg_starting_point_path %} + Path: {{scan_history.cfg_starting_point_path}} + {% endif %} +
{% for organization in scan_history.domain.get_organization %} {{ organization.name }} {% endfor %} @@ -173,6 +177,7 @@

Filters