Skip to content

Commit

Permalink
Incorperating command output from CS logs
Browse files Browse the repository at this point in the history
Removed deduplication filter from CS logs as it was hiding cases where a command failed initially then worked on retry.
  • Loading branch information
neonbunny committed Oct 10, 2024
1 parent 13b4911 commit 6262f2c
Show file tree
Hide file tree
Showing 3 changed files with 33 additions and 9 deletions.
15 changes: 15 additions & 0 deletions cobalt_strike_monitor/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

from django.contrib.contenttypes.fields import GenericRelation
from django.db import models
from django.db.models import Q
from django.utils.html import format_html, escape
from django.utils.timezone import now

Expand Down Expand Up @@ -250,6 +251,20 @@ def associated_beaconlog_input(self):
return BeaconLog.objects.filter(type="input", when__lte=self.when, beacon=self.beacon) \
.order_by("-when").first()

@property
def associated_beaconlog_output(self):
next_output_generator = BeaconLog.objects.filter(Q(type="input") | Q(type="task")) \
.filter(when__gt=timedelta(seconds=1) + self.when, beacon=self.beacon) \
.order_by("when")

outputs_between = BeaconLog.objects.filter(Q(type="output") | Q(type="error")) \
.filter(id__gte=self.id, beacon=self.beacon)

if next_output_generator.exists():
outputs_between = outputs_between.filter(id__lt=next_output_generator.first().id)

return outputs_between

@property
def associated_archive_tasks(self):
return Archive.objects.filter(type="task", when__gte=self.when, when__lt=self.when + timedelta(seconds=2), beacon=self.beacon) \
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
{% block head %}
{% include "base/external-libs/jquery.html" %}
{% include "base/external-libs/datatables-pdfexport.html" %}
<script src="https://cdn.jsdelivr.net/npm/[email protected]/jquery.expander.min.js" integrity="sha256-BsvwB7vfZSW/iWEZ7qtnb6RRILiSZ5ij8X+XoxCsPXk=" crossorigin="anonymous" referrerpolicy="no-referrer"></script>

<style nonce="{{request.csp_nonce}}">
.fa-ul {
Expand Down Expand Up @@ -41,6 +42,10 @@
margin-top: 1rem;
margin-bottom: 0;
}

.output {
font-style: italic;
}
</style>
{% endblock head %}

Expand Down Expand Up @@ -171,7 +176,8 @@
null,
null,
{ orderable: false },
]
],
drawCallback: function(settings) { $('.output').expander({slicePoint: 200}); }
} )
})
</script>
Expand Down
19 changes: 11 additions & 8 deletions event_tracker/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
from django.http import JsonResponse, HttpResponse, HttpRequest
from django.shortcuts import render, get_object_or_404, redirect
from django.template.defaultfilters import truncatechars_html
from django.utils import timezone
from django.utils import timezone, html
from django.utils.dateparse import parse_datetime
from django.utils.html import escape
from django.utils.safestring import mark_safe
Expand Down Expand Up @@ -945,12 +945,10 @@ class CSLogsListJSON(PermissionRequiredMixin, FilterableDatatableView):
filter_column_mapping = {'Timestamp': 'when'}

def apply_row_filter(self, qs):
# Removed hidden beacons, prefetches beacon data, and dedupes data
return (qs.filter(beacon__in=Beacon.visible_beacons()) # Hide filtered beacons
.select_related("beacon").select_related("beacon__listener") # Prefetch beacon data
.exclude(data="") # Remove empty rows
.annotate(prev_data=Window(expression=Lag('data', default=Value('')), partition_by=F("beacon")),
).exclude(data=F("prev_data"))) # Deduplicate data
# Removed hidden beacons, prefetches beacon data, remove empty rows
return qs.filter(beacon__in=Beacon.visible_beacons()) \
.select_related("beacon").select_related("beacon__listener") \
.exclude(data="")

def get_initial_queryset(self):
# Rows with type task where there is no input at the same time nor 1 second earlier
Expand Down Expand Up @@ -1001,6 +999,8 @@ def render_column(self, row, column):
if row.type == "input":
result += f"<pre><code>{row.data}</code></pre>"

result += f"<pre class='output'><code>{html.escape("\n".join(row.associated_beaconlog_output.values_list('data', flat=True)))}</code><pre>"

return result
elif column == '': # The column with button in
if row.event_mappings.exists() and self.request.user.has_perm('event_tracker.change_event'):
Expand Down Expand Up @@ -1395,6 +1395,9 @@ def get_initial(self):
else:
operator = None

input_evidence = cs_archive.data
output_evidence = "\n".join(cs_archive.associated_beaconlog_output.values_list('data', flat=True))

return {
"task": task,
"timestamp": timezone.localtime(cs_archive.when).strftime("%Y-%m-%dT%H:%M"),
Expand All @@ -1405,7 +1408,7 @@ def get_initial(self):
"mitre_attack_technique": technique,
"mitre_attack_subtechnique": subtechnique,
"description": cs_archive.associated_archive_tasks_description,
"raw_evidence": cs_archive.data if cs_archive.type == "input" else None
"raw_evidence": f"{input_evidence}{'\n\n' + output_evidence if output_evidence else ''}" if cs_archive.type == "input" else None
}

def get_context_data(self, **kwargs):
Expand Down

0 comments on commit 6262f2c

Please sign in to comment.