MosaicLoader Analysis

Gorilla Rage
16 min readOct 11, 2021

We will analyze MosaicLoader.

Summary

In July 2021, BitDefender published a WhitePaper on new Golang malware family “MosaicLoader.”

Golang programs are becoming increasingly popular amongst the adversaries, Pentesters and Red Teamers because it allows them to create malicious program that can support multiple OS platform with single source code. However due to it’s nature, by default it statically link all the libraries and it would not strip the Symbolic Tables. This allows analyst more information then usual, since all the function names and such can be somewhat be seen, unless creator obfuscate the code with packing.

MosaicLoader’s infection vector is known to be cracked softwares and it has four infection stages.

  1. Downloader
  2. First stage payload (app.exe)
  3. Second stage payload(prun.exe)
  4. Post-exploitation deliveries

We will focus four binaries within 1~3 stages in our analysis.

Downloader (SHA256:  0cdb630f148fe880bd223b6455daa7b6ef1cbe5b7b287920c67ea1cbf0fba91d)P3.exe (SHA256: 05f8948e087d01c9f9616d0ecdbd334372e37b9c212e3be236abeb7f4d1ed1b1)App.exe (SHA256: 1a98cc7728fc729c0947f445051dcacae38b86b4cee29384da22bb2ccc976747)Prun.exe (SHA256: 06550b2257d9e5620e98a2ea91ccce5f8ccf70a1f54197e12df8897b814f3b62)

Downloader

MITRE ATT&CK

Defense Evasion
T1562 Impair Defenses
Execution
T1059 Command and Scripting Interpreter
Command and Control
T1071 Application Layer Protocol
T1105 Ingress Tool Transfer

File Hash (SHA256)

0cdb630f148fe880bd223b6455daa7b6ef1cbe5b7b287920c67ea1cbf0fba91d

Execution Flow

From the static analysis, the file itself can be seen to have many Golang indications.

Figure 1: Golang related libraries
Figure 2: main entry’s name format is related to Golang.

Golang is known to statically link all the libraries and does not strip symbolic table by default. In addition, this binary is not packed and many of the key information can be seen from static analysis.

As we can see in Figure 3, many of the function names are intact with suggestive names.

Figure 3: Function names

When the downloader is executed, it shows the UAC prompt and shows a console in the background.

Figure 4: Console running in the background

The downloader appears to have following execution flow

  • Configure exclusion setting

Downloader appears to spawn multiple PowerShell commands, which modifies the Microsoft Defender with -Add-MpPreference cmdlet.

PowerShell command updates the settings for Windows Defender to exclude 12 different files from scheduled and real-time scanning to evade Microsoft Defender.

Figure 5: Adding exclusion setup of Microsoft Defender

The command excludes scanning by Microsoft Defender under “C:” or “C:\Program Files(x86)\PublicGaming” with .exe file extesion. Process specifically excluded are appsetup.exe, prun.exe, p1.exe, p2.exe, p3.exe, p4.exe, p5.exe, p6.exe, p7.exe, p8.exe, p9.exe, and p10.exe.

Appsetup.exe and prun.exe are identified as two stager by BitDefender’s whitepaper. The files p1.exe ~ p10.exe are assumed to be additional files that are downloaded from the C2 connection by this downloader and we will specifically look into p3.exe in the next section.

  • New directory creation

After configuring exclusion list for Microsoft Defender, downloader creates a directory C:\Program Files (x86)\PublicGaming, which appears to be utilizes later to place downloaded files appsetup.exe and prun.exe.

Figure 6: Directory created by downloader
  • Download additional malwares

Downloader attempts to download two zip files from two different locations.

https://source.activedirect[.]xyz/p6-2.zip
https://srv2.checkblanco[.]xyz/update-assets2.zip
Figure 7: source.activedirect[.]xyz

After the first download of p6–2.zip, it proceeds to download the second zip file update-assets2.zip from srv2.checkblanco[.]xyz.

Figure 8: main_downloadfile function
Figure 9: Argument passed
Figure 10: Argument passed is URL

After the download is successful, the downloader attempts to place the zip file under the temp folder.

C:\Users\<username>\AppData\Local\Temp\update-assets2.zip

Unfortunately we were not able to retrieve either of the zip files. However p6–2.zip was in VirusTotal and it appears to contain one of the files included in the exclusion list p3.exe.

