Key takeaways Cyble Research and Intelligence Labs (CRIL) uncovered a sophisticated multi-stage malware attack originating from an archive file that contains a malicious LNK file. The lure document observed in the campaign indicates that the Threat Actor (TA) is targeting job seekers and digital marketing professionals, especially those involved with Meta Ads. The malware employs several techniques to detect virtual machine environments, evading detection and analysis in sandboxed or emulated environments. The malware uses multiple anti-debugging techniques to detect if it is being debugged, making analysis or reverse engineering more challenging. The malware employs defense evasion techniques, including disabling event tracing and altering in-memory functions, to evade detection by security tools. The malware attempts to escalate privileges to administrative levels using different approaches and sets up persistence by hiding in system directories, ensuring continued execution even after system reboots. The malware employs AES encryption in several phases of execution to conceal the malicious payload, which is only decrypted in memory after passing anti-virtualization and anti-debugging checks. This strategy makes it difficult for static analysis engines to detect the threat, as the payload remains hidden until runtime. The final stage of the attack deploys Quasar RAT, a well-known open-source remote access trojan, granting attackers full control over the compromised system. This access enables activities such as data theft, surveillance, and further exploitation of the system, making it a versatile tool for malicious purposes. This campaign has been attributed to a Vietnamese Threat Actor based on its specific targeting of Meta Ads digital marketing professionals and the tools employed. The tactics and techniques used in this attack closely align with a previous campaign identified in July 2022, reinforcing the connection to the same threat group. Overview 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. Figure 1 - Execution flow Technical Analysis 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 Figure 2 - Content of 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. Figure 3 - Contents of the PowerShell script Lure Document Analysis: 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. Figure 4 - Lure Document Figure 5 - Lure Document Output.bat 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. Figure 6 - Process Tree The de-obfuscated PowerShell code is shown below Figure 7 - Contents of the de-obfuscated PowerShell code 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. Triage environment check 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. Figure 8 - Triage DiskDrive check Figure 9 – Triage Desktop wallpaper check Checks for Qemu 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. Figure 10 - Checking for Qemu Checks for Parallels 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. Figure 11 - Checking for CheckForParallels Sandbox Detection 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. Figure 12 - Sandbox checks Figure 13 - Sandboxie crash Emulation environment checks 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. Figure 14 - Checking for Emulation Username checks 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. Figure 15 - Virtual Environment usernames Wine Emulator check 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. Figure 16 - Wine emulator check VMWare check 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. Figure 17 - Checking for VMWare KVM check 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. Figure 18 - Checking for KVM Hyper-V check 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. Figure 19 - Checking for Hyper-V Check for VMWare-related files 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. Figure 20 - VMware, VirtualBox checks VMProcess Checks 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. Figure 21 - VM Processes Device Check 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. Figure 22 - Checking for Devices Operating System Edition check 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. Figure 23 - OS Edition check 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. DebuggerAttached 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. Figure 24 - Debugger check Using NtSetInformationThread 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." Figure 25 – NtSetInformationThread() Using PageGuard 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. Figure 26 – Using PageGuard Using Hardware Breakpoints 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. Figure 27 - Checking for Hardware breakpoints Debugger attach 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." Figure 28 - Debugger check 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. Antivirus check 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. Figure 29 - Gathering information on any installed antivirus software Privilege Escalation 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. Figure 30 - Using powershell.exe 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. Figure 31 - Using CMSTPLUA COM Object Persistence 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 " rbxonimai"intheC:\Windowsdirectoryandcopiesitselfintothisfolderas"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. Figure 32 - Self-copying to the Windows folder Defense Evasion Upon Execution, the .Net executable modifies the “EtwEventWrite” function in ntdll.dll to disable event tracing by inserting specific opcode code. On 32-bit systems, it replaces the function with the opcodes “0xC2, 0x14”, which translates to the assembly instruction RETN 20. This causes the function to return early and bypass its normal operations. On 64-bit systems, it uses the opcode “0xC3”, which corresponds to the instruction RET, making the function return immediately. Figure 33 - Disabling Event Viewer 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. Quasar RAT 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 "cntonimai2"InstallName"cnt-onimai2" Install Name "cnt-CO2.exe" Mutex "928569f3-e524-4f67-936e-0d7f0a47cfad" Startup Key "cntOnimai"LogDirectoryname"cnt-Onimai" Log Directory name "cnt-L