While working in a SOC, I encountered several alerts for mimikatz activity. Upon further analysis, the mimikatz activity turned out to be a PowerShell script that simply contained the string 'mimikatz' and other references to red team tools.
Script file s1_powersploit_indicators_script.ps1
contains all of the PS code I could find and I encourage you to start reading this file.
Adam's SentinelOne PowerSploit Indicator Script was pieced together after finding the following PowerShell logs:
-
part1_S1_PS_script_block_log.txt : Windows PowerShell Scriptblock log; event id 4104
-
part2_S1_PS_script_block_log.txt : Windows PowerShell Scriptblock log; event id 4104
-
part3_S1_PS_script_block_log.txt : Windows PowerShell Scriptblock log; event id 4104
senstive information has been redacted.
Within this repository, I have prepared 3 different files for analysis:
-
s1_powersploit_indicators_script.ps1
- This is the original PowerShell script that was pieced together from log messages
- All line numbers listed in this readme reference code in this file
-
s1_powersploit_indicators_deob_script.ps1
- This is the original PowerShell script after removing the basic obfuscation techniques
- The base64 has been decoded
- Any PowerShell aliases were rewritten in their long form name
- This is the original PowerShell script after removing the basic obfuscation techniques
-
s1_powersploit_indicators_deob_comments_script.ps1
- This is the deobfuscated script with my comments added to help explain the activity seen
-
Executes hook function calls starting on line 269
- Calls function
Set-HookFunctionTabs
- line 71- Calls function Get-Params - line 3
- Loads the hook function in the current session - line 88
- Calls function
-
Executes every
Set-PSBreakpoint
command starting on line 286- Sets up breakpoints on various commands that might execute and various variables that might change in the current session
-
Variable
$local:PowerSploitIndicators
is declared - line 871- Contains list of strings for various malicious commands
-
Executes a for-loop to set up more PSBreakpoints for each command in
$local:PowerSploitIndicators
- line 873 -
Changes the ExecutionPolicy for the current session to undefined - line 892
- 'Undefined' is the default execution policy for a session
So far all the script does is setup hook functions and setup PS break points.
If those break points fire, they will try to output a file.
So what is the point of this script?
Well.. lets assume this script loads in the background every time a new PS session is started.
The hook functions prevent an actor from using the following commands in the session:
Get-PSBreakpoint
New-Object
Set-ExecutionPolicy
Remove-PSBreakpoint
Disable-PSBreakpoint
Enable-PSBreakpoint
This is key, because without these functions you can't mess with the PS break points or create new objects with command New-Object.
If any of the commands are executed or variables changed that this script is looking for, then a new file is created.
My theory is that this script is simply checking for malicious commands executed in the current session. Then another process checks for these new files.
Every Set-PSBreakpoint command has the potential to create a new blank file in directory path ":::::\windows\sentinel\1"
You can see command '' | out-file ':::::\windows\sentinel\1'
in every Set-PSBreakpoint on lines: 300, 328, 355, 362, 381, 407, 475, 499, 532, 565, 583, 609, 636, 663, 690, 717, 742, 769, 786, 804, 821, 856, 879.
Wait! what is this file path reference?!
":::::\windows\sentinel\1"
Why are there 5 colons in the front of the path??
Executing this command by itself returns an error in PS.
Error: out-file : The given path's format is not supported.
However.. for the sake of this analysis. Lets assume the file path was correct and a new file was made.
Set-PSBreakpoint -Variable 'AgentDelay' -Mode write
Set-PSBreakpoint -Variable 'AgentJitter' -Mode write
Set-PSBreakpoint -Variable 'TaskURIs' -Mode write
Set-PSBreakpoint -Variable 'KillDays' -Mode write
Set-PSBreakpoint -Variable 'UserAgent' -Mode write
Eval: checks for specific variables being used.
'' | out-file ':::::\windows\sentinel\1'
Set-PSBreakpoint -Variable 'IDDELIMITER' -Mode write
Set-PSBreakpoint -Variable 'COMMANDSDELIMITER' -Mode write
Set-PSBreakpoint -Variable 'dojob' -Mode write
Set-PSBreakpoint -Command 'Init'
Set-PSBreakpoint -Variable 'HTMLReport' -Mode write
Set-PSBreakpoint -Command 'Get-RegAlwaysInstallElevated'
Set-PSBreakpoint -Command 'Get-VulnSchTask'
Set-PSBreakpoint -Command 'Get-VulnAutoRun'
Set-PSBreakpoint -Command 'Invoke-ServiceAbuse'
Set-PSBreakpoint -Command 'Get-ModifiableFile'
Set-PSBreakpoint -Command 'Invoke-AllChecks'
Eval: checks for ShinoBot or Eval: checks for commands installed
'' | out-file ':::::\windows\sentinel\3'
Set-PSBreakpoint -Command 'Get-DelegateType'
Set-PSBreakpoint -Command 'Get-Keystrokes'
Set-PSBreakpoint -Variable 'Powershell'
'' | out-file ':::::\windows\sentinel\2'
Set-PSBreakpoint -Command 'Get-ProcessTokenGroup'
'' | out-file ':::::\windows\sentinel\6'
Set-PSBreakpoint -Variable 'PSDefaultParameterValues' -Mode Read
'' | out-file ':::::\windows\sentinel\7'
Set-PSBreakpoint -Command $local:PowerSploitIndicators
'' | out-file ':::::\windows\sentinel\8'
Returning back to my theory from before. If another process is checking for these files, then what are they used for?
The unknown of what these files are used for makes this script mysterious.
Are they simply for creating a report? What about the multiple Set-PSBreakpoints that create the same file?
You will obviously notice the giant 2,317 character blob of base64 encoded text on lines 838 and 840.
I have decoded both of these for you using CyberChef
Okay.. but now i see these [chaR]([bytE]0x53
You can execute [chaR]([bytE]0x53
directly into PS which will convert a byte into a character
After this conversion, you are left with..
Line 838:
#Matt Graebers Reflection method
[Ref].Assembly.GetType('System.Management.Automation.AmsiUtils').GetField('amsilnitFailed','NonPublic,Static').SetValue($null,$true)
Line 840:
#Matt Graebers second Reflection method
[Ref].Assembly.GetType("System.Management.Automation.AmsiUtils").GetField("amsiContext",[Reflection.BindingFlags]"NonPublic,Static").GetValue($null)
You can view other variations of the Reflection method on GetRektBoy724 Github Gist
Within every Set-PSBreakpoint
command there is a code comment: <#sentinelbreakpoints#>
And every Out-File
command references parent directory '' | out-file ':::::\windows\sentinel\'
After searching around on SentienlOne's webisite, I found this article.. What Is Windows PowerShell?
In this article, SentinelOne explains PowerShell and then explains how dangerous PowerShell can be. SentinelOne even mentions and links directly to the PowerSploit Github!!
And they of course recommend their SentinelOne agent to mitigate the scary PowerShell.
Coincidence! I think not!