Other interesting data we can find in this binary :

C:\\Users\\<username>\\AppData\\Local\\Temp\\6198e55c-67f5–4bde-9dfc-155c077eaa9d.zip[HKEY_CURRENT_USER\Software\Asymetrix\Web3d20\FileDialogState]

P6–2.zip(p3.exe)

MITRE ATT&CK

Defense Evasion
T1140 Deobfuscate/Decode Files or Information
Command and Control
T1071 Application Layer Protocol
collection
T1113 Screen Capture
T1560 Archive Collected Data

File hash (SHA256)

05f8948e087d01c9f9616d0ecdbd334372e37b9c212e3be236abeb7f4d1ed1b1

Execution Flow

Downloader downloads additional zip file from the source.activedirect[.]xyz and it is one of excluded files p3.exe. As we can see from Figure 11, the file is masquarading as 7-Zip setup file. The file is not Golang binary.

Figure 11: Masquaradign as 7-Zip setup file.

The file appears to go through the following execution flow.

1. Create directory

The p3.exe creates a directory (C:\Users\<username>\AppData\Roaming\AHLaoTPZpYcd) to prepare dropping files.

2. Drop files

P3.exe prepares file drop by unpacking onto a memroy via VirtualAlloc and WriteFile.

Figure 12: Memory before getting unpacked
Figure 13: Memory after getting unpacked.

The executable proceeds to create 4 different .dot files.

Figure 14: 4 Files written to directory AHLaoTPZpYcd

The dropped file appears to be obfuscated files and AutoIT Scripts

Crepitando.dot: Obfuscated file.
Esistenza.dot: Obfuscated AutoIT Script
Sua.dot: Obfuscated VBS Script
Veduto.dot: Obfuscated PE file.

Since I was getting an error on debugger, I reran it with the procmon and noticed that one of the .dot files were turned into autoit related executable.

After the dropping of the file, one of the files Crepitando.dot was replaced by Getto.exe.com, which appears to be AutoIT executable.

More information on dropped files.

Filename  : Getto.exe.com
MD5 : 78ba0653a340bac5ff152b21a83626cc
Reference : https://www.virustotal.com/gui/file/05d8cf394190f3a707abfb25fb44d7da9d5f533d7d2063b23c00cc11253c8be7
Filename : Esistenza.dot
MD5 : 48B3E78FE366DC8ADA44269668CCCD57
Filename : Sua.dot
Md5 : 1F3DFE860832CB5AAB3469F1467DC470
Reference : https://www.virustotal.com/gui/file/bd70a24dcfa73cc8df03a0c9bbb88607f508d16575cdec99ef3f14a1ef9a0ff7
Filename : Veduto.dot
MD5 : A2BD0E0C363AA9CC7B1DC453E25663E6

It appears Sua.dot and Veduto.dot are part of infostealer malware, Taurus Stealer.

3. Possible Exfiltration

After the files are dropped, p3.exe proceeds to create a process makecab.exe in suspended mode and execute the process by calling ShellExecuteExW. Process makecab.exe is a “Cabinet Maker” which allows user to compress the files into .cab file.

Figure 15: CreateProcess’s sixth param, create suspended.
Figure 16: First and second param of CreateProcess
Figure 17: ShellExecuteExW call
Figure 18: _SHELLEXECUTEINFOA for ShellExecuteExW

There are few techniques by malicious actors to compress a file with makecab.exe and extract the file with extrac32.exe to exfiltrate the data from the infected machine.

However since I was getting an error with the debug, I was not able to obvserve this behavior.

4. Execute files

Figure 19: Execute Getto.exe.com after makecab.exe

After p3.exe dropping the file, it proceeds to execute the AutoIT executable Getto.exe.com to start it’s exfiltration.

Getto.exe.com spawns child processes cmd.exe to execute the .dot files.

Figure 20: Executing Sua.dot via cmd.exe

Later it proceeds to extract the obfuscated code Veduto.dot file by extracting specific strings with findstr command.

Figure 21: Findstr

Findstr command is using the following params.

/V: Prints only lines that doesn't contain a match/R: Processes search strings as regular expressions. This is the default setting.

In our case, it searches and ouputs all strings that does not match with:

