What We Do
How We Do
Resources
Company
Partners
Get Started
Blog

Unraveling Not AZORult but Koi Loader: A Precursor to Koi Stealer

BY eSentire Threat Response Unit (TRU)

April 9, 2024 | 12 MINS READ

Attacks/Breaches

Threat Intelligence

Threat Response Unit

TRU Positive/Bulletin

Want to learn more on how to achieve Cyber Resilience?

TALK TO AN EXPERT

Adversaries don’t work 9-5 and neither do we. At eSentire, our 24/7 SOCs are staffed with Elite Threat Hunters and Cyber Analysts who hunt, investigate, contain and respond to threats within minutes.

We have discovered some of the most dangerous threats and nation state attacks in our space – including the Kaseya MSP breach and the more_eggs malware.

Our Security Operations Centers are supported with Threat Intelligence, Tactical Threat Response and Advanced Threat Analytics driven by our Threat Response Unit – the TRU team.

In TRU Positives, eSentire’s Threat Response Unit (TRU) provides a summary of a recent threat investigation. We outline how we responded to the confirmed threat and what recommendations we have going forward.

Here’s the latest from our TRU Team…

What did we find?

At the end of March 2024, the eSentire Threat Response Unit (TRU) detected an infection by the stealer malware allegedly being tracked by some researchers as AZORult.

AZORult is an infostealer malware first discovered in 2016. AZORult's sales stopped at the end of 2018 and the seller announced the end of the project, which translates from Russian to English: "Every piece of software has its lifespan. And for AZORult, it has come to an end. With both sadness and joy, I announce that sales are closed forever" (Figure 1).

Figure 1: CrydBrox announcement on closing the AZORult sales

In early 2019, Kaspersky unveiled details indicating that AZORult had been rewritten from Delphi to C++. Fast-forward to the beginning of 2024, Cyble and Netskope have reported on yet another resurgence of AZORult, but this time with the code switched to .NET. However, a detailed comparison of Netskope’s, Cyble's, and Kaspersky's samples showed no code overlaps.

A Threat Researcher, Ernesto Fernández from Trellix, highlighted an article that identifies the malware as Koi Loader and Koi Stealer, reflecting the analyses by Cyble and Netskope. These findings including the Twitter post from Unit42 and some discussions with Principle Threat Researcher at Palo Alto have led us to adopt the terms Koi Loader and Koi Stealer for this article's discussion.

Initial Infection

The user received a phishing email about an unauthorized transaction on the sender's debit card containing an embedded link (Figure 2). This link directed the user to download a malicious ZIP archive named “chasebank_statement_mar.zip” (MD5: 8751223ced55a2079e876b893917a0f3). Notably, the file hashes of the archive change with each new download.

Figure 2: Phishing email

As seen in Figure 3, we found multiple reports on the sender’s email on spam[.]org with email subject lines such as:

Figure 3: Complaint report on spam[.]org

Upon visiting the embedded malicious page that is hosted on Google Sites, we received a CAPTCHA prompt (Figure 4).

Figure 4: CAPTCHA page on Google Sites

It’s worth noting that to receive the payload, the user would have to pass the CAPTCHA prompt first. If the user fails the CAPTCHA prompt, the server will respond with “NO” status (Figure 5).

Figure 5: Response from the server if the CAPTCHA prompt fails

If the user passes the CAPTCHA prompt, the server responds with “YES” status and serves the ZIP archive.

Figure 6: Response from the server if the CAPTCHA prompt is passed

The ZIP archive includes a shortcut file (.lnk) named “chasebank_statement_mar.lnk” (MD5: 044fd3c4d97a35f80792b7edee445c48), which downloads the next stage payload from the server, “m8hHxtkVLYPw.bat” (MD5: 099259c6d898c5d91dc3b01756e349d8), using curl.

This file is then stored in the %TEMP% folder. Additionally, it establishes persistence on the system by creating a Scheduled Task named “0BAduEnQZG9POyK”. (Figure 7).

Figure 7: Contents of the shortcut file

Koi payloads are usually all placed within the same opendir link, as shown in Figure 8.

Figure 8: Example of the opendir hosting Koi payloads

The download batch file “m8hHxtkVLYPw.bat” contains the PowerShell command (Figure 9) that is responsible for fetching another payload from the server “WLXUL6LWXQPB.js” (MD5: 48c7fd278ac590c9bd896ad9c7850c3a).

Figure 9: Contents of the batch script

The downloaded JavaScript file is responsible for self-replication, the script checks if its current filename is agent.js. If not, it attempts to copy itself to the %programdata% directory with the filename agent.js. It defines a mutex name “7z2LKLJ62LPA” and attempts to delete any file with that name in the %temp% directory. If a file with the mutex name does not exist (indicating that another instance may not be running), it proceeds to retrieve and execute additional payloads via PowerShell commands, as shown in Figure 10.

