Malicious PowerShell scripts are one of the most prevalent threats these days. There are many APT actors that have used PowerShell as delivery or migration mechanism. Commodity malware has been using PowerShell for a long time too. In many cases, the Office macro malware uses PowerShell script to proceed with their further attacks. MITRE ATT&CK matrix has a good summary of these trends. There are attack kits like PowerShell Empire and PowerSploit that uses PowerShell as main implementation language.
There are many reasons why PowerShell is so popular among attackers these days. Recent Windows platforms since Windows 7 have PowerShell installed by default. PowerShell is very powerful in interacting with Windows subsystems. Most of the usual malware activities like dropping files and running them and communicating with C&C servers are easily achievable with PowerShell. Some attack frameworks support fileless operations by loading downloaded executables reflectively on the memory. With these aspects, PowerShell is a perfect tool to perform fileless living-off-the-land attacks. Traditional defense mechanisms of Anti-malware industry really focused on file-based threats and still has a large technology gap in covering these new fileless trend.
The only problem with PowerShell from the attackers’ perspective is that the source code is revealed easily. To avoid easy analysis and detection, they put a lot of effort into obfuscating PowerShell scripts. They also split their scripts into many components and deliver them one by on, delivering the final threat after multiple obfuscated PowerShell executions. These tactics work very well in many cases. Analyzing and understanding PowerShell-based threats are very time-consuming work in most cases due to these facts.
Through this article, we are going to analyze one of the most typical cases where obfuscation and payload splitting are used and will talk about a new approach that can help analysts to perform their work more effectively.
Obfuscated PowerShell Code
The following code shows one of the PowerShell threats that are delivered through Office Excel VBA script. The behavior of this threat is well observed with Sandbox technology. For example, through an any.run session, you can observe how Excel runs PowerShell script to run further attacks. A PowerShell instance is used after setting up obfuscated script as a Windows environment variable from cmd.exe.
[Process tree of the threat]
The PowerShell script code is heavily obfuscated and manually de-obfuscating this threat can take sizable efforts from human researchers to fully understand these kinds of threats.
[Heavily obfuscated PowerShell code]
There are many elements that make this script very hard to analyze. First, it splits PowerShell class and function names into multiple strings and combine them on the fly. Secondly, it uses base64 encoding to encode next stage script and randomize their order to prevent brute-forcing decoding of the script. Thirdly, it uses compression to prevent partial decoding of the encoded base64 code. Also, it uses multiple variables to split logic in different location of the code.
Introducing PowerShell Debugger
Fortunately, PowerShell has a debugger functionality embedded with it. This article has a good pointer in using debugging features with PowerShell ISE. It is possible to use ISE for analyzing malicious PowerShell code. But, in most cases where you want some level of automation, custom PowerShell debugger might work better.
You can embed PowerShell debugger in a standalone program and register your own StopEvent callbacks to implement some automation features. We released PowerShellRunBox, the project that uses PowerShell debugging to give some convenience features for analyzing highly obfuscated PowerShell code. We are going to share how this new tool can help analyzing highly obfuscated PowerShell code.
Overview of the obfuscated PowerShell script
The overall infection flow looks like following. It has three checking routines (yellow blocks) where it makes sure the threat is run inside Italian Windows language environment.
[Flow of the malicious PowerShell script execution]
It does extra checks using https://ipinfo.io/country to check if public IP address of the machine and network belongs to Italy. Basically, it has multiple level of language pack and geolocation checks with it, it will not download next stage payload when it doesn’t find it running in specific region with specific language pack.
This is a huge problem for dynamic analysis. For example, the session run through detonation services like any.run will not be complete unless it runs on the very specific environment the malware is expecting. Without analyzing the PowerShell code itself, you will not have the information in the first place. This can be one of the tactics for the attackers to evade dynamic analysis.
In many cases, we need to understand micro-behaviors of PowerShell script mainly because some scripts perform a lot of anti-analysis. Those behaviors can’t be observed by looking macro behaviors like file or process creations. PowerShellRunBox can help with this problem by giving you fine look into how script code runs through.
To prepare debugging environment, first replace PowerShell command to PowerShellRunBox from the script.
[Modifying command line to run PowerShellRunBox]
Now run the script in a disposable environment. Please note that the tool will actually run the script and can potentially infect the machine.
The following screen shows the “mZug” variable set by the first statement of the script. The PowerShellRunBox will show the modified or newly created variables automatically through debugging session.
[New or modified variables will be automatically shown]
The following screen shows the new PowerShell script created by evaluating the previous statement.
[Newly evaluated script]
Forcing Variable Assignment
The script has an “if” condition that checks for locale of the PowerShell environment. You can observe that “RAd” variable is set to “en-US” here as we ran the script on English language environment.
[If condition to check if the language is Italian]
Just following after the if condition, the script will exit if the language pack is not Italian.
[Exit if the locale doesn’t match Italian language set]
After iterating multiple debugging sessions, we can come up with variable names that the malicious code checks. We can use Config.json file to set the variables to be replaced on the fly with debugging session. This automation feature can save you a lot of time.
[Config.json to setup variables]
The PowerShellRunBox supports multiple execution of a command. The following shows running of s(tep) command 100 times.
[Repeating “s” command 100 times]
The following shows the URLs extracted from the script.
[Encoded image file URLs]
The downloaded image looks like just a normal image file, but it actually contains pixels with next stage script information.
[Encoded image file]
The following code shows the loop where it decodes next stage PowerShell script from the image downloaded from image services like imgbox.com or imgur.com.
From inside the loop, you might want to step out of it using “o” command.
[Debugger commands - use “o” command to get out of for loop]
The following is a script block that will reconstruct next stage script code.
[Next stage code assigned to MICd variable]
The MICd variable contains reversed strings of the next stage code.
Checking geolocation and cultural information
Using https://ipinfo.io/country to determine the location of the machine the script is running.
Checking language settings again using Get-Culture function.
[Getting culture information]
[Our configuration replaces Ee variable to “ita”]
Decoded C&C URL
Here’s the code to download final PE payload from a C&C server.
[Stage 3 PE payload URL]
We went through the debugging session for one of the most complicated malicious PowerShell scripts. Manually analyzing these threats take huge amount of time and valuable efforts. There are not many sandbox or detonation technology that understands PowerShell internal-level behaviors like language checks or steganography actions. PowerShellRunBox can be used to tackle this problem and potentially we can add more automation features as we go - for example, automatic loop detection and code coverage brute-forcing with automatic variable modification. It has a good potential to be used for automatic threat analysis.