gEBDTrNLizVyhMqQJJHhSQmHPcJFUXlDpKKZOnNDyQtFGaCxfhSqXcvbtTObziRlpsWCwqwlWrowMUwvTgfGtpGNgjUVJfpXZUv

Getto.exe.com proceed to execute the following.

  • Checks a file aaa_TouchMeNot_.txt
Figure 22: Checks aaa_TouchMeNot_.txt
  • Dump system information into text file.
Figure 23: Dump system information into text file
  • Checks and dumps cryptocurrency wallet, files, cache/cookie in Browser, and screenshot of Desktop into a dedicated directory.
Figure 24: Dump infostealing data.
Figure 25: ScreenShot taken by the Getto.exe.com
  • Zip the retrieved data.
Figure 26: Compressing retrieved data into zip file
  • C2 connection

Getto.exe.com appears to create connection to the following domains to exfiltrate the retrieved data.

EtRlyZLogOuuBHUl[.]EtRlyZLogOuuBHUlaufxio33[.]top 
Figure 27: Attempted DNS query to C2 EtRlyZLogOuuBHUl[.]EtRlyZLogOuuBHUl

appsetup.exe

MITRE ATT&CK

Defense Evasion 
T1497 Virtualization/Sandbox Evasion
Persistence
T1543 Create or Modify System Process
Command and Control
T1071 Application Layer Protocol

File hash (SHA256)

1a98cc7728fc729c0947f445051dcacae38b86b4cee29384da22bb2ccc976747

Execution Flow

This file is assumed to be downloaded via update-assets2.zip. The zipfile appears to be different name from the whitepaper, but this is due to using different binary.

Again, this binary is Golang.

Figure 28: Function names

main_run function is called as soon as it enters main_main, and it appears to contain flag package. According to Go documentation:

Package flag implements command-line flag parsing.

Within the execution flow, this function appears to support command-line flag service .

Figure 29: main_run, flagset.string is set
Figure 30: Flag parsing

appsetup.exe is seen to load a library service and this allows the executable to manage system services.

Figure 31: Example code from Service library of New Function

As we execute the code, it appears to be creating a new service pubgame-updater for persistence purposes .

Figure 32: Config struct, when New function is called.
Figure 33: Name from config struct
Figure 34: DisplayName from config struct

In Go, function args appears to follow the pattern args ptr and the length of the ptr value in assembly.

For example in Figure 32, the first arg at 0x9D6D32 is followed by value 0xF(15). First arg is the ptr to the value the function New receives which is "pubgame-updater" and this appears to have length of 15. This rule applies to other args as well.

Later in the execution flow main_isinstalled is called, which has an indirect call :

Figure 35: Indirect call
Figure 36: The called address
Figure 37: Pointed Value

The address 0x71CF40 turns out to be function_windowsService__Status, which checks if service pubgame-updater exist within the machine.

Figure 38: Status error handling
Figure 49: Return value eax
Figure 40: Eax address

This will eventually return ErrNotInstalled, which is an indicator that the given service is not installed within the environment.

Figure 41: Checks the Service status

After the service status check in main_installed, execution flow goes back to main_run which hits conditional block. It will take the jump(jz) if the service does not exist within the machine.

Figure 42: Conditional block.

Once the jump is taken, we can seen that the value prun.exe is seen to be placed.

Figure 43: Enlarged image of the block.

Within this codeblock, it has main_setupWrapper which calls cryptio’s go-startup. It is a library that manages startup applications in Windows. It appears to enable the startup services:

Figure 44: Startup service creation
Figure 45: Startup service name

From the above, it is configuring the following.

Name — prunsas
Executable — “C:\Windows\PublicGaming\prun.exe”
startuptype — CURRENT_USER
runonce — False

This startup setup is also placed for another executable, WinFlow.exe.

Figure 46: Startup setup WinFlow.exe
Name — WinFlow
Executable — C:\ProgramData\WinFlow.exe
startuptype — CURRENT_USER
runonce — False

After the Startup is enabled, it calls windowsService’s Install function where it appears to call CreateService() to finish creating the Service.

Figure 47: Service pubgame-updater created

Before program termniates, it will start the service by calling Start() function.

Figure 48: pubgame-updater start

Now we go back to the conditional block from Figure 42, which will not take the jump, if the service already exist.

In this execution flow, it checks if the process is running in interactive mode. If it is, the program will terminate abruptly and it appears this is one of anti-debugging technique, which is essentially checking if the process is being debugged.