Figure 10: Contents of the JavaScript file

The PowerShell script agent1.ps1 (MD5: 96b251e61f987648f69767f398324652) contains a one-liner command that is responsible for AMSI bypass as shown in Figure 11.

Figure 11: AMSI bypass

agent3.ps1 (MD5: a3ee8655f45c72f5231ded7a4a1c7e43) contains the instructions to download Koi loader written in C++ as well as loading the shellcode in a separate thread along with the loader. The shellcode is responsible for allocating the memory for the loader and jumping to the loader’s entry point (Figure 12).

Figure 12: Shellcode that is responsible for accessing the loader at the entry point

Koi Loader

Anti-VM

The Koi Loader malware is written in C. The final loader payload is extracted and decrypted using XOR from the resource section, where the XOR key is also located.

We will proceed to the decrypted Koi payload. The loader begins by implementing the anti-CIS feature, which terminates the process if any of the languages listed in Figure 13 are detected.

Figure 13: Anti-CIS / language check

Additionally, the loader employs an anti-VM capability. It uses EnumDisplayDevicesW to enumerate display devices attached to the desktop, searching for device strings that match known virtual machine display adapters (Hyper-V, VMWare, Parallels, Red Hat QXL). It then checks for specific files related to VirtualBox (VBoxService.exe and VBoxTray.exe), indicating the system is running inside a VirtualBox VM.

The loader further inspects certain directories and files for evidence of a VM environment. This includes looking for specific files in the user's system and application data folders, which may indicate automated testing or sandboxing environments, such as Recently.docx, Opened.docx, These.docx, Resource.txt, OpenVPN.txt (Figure 14).

Figure 14: VirutalBox and file checks

Next, the loader retrieves the computer name and name of the currently logged-in user against WILLCARTER-PC, FORTI-PC, SFTOR-PC and Joe Cage, STRAZNJICA.GRUBUTT, Paul Jones, PJones, Harry Johnson, WDAGUtilityAccount, sal.rosenburg, and d5.vc/g accordingly. The computer name and username values can indicate automated analysis environments or generic usernames commonly used in virtual environments.

GlobalMemoryStatusEx is called to retrieve the system's memory status. It checks if the total physical is greater than or equal to 3050 MB. This check is performed to determine whether the system might be a VM or a typical end-user device, as analysis environments might allocate less memory to each VM instance.

Interestingly, the Koi Loader performs checks on files with extensions like doc, docx, xls, and xlsx. It verifies that these files are exactly 15 bytes in size and that their filenames contain 30 characters. Additionally, it assesses whether the total number of files matching these criteria is 20 or fewer, and if it does, it proceeds with another check for the presence of powershell.exe in the process's executable path.

If all these conditions are met, the loader interprets it as a sign that it might be running in a controlled or analysis environment (Figure 15).

Figure 15: File size and filename character check

The loader creates the mutex to avoid re-infection. The mutex creation algorithm is based on the calculations of the Volume Serial Number with other constants in the code. The reproduced algorithm is shown in Figure 16.

Additional Analysis

Figure 16: Mutex generation algorithm

Next, the loader proceeds with setting the file attributes of agent.js that was previously mentioned to hidden via SetFileAttributesW as well as creating the scheduled task named “Firefox Default Browser Agent 458046B0AF4A39CB” via ITaskScheduler interface that runs agent.js file via wscript.exe.

Command and Control

For the initial check-in, Koi Loader sends the following to the C2 as an example:

Where “VoYGkc5R” is the hardcoded marker followed by a randomly generated Base64-encoded string (Figure 17).

Figure 17: Initial check-in with C2

After the initial check-in, the infected machine sends another request containing the information gathered from the machine, including OSMajorVersion, OSMinorVersion, OSBuildNumber, Username, ComputerName, and the domain name if present. The collected information is then XOR'ed with a randomly generated 16-character value, which is subsequently processed via the modified MD5 algorithm and sent over to C2.

For the XOR key generation algorithm, the approach is to prepend a fixed byte sequence to the actual input data before hashing. This customizes the MD5 hashing process, making the output distinct from hashing the input alone with a standard MD5 algorithm. This customization affects how the data is processed and, consequently, the final hash.

The secondary POST request format:

Figure 18: Secondary POST request with the host information

Next, the loader proceeds to check if NET Framework 2.0.50727 compiler exists on the infected host and if it exists, it downloads and executes “sd2.ps1” (MD5: 4f55be0b55ec67dfda42b88e9c743a2a) script from the server via PowerShell.

