Intro
Due to its deep roots on Windows systems and access to .NET libraries, Powershell is a powerful tool for administrators and attackers alike. It offers a wide range of possibilities to write regular and malicious script code and obfuscate it to the point of unreadability and undetectability by antiviruses. Moreover the code can be executed directly in memory, so that no files and further traces are left on the drive.
The using of Powershell allows an attacker initial access to the victim system - for example through phishing attachments - as well as to move laterally on the victim network and inject further malicious code.
So it is not surprising that this tool stands at the top of the attacker tool list. Be it through self-written and obfuscated code or through frameworks, such as Empire and PowerSploit.
In this blog post I will show many of the attacker methods to obfuscate and hide malicious Powershell code and how to detect and analyze it. The shown obfuscation techniques must not stand alone but can also be used in combination.
Powershell
Powershell offers a variety of ways to obfuscate the script code. In the following the language-specific features of Powershell are described, that are suitable for script obfuscation.
Function Overwriting
Powershell offers built-in functions (Cmdlets) that can be overridden in the profiles. This way malicious code can be added to a regular Cmdlet, which is executed in the background when the Cmdlet is called.
In the following example the build-in function "Get-ChildItem" is extended through the profile.
function Get-ChildItem {
Microsoft.PowerShell.Management\Get-ChildItem
# malicious code...
Write-Output "[+] Malicious code executed!"
}
# PS> Get-ChildItem
#
# Directory: C:\Users\Victim\Desktop\Files
#
# Mode LastWriteTime Length Name
# ---- ------------- ------ ----
# -a---- 01.05.2023 13:05 0 file1
# -a---- 01.05.2023 13:05 0 file2
# -a---- 01.05.2023 13:05 0 file3
#
# [+] Malicious code executed!
Aliases
Powershell allows a large number of alternative notations and abbreviations for built-in functions, which can be viewed using the 'Get-Alias' command. Parameters can also be abbreviated as desired, as long as there are no ambiguities (e.g. 'ExecutionPolicy' to 'ep' or 'exe'). All of the commands listed below result in identical output.
Write-Output "Malicious code executed!"
write "Malicious code executed!"
echo "Malicious code executed!"
# PS> Malicious code executed!
Calloperators, Wildcards
With the call operators ('&' or '.') a function can be called. In combination with the 'Get-Command' function (abbr. 'gcm'), which enables the command search with the wildcard character '*', a command call can be shortened and thus obfuscated.
&(gcm *te-Ou*) "Malicious code executed!"
# PS> Malicious code executed!
Case, Escape-, Whitespacecharacters
There is no distinction between upper and lower case. Spaces are ignored regardless of their length, allowing malicious code to be hidden at the end of a regular command. The backtick '`' is used as an escape character. Unless this is used before a predefined character such as '`n' for a new line or '`t' for a tab, it is also ignored.
&("`wR"+ "It`e" + "-o"+"ut`Put") "`Ma`licious` code e`xecuted`!"
# PS> Malicious code executed!
.NET-Functions
Through .NET access, the .NET functions can also be addressed instead of native Powershell functions.
[System.Console]::WriteLine("Malicious code executed!")
# PS> Malicious code executed!
Specialcharacters
Another obfuscation option is to assign special character variables '${}' to integers, which can be incremented through the increment operator '++' and casted to suitable characters. The special variable assignment using '${}' allows the use of special characters (including spaces) as variable identifiers. The cast operator and required Cmdlets - such as 'iex' - can be assembled using the subexpression operator '$()', method resolutions and array index operators.
The following special character command was obfuscated with Invoke-Obfuscation by Daniel Bohannon.
${~%=}=+$();${~^}=${~%=};${=[}=++${~%=};${+-}=++${~%=};${=-^}=++${~%=};${*]}=++${~%=};${~``}=++${~%=};${)^}=++${~%=};${)}=++${~%=};${$^}=++${~%=};${.]}=++${~%=};${``}="["+"$(@{})"[${)}]+"$(@{})"["${=[}${.]}"]+"$(@{})"["${+-}${~^}"]+"$?"[${=[}]+"]";${~%=}="".("$(@{})"["${=[}${*]}"]+"$(@{})"["${=[}${)^}"]+"$(@{})"[${~^}]+"$(@{})"[${*]}]+"$?"[${=[}]+"$(@{})"[${=-^}]);${~%=}="$(@{})"["${=[}"+"${*]}"]+"$(@{})"[${*]}]+"${~%=}"["${+-}"+"${)}"];.${~%=}("${``}${$^}${)}+${``}${=[}${=[}${*]}+${``}${=[}${~^}${~``}+${``}${=[}${=[}${)^}+${``}${=[}${~^}${=[}+${``}${*]}${~``}+${``}${)}${.]}+${``}${=[}${=[}${)}+${``}${=[}${=[}${)^}+${``}${=[}${=[}${+-}+${``}${=[}${=[}${)}+${``}${=[}${=[}${)^}+${``}${=-^}${+-}+${``}${=-^}${.]}+${``}${)}${)}+${``}${.]}${)}+${``}${=[}${~^}${$^}+${``}${=[}${~^}${~``}+${``}${.]}${.]}+${``}${=[}${~^}${~``}+${``}${=[}${=[}${=[}+${``}${=[}${=[}${)}+${``}${=[}${=[}${~``}+${``}${=-^}${+-}+${``}${.]}${.]}+${``}${=[}${=[}${=[}+${``}${=[}${~^}${~^}+${``}${=[}${~^}${=[}+${``}${=-^}${+-}+${``}${=[}${~^}${=[}+${``}${=[}${+-}${~^}+${``}${=[}${~^}${=[}+${``}${.]}${.]}+${``}${=[}${=[}${)}+${``}${=[}${=[}${)^}+${``}${=[}${~^}${=[}+${``}${=[}${~^}${~^}+${``}${=-^}${=-^}+${``}${=-^}${.]}|${~%=}")
# PS> Malicious code executed!
The Powershell code after resolving the special characters:
[CHar]87+[CHar]114+[CHar]105+[CHar]116+[CHar]101+[CHar]45+[CHar]79+[CHar]117+[CHar]116+[CHar]112+[CHar]117+[CHar]116+[CHar]32+[CHar]39+[CHar]77+[CHar]97+[CHar]108+[CHar]105+[CHar]99+[CHar]105+[CHar]111+[CHar]117+[CHar]115+[CHar]32+[CHar]99+[CHar]111+[CHar]100+[CHar]101+[CHar]32+[CHar]101+[CHar]120+[CHar]101+[CHar]99+[CHar]117+[CHar]116+[CHar]101+[CHar]100+[CHar]33+[CHar]39|iex
# PS> Malicious code executed!
The final code after resolving the byte characters:
Write-Output 'Malicious code executed!' | iex
# PS> Malicious code executed!
Strings
In the following common string obfuscation techniques are shown, which can be used with any programming language in this or similar form.
Chaining
Characters can be concatenated by '-split', '-join' or the '+' sign.
Write-Output ("Malicious" + " " + "code" + " " + "executed!")
Write-Output ("MaliciousXXXcodeXXXexecuted!" -split "XXX" -join " ")
# PS> Malicious code executed!
Replacing
Character strings can be exchanged using the '-replace' option.
Write-Output ("MaliciousXXXcodeXXXexecuted!" -replace("XXX"," "))
# PS> Malicious code executed!
The format operator '-f' can be used to rearrange strings based on number order.
Write-Output ("{1} {0} {2}" -f "code", "Malicious", "executed!")
# PS> Malicious code executed!
Reversing
Characters can be reversed via array slicing.
Write-Output (-join "!detucexe edoc suoicilaM"[24 .. 0])
# PS> Malicious code executed!
Encoding
Obfuscation can also be achieved using common encoding methods.
Base64
The code to be executed can be encoded and passed to Base64. The command encoded in this way can be started via Powershell with the '-EncodedCommand' option.
[Convert]::ToBase64String([Text.Encoding]::Unicode.GetBytes("Write-Output 'Malicious code executed!'")) | Write-Host
# PS> VwByAGkAdABlAC0ATwB1AHQAcAB1AHQAIAAnAE0AYQBsAGkAYwBpAG8AdQBzACAAYwBvAGQAZQAgAGUAeABlAGMAdQB0AGUAZAAhACcA
powershell -EncodedCommand "VwByAGkAdABlAC0ATwB1AHQAcAB1AHQAIAAnAE0AYQBsAGkAYwBpAG8AdQBzACAAYwBvAGQAZQAgAGUAeABlAGMAdQB0AGUAZAAhACcA"
# PS> Malicious code executed!
ASCII
ASCII-encoded commands can be executed after a byte conversion, for example via 'Invoke-Expression'.
[Text.Encoding]::ASCII.GetBytes("Write-Output 'Malicious code executed!'") | Out-String | ForEach-Object {$_.Replace("`n",",").TrimEnd(",")}
# PS> 87,114,105,116,101,45,79,117,116,112,117,116,32,39,77,97,108,105,99,105,111,117,115,32,99,111,100,101,32,101,120,101,99,117,116,101,100,33,39
Invoke-Expression( -join [char[]](87,114,105,116,101,45,79,117,116,112,117,116,32,39,77,97,108,105,99,105,111,117,115,32,99,111,100,101,32,101,120,101,99,117,116,101,100,33,39))
# PS> Malicious code executed!
Hex
The use of hex encoding is possible in a similar way.
[Text.Encoding]::UTF8.GetBytes("Write-Output 'Malicious code executed!'") | ForEach-Object {"'" + $_.ToString("X2") + "'"} | Out-String | ForEach-Object {$_.Replace("`n",",").TrimEnd(",")}
# PS> '57','72','69','74','65','2D','4F','75','74','70','75','74','20','27','4D','61','6C','69','63','69','6F','75','73','20','63','6F','64','65','20','65','78','65','63','75','74','65','64','21','27'
Invoke-Expression( -join ('57','72','69','74','65','2D','4F','75','74','70','75','74','20','27','4D','61','6C','69','63','69','6F','75','73','20','63','6F','64','65','20','65','78','65','63','75','74','65','64','21','27' | ForEach-Object { ([Convert]::ToInt16($_.ToString(),16) -as [char]) }))
# PS> Malicious code executed!
Octal
Similar with octal encoding.
[Text.Encoding]::UTF8.GetBytes("Write-Output 'Malicious code executed!'") | ForEach-Object { [Convert]::ToString($_, 8) } | Out-String | ForEach-Object {$_.Replace("`n",",").TrimEnd(",")}
# PS> 127,162,151,164,145,55,117,165,164,160,165,164,40,47,115,141,154,151,143,151,157,165,163,40,143,157,144,145,40,145,170,145,143,165,164,145,144,41,47
Invoke-Expression( -join (127,162,151,164,145,55,117,165,164,160,165,164,40,47,115,141,154,151,143,151,157,165,163,40,143,157,144,145,40,145,170,145,143,165,164,145,144,41,47 | ForEach-Object { ([Convert]::ToInt16($_.ToString(),8) -as [char]) }))
# PS> Malicious code executed!
Binary
And the same with binary encoding.
[Text.Encoding]::UTF8.GetBytes("Write-Output 'Malicious code executed!'") | ForEach-Object { [Convert]::ToString($_, 2) } | Out-String | ForEach-Object {$_.Replace("`n",",").TrimEnd(",")}
# PS> 1010111,1110010,1101001,1110100,1100101,101101,1001111,1110101,1110100,1110000,1110101,1110100,100000,100111,1001101,1100001,1101100,1101001,1100011,1101001,1101111,1110101,1110011,100000,1100011,1101111,1100100,1100101,100000,1100101,1111000,1100101,1100011,1110101,1110100,1100101,1100100,100001,100111
Invoke-Expression( -join (1010111,1110010,1101001,1110100,1100101,101101,1001111,1110101,1110100,1110000,1110101,1110100,100000,100111,1001101,1100001,1101100,1101001,1100011,1101001,1101111,1110101,1110011,100000,1100011,1101111,1100100,1100101,100000,1100101,1111000,1100101,1100011,1110101,1110100,1100101,1100100,100001,100111 | ForEach-Object { ([Convert]::ToInt16($_.ToString(),2) -as [char]) }))
# PS> Malicious code executed!
Encryption
XOR- and SecureString encryption are shown below. Because of the .NET interface all the other .NET encryption algorithms can also be used with Powershell.
XOR
A command can be obfuscated using XOR and converted back again. The following example performs an XOR operation on each byte of the instruction with the value '0x42'. The bytes can be reconverted with the same value and ASCII casting.
[Text.Encoding]::UTF8.GetBytes("Write-Output 'Malicious code executed!'") | ForEach-Object { $_ -bxor 0x42 } | Out-String | ForEach-Object {$_.Replace("`n",",").TrimEnd(",")}
# PS> 21,48,43,54,39,111,13,55,54,50,55,54,98,101,15,35,46,43,33,43,45,55,49,98,33,45,38,39,98,39,58,39,33,55,54,39,38,99,101
Invoke-Expression (-join (21,48,43,54,39,111,13,55,54,50,55,54,98,101,15,35,46,43,33,43,45,55,49,98,33,45,38,39,98,39,58,39,33,55,54,39,38,99,101 | ForEach-Object { [char]($_ -bxor 0x42) }))
# PS> Malicious code executed!
SecureString (AES)
A command can be AES encrypted by using the SecureString function.
# 16, 24 or 32 byte key size
$key = ([Text.Encoding]::Default.GetBytes("MySecretPassword"))
"Write-Output 'Malicious code executed!'" | ConvertTo-SecureString -AsPlainText -Force | ConvertFrom-SecureString -Key $key
# PS> 01000000d08c9ddf0115d1118c7a00c04fc297eb01000000178e91759fcacf468c330156d58c66bc000000000200000000001066000000010000200000003a3a0b242a307a48d03f6e105db3e209fa3b58021775721753365e5e72bc4f56000000000e8000000002000020000000cf4f9098c27d1aeeb7ba3ecd88252461b812830d3ea934b54e135c270c174a0e500000009e08b1ff1bc319d94ff2611253ecefdc46bb64e5a8a55ed2457cfc516ff6512cdebf9466e52452e4495e1582e2c048097fe6e317ad00692147dd03c8d6db7995028a534a807f22b44d720ebc80d9788a40000000c026f435426f31cdca4c087487fd32ea9ab53498ad8de3bfd2a73b27a59deb46f41c6670f3641a20323573e29ed04d0cc60d07ca3efcec0c6069bcda3d30edbb
Invoke-Expression( ([Runtime.InteropServices.Marshal]::PtrToStringAuto([Runtime.InteropServices.Marshal]::SecureStringToBSTR( ("01000000d08c9ddf0115d1118c7a00c04fc297eb01000000178e91759fcacf468c330156d58c66bc000000000200000000001066000000010000200000003a3a0b242a307a48d03f6e105db3e209fa3b58021775721753365e5e72bc4f56000000000e8000000002000020000000cf4f9098c27d1aeeb7ba3ecd88252461b812830d3ea934b54e135c270c174a0e500000009e08b1ff1bc319d94ff2611253ecefdc46bb64e5a8a55ed2457cfc516ff6512cdebf9466e52452e4495e1582e2c048097fe6e317ad00692147dd03c8d6db7995028a534a807f22b44d720ebc80d9788a40000000c026f435426f31cdca4c087487fd32ea9ab53498ad8de3bfd2a73b27a59deb46f41c6670f3641a20323573e29ed04d0cc60d07ca3efcec0c6069bcda3d30edbb" | ConvertTo-SecureString -Key $key)))))
# PS> Malicious code executed!
Compression
In Powershell the RAW- and GZip compressions are mainly used.
RAW
The command can be compressed and encoded as Base64. It can be decoded or decompressed in the reverse order.
$memory_stream = New-Object IO.MemoryStream
$compressor = New-Object IO.Compression.DeflateStream($memory_stream, [IO.Compression.CompressionMode]::Compress)
$stream_writer = New-Object IO.StreamWriter($compressor)
$stream_writer.Write("Write-Output 'Malicious code executed!'")
$stream_writer.Close()
[System.Convert]::ToBase64String($memory_stream.ToArray())
# PS> Cy/KLEnV9S8tKSgtUVD3TczJTM7MLy1WSM5PSVVIrUhNLi1JTVFUBwA=
Invoke-Expression(New-Object IO.StreamReader((New-Object IO.Compression.DeflateStream([IO.MemoryStream][Convert]::FromBase64String("Cy/KLEnV9S8tKSgtUVD3TczJTM7MLy1WSM5PSVVIrUhNLi1JTVFUBwA="),[IO.Compression.CompressionMode]::Decompress)),[Text.Encoding]::ASCII)).ReadToEnd()
# PS> Malicious code executed!
GZip
The compression is done the same as in RAW except that the 'GZipStream' function is used.
$memory_stream = New-Object IO.MemoryStream
$compressor = New-Object IO.Compression.GZipStream($memory_stream, [IO.Compression.CompressionMode]::Compress)
$stream_writer = New-Object IO.StreamWriter($compressor)
$stream_writer.Write("Write-Output 'Malicious code executed!'")
$stream_writer.Close()
[System.Convert]::ToBase64String($memory_stream.ToArray())
# PS> H4sIAAAAAAAEAAsvyixJ1fUvLSkoLVFQ903MyUzOzC8tVkjOT0lVSK1ITS4tSU1RVAcAcDv1sCcAAAA=
Invoke-Expression(New-Object IO.StreamReader((New-Object IO.Compression.GZipStream([IO.MemoryStream][Convert]::FromBase64String("H4sIAAAAAAAEAAsvyixJ1fUvLSkoLVFQ903MyUzOzC8tVkjOT0lVSK1ITS4tSU1RVAcAcDv1sCcAAAA="),[IO.Compression.CompressionMode]::Decompress)),[Text.Encoding]::ASCII)).ReadToEnd()
# PS> Malicious code executed!
Persistence
Powershell code can be hidden at operating system level, so that no malicious script files have to be applied to the victim system. This way persistence can be achieved on the system or the anti-virus protection can be bypassed.
The following shows ways in which Powershell code can be applied to the system and executed without dropping files.
Profile
As described at 'Function Overwriting' the native Powershell functions can be overwritten system- and user-wide using Powershell profiles. This way malicious code can be executed automatically as soon as Powershell is started. An automated start of Powershell in the background can be forced via the Task Scheduler or WMI for example.
The following code was saved in the user profile file, which is always executed automatically when Powershell is started.
# Microsoft.PowerShell_profile.ps1
Write-Output "Malicious code automatically executed from: $($PSCommandPath)"
# PS> Malicious code automatically executed from: C:\Users\Victim\Documents\WindowsPowerShell\Microsoft.PowerShell_profile.ps1
Task Scheduler
Powershell code can be persisted and executed from the Task Scheduler. In the following a Powershell window with the output listed below is displayed every minute.
$action = New-ScheduledTaskAction -Execute "powershell.exe" -Argument "-ep bypass -NoExit -c Write-Output 'Malicious code executed every minute!'"
$trigger = New-ScheduledTaskTrigger -Once -At (Get-Date) -RepetitionInterval (New-TimeSpan -Minutes 1)
$principal = New-ScheduledTaskPrincipal -UserId $env:username -LogonType Interactive
Register-ScheduledTask -TaskName "MALICIOUS" -Action $action -Trigger $trigger -Principal $principal
# PS> Malicious code executed every minute!
Registry
Powershell code can also be stored in the Registry. Here the command is first base64-encoded and stored in a newly created registry key 'XXX'. The value can then be read out with Powershell.
$reg_val = [Convert]::ToBase64String([Text.Encoding]::Unicode.GetBytes("Write-Output 'Malicious code executed!'"))
New-Item "HKCU:\SOFTWARE" -Name "Malsoft"
New-ItemProperty "HKCU:\SOFTWARE\Malsoft" -Name "XXX" -Value $reg_val
powershell -EncodedCommand (Get-ItemProperty "HKCU:\SOFTWARE\Malsoft" -Name "XXX").XXX
# PS> Malicious code executed!
Environmentvariable
The environment variables also offer the possibility to save Powershell code.
In the following example the environment variable 'XXX' is created, where a base64-encoded Powershell command is stored. The command can then be invoked using the environment variable value.
$env_var = [Convert]::ToBase64String([Text.Encoding]::Unicode.GetBytes("Write-Output 'Malicious code executed!'"))
[Environment]::SetEnvironmentVariable("XXX", $env_var, [EnvironmentVariableTarget]::User)
powershell -EncodedCommand ([Environment]::GetEnvironmentVariable("XXX"))
# PS> Malicious code executed!
Environment variables can also be used in this way to create a Cmdlet or a command from individual characters.
In the following the abbreviation for the Invoke-Expression Cmdlet 'iex' is created of the ComSpec environment variable from the 4th, 26th and 25th character (counted from 0).
$env:ComSpec
# PS> C:\Windows\system32\cmd.exe
&($env:ComSpec[4,26,25]-join'') ("Write-Output 'Malicious code executed!'")
# PS> Malicious code executed!
Windows Management Instrumentation (WMI)
Windows Management Instrumentation offers the possibility of reacting to specific events using Event Filters, such as logging onto a system, the start of a specific process or the reaching of a specific date and time. This event can then be linked to an Consumeraction, which can be used to execute PowerShell code.
The following code sets an Event Filter, that triggers every minute at second 42. This filter is connected to a 'CommandLineEventConsumer', which executes the obfuscated PowerShell code, which leads to the output via 'msg.exe'. The configuration saved in this way remains also active after a system restart.
# Filter
$filter = Set-WmiInstance -Namespace "root\subscription"
-Class "__EventFilter"
-Arguments @{
Name = "MyEventFilter"
EventNameSpace = "root\cimv2"
QueryLanguage = "WQL"
Query = "SELECT * FROM __InstanceModificationEvent WHERE TargetInstance ISA 'Win32_LocalTime' AND TargetInstance.Second=42"
}
# Consumer
$consumer = Set-WmiInstance -Namespace "root\subscription"
-Class "CommandLineEventConsumer"
-Arguments @{
Name = "MyEventConsumer"
CommandLineTemplate = "powershell.exe -ExecutionPolicy Bypass
-EncodedCommand bQBzAGcALgBlAHgAZQAgACoAIAAnAE0AYQBsAGkAYwBpAG8AdQBzACAAYwBvAGQAZQAgAGUAeABlAGMAdQB0AGUAZAAhACcA"
}
# Binding
Set-WmiInstance -Namespace "root\subscription"
-Class "__FilterToConsumerBinding"
-Arguments @{
Filter = $filter
Consumer = $consumer
}
Alternate Data Streams (ADS)
ADS is an NTFS feature, which allows to append several data streams to a file. The default data stream is stored in the '$DATA' attribute.
In the following a base64-encoded command is written to another stream of a regular file named 'hidden', which then can be executed by getting the contents of this hidden stream.
Set-Content '.\RegularFile.txt' -Stream hidden -Value "VwByAGkAdABlAC0ATwB1AHQAcAB1AHQAIAAnAE0AYQBsAGkAYwBpAG8AdQBzACAAYwBvAGQAZQAgAGUAeABlAGMAdQB0AGUAZAAhACcA"
powershell -ec (Get-Content '.\RegularFile.txt' -Stream 'hidden')
# PS> Malicious code executed!
This section describes some tools to detect and analyze obfuscated Powershell code.
Eventlog
Since Powershell 5 the call to arguments and commands of typical obfuscated scripts - such as 'Bypass', 'FromBase64String', 'DeflateStream' - are automatically logged along with its contents at the warning level in the following event log under event ID 4104.
Microsoft-Windows-PowerShell%4Operational.evtx
To get more inside of the logged obfuscated script, it is recommended to turn on 'Script Block Logging' in the Group Policy.
Group Policy > Computer Configuration > Administrative Templates > Windows PowerShell > Turn on PowerShell Script Block Logging
By activating 'Script Block Logging' the resolved script block layers are additionally logged at the verbose level under event ID 4104
For an even deeper logging of Powershell scripts the 'Module Logging' and 'PowerShell Transcription' can also be activated in the Group Policy.
Revoke-Obfuscation
Revoke-Obfuscation is a Powershell module by Daniel Bohannon, which is using two main functions 'Get-RvoScriptBlock' and 'Measure-RvoObfuscation'.
The function 'Get-RvoScriptBlock' enables the extraction of local or extern Eventlogs, while the function 'Measure-RvoObfuscation' is trying to classify a Powershell script as obfuscated or not. This function takes whole Powershellscript files or extracted script blocks as input.
In the following example the script blocks of the Powershell Eventlog are extracted through 'Get-RvoScriptBlock' and then analyzed through 'Measure-RvoObfuscation'.
$skriptblock = Get-WinEvent Microsoft-Windows-PowerShell/Operational | Get-RvoScriptBlock | Measure-RvoObfuscation -Verbose
The output shows the recognized obfuscated scripts and their hash values.
...
[34 of 46] NOT OBFUSCATED :: (34496E64527BF40DEAD53CF193D97E55F3...)
[35 of 46] NOT OBFUSCATED :: (B5CA27664A1E634B4E551F638FF3A4ACD9...)
[36 of 46] OBFUSCATED :: (FF19200070919B612CAFDDBC1F03541FAE...)
[37 of 46] OBFUSCATED :: (25E323F9DA18AC845E1C436538C74A2D82...)
...
The scripts can been viewed over the index (-1) of the previously defined variable.
$skript_block[35].Source
# @{ScriptBlock=.("{0}{2}{1}"-f'seT','item','-') ('vaRI'+'ABLE'+':U'+'7…
Chainsaw, Hayabusa
Chainsaw and Hayabusa are both CLI applications, which are working with Sigma rules to scan one or more Eventlogs. The applications can be executed with the following commands to scan for suspicious Powershell code:
chainsaw-2.5.0.exe hunt ".\Microsoft-Windows-PowerShell%4Operational.evtx" -s sigma/ --mapping mappings/sigma-event-logs-all.yml
hayabusa-2.2.2.exe csv-timeline -f ".\Microsoft-Windows-PowerShell%4Operational.evtx"
Besides obfuscation rules, there are several other rules for suspicious Powershell code among others:
Rule Suspicious Script
---- -----------------
Powershell Token Obfuscation obfuscated.ps1
Change PowerShell Policies to an Insecure Level obfuscated.ps1
Malicious PowerShell Keywords obfuscated.ps1
Potential WinAPI Calls Via PowerShell Scripts obfuscated.ps1
Suspicious PowerShell Keywords obfuscated.ps1
...
THOR
THOR and the free THOR-lite CLI applications are also working with Sigma rules. Furthermore they are using YARA rules, so obfuscated or suspicious Powershell code can be recognized in other files. For example in LNK files, which can contain malicious Powershell code to load further code or files.
PSDecode
PSDecode is a helpful Powershell module when analyzing obfuscated Powershell code manually. It can recognize and reverse typical obfuscation techniques - like base64- or string obfuscation - so the analyzed code can become much more readable for further analysis.