Figure 49: Go Document on Interactive function()

In figure 50, call eax on the first block is calling Interactive() function and placing the return value into eax. In the next code block in loc_924FDA, it checks the return value of Interactive().

Figure 50: Interactive check flow

If the executable returns false for Interactive() function, the execution flow will continue to proceed creating a connection to http://green.cablesparking[.]net/fetch.json in function main_getHubEndopoint function.

Figure 51: main_getHubEndpoint function calling http library’s Client.get
Figure 52: https://green.cablesparking[.]net/
Figure 53: DNS query

https://green.cablesparking[.]net is identified as malicious by threat intel, and it appears tobe utilized by NetBounce malware in the past.

Figure 54: NetBounce utilized green.cablesparking[.]net

Within this malware, it also has similar IP address 195.181.164[.]195 from Twitter post and it may be one of the indicator this malware is related to NetBounce.

Figure 55: Listed IP address.

When we access the url, it retrieves the netbounce related information.

Figure 56: fetch.json

The executable also has function main_equinoxUpdate, which appears to create an additional C2 connection before the program termination.

Figure 57: main_equinoxUpdate call
Figure 58: main_sendnotification call

The domain spark.lightburst[.]xyz is identified as malicious by threat intel.

The function main_sendnotification function is only seen in Program_Shutdown and Program_Stop functions.

Prun.exe

MITRE ATT&CK

Defense Evasion
T1055 Process Injection
T1140 Deobfuscate/Decode Files or Information
Command and Control
T1071 Application Layer Protocol
Execution
T1106 Native API

File hash (SHA256)

06550b2257d9e5620e98a2ea91ccce5f8ccf70a1f54197e12df8897b814f3b62

Execution Flow

This executable does not appeared to be Golang executable and it has some suggestions that this is packed by Delphi, Inno Setup.

Figure 59: Inno Setup
Figure 60: Delphi

Looking into the sections, this executable has interesting section VolcaniM which has executable persmission.

Figure 61: Sections

When prun.exe is executed, execution flow will enter section VolcaniM.

Figure 62: Call volcaniM section
Figure 63: Entrypoint of VolcaniM

Within VolcaniM section, it scans through each sections within this executable and it tried to read pass the end of file (0xC89000) which triggers an EXCEPTION_ACCESS_VIOLATION.

Figure 64: It tries to read till 0xC90000
Figure 65: The last section Challope extends till C89000

There’s a conditional that checks the end of the file, however this was set to 0x7FFFFFFF which was causing this exception to occur in the first place. Changing the conditional value to 0xC80000 fixed the issue and bypassed the exception error.

Figure 66: Original conditional check. It checks with 7FFFFFFFF
Figure 67: EDI value before getting an error at the original check.
Figure 68: Updated conditional

Then executable proceeds to allocate code into memory via VirtualAlloc three times.

1. Unpacked Code

Indirect call to VirtualAlloc can be seen and this appears to be allocating Exeute/Read/Write permission.

Figure 69: VirtualAlloc indirect call
Figure 70: Allocated memory permission

After the memory is allocated, it loads the function RtlDecompressBuffer from ntdll.dll by using GetProcAddress and LoadLibraryA.

Figure 71: GetProcAddress the function RtlDecompressBuffer

RtlDecompressBuffer Function defined by msdn as:

The RtlDecompressBuffer function decompresses an entire compressed buffer.

This is assumed to be one of the unpacking mechanism of this executable.

After the GetProcAddress of RtlDecompressBuffer, the execution flow proceeds to move certain amount of data from VolcaniM to allocated memory location by using combination of rep movsb instruction.

Figure 72: Moving data to allocated memory

REP instruction is a repeat instruction which is used as a prefix written before the actual instruction. In this case, movsb instruction gets repeated , which is an instruction that fetches the byte at address SI(Source) and store it into DI(Destination). This instruction allows the executable to copy part of VolcaniM to allocated memory.

Figure 73: The rep movsb is moving ESI(VolcanoM location) to EDI(allocated memory)

This moves each byte from 0x4BD000(VolcanM) to 0x29947EE(allocated memory) and it loops 15932 times, which is roughly 16kb of data from VolcanM.

※ I was getting an exception “Access Violation Exception” while executing rep movsb, due to an invalid access permission(according to debugger). This was resolved by resetting the allocated memory’s permission to full access.

