Files
rengine/web/scanEngine/views.py
T

622 lines
24 KiB
Python

import glob
import os
import re
import shutil
import subprocess
from datetime import datetime
from django import http
from django.contrib import messages
from django.shortcuts import get_object_or_404, render
from django.urls import reverse
from rolepermissions.decorators import has_permission_decorator
from reNgine.common_func import *
from reNgine.tasks import (run_command, send_discord_message, send_slack_message,send_lark_message, send_telegram_message)
from scanEngine.forms import *
from scanEngine.forms import ConfigurationForm
from scanEngine.models import *
def index(request, slug):
engine_type = EngineType.objects.order_by('engine_name').all()
context = {
'engine_ul_show': 'show',
'engine_li': 'active',
'scan_engine_nav_active': 'active',
'engine_type': engine_type,
}
return render(request, 'scanEngine/index.html', context)
@has_permission_decorator(PERM_MODIFY_SCAN_CONFIGURATIONS, redirect_url=FOUR_OH_FOUR_URL)
def add_engine(request, slug):
form = AddEngineForm()
if request.method == "POST":
form = AddEngineForm(request.POST)
if form.is_valid():
form.save()
messages.add_message(
request,
messages.INFO,
'Scan Engine Added successfully')
return http.HttpResponseRedirect(reverse('scan_engine_index', kwargs={'slug': slug}))
context = {
'scan_engine_nav_active': 'active',
'form': form
}
return render(request, 'scanEngine/add_engine.html', context)
@has_permission_decorator(PERM_MODIFY_SCAN_CONFIGURATIONS, redirect_url=FOUR_OH_FOUR_URL)
def delete_engine(request, slug, id):
obj = get_object_or_404(EngineType, id=id)
if request.method == "POST":
obj.delete()
responseData = {'status': 'true'}
messages.add_message(
request,
messages.INFO,
'Engine successfully deleted!')
else:
responseData = {'status': 'false'}
messages.add_message(
request,
messages.ERROR,
'Oops! Engine could not be deleted!')
return http.JsonResponse(responseData)
@has_permission_decorator(PERM_MODIFY_SCAN_CONFIGURATIONS, redirect_url=FOUR_OH_FOUR_URL)
def update_engine(request, slug, id):
engine = get_object_or_404(EngineType, id=id)
form = UpdateEngineForm(
initial={
'yaml_configuration': engine.yaml_configuration,
'engine_name': engine.engine_name
})
if request.method == "POST":
form = UpdateEngineForm(request.POST, instance=engine)
if form.is_valid():
form.save()
messages.add_message(
request,
messages.INFO,
'Engine edited successfully')
return http.HttpResponseRedirect(reverse('scan_engine_index', kwargs={'slug': slug}))
context = {
'scan_engine_nav_active': 'active',
'form': form
}
return render(request, 'scanEngine/update_engine.html', context)
@has_permission_decorator(PERM_MODIFY_WORDLISTS, redirect_url=FOUR_OH_FOUR_URL)
def wordlist_list(request, slug):
wordlists = Wordlist.objects.all().order_by('id')
context = {
'scan_engine_nav_active': 'active',
'wordlist_li': 'active',
'wordlists': wordlists}
return render(request, 'scanEngine/wordlist/index.html', context)
@has_permission_decorator(PERM_MODIFY_WORDLISTS, redirect_url=FOUR_OH_FOUR_URL)
def add_wordlist(request, slug):
context = {'scan_engine_nav_active': 'active', 'wordlist_li': 'active'}
form = AddWordlistForm(request.POST or None, request.FILES or None)
if request.method == "POST":
if form.is_valid() and 'upload_file' in request.FILES:
txt_file = request.FILES['upload_file']
if txt_file.content_type == 'text/plain':
wordlist_content = txt_file.read().decode('UTF-8', "ignore")
wordlist_file = open(
'/usr/src/' +
'wordlist/' +
form.cleaned_data['short_name'] + '.txt',
'w')
wordlist_file.write(wordlist_content)
Wordlist.objects.create(
name=form.cleaned_data['name'],
short_name=form.cleaned_data['short_name'],
count=wordlist_content.count('\n'))
messages.add_message(
request,
messages.INFO,
'Wordlist ' + form.cleaned_data['name'] +
' added successfully')
return http.HttpResponseRedirect(reverse('wordlist_list', kwargs={'slug': slug}))
context['form'] = form
return render(request, 'scanEngine/wordlist/add.html', context)
@has_permission_decorator(PERM_MODIFY_WORDLISTS, redirect_url=FOUR_OH_FOUR_URL)
def delete_wordlist(request, slug, id):
obj = get_object_or_404(Wordlist, id=id)
if request.method == "POST":
obj.delete()
try:
os.remove(
'/usr/src/' +
'wordlist/' +
obj.short_name +
'.txt')
responseData = {'status': True}
except Exception as e:
responseData = {'status': False}
messages.add_message(
request,
messages.INFO,
'Wordlist successfully deleted!')
else:
responseData = {'status': 'false'}
messages.add_message(
request,
messages.ERROR,
'Oops! Wordlist could not be deleted!')
return http.JsonResponse(responseData)
@has_permission_decorator(PERM_MODIFY_INTERESTING_LOOKUP, redirect_url=FOUR_OH_FOUR_URL)
def interesting_lookup(request, slug):
lookup_keywords = None
context = {}
context['scan_engine_nav_active'] = 'active'
context['interesting_lookup_li'] = 'active'
context['engine_ul_show'] = 'show'
form = InterestingLookupForm()
if InterestingLookupModel.objects.filter(custom_type=True).exists():
lookup_keywords = InterestingLookupModel.objects.filter(custom_type=True).order_by('-id')[0]
else:
form.initial_checkbox()
if request.method == "POST":
if lookup_keywords:
form = InterestingLookupForm(request.POST, instance=lookup_keywords)
else:
form = InterestingLookupForm(request.POST or None)
if form.is_valid():
form.save()
messages.add_message(
request,
messages.INFO,
'Lookup Keywords updated successfully')
return http.HttpResponseRedirect(reverse('interesting_lookup', kwargs={'slug': slug}))
if lookup_keywords:
form.set_value(lookup_keywords)
context['interesting_lookup_found'] = True
context['form'] = form
context['default_lookup'] = InterestingLookupModel.objects.filter(id=1)
return render(request, 'scanEngine/lookup.html', context)
@has_permission_decorator(PERM_MODIFY_SCAN_CONFIGURATIONS, redirect_url=FOUR_OH_FOUR_URL)
def tool_specific_settings(request, slug):
context = {}
# check for incoming form requests
if request.method == "POST":
if 'gfFileUpload[]' in request.FILES:
gf_files = request.FILES.getlist('gfFileUpload[]')
upload_count = 0
for gf_file in gf_files:
original_filename = gf_file.name if isinstance(gf_file.name, str) else gf_file.name.decode('utf-8')
# remove special chars from filename, that could possibly do directory traversal or XSS
original_filename = re.sub(r'[\\/*?:"<>|]',"", original_filename)
file_extension = original_filename.split('.')[len(gf_file.name.split('.'))-1]
if file_extension == 'json':
base_filename = os.path.splitext(original_filename)[0]
file_path = '/root/.gf/' + base_filename + '.json'
file = open(file_path, "w")
file.write(gf_file.read().decode("utf-8"))
file.close()
upload_count += 1
messages.add_message(request, messages.INFO, f'{upload_count} GF files successfully uploaded')
return http.HttpResponseRedirect(reverse('tool_settings', kwargs={'slug': slug}))
elif 'nucleiFileUpload[]' in request.FILES:
nuclei_files = request.FILES.getlist('nucleiFileUpload[]')
upload_count = 0
for nuclei_file in nuclei_files:
original_filename = nuclei_file.name if isinstance(nuclei_file.name, str) else nuclei_file.name.decode('utf-8')
original_filename = re.sub(r'[\\/*?:"<>|]',"", original_filename)
file_extension = original_filename.split('.')[len(nuclei_file.name.split('.'))-1]
if file_extension in ['yaml', 'yml']:
base_filename = os.path.splitext(original_filename)[0]
file_path = '/root/nuclei-templates/' + base_filename + '.yaml'
file = open(file_path, "w")
file.write(nuclei_file.read().decode("utf-8"))
file.close()
upload_count += 1
if upload_count == 0:
messages.add_message(request, messages.ERROR, 'Invalid Nuclei Pattern, upload only *.yaml extension')
messages.add_message(request, messages.INFO, f'{upload_count} Nuclei Patterns successfully uploaded')
return http.HttpResponseRedirect(reverse('tool_settings', kwargs={'slug': slug}))
elif 'nuclei_config_text_area' in request.POST:
with open('/root/.config/nuclei/config.yaml', "w") as fhandle:
fhandle.write(request.POST.get('nuclei_config_text_area'))
messages.add_message(request, messages.INFO, 'Nuclei config updated!')
return http.HttpResponseRedirect(reverse('tool_settings', kwargs={'slug': slug}))
elif 'subfinder_config_text_area' in request.POST:
with open('/root/.config/subfinder/config.yaml', "w") as fhandle:
fhandle.write(request.POST.get('subfinder_config_text_area'))
messages.add_message(request, messages.INFO, 'Subfinder config updated!')
return http.HttpResponseRedirect(reverse('tool_settings', kwargs={'slug': slug}))
elif 'naabu_config_text_area' in request.POST:
with open('/root/.config/naabu/config.yaml', "w") as fhandle:
fhandle.write(request.POST.get('naabu_config_text_area'))
messages.add_message(request, messages.INFO, 'Naabu config updated!')
return http.HttpResponseRedirect(reverse('tool_settings', kwargs={'slug': slug}))
elif 'amass_config_text_area' in request.POST:
with open('/root/.config/amass.ini', "w") as fhandle:
fhandle.write(request.POST.get('amass_config_text_area'))
messages.add_message(request, messages.INFO, 'Amass config updated!')
return http.HttpResponseRedirect(reverse('tool_settings', kwargs={'slug': slug}))
elif 'theharvester_config_text_area' in request.POST:
with open('/usr/src/github/theHarvester/api-keys.yaml', "w") as fhandle:
fhandle.write(request.POST.get('theharvester_config_text_area'))
messages.add_message(request, messages.INFO, 'theHarvester config updated!')
return http.HttpResponseRedirect(reverse('tool_settings', kwargs={'slug': slug}))
context['settings_nav_active'] = 'active'
context['tool_settings_li'] = 'active'
context['settings_ul_show'] = 'show'
gf_list = (subprocess.check_output(['gf', '-list'])).decode("utf-8")
nuclei_custom_pattern = [f for f in glob.glob("/root/nuclei-templates/*.yaml")]
context['nuclei_templates'] = nuclei_custom_pattern
context['gf_patterns'] = sorted(gf_list.split('\n'))
return render(request, 'scanEngine/settings/tool.html', context)
@has_permission_decorator(PERM_MODIFY_SYSTEM_CONFIGURATIONS, redirect_url=FOUR_OH_FOUR_URL)
def rengine_settings(request, slug):
context = {}
total, used, _ = shutil.disk_usage("/")
total = total // (2**30)
used = used // (2**30)
context['total'] = total
context['used'] = used
context['free'] = total-used
context['consumed_percent'] = int(100 * float(used)/float(total))
context['settings_nav_active'] = 'active'
context['rengine_settings_li'] = 'active'
context['settings_ul_show'] = 'show'
return render(request, 'scanEngine/settings/rengine.html', context)
@has_permission_decorator(PERM_MODIFY_SCAN_CONFIGURATIONS, redirect_url=FOUR_OH_FOUR_URL)
def notification_settings(request, slug):
context = {}
form = NotificationForm()
notification = None
if Notification.objects.all().exists():
notification = Notification.objects.all()[0]
form.set_value(notification)
else:
form.set_initial()
if request.method == "POST":
if notification:
form = NotificationForm(request.POST, instance=notification)
else:
form = NotificationForm(request.POST or None)
if form.is_valid():
form.save()
send_slack_message('*reNgine*\nCongratulations! your notification services are working.')
send_lark_message('*reNgine*\nCongratulations! your notification services are working.')
send_telegram_message('*reNgine*\nCongratulations! your notification services are working.')
send_discord_message('**reNgine**\nCongratulations! your notification services are working.')
messages.add_message(
request,
messages.INFO,
'Notification Settings updated successfully and test message was sent.')
return http.HttpResponseRedirect(reverse('notification_settings', kwargs={'slug': slug}))
context['settings_nav_active'] = 'active'
context['notification_settings_li'] = 'active'
context['settings_ul_show'] = 'show'
context['form'] = form
return render(request, 'scanEngine/settings/notification.html', context)
@has_permission_decorator(PERM_MODIFY_SCAN_CONFIGURATIONS, redirect_url=FOUR_OH_FOUR_URL)
def proxy_settings(request, slug):
context = {}
form = ProxyForm()
context['form'] = form
proxy = None
if Proxy.objects.all().exists():
proxy = Proxy.objects.all()[0]
form.set_value(proxy)
else:
form.set_initial()
if request.method == "POST":
if proxy:
form = ProxyForm(request.POST, instance=proxy)
else:
form = ProxyForm(request.POST or None)
if form.is_valid():
form.save()
messages.add_message(
request,
messages.INFO,
'Proxies updated.')
return http.HttpResponseRedirect(reverse('proxy_settings', kwargs={'slug': slug}))
context['settings_nav_active'] = 'active'
context['proxy_settings_li'] = 'active'
context['settings_ul_show'] = 'show'
return render(request, 'scanEngine/settings/proxy.html', context)
@has_permission_decorator(PERM_MODIFY_SCAN_CONFIGURATIONS, redirect_url=FOUR_OH_FOUR_URL)
def test_hackerone(request, slug):
if request.method == "POST":
headers = {
'Accept': 'application/json'
}
body = json.loads(request.body)
r = requests.get(
'https://api.hackerone.com/v1/hackers/payments/balance',
auth=(body['username'], body['api_key']),
headers = headers
)
if r.status_code == 200:
return http.JsonResponse({"status": 200})
return http.JsonResponse({"status": 401})
@has_permission_decorator(PERM_MODIFY_SCAN_CONFIGURATIONS, redirect_url=FOUR_OH_FOUR_URL)
def hackerone_settings(request, slug):
context = {}
form = HackeroneForm()
context['form'] = form
hackerone = None
if Hackerone.objects.all().exists():
hackerone = Hackerone.objects.all()[0]
form.set_value(hackerone)
else:
form.set_initial()
if request.method == "POST":
if hackerone:
form = HackeroneForm(request.POST, instance=hackerone)
else:
form = HackeroneForm(request.POST or None)
if form.is_valid():
form.save()
messages.add_message(
request,
messages.INFO,
'Hackerone Settings updated.')
return http.HttpResponseRedirect(reverse('hackerone_settings', kwargs={'slug': slug}))
context['settings_nav_active'] = 'active'
context['hackerone_settings_li'] = 'active'
context['settings_ul_show'] = 'show'
return render(request, 'scanEngine/settings/hackerone.html', context)
@has_permission_decorator(PERM_MODIFY_SCAN_REPORT, redirect_url=FOUR_OH_FOUR_URL)
def report_settings(request, slug):
context = {}
form = ReportForm()
context['form'] = form
primary_color = '#FFB74D'
secondary_color = '#212121'
report = None
if VulnerabilityReportSetting.objects.all().exists():
report = VulnerabilityReportSetting.objects.all()[0]
primary_color = report.primary_color
secondary_color = report.secondary_color
form.set_value(report)
else:
form.set_initial()
if request.method == "POST":
if report:
form = ReportForm(request.POST, instance=report)
else:
form = ReportForm(request.POST or None)
if form.is_valid():
form.save()
messages.add_message(
request,
messages.INFO,
'Report Settings updated.')
return http.HttpResponseRedirect(reverse('report_settings', kwargs={'slug': slug}))
context['settings_nav_active'] = 'active'
context['report_settings_li'] = 'active'
context['settings_ul_show'] = 'show'
context['primary_color'] = primary_color
context['secondary_color'] = secondary_color
return render(request, 'scanEngine/settings/report.html', context)
@has_permission_decorator(PERM_MODIFY_SYSTEM_CONFIGURATIONS, redirect_url=FOUR_OH_FOUR_URL)
def tool_arsenal_section(request, slug):
context = {}
tools = InstalledExternalTool.objects.all().order_by('id')
context['installed_tools'] = tools
return render(request, 'scanEngine/settings/tool_arsenal.html', context)
@has_permission_decorator(PERM_MODIFY_SYSTEM_CONFIGURATIONS, redirect_url=FOUR_OH_FOUR_URL)
def llm_toolkit_section(request, slug):
context = {}
list_all_models_url = f'{OLLAMA_INSTANCE}/api/tags'
response = requests.get(list_all_models_url)
all_models = []
selected_model = None
all_models = DEFAULT_GPT_MODELS.copy()
if response.status_code == 200:
models = response.json()
ollama_models = models.get('models')
date_format = "%Y-%m-%dT%H:%M:%S"
for model in ollama_models:
all_models.append({**model,
'modified_at': datetime.strptime(model['modified_at'].split('.')[0], date_format),
'is_local': True,
})
# find selected model name from db
selected_model = OllamaSettings.objects.first()
if selected_model:
selected_model = {'selected_model': selected_model.selected_model}
else:
# use gpt3.5-turbo as default
selected_model = {'selected_model': 'gpt-3.5-turbo'}
for model in all_models:
if model['name'] == selected_model['selected_model']:
model['selected'] = True
context['installed_models'] = all_models
# show error message for openai key, if any gpt is selected
openai_key = get_open_ai_key()
if not openai_key and 'gpt' in selected_model['selected_model']:
context['openai_key_error'] = True
return render(request, 'scanEngine/settings/llm_toolkit.html', context)
@has_permission_decorator(PERM_MODIFY_SYSTEM_CONFIGURATIONS, redirect_url=FOUR_OH_FOUR_URL)
def api_vault(request, slug):
context = {}
if request.method == "POST":
key_openai = request.POST.get('key_openai')
key_netlas = request.POST.get('key_netlas')
key_chaos = request.POST.get('key_chaos')
key_hackerone = request.POST.get('key_hackerone')
username_hackerone = request.POST.get('username_hackerone')
if key_openai:
openai_api_key = OpenAiAPIKey.objects.first()
if openai_api_key:
openai_api_key.key = key_openai
openai_api_key.save()
else:
OpenAiAPIKey.objects.create(key=key_openai)
if key_netlas:
netlas_api_key = NetlasAPIKey.objects.first()
if netlas_api_key:
netlas_api_key.key = key_netlas
netlas_api_key.save()
else:
NetlasAPIKey.objects.create(key=key_netlas)
if key_chaos:
chaos_api_key = ChaosAPIKey.objects.first()
if chaos_api_key:
chaos_api_key.key = key_chaos
chaos_api_key.save()
else:
ChaosAPIKey.objects.create(key=key_chaos)
if key_hackerone and username_hackerone:
hackerone_api_key = HackerOneAPIKey.objects.first()
if hackerone_api_key:
hackerone_api_key.username = username_hackerone
hackerone_api_key.key = key_hackerone
hackerone_api_key.save()
else:
HackerOneAPIKey.objects.create(
username=username_hackerone,
key=key_hackerone
)
openai_key = OpenAiAPIKey.objects.first()
netlas_key = NetlasAPIKey.objects.first()
chaos_key = ChaosAPIKey.objects.first()
h1_key = HackerOneAPIKey.objects.first()
if h1_key:
hackerone_key = h1_key.key
hackerone_username = h1_key.username
else:
hackerone_key = None
hackerone_username = None
context['openai_key'] = openai_key
context['netlas_key'] = netlas_key
context['chaos_key'] = chaos_key
context['hackerone_key'] = hackerone_key
context['hackerone_username'] = hackerone_username
return render(request, 'scanEngine/settings/api.html', context)
@has_permission_decorator(PERM_MODIFY_SYSTEM_CONFIGURATIONS, redirect_url=FOUR_OH_FOUR_URL)
def add_tool(request, slug):
form = ExternalToolForm()
if request.method == "POST":
form = ExternalToolForm(request.POST)
if form.is_valid():
# add tool
install_command = form.data['install_command']
github_clone_path = None
if 'git clone' in install_command:
project_name = install_command.split('/')[-1]
install_command = install_command + ' /usr/src/github/' + project_name + ' && pip install -r /usr/src/github/' + project_name + '/requirements.txt'
github_clone_path = '/usr/src/github/' + project_name
# if github cloned we also need to install requirements, atleast found in the main dir
install_command = 'pip3 install -r /usr/src/github/' + project_name + '/requirements.txt'
run_command(install_command)
run_command.apply_async(args=(install_command,))
saved_form = form.save()
if github_clone_path:
tool = InstalledExternalTool.objects.get(id=saved_form.pk)
tool.github_clone_path = github_clone_path
tool.save()
messages.add_message(
request,
messages.INFO,
'External Tool Successfully Added!')
return http.HttpResponseRedirect(reverse('tool_arsenal', kwargs={'slug': slug}))
context = {
'settings_nav_active': 'active',
'form': form
}
return render(request, 'scanEngine/settings/add_tool.html', context)
@has_permission_decorator(PERM_MODIFY_SYSTEM_CONFIGURATIONS, redirect_url=FOUR_OH_FOUR_URL)
def modify_tool_in_arsenal(request, slug, id):
external_tool = get_object_or_404(InstalledExternalTool, id=id)
form = ExternalToolForm()
if request.method == "POST":
form = ExternalToolForm(request.POST, instance=external_tool)
if form.is_valid():
form.save()
messages.add_message(
request,
messages.INFO,
'Tool modified successfully')
return http.HttpResponseRedirect(reverse('tool_arsenal', kwargs={'slug': slug}))
else:
form.set_value(external_tool)
context = {
'scan_engine_nav_active':
'active', 'form': form}
return render(request, 'scanEngine/settings/update_tool.html', context)