Cyble Research and Intelligence Lab (CRIL) has uncovered an advanced attack campaign that likely originates from spam emails containing phishing attachments. These emails include an archive file with an LNK file disguised as a PDF file. The attack begins when the LNK file triggers PowerShell-based commands, which proceed to download and execute additional scripts hosted externally. These scripts are highly encoded and obfuscated to evade detection by security tools. The TAs use a variety of evasion techniques, including checks for virtual machines, sandbox environments, and debugging tools, ensuring that the malicious code can remain undetected and function stealthily in non-virtualized environments while bypassing standard security defenses.
Once the environment is confirmed to be free from sandboxing or analysis, the payload is decrypted using hardcoded keys, resulting in the execution of Quasar RAT. In this final stage, attackers gain complete control over the compromised system, allowing them to conduct a range of malicious activities, including data exfiltration, persistent access, data theft, and even deploying additional malware.
In July 2022, a Vietnamese threat group began spreading Ducktail malware, an info-stealer targeting digital marketing professionals. Over time, the group expanded its operations, distributing additional information stealers and remote access trojans (RATs). They also leveraged Malware-as-a-Service (MaaS) to facilitate payload delivery, making their campaigns more versatile and scalable.
This campaign is attributed to the Vietnamese threat group due to several indicators: the choice of target victims, the tools employed in the attacks, the payload delivery mechanisms, and the creation of lure documents. These elements closely mirror the tactics, techniques, and procedures (TTPs) observed in previous campaigns identified by WithSecure, further linking this campaign to the same group. We have observed that the TA behind this campaign is also delivering malware families such as Stromkitty. The figure below shows the execution flow of this campaign.
Cyble Research and Intelligence Lab (CRIL) has identified a campaign that leverages a malicious LNK file containing a PowerShell command designed to download and execute an additional PowerShell script hosted on Dropbox using the link: hxxps://www.dropbox[.]com/scl/fi/b9diosgl68vg9xlaytsbz/sav3_encrypt[.]txt?rlkey=k2ojylfvks6xyef3vb21n45gp&st=suprpdhv&dl=1. This is done by using the Invoke-Expression (IEX) and Invoke-RestMethod (irm) PowerShell commands from the LNK file
Once downloaded, the script contains two components encoded in base64: a lure PDF file and a batch file. These are decoded and saved to the Downloads folder as “PositionApplied_VoyMedia.pdf” and “output.bat.” The script then executes these files using the “Start-Process” PowerShell command.
The potential target of this attack is likely job seekers or professionals in the digital marketing, e-commerce, or performance marketing sectors, particularly those specializing in Meta (Facebook, Instagram) Ads within the United States. Figures 4 and 5 below show the lure document used in this campaign.
Upon execution, the “output.bat” file retrieves the disk drive type and manufacturer name using WMIC commands to detect if the system is running in a virtual machine. It checks for disk types such as “DADY HARDDISK”, “WDS100T2B0A” or “QEMU HARDDISK” and manufacturers like “BOCHS_”, “BXPC___”, “QEMU“, or “VirtualBox“. If any of these checks indicate a virtual environment, the script exits without further execution. However, if no virtual machine is detected, it proceeds to execute another obfuscated PowerShell script. The process tree below illustrates how malware checks the virtual environment.
The de-obfuscated PowerShell code is shown below
The PowerShell script reads the content of the “output.bat” file by scanning each line that begins with “:: “ and extracts a substring. A separator is used to split the line into two sets of base64 strings. These base64 strings are then decoded and passed through an AES decryption process using a hardcoded key and IV, both of which are base64 encoded. After decryption, the data is decompressed using a GZip stream, and the resulting output is executed using Invoke-Expression through PowerShell.exe, as shown in Figure 7.
The decrypted payload results in a .NET executable, which is executed in memory via Invoke-Expression. It then carries out a series of detection-evasion checks using various methods defined within the .NET loader.
This method performs a check for a Triage sandbox by querying the disk drive model using the command “SELECT * FROM Win32_DiskDrive”. It retrieves the model of the disk drive and compares it with “DADY HARDDISK” or “QEMU HARDDISK”. Additionally, it checks the Triage sandbox VM’s desktop wallpaper by comparing the bytes of the current wallpaper image file with a hardcoded set of bytes. If either of these checks detects the presence of Triage, the program throws an exception and halts execution.
This method checks if the system is running in a QEMU virtual environment by searching for specific QEMU-related files in the system directory. It iterates through all the files in the system folder and checks if any file names contain the strings “qemu-ga” or “qemuwmi”. If a match is found, the method returns true; otherwise, it returns false.
This method checks if the system is running in a Parallels virtual environment by searching for specific Parallels-related files in the system directory. It looks for file names containing the strings “prl_sf”, “prl_tg”, or “prl_eth”. If any of these strings are found in the system folder, the method returns true; otherwise, it returns false.
These methods are designed to detect the presence of various sandboxing solutions by checking if specific DLL modules are loaded in the system. The malware detects Sandboxie by looking for the “SbieDll.dll” module, and if found, the method is designed to crash the Sandboxie environment. Similarly, the Comodo sandbox is identified by searching for either the “cmdvrt32.dll” or “cmdvrt64.dll” modules, while the Qihoo 360 sandbox is detected by checking for the “SxIn.dll” module. For Cuckoo sandbox detection, the system searches for the “cuckoomon.dll” module. If any of these modules are found, the system returns true, indicating a sandbox environment. In the case of Sandboxie, it intentionally crashes the environment.
This method checks for emulation by measuring the system’s tick count before and after a 500-millisecond pause. If the time difference is less than 500 milliseconds, it suggests the system may be running in an emulated environment, returning true. Otherwise, it returns false.
This method checks if the current system’s username matches any common usernames often associated with virtual machines, sandboxes, or test environments. It converts the current username to lowercase and compares it against a predefined list, which includes names like “Johnson,” “Miller,” “malware,” “Sandbox,” ”virus,” ”John Doe,” “test user,” “sand box,” “WDAGUtilityAccount,” “DefaultUser,” If a match is found, it returns true, indicating that the system is running under virtual environment. Otherwise, it returns false.
This method checks if the system is running in a Wine environment by looking for the presence of the “wine_get_unix_file_name” function in the kernel32.dll module. If the function is found, it returns true, indicating Wine is present; otherwise, it returns false.
This method checks if the system is running in a VMware or VirtualBox virtual environment by querying the “Select * from Win32_ComputerSystem”. It retrieves the manufacturer and model information of the system. If the manufacturer is “Microsoft Corporation” and the model contains “VIRTUAL,” or if the manufacturer contains “vmware,” it returns `true`, indicating the presence of a virtual environment. If no such conditions are met, it returns false.
This method checks if the system is running in a KVM (Kernel-based Virtual Machine) environment by searching for specific KVM-related drivers in the system directory. It looks for file names such as “balloon.sys“, “netkvm.sys“, “vioinput“, “viofs.sys”, and “vioser.sys”. If any of these files are found, it returns true, indicating a KVM environment.
This method checks if the system is running in a Hyper-V environment by inspecting the services currently running. It looks for services with names that contain “vmbus”, “VMBusHID”, or “hyperkbd”. If any of these services are found, it returns true, indicating the presence of Hyper-V.
This method checks for the presence of virtual machine/Virtual box-related files and directories to detect a virtual environment. It searches the system directory for specific files like “VBoxMouse.sys”, “VBoxGuest.sys”, “VBoxSF.sys”, “VBoxVideo.sys”, “vmmouse.sys”, “vboxogl.dll”, and “vmmouse.sys” that are associated with VMware or VirtualBox. Additionally, it checks for the existence of directories like “C:\\Program Files\\VMware” or “C:\\Program Files\\oracle\\virtualbox guest additions”. If any of these files or directories are found, it returns true, indicating the system is running in a virtual machine.
This Method checks for the presence of processes associated with virtual machine environments by searching for specific process names, such as “vboxservice,” “VGAuthService,” “vmusrvc,” and “qemu-ga.” If any of these processes are found running on the system, it returns true, indicating the presence of a virtual machine. If none of these processes are detected, it returns false.
This method checks for the presence of specific virtual machine-related device files by attempting to open paths such as \\.\pipe\cuckoo, \\.\HGFS, \\.\vmci, \\.\VBoxMiniRdrDN, \\.\VBoxGuest , \\.\pipe\VBoxMiniRdrDN , \\.\VBoxTrayIPC. If any of these device files are successfully opened, it closes the file and returns true, indicating the system is likely running in a virtual machine.
This method checks if the operating system is an Enterprise, Business, or Server edition by querying the Win32_OperatingSystem class and retrieving the OS name from the “Caption” field. If the OS name contains the words “Enterprise,” “Business,” or “Server,” it returns true, indicating that the system is running one of these editions.
If any of the above-mentioned methods return “True”, the program triggers an exception, halting the execution and preventing the intended malicious activity from being executed.
After these environmental checks, the program proceeds to assess whether it is being debugged. This stage typically involves additional scrutiny for signs of a debugging process or sandbox environment, such as monitoring for attached debuggers or identifying system artifacts that suggest the program is running under observation.
This method performs various checks to detect if a debugger is attached to the current process. It checks for a debugger using standard .NET methods and by querying system information via the “NtQueryInformationProcess” function. These checks look for specific flags, ports, and object handles that indicate the presence of a debugger. If any of these conditions are met, the methods return `true`, indicating that the process is being debugged.
This method attempts to hide threads from a debugger by iterating through the current process’s threads. It opens each thread and uses the “NtSetInformationThread” function to hide it. If the operation succeeds for all threads, it returns “Success”; otherwise, if an error occurs, it returns “Failed.”
This method allocates a block of memory using “VirtualAlloc” and sets specific protections to detect if a debugger is present. It writes data to the allocated memory and changes its protection to include guard pages. If an exception is triggered when executing code from this memory block, it indicates the presence of a debugger, returning false. If no exception occurs, the memory is freed, and it returns true, indicating no debugger is detected.
This code checks for hardware breakpoints by retrieving the current thread’s context, specifically the debug registers. If any of the debug registers (Dr1, Dr2, Dr3, Dr4, Dr5, Dr6, or Dr7) contain non-zero values, it indicates the presence of a hardware breakpoint, returning true. If no breakpoints are detected, it returns false.
This method attempts to prevent debugging by modifying the behavior of specific functions in ntdll.dll. It retrieves the addresses of DbgUiRemoteBreakin and DbgBreakPoint and overwrites them with custom instructions (0xCC for DbgUiRemoteBreakin and 0xC3 for DbgBreakPoint). If the memory modification is successful, it returns “Success”; otherwise, it returns “Failed.”
If any of the above methods detect that the process is being debugged, the program immediately triggers an exception. This action effectively halts further execution and prevents the program from continuing its operations.
Upon execution, the program specifically checks for antivirus products like Kaspersky, BitDefender, or Avast Antivirus. However, the presence of these security products on the system does not interfere with or halt the program’s execution. It continues running as intended.
Once the .NET executable completes its checks, it verifies if it has administrative privileges. If not, it modifies the Process Environment Block (PEB) of the current process to change its image path and command line to “C:\Windows\explorer.exe“. After modifying the PEB, it initiates a new instance of the current process using a PowerShell command with the “-Verb runas” option, running the process in hidden mode with elevated admin privileges.
If the PowerShell method fails for any reason, the process switches to an alternative approach by invoking a COM object (CMSTPLUA) using the CLSID “3E5FC7F9-9A51-4367-9063-A120244FBEC7” with the prefix “Elevation:Administrator!new:”. It then calls ShellExec to launch a new instance of the current process with elevated administrative privileges.
After achieving privilege escalation, the .NET executable checks the process’s origin to determine whether it is running from the “Windows” directory. If the process is not operating from this directory, it sets up persistence by creating a hidden folder named “$rbx-onimai” in the “C:\Windows” directory and copies itself into this folder as “$rbx-CO2.bat”. The original file located in the “Downloads” folder is then deleted. Afterward, it initiates a new instance from the “C:\Windows\$rbx-onimai” folder using the following command.
Command : cmd.exe /C echo Start-Process -FilePath C:\Windows\$rbx-onimal\$rbx-CO2.bat -WindowStyle Hidden | powershell.exe -WindowStyle Hidden
It also creates a run entry by modifying the registry key of “SOFTWARE\Microsoft\Windows\CurrentVersion\Run\$rbx-XVR” to point to the newly copied file, ensuring it runs automatically after restart.
Upon Execution, the .Net executable modifies the “EtwEventWrite” function in ntdll.dll to disable event tracing by inserting specific opcode code.
After this, it decrypts data from its resource section labeled “1789d7d0-48bf-48f5-bad6-e0262117d577.tmp” using AES decryption with a hardcoded base64 key and IV. The decrypted data is subsequently decompressed using GZip.
In the final step, the .NET executable runs the decompressed payload using the Invoke command. The payload has been identified as Quasar RAT, but the threat actor has made several modifications, such as changing the certificate name and other references where “Quasar RAT” typically appears. These alterations are likely intended to evade detection and attribution.
Quasar RAT configuration:
Field | Value |
Tag | Team |
version | 1.7.3 |
Hosts | “144.76.68.248:4782;” |
Sub-Directory | “$cnt-onimai2” |
Install Name | “$cnt-CO2.exe” |
Mutex | “928569f3-e524-4f67-936e-0d7f0a47cfad” |
Startup Key | “$cnt-Onimai” |
Log Directory name | “$cnt-Logs” |
Additionally, the program includes several checks designed to detect debuggers and evaluate the system’s environment, though these specific methods are not directly called. This suggests that the TA may have implemented these checks as part of a more extensive anti-debugging or evasion mechanism. By leaving these methods dormant, the TA retains the option to enable further checks in the future, enhancing the program’s ability to evade detection or analysis in debugging or virtualized environments.
This method checks for debugging or reverse engineering tools by searching for specific process window titles or the foreground window’s title. It looks for tools like x32dbg, x64dbg, windbg, ollydbg, dnspy, immunity debugger, hyperdbg, cheat engine, cheatengine, ida, and wireshark. If any of these tools are detected in the process list or as the current foreground window, it either closes the process or flags their presence by returning true. If none are found, it returns false.
This code attempts to detect a debugger by logging a message and checking the result of “GetLastWin32Error()”, returning true if no error is found. Additionally, it logs a specially crafted format string to potentially exploit vulnerabilities in certain debuggers like OllyDbg by flooding it with “%s” format specifiers.
These two methods check whether unsigned drivers and test-signed drivers are allowed to run on the system by querying the system’s Code Integrity settings using NtQuerySystemInformation. This is done to evaluate if the machine could be a potential malware-testing environment. If unsigned drivers are allowed, the method returns True. Similarly, if test-signed drivers are permitted, it also returns True.
The method checks if kernel debugging is active on the system. It does so by querying the system information using the “NtQuerySystemInformation” function (with a specific system information class ‘35’). It retrieves the status of the kernel debugger through the “SYSTEM_KERNEL_DEBUGGER_INFORMATION” structure. The method returns true if either the kernel debugger is enabled or present but not active. Otherwise, it returns false.
This check helps determine if the system is being debugged, which can be useful in detecting potential test environments.
The method checks if Secure Boot is enabled on the system. It queries system information using NtQuerySystemInformation with a system information class 145 to retrieve Secure Boot status via the SYSTEM_SECUREBOOT_INFORMATION structure. The method returns true if the system is Secure Boot capable and Secure Boot is enabled. Otherwise, it returns false.
This method checks if Virtualization-Based Security (VBS) is enabled on the system. It queries the system using WMI to check the encryption status of the system volume (C: drive) through the Win32_EncryptableVolume class. The method returns true if the volume’s “ProtectionStatus” is 1, indicating that encryption is enabled (suggesting VBS is active). If any errors occur during the query, the method catches the exception and returns false.
This method checks if Memory Integrity (also known as Hypervisor-Enforced Code Integrity) is enabled on the system. It reads a specific registry key: HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\DeviceGuard\Scenarios\HypervisorEnforcedCodeIntegrity. If the Enabled value in the registry key is set to 1, the method returns true, indicating that Memory Integrity is enabled. If the key or value is not present or an error occurs, the method returns false.
This method checks whether the currently executing assembly (the program) is running from a different location than the application’s expected executable path. It does this by comparing the location of the executing assembly (Assembly.GetExecutingAssembly().Location) with the application’s executable path (Application.ExecutablePath). If they differ, the method returns true, indicating that the assembly may have been invoked or loaded from an unusual or external location.
The code performs several security checks to determine if the system is potentially insecure or used for malware testing. It checks conditions like whether unsigned drivers are permitted, whether Secure Boot is disabled, kernel debugging is active, and whether key security features like Virtualization-Based Security and Memory Integrity are turned off. It also verifies if the program is running from an unexpected location. Additionally, it cross-checks the username against a list of blacklisted names commonly associated with testing environments. If any of these checks trigger a flag, and the username matches a blacklisted name, the program throws an exception, stopping any malicious activity from proceeding.
This attack demonstrates a sophisticated, multi-layered approach to deploying the Quasar RAT, using a seemingly benign LNK file as the initial entry point. Through the use of sandbox evasion, anti-virtualization checks, and privilege escalation techniques (such as PowerShell and CMSTP), the TA ensures that the payload bypasses detection and establishes persistent control over compromised systems.
The employment of AES encryption for the payload, along with anti-debugging techniques and advanced .NET-based obfuscation, illustrates the attackers’ strong focus on evading traditional security solutions and complicating the analysis and reverse engineering process. The modular structure of the attack, with some evasion techniques left unused but ready to deploy based on the target environment, underscores the threat actor’s adaptability and capability to overcome varying levels of defense.
This campaign aligns closely with the ongoing operations of a Vietnamese threat group that has been active since July 2022. The group initially spread Ducktail malware, targeting digital marketing professionals. Over time, the group has evolved, expanding its operations through the use of Malware-as-a-Service (MaaS) and replicating its tactics, techniques, and procedures (TTPs) across multiple campaigns.
Tactic | Technique | Procedure |
Initial Access (TA0001) | Phishing (T1566) | The LNK file in a RAR archive may be delivered through phishing or spam emails. |
Execution (TA0002) | Command and Scripting Interpreter: PowerShell (T1059.001) | The LNK file executes PowerShell commands |
Execution (TA0002) | Windows Command Shell (T1059.003) | Uses cmd.exe to execute wmic and findstr commands |
Persistence (TA0003) | Boot or Logon Autostart Execution: Registry Run Keys / Startup Folder (T1547.001) | Added Run entry by modifying the Registry key |
Privilege Escalation (TA0004) | CMSTP (T1218.003) | CMSTPLUA is used for UAC bypass |
Defense Evasion (TA0005) | Obfuscated Files or Information: LNK Icon Smuggling (T1027.012) | LNK file comes with PDF Icon |
Defense Evasion (TA0005) | Obfuscated Files or Information: Encrypted/Encoded File (T1027.013) | TA decrypts the payload using AES decryption |
Defense Evasion (TA0005) | Disabling Security Tools (T1562.001) | EtwEventWrite function in ntdll.dll is modified to disable event tracing |
Defense Evasion (TA0005) | Virtualization/Sandbox Evasion (T1497) | Checks for virtual environments (e.g., QEMU, VirtualBox, VMware, Sandboxie) |
Defense Evasion (TA0005) | Process Injection (T1055) | Invoke-Expression is used to invoke decrypted payloads |
Discovery (TA0007) | Query Registry (T1012) | The script queries registry keys to gather system information for further checks, including checks related to virtualization. |
Discovery (TA0007) | System Information Discovery (T1082) | Using Windows Management Instrumentation Control gathers system information. |
Command and Control (TA0011) | Encrypted Channel (T1573) | The final payload, Quasar RAT, establishes C2 communication over an encrypted channel (AES encryption used in earlier stages). |
Command and Control (TA0011) | Application Layer Protocol (T1071) | After the payload is executed, the Quasar RAT communicates with its C2 server over standard HTTP or other application layer protocols. |
Indicators | Indicator Type | Description |
dc616cc55a345e448a058368aea7c99ab9dd2a9c8ec42674312b66dbc29b7878 | SHA-256 | Career_Development_Plan_for_Meta_Ads_Specialist_Hotpoint_With_Numerical.rar |
3de5e0b27c69c93b4c4b4812ed4453d4b81e99b7d407640a752e62e33b1ede2a | SHA-256 | Career_Development_Plan_for_Meta_Ads_Specialist_Hotpoint_With_Numerical/Career_Development_Plan_for_Meta_Ads_Specialist_Hotpoint_With_Numerical.lnk |
hxxps://www.dropbox.com/scl/fi/9p8no6tz85e09vg59kfwk/sav2_encrypt.txt?rlkey=hw7c83mq8uws216q3d4b1cfyi&st=4oycb9or&dl=1 | URL | URL from LNK |
9a00d0859bc7a81d6e289a414c39aa2bd95319fa3d1d0e5f1be6d348604d640c | SHA-256 | payload_1.ps1 (downloaded from Dropbox) |
b35452610c2cbc5a6a2bebd82af7c3883037b40be7072e43fc5989298bb26ea5 | SHA-256 | PositionApplied_VoyMedia.pdf <space> .lnk |
d8bc59a1acf2f9a14a2fb96de979672dbed27d798eecc9454021f352f2bf973a | SHA-256 | PositionApplied_VoyMedia.rar |
16ef774020e5754e4a8890789b7c798376a9521823c8897f9c97af5b33b27013 | SHA-256 | payload_1.bin |
8229f281a93f18612a47843aa69e94312b52180e7f775fd58e5ea04608e23bd0 | SHA-256 | LNK file delivers stromkitty |