As soon as the copy is done, it will proceed and executes RtlDecompressBuffer.

Figure 74: RtlDecompressBuffer call
Figure 75: RtlDecompressBuffer args
NT_RTL_COMPRESS_API NTSTATUS RtlDecompressBuffer( 
USHORT CompressionFormat,
PUCHAR UncompressedBuffer,
ULONG UncompressedBufferSize,
PUCHAR CompressedBuffer,
ULONG CompressedBufferSize,
PULONG FinalUncompressedSize
);

As we can see from the above, the argument four(CompressedBuffer) is pointing to the beginning of the address where the copied data are placed with the same count size as ECX in argument five(CompressedBufferSize). Decompressed data will be pasted onto the beginning of the memory (2931000), which is evident from argument two(UncompressedBuffer).

Figure 76: Before buffer decompress
Figure 77: After buffer decompress

Once the decompression is done, the execution flow will proceed with direct call to this unpacked memory location.

Figure 78: Direct call to decompressed memory

2. ntdll.dll

Before the execution flow enters the unpacked memory, it proceeds to execute another VirtualAlloc with Execute/Read/Write permission.

Figure 79: Second allocated memory.
Figure 80: Allocated memory permission

This memory allocation is used to dump ntdll.dll within the instructions of decompressed memory.

Figure 81: After PE file getting dumped
Figure 82: Evidence from export table that this is ntdll.dll

3. Injection preparation file

The third VirtualAlloc is called and within the unpacked memory instructions, it dumps PE file. This PE file appears to be part of prun.exe, since it asks for the symbol file name by prun.pdb.

Figure 83: Allocated PE file

Within the unpacked memory, the executable proceeds to execute Process Hollowing and injects this 3rd allocated PE into itself. The process is created via CreateProcess before hand in the suspended mode.

  1. Unpack the malicious PE to the 3rd VirtualAlloced memory.
  2. ZwUnmapViewofSection of the unpacked/dumped malicious PE.
Figure84: ZwUnmapViewofSection
Figure 85: ZwUnmapViewofSection args and pointing to third VirtualAlloced memory

3. Writes the unmapped section by using ZwWriteVirtualMemory into child process.

Figure 86: ZwWriteVirtualMemory args
Figure 87: Handle of child process
Figure 88: Process Handle mentioned in ZwWriteVirtualMemory.

4. Start the hollowed process by using CreateProcessW.

Figure 89: CreateProcessW call
Figure 90: CreateProcessW args

Looking at the PE file, it is evident this PE file will create C2 connections as we can see that it is loading ws2_32.dll for connectivity and we can also see the C2 domain class.checkblanco[.]xyz listed.

Figure 84: class.checkblanco[.]xyz
Figure 85: Send function call

This injected PE file appears to be utilized purely for C2 communication.

Figure 86: getaddrinfo call
Figure 87: getaddrinfo args
Figure 88: DNS query to the C2

Conclusion

Each executables that we covered can be identified as the following.

  • Downloader: Loader
  • P3: Infostealer (Taurus Infostealer)
  • appsetup: Loader (NetBounce)
  • prun.exe: Loader

To prevent the infection with this malware, it is always recommended to avoid downloading and installing cracked softwares.

If the machine gets infected by this malware, then it is recommended to re-install the OS however if this is not valid, then following actions can be taken.

  • Reset the user credentials of any web applications that was accessed via Browsers.
  • Delete service (pubgame-updater)
  • Delete all the files under directory (C:\Windows\PublicGaming)
  • Remove the exclusion processes set by Add-MpPreference
  • Block the detected domains and IP address. (EtRlyZLogOuuBHUl[.]EtRlyZLogOuuBHUl, aufxio33[.]top, source.activedirect[.]xyz, srv2.checkblanco[.]xyz, green.cablesparking[.]net, class.checkblanco[.]xyz, 195.181.164[.]195)

However assuming that the C2 connections were successful and it reaches prun.exe execution, it is likely that post-exploitation or additional malwares are downloaded and executed. Re-Imaging the OS and recovering from the backup would be the safest approach to disinfect the machine.

Reference

https://www.virustotal.com/gui/file/05f8948e087d01c9f9616d0ecdbd334372e37b9c212e3be236abeb7f4d1ed1b1/detection

--

--