Skip to content

Commit

Permalink
improve plugin install, regipy plugins
Browse files Browse the repository at this point in the history
  • Loading branch information
Davide Arcuri committed Mar 21, 2024
1 parent 0c6a685 commit 4aabe06
Show file tree
Hide file tree
Showing 11 changed files with 184 additions and 55 deletions.
12 changes: 7 additions & 5 deletions orochi/templates/base.html
Original file line number Diff line number Diff line change
Expand Up @@ -204,12 +204,14 @@

// TASK STATUS
setInterval(function () {
$.get("{% url 'website:dask_status' %}").done(
function (data) {
$.ajax({
url: "{% url 'website:dask_status' %}",
success: function (data) {
$("#tasks_running").html(data.running);
}
)
}, 5000);
},
timeout: 2000
});
}, 10000);

// ABOUT
$(document).on("click", "#about", function () {
Expand Down
1 change: 1 addition & 0 deletions orochi/templates/website/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -716,6 +716,7 @@ <h5 class="offcanvas-title" id="leftNoteLabel">History Log</h5>
});
update_sidebar();
update_main_stage();
selected_plugin = null;
$.toast({
title: 'Index delete!',
content: 'Index has been deleted successfully.',
Expand Down
2 changes: 1 addition & 1 deletion orochi/templates/website/partial_folder.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
{{ form.media }}
{% csrf_token %}
<div class="modal-header">
<h5 class="modal-title">Create a new foledr</h5>
<h5 class="modal-title">Create a new folder</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
Expand Down
80 changes: 61 additions & 19 deletions orochi/templates/website/partial_info.html
Original file line number Diff line number Diff line change
@@ -1,25 +1,67 @@
<div class="modal-header">
<h4>{{dump.name}}</h4>
<h4>
{{dump.name}}
</h4>
</div>
<div class="modal-body">
<dl class="row small-dlg">
<dt class="col-sm-3">md5</dt>
<dd class="col-sm-9">{{dump.md5}}</dd>
<dt class="col-sm-3">sha256</dt>
<dd class="col-sm-9">{{dump.sha256}}</dd>
<dt class="col-sm-3">Size</dt>
<dd class="col-sm-9">{{dump.size}}</dd>
<dt class="col-sm-3">Index</dt>
<dd class="col-sm-9">{{dump.index}}</dd>
<dt class="col-sm-3">Filepath</dt>
<dd class="col-sm-9">{{dump.upload}}</dd>
{% if dump.comment %}<dt class="col-sm-3">Comment</dt>
<dd class="col-sm-9">{{dump.comment}}</dd>{% endif %}
{% if dump.banner %}<dt class="col-sm-3">Banner</dt>
<dd class="col-sm-9">{{dump.banner}}</dd>{% endif %}
{% if dump.suggested_symbols_path %}<dt class="col-sm-3">Suggested Symbol Path</dt>
<dd class="col-sm-9">{{dump.suggested_symbols_path.0}}</dd>{% endif %}
</dl>

{% if dump.regipy_plugins %}
<ul class="nav nav-tabs" id="myTab" role="tablist">
<li class="nav-item" role="presentation">
<button class="nav-link active" id="home-tab" data-bs-toggle="tab" data-bs-target="#home" type="button"
role="tab" aria-controls="home" aria-selected="true">Details</button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link" id="regipy-tab" data-bs-toggle="tab" data-bs-target="#regipy" type="button"
role="tab" aria-controls="regipy" aria-selected="false">Regipy Plugins</button>
</li>
</ul>
{% endif %}
<div class="tab-content">
<div class="tab-pane fade show active" id="home" role="tabpanel" aria-labelledby="home-tab">
<dl class="row small-dlg">
<dt class="col-sm-3">md5</dt>
<dd class="col-sm-9">{{dump.md5}}</dd>
<dt class="col-sm-3">sha256</dt>
<dd class="col-sm-9">{{dump.sha256}}</dd>
<dt class="col-sm-3">Size</dt>
<dd class="col-sm-9">{{dump.size}}</dd>
<dt class="col-sm-3">Index</dt>
<dd class="col-sm-9">{{dump.index}}</dd>
<dt class="col-sm-3">Filepath</dt>
<dd class="col-sm-9">{{dump.upload}}</dd>
{% if dump.comment %}<dt class="col-sm-3">Comment</dt>
<dd class="col-sm-9">{{dump.comment}}</dd>{% endif %}
{% if dump.banner %}<dt class="col-sm-3">Banner</dt>
<dd class="col-sm-9">{{dump.banner}}</dd>{% endif %}
{% if dump.suggested_symbols_path %}<dt class="col-sm-3">Suggested Symbol Path</dt>
<dd class="col-sm-9">{{dump.suggested_symbols_path.0}}</dd>{% endif %}
</dl>
</div>
<div class="tab-pane fade" id="regipy" role="tabpanel" aria-labelledby="regipy-tab">
{% if dump.regipy_plugins %}
<div class="accordion" id="regylist">
{% for item in dump.regipy_plugins %}
<div class="accordion-item">
<h2 class="accordion-header" id="heading{{forloop.counter}}">
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse"
data-bs-target="#collapse{{forloop.counter}}" aria-expanded="true"
aria-controls="collapse{{forloop.counter}}">
{{item.plugin}}@{{item.hive}}
</button>
</h2>
<div id="collapse{{forloop.counter}}" class="accordion-collapse collapse"
aria-labelledby="heading{{forloop.counter}}" data-bs-parent="#regylist">
<div class="accordion-body">
<pre>{{item.data|pprint}}</pre>
</div>
</div>
</div>
{% endfor %}
</div>
{% endif %}
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
Expand Down
6 changes: 4 additions & 2 deletions orochi/utils/plugin_install.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,9 +54,11 @@ def install_process(bash_script, reqs_script, tmp_folder):
_ = contexts.Context()
_ = framework.import_files(volatility3.plugins, True)
plugin_names = [
x for x in framework.list_plugins() if x.startswith(f"custom.{py_name}")
{x: y}
for x, y in framework.list_plugins().items()
if x.startswith(f"custom.{py_name}")
]
logging.debug(f"Installed plugins: {','.join(plugin_names)}")
logging.debug("Plugins installed successfully.")
return plugin_names
except Exception as e:
logging.error(f"Error installing plugin: {e}")
Expand Down
68 changes: 52 additions & 16 deletions orochi/utils/volatility_dask_elk.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,12 @@
from django.conf import settings
from elasticsearch import Elasticsearch, helpers
from elasticsearch_dsl import Search
from regipy.exceptions import RegistryParsingException
from regipy.exceptions import (
NoRegistrySubkeysException,
RegistryKeyNotFoundException,
RegistryParsingException,
)
from regipy.plugins.utils import dump_hive_to_json, run_relevant_plugins
from regipy.registry import RegistryHive
from volatility3 import cli, framework
from volatility3.cli.text_renderer import (
Expand Down Expand Up @@ -252,7 +257,7 @@ async def run_vt(filepath):
to_check = hash_checksum(filepath)[0]
report = await client.get_object_async(f"/files/{to_check}")
if report := report.to_dict().get("attributes"):
stats = {k: v for k, v in report.get("last_analysis_stats", {}).items()}
stats = dict(report.get("last_analysis_stats", {}).items())
if scan_date := report.get("last_analysis_date"):
scan_date = datetime.datetime.fromtimestamp(scan_date).strftime(
"%m/%d/%Y"
Expand All @@ -272,12 +277,14 @@ async def run_vt(filepath):
return


def run_regipy(filepath):
def run_regipy(filepath, plugins=False):
"""
Runs regipy on filepath
"""
try:
registry_hive = RegistryHive(filepath)
*a, index, _, hive_name = filepath.split("/")
dump = Dump.objects.get(index=index)
data = []
try:
data.extend(
Expand All @@ -288,15 +295,32 @@ def run_regipy(filepath):
)
except RegistryParsingException as e:
logging.error(e)
root = {"values": data}
root = json.loads(json.dumps(root).replace(r"\u0000", ""))
with open(f"{filepath}.regipy.json", "w") as f:
json.dump(root, f)
json.dump(json.loads(json.dumps(data).replace(r"\u0000", "")), f)
if plugins:
try:
if plugins_data := run_relevant_plugins(registry_hive, as_json=True):
for plugin, plugin_data in plugins_data.items():
if plugin_data:
info = {
"hive": hive_name,
"plugin": plugin,
"data": plugin_data,
}
dump.regipy_plugins.append(info)
dump.save()
except (
ModuleNotFoundError,
RegistryParsingException,
RegistryKeyNotFoundException,
NoRegistrySubkeysException,
) as e:
logging.error(e)
except Exception as e:
logging.error(e)


def run_plugin(dump_obj, plugin_obj, params=None, user_pk=None):
def run_plugin(dump_obj, plugin_obj, params=None, user_pk=None, regipy_plugins=False):
"""
Execute a single plugin on a dump with optional params.
If success data are sent to elastic.
Expand Down Expand Up @@ -324,8 +348,8 @@ def run_plugin(dump_obj, plugin_obj, params=None, user_pk=None):
ctx.config["automagic.LayerStacker.stackers"] = stacker.choose_os_stackers(
plugin
)
# LOCAL DUMPS REQUIRES FILES
local_dump = plugin_obj.local_dump
# LOCAL DUMPS REQUIRES FILES - Also regipy plugins
local_dump = plugin_obj.local_dump or regipy_plugins

# ADD PARAMETERS, AND IF LOCAL DUMP ENABLE ADD DUMP TRUE BY DEFAULT
plugin_config_path = interfaces.configuration.path_join(
Expand Down Expand Up @@ -508,14 +532,16 @@ def run_plugin(dump_obj, plugin_obj, params=None, user_pk=None):
)

# RUN VT AND REGIPY ON CREATED FILES
if plugin_obj.vt_check or plugin_obj.regipy_check:
if plugin_obj.vt_check or plugin_obj.regipy_check or regipy_plugins:
dask_client = get_client()
for file_id in file_list:
output_path = f"{local_path}/{file_id.preferred_filename}"
if plugin_obj.vt_check:
fire_and_forget(dask_client.submit(run_vt, output_path))
if plugin_obj.regipy_check:
fire_and_forget(dask_client.submit(run_regipy, output_path))
if plugin_obj.regipy_check or regipy_plugins:
fire_and_forget(
dask_client.submit(run_regipy, output_path, regipy_plugins)
)

# EVERYTHING OK
result = Result.objects.get(plugin=plugin_obj, dump=dump_obj)
Expand Down Expand Up @@ -790,6 +816,7 @@ def unzip_then_run(dump_pk, user_pk, password, restart, move):
if banner:
banner.result = RESULT_STATUS_RUNNING
banner.save()
logging.info(f"[dump {dump_pk}] Running banners plugin")
run_plugin(dump, banner.plugin)
time.sleep(1)
banner_result = get_banner(banner)
Expand All @@ -799,16 +826,25 @@ def unzip_then_run(dump_pk, user_pk, password, restart, move):
f"[dump {dump_pk}] guessed banner '{dump.banner}'"
)
dump.save()
elif dump.operating_system == "Windows":
regipy = dump.result_set.get(
plugin__name="windows.registry.hivelist.HiveList"
)
logging.info(f"[dump {dump_pk}] Running regipy plugins")
run_plugin(dump, regipy.plugin, regipy_plugins=True)

if restart or check_runnable(dump.pk, dump.operating_system, dump.banner):
dask_client = get_client()
secede()
tasks = []
tasks_list = (
if dump.operating_system == "Linux":
tasks_list = dump.result_set.exclude(plugin__name="banners.Banners")
elif dump.operating_system == "Windows":
tasks_list = dump.result_set.exclude(
plugin__name="windows.registry.hivelist.HiveList"
)
else:
dump.result_set.all()
if dump.operating_system != "Linux"
else dump.result_set.exclude(plugin__name="banners.Banners")
)
if restart:
tasks_list = tasks_list.filter(plugin__pk__in=restart)
for result in tasks_list:
Expand Down
34 changes: 25 additions & 9 deletions orochi/website/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@
)

from orochi.utils.plugin_install import plugin_install
from orochi.website.models import Bookmark, Dump, Folder, Plugin
from orochi.website.defaults import RESULT_STATUS_NOT_STARTED
from orochi.website.models import Bookmark, Dump, Folder, Plugin, Result, UserPlugin


######################################
Expand Down Expand Up @@ -162,7 +163,7 @@ def __init__(self, *args, **kwargs):
required=not field["optional"],
)
self.fields[field["name"]].help_text = (
"List of '{}' comma separated".format(field["type"].__name__)
f"""List of '{field["type"].__name__}' comma separated"""
)


Expand Down Expand Up @@ -223,13 +224,28 @@ class Meta:

def save(self, commit=True):
plugin = self.cleaned_data["plugin"]
plugin_name = plugin_install(plugin.file.path)
plugin = super(PluginCreateAdminForm, self).save(commit=commit)
plugin.name = plugin_name
plugin.local = True
plugin.local_date = datetime.now()
plugin.save()
return plugin
plugin_names = plugin_install(plugin.file.path)
first = None
for plugin_data in plugin_names:
plugin_name, plugin_class = list(plugin_data.items())[0]
plugin = super(PluginCreateAdminForm, self).save(commit=commit)
plugin.comment = plugin_class.__doc__
plugin.name = plugin_name
plugin.local = True
plugin.local_date = datetime.now()
plugin.save()
for user in get_user_model().objects.all():
UserPlugin.objects.get_or_create(user=user, plugin=plugin)
for dump in Dump.objects.all():
if plugin.operating_system in [dump.operating_system, "Other"]:
Result.objects.update_or_create(
dump=dump,
plugin=plugin,
defaults={"result": RESULT_STATUS_NOT_STARTED},
)
if not first:
first = plugin
return first


class PluginEditAdminForm(FileFormMixin, forms.ModelForm):
Expand Down
25 changes: 25 additions & 0 deletions orochi/website/migrations/0054_dump_regipy_plugins.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Generated by Django 5.0.3 on 2024-03-20 13:59

import django.contrib.postgres.fields
from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
("website", "0053_create_default_groups"),
]

operations = [
migrations.AddField(
model_name="dump",
name="regipy_plugins",
field=django.contrib.postgres.fields.ArrayField(
base_field=models.JSONField(blank=True, null=True),
blank=True,
null=True,
size=None,
default=list,
),
),
]
3 changes: 3 additions & 0 deletions orochi/website/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,9 @@ class Dump(models.Model):
)
banner = models.CharField(max_length=500, blank=True, null=True)
upload = models.FileField(upload_to="uploads")
regipy_plugins = ArrayField(
models.JSONField(blank=True, null=True), blank=True, null=True, default=list
)
folder = models.ForeignKey(Folder, on_delete=models.SET_NULL, blank=True, null=True)
comment = models.TextField(blank=True, null=True)
description = models.TextField(blank=True, null=True)
Expand Down
6 changes: 4 additions & 2 deletions orochi/website/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -327,10 +327,12 @@ def install_plugin(request):
f.write(r.content)
f.close()
if plugin_names := plugin_install(f.name):
for plugin_name in plugin_names:
for plugin_data in plugin_names:
plugin_name, plugin_class = list(plugin_data.items())[0]
plugin, _ = Plugin.objects.update_or_create(
name=plugin_name,
defaults={
"comment": plugin_class.__doc__,
"operating_system": operating_system,
"local": True,
"local_date": datetime.now(),
Expand Down Expand Up @@ -839,7 +841,7 @@ def json_view(request, filepath):
):
raise Http404("404")
with open(filepath, "r") as f:
values = json.load(f).get("values", [])
values = json.load(f)
context = {"data": json.dumps(values)}
return render(request, "website/json_view.html", context)

Expand Down
Loading

0 comments on commit 4aabe06

Please sign in to comment.