In cyberattacks, PowerShell is often used to run malicious code stealthily on a target computer, but calling powershell.exe can be detected by security solutions.
Attackers use other Windows features such as Microsoft Office Macro, WMI, HTA Scripts, and many more to avoid calling powershell.exe. With these features, it is possible to run malicious PowerShell scripts without triggering basic security solutions.
Working of these PowerShell scripts and Event IDs generated by them (both Windows and Operational logs) is out of the scope of this article. However, other than monitoring use of cmdlets, following is the summary of most common evasion techniques observed:
- PS Web Calls: Most of the malicious scripts do not include all the code in a script. They make Web calls like System.Net.WebClient and use functions like DownloadString/DownloadFile/DownloadData to download actual code.
- Obfuscation: PowerShell is fundamentally case insensitive, so the function DownloadString() could be spelled as dOwnLOadStrIng. It also supports various string manipulation techniques which are executed on the fly while executing commands, such as the above DownloadString function can be written as ‘d’+’own’+’loadfi’+’le’. Attackers can make use of ‘+’ to add/connect variables, single-quote (‘) to break work checks, dollar ($) to designate variables, ampersand (&) to execute command, script, or function.
- PowerShell autocomplete feature makes detection of these obfuscation techniques more difficult. For example, -encodedCommand can be written in various ways such as -en, -enc, -enco, etc.
- Encoding: PowerShell accepts –EncodedCommand parameter, which is a way to ‘wrap’ DOS-unfriendly command strings in such a way as to be safely passed into PS for execution. It accepts a base-64-encoded string version of a command.
Following are some defense mechanisms, to detect PS scripts which make use of above evasion techniques to hide their bad deeds:
- PS Web Call: Look for PowerShell Web calls.
- PS Suspicious Commands (buzzwords): Scan for all the buzzwords listed in the previous article (suspicious use of PowerShell flags and module calls).
- PS Count Obfuscation Chars: Count of special characters can tell once isolated or extracted from PowerShell script. An excessive amount of obfuscation increases the count of these special characters. Normally administrators written scripts have around 20–30 of these special characters. Analysts need to calibrate this count based on their PowerShell usage.
- PS base64 blocks: In general good PowerShell scripts are readable, so use of -EncodedCommand can be marked as suspicious. However, we cannot solely depend on this one criterion. We need to look for more clues.
- PS ScriptBlock size: Malicious scripts are usually very large. For example, PoweSploit’s Powerview module contains more than 3,50,000 characters. So set triggers on when script length is more than a fixed number of characters (e.g., 1000). This condition could trigger false positive, so if a script is readable then exclude it. Because if it is suspicious and not obfuscated then it will be captured in your regular scan for buzzwords.
- In Event ID 4104, look for Type: Warning. PowerShell operational logs set this value, only if it breaks any of the PowerShell rules.
- Sign all your internal administrative scripts and set execution-policy as Signed.
- Whitelist PowerShell in the log based on the name/Secret Code/key. Make the scripts executable on obvious things only you and your organization does or knows. One of the easy ways is to make sure your scripts contain something only you know that is a ‘secret key’ to exclude.
There is no straightforward approach to detect malicious PowerShell script execution. Since PS is highly reputable, has a trusted signature, is loaded directly through system memory (which cannot be scanned using heuristics) and has unrestricted access to the OS, We as a defender needs to implement the defense-in-depth approach. Following is the recommended approach to do the same on PS version 5:
- Windows Security Log — Process Create (EventID 4688):
A. Check if “New Process Name” contains PowerShell execution. If yes, then parse following extra fields from IR (incident response) perspective:
New Process ID — New Process ID in Hex format, Creator Process ID — Parent Process ID in Hex format, Creator Process Name — parent process name.
B. Check for use of -executionPolicy bypass
C. Check for suspicious command buzzwords
D. Count number of Obfuscation Characters +’$;&
2. PowerShell version 2 logs (EventID 200, 400, 800)
A. Event ID 200 (Command Health) — Check for Level: Warning
B. Event ID 400 (Engine Lifecycle) — Focus on HostApplication Field. Check for what command is executed and the command-line flags, check if “no Profile” (-nop) is not bypassed.
C. Event ID 200, 400, 800 — Check for PS Web Call, PS Count Obfuscation Chars, PS ScriptBlock size (>1000), PS base64 blocks, PS Level: WARNINGS
3. PowerShell v5 Operational logs (EventID 4100, 4103, 4104)
A. Event IDs 4100/4103 (Execution Pipeline) — Check for Level: Warning
B. Event ID 4104 (Execute a Remote Command) — Check for Level: WARNING
C. Event IDs 4100/4103 and/or 4104 — Check for PS Web Call, PS Suspicious Commands (buzzwords), PS Count Obfuscation Chars, PS ScriptBlock size (>1000), PS base64 blocks
4. Sysmon (EventID 7)
To capture PowerShell calls which bypass powershell.exe execution, monitor Sysmon logs for Event ID 7 — Module Loads. Look for the process that is calling System.Management.* DLLs