Local PowerShell Execution
Contents
Local PowerShell Execution#
Hypothesis#
Adversaries might be leveraging PowerShell to execute code within my environment
Technical Context#
Offensive Tradecraft#
Adversaries can use PowerShell to perform a number of actions, including discovery of information and execution of code. Therefore, it is important to understand the basic artifacts left when PowerShell is used in your environment.
Pre-Recorded Security Datasets#
Metadata |
Value |
---|---|
docs |
https://securitydatasets.com/notebooks/atomic/windows/execution/SDWIN-190518182022.html |
link |
Download Dataset#
import requests
from zipfile import ZipFile
from io import BytesIO
url = 'https://raw.githubusercontent.com/OTRF/Security-Datasets/master/datasets/atomic/windows/execution/host/empire_launcher_vbs.zip'
zipFileRequest = requests.get(url)
zipFile = ZipFile(BytesIO(zipFileRequest.content))
datasetJSONPath = zipFile.extract(zipFile.namelist()[0])
Read Dataset#
import pandas as pd
from pandas.io import json
df = json.read_json(path_or_buf=datasetJSONPath, lines=True)
Analytics#
A few initial ideas to explore your data and validate your detection logic:
Analytic I#
Within the classic PowerShell log, event ID 400 indicates when a new PowerShell host process has started. You can filter on powershell.exe as a host application if you want to or leave it without a filter to capture every single PowerShell host.
Data source |
Event Provider |
Relationship |
Event |
---|---|---|---|
Powershell |
Windows PowerShell |
Application host started |
400 |
Powershell |
Microsoft-Windows-PowerShell/Operational |
User started Application host |
4103 |
Logic#
SELECT `@timestamp`, Hostname
FROM dataTable
WHERE (Channel = "Microsoft-Windows-PowerShell/Operational" OR Channel = "Windows PowerShell")
AND (EventID = 400 OR EventID = 4103)
Pandas Query#
(
df[['@timestamp','Hostname']]
[((df['Channel'] == 'Windows PowerShell') | (df['Channel'] == 'Microsoft-Windows-PowerShell/Operational'))
& (
(df['EventID'] == 400)
| (df['EventID'] == 4103)
)
]
.head()
)
Analytic II#
Look for non-interactive powershell session might be a sign of PowerShell being executed by another application in the background.
Data source |
Event Provider |
Relationship |
Event |
---|---|---|---|
Process |
Microsoft-Windows-Security-Auditing |
Process created Process |
4688 |
Logic#
SELECT `@timestamp`, Hostname, NewProcessName, ParentProcessName
FROM dataTable
WHERE LOWER(Channel) = "security"
AND EventID = 4688
AND NewProcessName LIKE "%powershell.exe"
AND NOT ParentProcessName LIKE "%explorer.exe"
Pandas Query#
(
df[['@timestamp','Hostname','NewProcessName','ParentProcessName']]
[(df['Channel'].str.lower() == 'security')
& (df['EventID'] == 4688)
& (df['NewProcessName'].str.lower().str.endswith('powershell.exe', na=False))
& (~df['ParentProcessName'].str.lower().str.endswith('explorer.exe', na=False))
]
.head()
)
Analytic III#
Look for non-interactive powershell session might be a sign of PowerShell being executed by another application in the background.
Data source |
Event Provider |
Relationship |
Event |
---|---|---|---|
Process |
Microsoft-Windows-Sysmon/Operational |
Process created Process |
1 |
Logic#
SELECT `@timestamp`, Hostname, Image, ParentImage
FROM dataTable
WHERE Channel = "Microsoft-Windows-Sysmon/Operational"
AND EventID = 1
AND Image LIKE "%powershell.exe"
AND NOT ParentImage LIKE "%explorer.exe"
Pandas Query#
(
df[['@timestamp','Hostname','Image','ParentImage']]
[(df['Channel'] == 'Microsoft-Windows-Sysmon/Operational')
& (df['EventID'] == 1)
& (df['Image'].str.lower().str.endswith('powershell.exe', na=False))
& (~df['ParentImage'].str.lower().str.endswith('explorer.exe', na=False))
]
.head()
)
Analytic IV#
Monitor for processes loading PowerShell DLL *system.management.automation*
.
Data source |
Event Provider |
Relationship |
Event |
---|---|---|---|
Module |
Microsoft-Windows-Sysmon/Operational |
Process loaded Dll |
7 |
Logic#
SELECT `@timestamp`, Hostname, Image, ImageLoaded
FROM dataTable
WHERE Channel = "Microsoft-Windows-Sysmon/Operational"
AND EventID = 7
AND (lower(Description) = "system.management.automation" OR lower(ImageLoaded) LIKE "%system.management.automation%")
Pandas Query#
(
df[['@timestamp','Hostname','Image','ImageLoaded']]
[(df['Channel'] == 'Microsoft-Windows-Sysmon/Operational')
& (df['EventID'] == 7)
& (
(df['Description'].str.lower() == 'system.management.automation')
| (df['ImageLoaded'].str.lower().str.contains('.*system.management.automation.*', regex=True))
)
]
.head()
)
Analytic V#
Monitoring for PSHost* pipes is another interesting way to find PowerShell execution.
Data source |
Event Provider |
Relationship |
Event |
---|---|---|---|
Named Pipe |
Microsoft-Windows-Sysmon/Operational |
Process created Pipe |
17 |
Logic#
SELECT `@timestamp`, Hostname, Image, PipeName
FROM dataTable
WHERE Channel = "Microsoft-Windows-Sysmon/Operational"
AND EventID = 17
AND lower(PipeName) LIKE "\\\\pshost%"
Pandas Query#
(
df[['@timestamp','Hostname','Image','PipeName']]
[(df['Channel'] == 'Microsoft-Windows-Sysmon/Operational')
& (df['EventID'] == 17)
& (df['PipeName'].str.lower().str.startswith('\pshost', na=False))
]
.head()
)
Analytic VI#
The PowerShell Named Pipe IPC
event will indicate the name of the PowerShell AppDomain that started. Sign of PowerShell execution.
Data source |
Event Provider |
Relationship |
Event |
---|---|---|---|
Powershell |
Microsoft-Windows-PowerShell/Operational |
Application domain started |
53504 |
Logic#
SELECT `@timestamp`, Hostname, Message
FROM dataTable
WHERE Channel = "Microsoft-Windows-PowerShell/Operational"
AND EventID = 53504
Pandas Query#
(
df[['@timestamp','Hostname','Message']]
[(df['Channel'] == 'Microsoft-Windows-PowerShell/Operational')
& (df['EventID'] == 53504)
]
.head()
)
Known Bypasses#
False Positives#
Hunter Notes#
Explore the data produced in your environment with the analytics above and document what normal looks like from a PowerShell perspective.
If execution of PowerShell happens all the time in your environment, I suggest to categorize the data you collect by business unit to build profiles and be able to filter out potential noise.
You can also stack the values of the command line arguments being used. You can hash the command line arguments too and stack the values.
Hunt Output#
Type |
Link |
---|---|
Sigma Rule |
|
Sigma Rule |