If the .NET 2.0 compiler does not exist, it then checks for the presence of .NET Framework 4.0.30319 compiler.

If this exists, the loader proceeds to download the "sd4.ps1" (MD5: 607b42bd61902ad5a5ea9f508e18a5a4) script instead (Figure 19).

Figure 19: Retrieving sd4.ps1 or sd2.ps1 scripts based on .NET Framework versions

We will analyze the "sd2.ps1" and "sd4.ps1" payloads later in this article. Now, let's return to the command-and-control part.

If the host receives the "INIT" response from the server, it resubmits the check-in to the server, as illustrated in Figure 17, appending the GUID value and a Base64-encoded string to the POST request. Otherwise, the host waits for additional tasks from the C2 server, with a one-minute sleep interval between each connection.

The list of commands/tasks is shown below:

Command

Description

0x67

Executes scripts/commands via Command Prompt

0x68

Executes scripts/commands via PowerShell

0x69

Enables system shutdown privilege for the running process and performs the shutdown

0x6A

Creates a scheduled task to run agent.js and removes agent.js if present on the host

0x6C

Establishes communication with a C2 server

0x6E

Performs process injection into either explorer.exe or certutil.exe based on the subsystem value (if the subsystem is Console User Interface, the payload is injected into certutil.exe, if it’s Graphical User Interface, the payload is injected into explorer.exe) or writes the payload to %TEMP% folder and directly executes it (the naming convention for the payload is generated with PRNG)

0x70

Dynamically loads and executes a function from a DLL, in our sample, the export function is “Release”

Koi Stealer

The retrieved scripts “sd2.ps1” and “sd4.ps1” include code for decrypting the final Koi Stealer binary using XOR, as well as for executing the binary with “config” parameters received from the C2 server.

The XOR key is obtained from the C2 server at the URL hxxp://91.202.233[.]209/index.php?id=$guid&subid=px8eIkut, where $guid represents the GUID of the infected machine (refer to Figure 20).

Figure 20: Snippet of “sd2.ps1” and “sd4.ps1” scripts

It's worth noting that the decrypted Koi Stealer payload exhibits similar anti-VM capabilities to those previously mentioned in the loader (see Figure 21).

Figure 21: Anti-vm capabilities (Final Koi Stealer)

Koi Stealer copies sensitive data, including cookies, history, and login information, to the %AppData% folder. For each copied file, it generates a unique GUID as a naming convention. The files are then immediately deleted after their contents have been fully processed (Figure 22).

Figure 22: Removing the copied files after processing

You can access the list of collected data for exfiltration on GitHub.

In the loader component, the program searches for the distinct identifier "LDR," retrieves commands from the C2 server, decodes them from Base64, and decrypts them using XOR with a shared secret as the key.

Subsequently, the secondary payload is downloaded from a URL provided by the C2 server and executed. The messages will be logged for successful or failed execution in the errors.txt file and sent over to C2 (Figure 23).

Figure 23: Logged messages

Koi Stealer collects the build ID of the payload, in our case it’s the second position of the previously mentioned “config”, which is “px8eIkut”, basic system information such as PC name, current username, GUID, GPU and CPU information, total visible RAM size, screen resolution, system configuration (system language, architecture, operating system), security software, installed applications and save them to a system.txt.

The infostealer generates a private-public key pair and a shared secret using the Curve25519 algorithm, then compresses the harvested data using GZip and encrypts it with XOR, employing the shared secret as the encryption key. Subsequently, it sends the data to the C2 server, with the POST request beginning with the public key, succeeded by a delimiter of 0x4b, and then the encrypted, compressed data (Figure 24).

Figure 24: Transmitted data

What can you learn from this TRU Positive?

What did we do?

Our 24/7 SOC Cyber Analysts investigated the suspicious activities, notified the customer, and isolated the affected device.

Recommendations from our Threat Response Unit (TRU) Team:

Detection Rules

You can access the detection rules here.

Indicators of Compromise

You can access the indicators of compromise here.

References

eSentire Threat Response Unit (TRU)
eSentire Threat Response Unit (TRU)

The eSentire Threat Response Unit (TRU) is an industry-leading threat research team committed to helping your organization become more resilient. TRU is an elite team of threat hunters and researchers that supports our 24/7 Security Operations Centers (SOCs), builds threat detection models across the eSentire XDR Cloud Platform, and works as an extension of your security team to continuously improve our Managed Detection and Response service. By providing complete visibility across your attack surface and performing global threat sweeps and proactive hypothesis-driven threat hunts augmented by original threat research, we are laser-focused on defending your organization against known and unknown threats.

Read the Latest from eSentire