Task-specific Atlas Agents investigate threats at machine speed with full transparency, expert validation, and explainable outcomes you can trust.
Atlas Extended Detection and ResponseOpen XDR with Agentic AI & machine learning that eliminates noise, enables real-time detection and response, and automatically blocks threats.
Atlas User ExperienceSee what our SOC sees, review investigations, and see how we are protecting your business.
Atlas Platform IntegrationsSeamless integrations and threat investigation that adapts to your tools and evolves with your business.
24/7 SOC-as-a-Service with unlimited threat hunting and incident handling.
Threat Response Unit (TRU)Proactive threat intelligence, original threat research and a world-class team of seasoned industry veterans.
Cyber Resilience TeamExtend your team capabilities and prevent business disruption with expertise from eSentire.
Response and RemediationWe balance automated blocks with rapid human-led investigations to manage threats.
Combine AI-driven security operations, multi-signal attack surface coverage and 24/7 Elite Threat Hunters to help you take your security program to the next level.
Get unlimited Incident Response with threat suppression guarantee - anytime, anywhere.
CTEM and advisory programs that identify security gaps and build proactive strategies to address them.
Flexible MDR pricing and packages that fit your unique security requirements.
Entry level foundational MDR coverage
Comprehensive Next Level eSentire MDR
Next Level eSentire MDR with Cyber Risk Advisors to continuously advance your security program
Stop ransomware before it spreads.
Identity ResponseStop identity-based cyberattacks.
Zero Day AttacksDetect and respond to zero-day exploits.
Cybersecurity ComplianceMeet regulatory compliance mandates.
Third-Party RiskDefend third-party and supply chain risk.
Cloud MisconfigurationEnd misconfigurations and policy violations.
Cyber RiskAdopt a risk-based security approach.
Mid-Market SecurityMid-market security essentials to prioritize.
Sensitive Data SecurityProtect your most sensitive data.
Cyber InsuranceMeet insurability requirements with MDR.
Cyber Threat IntelligenceOperationalize cyber threat intelligence.
Security LeadershipBuild a proven security program.
On March 31st, 2026, threat actors compromised the Axios npm (Node Package Manager) package, publishing two malicious versions, 1.14.1 and 0.30.4, to the npm registry. This supply chain…
On March 27th, 2026, F5 confirmed exploitation of a previously known vulnerability in its BIG-IP Access Policy Manager (APM) versions. The flaw, tracked as CVE-2025-53521 (CVSS: 9.3),…
eSentire is The Authority in Managed Detection and Response Services, protecting the critical data and applications of 2000+ organizations in 80+ countries from known and unknown cyber threats. Founded in 2001, the company’s mission is to hunt, investigate and stop cyber threats before they become business disrupting events.
About Us Leadership Careers Event Calendar → Newsroom → Aston Villa Football Club →We provide sophisticated cybersecurity solutions for Managed Security Service Providers (MSSPs), Managed Service Providers (MSPs), and Value-Added Resellers (VARs). Find out why you should partner with eSentire, the Authority in Managed Detection and Response, today.
Search our site
Multi-Signal MDR with 300+ technology integrations to support your existing investments.
24/7 SOC-as-a-Service with unlimited threat hunting and incident handling.
We offer three flexible MDR pricing packages that can be customized to your unique needs.
The latest security advisories, blogs, reports, industry publications and webinars published by TRU.
Compare eSentire to other Managed Detection and Response vendors to see how we stack up against the competition.
See why 2000+ organizations globally have chosen eSentire for their MDR Solution.
In late February 2026, eSentire's Threat Response Unit (TRU) observed an attempted delivery of a previously undocumented malware family within a customer environment in the Finance industry. TRU is tracking this threat as STX RAT, named for its consistent use of the Start of Text (STX) magic byte "\x02" prefixed to C2 messages.
TRU observed attempted delivery of the malware via a browser-downloaded VBScript file; by early March 2026, Malwarebytes reported a separate initial access vector in their blog, "A fake FileZilla site hosts a malicious download", where the malware was distributed through trojanized FileZilla installers.
Though we were unable to recover the original VBScript, we found a nearly identical sample in VirusTotal with hash: 799b29f409578c79639c37ea4c676475fd88f55251af28eb49f8199b904a51f3. The VBScript concatenates JScript contents, writes them to a JScript file on disk, and executes the JScript file via WScript with elevated privileges.

Note, the name of the JScript file varies between campaigns. In this incident, we observed the following command line:
"C:\Windows\System32\wscript.exe" "C:\Users\<Username>\AppData\Local\Temp\business-structure.xlsx.js" /elevated
The JScript file's contents are shown in the figure below, whose purpose is to download a TAR file, extract it to disk, and execute the next stage PowerShell loader.
The TAR file contains two files:

The PowerShell script acts as an in-memory loader: it takes the 1.bin payload string, reverses it and Base64-decodes it into bytes, allocates RWE memory using VirtualAlloc, copies both the payload and a parameter blob into those buffers via WriteProcessMemory, then computes the entrypoint address as base + offset and transfers execution to it by converting that address into a callable delegate with Marshal.GetDelegateForFunctionPointer and invoking it.

The packer used by the malware is characterized by two exports named "init" and "run", and the use of XXTEA decryption / Zlib decompression on a byte array stored in the .DATA section. Following "tail jumps" is an effective technique in unpacking the core STX RAT payload.

As a means of slowing the malware analysis process, strings stored throughout the binary are rolling XOR-encoded and AES-128-CTR-encrypted.
The first set of strings is stored throughout the malware and referenced by memory address. In multiple basic blocks, the plaintext is recovered using a rolling XOR-decryption loop in which the key index is advanced per byte and wrapped using a predefined modulus (i.e., when the index reaches the limit, it returns to the starting position and repeats).
For example, in many blocks the XOR key begins at 0x39 and increments for each ciphertext byte (so the next key byte would be 0x3A). When the key reaches a predefined upper bound (e.g., 0x6C), it wraps back to the initial value (e.g., 0x39) and the cycle repeats. The starting key differs between basic blocks. The figure below shows an example block that decrypts the C2 server address 95.216.51.236 (AS24940 - Hetzner Online GmbH).

The second set of strings are stored in a table and are decrypted on an as-needed basis via an offset into the table. The pseudo-code below displays the routine responsible for decrypting a string via offset. This routine eventually calls the CryptDecrypt API to decrypt a string via AES-128-CTR.

The table itself contains records matching the structure shown below.
struct AesEncryptedRecord {
BYTE aesKey[16]; // 128-bit key (16 bytes)
BYTE nonce[5]; // 5-byte Nonce
DWORD dwCiphertextLength; // Length of ciphertext
BYTE ciphertext[dwCiphertextLength]; // Ciphertext
};
The annotated figure below further illustrates an example record from this table and highlights its layout: the AES key, nonce, ciphertext length, and finally the ciphertext itself.

The malware uses two lookup tables: one for resolving exported APIs and another for modules. The exports table contains records in the form [module_index, export_sha1, export_salt]. The module_index points to a record in the second table, which stores per-module SHA‑1/salt pairs.
To resolve an API, the malware enumerates modules already loaded in the process via the PEB loader data. It lowercases each DLL name and identifies the target module by computing:
If the module is not currently loaded, it searches the system directory for candidate DLLs, lowercases each filename, and manually loads any DLL whose hash matches the expected value.
Once the correct module is loaded, it enumerates that module's exported function names. For each export, it computes:
It then compares the result to the expected export_sha1 to locate the desired API. More details on this process are provided below.
Call a lookup function (renamed here as mw_resolve_api, see Figure 10) and pass a single argument - an index into a table of "ExportSha1SaltBlock" structures. Through pointer math, the index enables retrieving the "wanted" exports associated SHA-1/Salt structure in the table. Each structure is 48 bytes long and follows the format below.
struct ExportSha1SaltBlock {
DWORD dwModuleIndex; // Used in calculating the virtual address of the export's associated module sha1/salt pair
BYTE sha1Hash[20]; // 20 bytes representing the SHA-1 hash to check against the combined export name and salt
BYTE salt[10]; // 10 bytes of salt to combine with the export name
BYTE reserved1[14];
};
The first member dwModuleIndex from the prior structure is then used in another lookup to determine the export's associated module sha1/salt structure. The function (renamed here as mw_get_module_base_by_hash_and_salt in Figure 8) also takes a single argument - an index into an array of structures. Each structure is also 48 bytes long, but follows a different format:
struct ModuleSha1SaltBlock {
BYTE sha1Hash[20]; // 20 bytes representing the SHA-1 hash to check against the combined module name (lowercase) and salt
BYTE salt[10]; // 10 bytes representing the salt to combine with the module name
BYTE reserved2[18];
};
Next, the routine resolves the base address of the module associated with the export by querying the process loader data (PEB_LDR_DATA). It enumerates the InMemoryOrderModuleList and, for each entry, retrieves the module's base name as a wide-character string. It then iterates over the string, converting it to lowercase ASCII. The normalized name, along with the SHA-1/salt structure, is passed to another subroutine (shown here as mw_sha1_verify_with_salt), which returns a boolean indicating whether the string matches the salted hash, e.g. SHA-1(dll_name + salt). If a match is found, the routine breaks out of the loop and returns the module's base address.

Here is a look at the raw data, which has been annotated with the formula for indexing into the table with the SHA-1 and salt for psapi.dll as an example.

The resolved module base address is then used to parse the module's export directory. The routine iterates the module's exported function names and hashes each name as-is (no lowercase conversion like for module names), comparing the result to the SHA-1 hash from step 1. When a match is found, it resolves the corresponding function address (via export name/ordinal mapping) and returns it.

Here is a look at the raw data, which has been annotated with the formula for indexing into the table with the SHA-1 and salt.

The table below lists modules and associated SHA1/SALT:
| DLL | SHA-1 | SALT |
| psapi.dll | de6a4b679e42b6b429bbe520c22b849224b2de26 | 4208deb22663bfc976e3 |
| advapi32.dll | d4a90f0a6efd9add984f8bbb51bc4565746874ad | ef4453807d1f1fa6eb12 |
| gdi32.dll | 92bbc333b52c26ed10eaf38b9134d4ccfdf4389b | 2c4c88c9cd454d5386a1 |
| userenv.dll | 2bad076733e220cde1dd42747ffd13e8eb382c2d | fcc38246d4b212c09e83 |
| shell32.dll | 0207adb637fcb98598e74e6b90c817ad7079fc1b | 1fc95c810e8b40d5c78c |
| mpr.dll | e52557da86b7bde2d869074431ade27f6e26d0ae | c6413ab9ee98bcdb558e |
| iphlpapi.dll | 26647081133efeddf28e40be13c71355dc75dd53 | a08da86af6b7bcaef571 |
| crypt32.dll | 6fee56f2e8d3fc710c2f98409fe20c2aa252fc7a | f77530c3112e84739fca |
| ws2_32.dll | ac708f17875fc75d328ecfe524e3614895cf042e | e667dee10a8446e75a5c |
| ntdll.dll | 5095ca6e522937910ae8367867d10a35c376d770 | 900d814448e86b5ceae1 |
| user32.dll | aa9d86d6096350faf04d0ea9e1fa0956766f7bc7 | cbabf6a3fa051fe8d54e |
| kernel32.dll | e2e3b4f81f27654d1c3579a3803ed0bfcaf833e3 | 8b29a4434d0192d729e5 |
| wtsapi32.dll | d90c15ba3566a54742312fbca2a022f586266813 | 0b65bf7b2fb9dda4192f |
| shlwapi.dll | 2a9abfad84057d0d71e7bc4ecb23d29013c2e11e | 50079b1f09075cfa8f48 |
The python snippet shown below can be used to emulate the aforementioned hashing behavior:
import hashlib
module_name = b'shlwapi.dll'
salt = bytes.fromhex('50079b1f09075cfa8f48')
result = hashlib.sha1(module_name + salt).hexdigest()
print(result)
# 2a9abfad84057d0d71e7bc4ecb23d29013c2e11e
The tables below describe various Anti-VM detection artifacts, (i.e. files, registry keys/values, services) checked by the malware. If an artifact is found, the malware "jitter exits" (sleeps with a randomized delay and exits).
| Registry Key | Hypervisor |
| HKLM\HARDWARE\ACPI\DSDT\VBOX__ | VirtualBox |
| HKLM\HARDWARE\ACPI\FADT\VBOX__ | VirtualBox |
| HKLM\HARDWARE\ACPI\RSDT\VBOX__ | VirtualBox |
| HKLM\SOFTWARE\Oracle\VirtualBox Guest Additions | VirtualBox |
| HKLM\System\CurrentControlSet\Services\VBoxGuest | VirtualBox |
| HKLM\System\CurrentControlSet\Services\VBoxMouse | VirtualBox |
| HKLM\System\CurrentControlSet\Services\VBoxService | VirtualBox |
| HKLM\System\CurrentControlSet\Services\VBoxSF | VirtualBox |
| HKLM\SYSTEM\ControlSet001\Services\vmdebug | VMware |
| HKLM\SOFTWARE\VMware, Inc.\VMware Tools | VMware |
| HKLM\SYSTEM\ControlSet001\Services\VMMEMCTL | VMware |
| HKLM\SYSTEM\ControlSet001\Services\VMTools | VMware |
| HKLM\System\CurrentControlSet\Services\vioscsi | QEMU |
| HKLM\System\CurrentControlSet\Services\viostor | QEMU |
| HKLM\System\CurrentControlSet\Services\VirtIO-FS Service | QEMU |
| HKLM\System\CurrentControlSet\Services\VirtioSerial | QEMU |
| HKLM\System\CurrentControlSet\Services\BALLOON | QEMU |
| HKLM\System\CurrentControlSet\Services\BalloonService | QEMU |
| HKLM\System\CurrentControlSet\Services\netkvm | QEMU |
| Registry Key\Value Name | Substrings |
| HKLM\HARDWARE\DEVICEMAP\Scsi\Scsi Port X\Scsi Bus X\Target Id X\Logical Unit Id X\Identifier | qemu, vbox, vmware |
| HKLM\HARDWARE\DESCRIPTION\System\SystemBiosVersion | qemu, vbox |
| HKLM\HARDWARE\DESCRIPTION\System\VideoBiosVersion | qemu, virtualbox |
| HKLM\HARDWARE\DESCRIPTION\System\SystemBiosDate | 06/23/99 |
| HKLM\System\CurrentControlSet\Control\SystemInformation\SystemManufacturer | qemu, vmware |
| HKLM\System\CurrentControlSet\Control\SystemInformation\SystemProductName | qemu, vmware |
| HKEY_LOCAL_MACHINE\HARDWARE\DESCRIPTION\System\CentralProcessor\0\ProcessorNameString | qemu |
| File | Hypervisor |
| vdservice.exe | QEMU |
| vdagent.exe | QEMU |
| balloon.sys | QEMU |
| netkvm.sys | QEMU |
| pvpanic.sys | QEMU |
| viogpudo.sys | QEMU |
| vioinput.sys | QEMU |
| viorng.sys | QEMU |
| vioscsi.sys | QEMU |
| vioser.sys | QEMU |
| viostor.sys | QEMU |
| vboxservice.exe | VirtualBox |
| vboxtray.exe | VirtualBox |
| VBoxMouse.sys | VirtualBox |
| VBoxGuest.sys | VirtualBox |
| VBoxSF.sys | VirtualBox |
| VBoxVideo.sys | VirtualBox |
| VGAuthService.exe | VMware |
| vmnet.sys | VMware |
| vmmouse.sys | VMware |
| vmusb.sys | VMware |
| vm3dmp.sys | VMware |
| vmci.sys | VMware |
| vmhgfs.sys | VMware |
| vmx86.sys | VMware |
| vmkdb.sys | VMware |
| vmnetuserif.sys | VMware |
| vmnetadapter.sys | VMware |
After checking for virtual machine artifacts, anti-analysis checks are performed. Notably, the malware assumes it only runs in the context of PowerShell/MSBuild, otherwise it jitter exits. This is an effective technique to hinder malware analysis via debugger as malware is often dumped to disk in the analysis process and executed via rundll32 or a custom loader.

The malware then hides its own window by calling EnumWindows to enumerate top-level windows, and passes a callback that retrieves each window's class name via GetClassNameA and compares the window's class name against the string, "CASCADIA_HOSTING_WINDOW_CLASS" (Windows Terminal class name). Upon match, it modifies the window's extended style to include WS_EX_TOOLWINDOW (0x80), causing the window to be hidden from Alt+Tab and the Taskbar.


Immediately after the malware hides its window, if configured to do so, it uses a known AMSI (Anti-Malware Scan Interface) -bypass technique called AMSI Ghosting, where it patches the Windows API rpcrt4!NdrClientCall3, effectively disabling a core RPC layer that AMSI depends on and preventing security solutions from being able to act on telemetry produced by AMSI.


The table below describes the processes and drivers the malware searches for in an effort to identify security software present on the machine. Each identified product is appended to a list that is sent in the introduction message to the C2.
| Process Name | Anti-Virus Product |
| mcsysmon.exe | McAfee SystemGuards |
| vsdatant.sys | ZoneAlarm |
| GDBehave.sys | Bitdefender |
| clamd.exe | ClamAV |
| AntiSpyWare2.exe | Trend Micro |
| AshAvScan.sys | Avast |
| dwengine.exe | Dr. Web |
| dwservice.exe | Dr. Web |
| fortiscand.exe | Fortinet FortiClient |
| fmon.exe | Fortinet FortiClient |
| AvastSvc.exe | Avast |
| aswSP.exe | Avast |
| aswFsBlk.exe | Avast |
| AVGIDSAgent.exe | AVG |
| avgwdsvc.exe | AVG |
| AVGIDSErHr.sys | AVG |
| AVGIDSxx.sys | AVG |
| avguard.exe | Avira |
| avgntmgr.sys | Avira |
| avgntdd.sys | Avira |
| bdagent.exe | Bitdefender |
| BDHV.sys | Bitdefender |
| avc3.sys | AVG |
| cmdagent.exe | Comodo |
| cis.exe | Comodo |
| inspect.sys | Comodo |
| cmdhlp.sys | Comodo |
| fsav32.exe | F-Secure |
| FSSM32.exe | F-Secure |
| fspex.exe | F-Secure |
| fsdfw.sys | F-Secure |
| WAHost.exe | Windows Defender |
| pavproc.sys | Panda Security |
| pavboot64.sys | Panda Security |
| savmain.exe | Sophos |
| wscclient.exe | Sophos |
| savonaccess.sys | Sophos |
| SMC.exe | Symantec |
| nis.exe | Symantec |
| SMCgui.exe | Symantec |
The malware communicates with its C2 server over TCP sockets via Winsock and protects C2 traffic using ECDH (Elliptic Curve Diffie–Hellman) key exchange. The figure below illustrates the key exchange process between the victim machine (e.g. Alice) and the C2 (e.g. Bob).

During the handshake, the C2 sends a ServerHello containing its X25519 public key and an Ed25519 signature of that key. The client verifies the signature using a hard-coded Ed25519PublicKey (stored as a rolling XOR–encoded Base64 string: 4DwvIfxy4thDpGXKYjew8MTI1jYwFEIs2oHuW35BtVM=), then completes the ECDH exchange. If the signature doesn't match, the malware assumes the C2 is not authentic and sends empty packets to the C2 in a loop.
The victim's X25519 private key is generated using the Windows API CryptGenRandom. By hooking this API and breaking just before it executes, you can detect the key-generation call by checking rdx (dwLen) for 0x20 (32 bytes), then save the output buffer pointer stored in r8 (pbBuffer), then step over the call and dump 32 bytes from that address.
Recovering this private key lets a researcher derive the shared secret and decrypt traffic exchanged between the victim and the C2. The routine that generates the victim's private key bytes can be seen in the figure below.

Another effective technique in dumping the private key involves hooking the X25519 clamping routine and dumping the private key bytes at rcx (first argument):

Simulating C2 communications requires patching the Ed25519 base64 string in the malware with a new Ed25519 public key and signing the returned X25519 key and returning that signature to the implant. We used the following code to convert our Ed25519 public key into hex bytes and patched the threat actor's Ed25519 public key with our own.
patch = b'dckcTs0XHVxirXbvz7g45gZTZ8eTBJ6hkXyHUqC9tYw=\x00'
patch_bytes = bytearray()
start = 0x34
for i in patch: # Rolling XOR over base64-encoded Ed25519 key
patch_bytes.append(i ^ start)
start += 1
print(patch_bytes.hex())
#50565d546c4a0a63746b4656321920353e7221737d2e101f16752b1b121b643b3f0d2f1f0d2819622804296260

The key exchange between the C2 and victim produces a shared secret, which the malware feeds into HKDF-SHA256 as the input keying material. It derives a 32‑byte key with no salt and an empty info/context field. That key is then used with ChaCha20‑Poly1305 to encrypt victim data and decrypt C2 data, with Poly1305 ensuring integrity via tag verification. Encrypted messages follow the format [chacha20_nonce₁₂ ∣ ciphertext ∣ poly1305_tag₁₆].
This communication protocol provides the threat actors with several operational benefits:
The malware implements its own message framing on top of TCP, where each frame/message is encoded as:
DWORD length
BYTE payload[length]
Note: all traffic in both directions (victim ↔︎ C2) uses the same length-prefixed framing. Each side reads exactly 4 bytes to obtain the payload length, then continues reading from the TCP stream until length bytes of payload have been received.
1) C2 → Victim: ServerHello
The server sends two length‑prefixed values. The first is its X25519 public key, which the client (victim machine) uses to perform key exchange and compute the shared secret. The second is an Ed25519 signature over that X25519 key, which the client verifies using an embedded Ed25519PublicKey (decoded at run-time via pre-described rolling XOR block): 4DwvIfxy4thDpGXKYjew8MTI1jYwFEIs2oHuW35BtVM=.
DWORD len_pubkey // 0x20 (32)
BYTE server_pubkey[len_pubkey]
DWORD len_ed25519_signature // 0x40 (64)
BYTE ed25519_signature[len_ed25519_signature]
server_pubkey: the C2's 32‑byte X25519 public key used for key exchange
ed25519_signature: a 64‑byte Ed25519 signature used to verify the server_pubkey
2) Victim → C2: ClientHello
The client generates an ephemeral keypair and returns its public key:
DWORD len_victim_pubkey // 0x20 (32)
BYTE client_pubkey[len_victim_pubkey]
The client computes a shared secret and derives the ChaCha20 key, generates a nonce, then encrypts and sends the message to the C2. The nonce is prepended to the message.
Key Agreement
Describes the process of generating the ChaCha20 key via pseudocode.
shared_secret = ECDH(client_privkey, server_pubkey)
key_material = HKDF(shared_secret)
Encrypted Message
The data structure of encrypted messages is described in the pseudo-structure below.
BYTE chacha_nonce[12]
BYTE ciphertext[len_message - 12 - 16]
BYTE poly1305_tag[16]
Message Decryption Process
Describes the process of decrypting an Encrypted Message sent to and received from, the C2.
The data structure of decrypted messages varies, however it generally follows the format shown below. The "data" key stores different data depending on the "type" key. The message shown below is the first message sent to the C2 by the malware and is essentially an introductory message to notify the operators of a new bot having joined the botnet.
If the version value sent in this JSON is considered out-of-date by the C2, the C2 sends the update command, otherwise it sends the get_creds command.
\x02 // Magic byte
{
"type": "intro",
"channel": 0,
"data": {
"user_id": "<SID-derived-hash>",
"machine_id": "<SID-derived-hash>",
"hostname": "DESKTOP-1UG9QX", // Victim hostname
"username": "john.smith", // Victim username
"os": "Windows 11 Pro",
"version": "2025.11.003", // Malware build version
"arch": "x64", // OS architecture
"is_admin": 1, // Is admin
"pid": 7953, // Malware process's PID
"process_path": "C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe",
"server": "95.216.51.236", // C2 server
"utm_group": "" // (optional) Victim clipboard match regex: # Tag: ([^\s]*)\s
"utm_source": "", // (optional) Victim clipboard match regex: # Hostname: ([^\s]*)\s
"ram": 8191, // Installed RAM
"av": [] // AntiVirus names
}
}
The snippet below shows the decrypted message JSON the C2 sends the malware in the event it considers the introductory JSON acceptable.
\x02 // Magic byte
{
"type": "get_creds"
}
After receiving the get_creds command, and prior to sending harvested credentials, the malware creates a screenshot of the victim desktop via BitBlt/GDI functions, and transmits it to the C2 as a base64 encoded JPG file.


The figure below displays the format of the payload returned when the implant is considered out-of-date by the C2 and the C2 returns the update command. This payload format is similar for the run command. Rather than JSON format, a custom binary format is returned by the C2. The header of the file is simple and follows a DWORD length pertaining to field names and the field names themselves. The remaining data includes a stub (beginning with FC 48 83...) and the update payload itself.

The malware parses the aforementioned structure, allocates PAGE_EXECUTE_READWRITE (RWX) memory via VirtualAlloc, writes the payload to it, and executes the stub, which in turn calculates the offset of the OEP in the payload and calls it. The update payload itself is packed by the same packer we previously mentioned, e.g. init/run exports and follows the same unpacking process. The use of a stub to calculate the OEP is a clever trick used by the malware to prevent analysis of payloads in sandboxes. The figure below displays the stub's disassembly.

The introduction JSON previously described contains two optional keys, utm_group and utm_source. These keys are set after the malware gets the victim's clipboard via OpenClipboard/GetClipboardData and parses it via the regex patterns shown in the table below.
Notably, the cid value stores an AES-128-CTR encrypted callback domain for additional campaign tracking purposes. It is decrypted using a hard-coded key, "a91b5f85e4572d3030fc9f4e18fa7896" and nonce, "37927fee241db9697f94c0f3ca488946". After it is decrypted, it is checked to ensure it begins with the magic byte "\x02" to ensure authenticity of the decrypted callback domain.
The format of the callback URL is as follows:
https://<decrypted_domain>/api/v1?action=signal&ray_id=<ray_id>
| Regex Pattern | Description |
| # Tag: ([^\s]*)\s | Regex match is set as utm_group in introductory message to the C2 |
| # Hostname: ([^\s]*)\s | Regex match is set as utm_source in introductory message to the C2 |
| cid=([0-9a-f]+) | AES-128-CTR encrypted callback domain (pre-fixed with STX byte \x02) |
| ray_id=([0-9a-f]+) | Sent via callback URL via query parameter ray_id |
The figure below displays pseudo-code of the start of the routine where APIs are resolved, the first regex pattern decoded via rolling XOR, and regex match against the victim's clipboard data.

The table below describes commands identified in our analysis (non-exhaustive). The update and get_creds commands are listed first, as they are the primary commands delivered automatically when the malware checks into the C2 backend. The remainder of commands are sent by threat actor(s) via hands-on-keyboard, most likely through a web panel.
| Command AKA "type" | Description |
| update | Sent by the C2 if the intro message's version value is considered out-of-date. Releases mutex, and executing the payload from the C2. Supported payload types include: exe, bin, dll, ps1, and shellcode. If the update is a success, it calls RtlExitUserProcess to exit, otherwise it regenerate mutex and continue running. |
| get_creds | Typically sent automatically by the C2 Credential collection and transmission to C2 in a JSON formatted message |
| die | Deletes persistence mechanisms, walks destructors, and calls RtlExitUserProcess, terminating itself |
| run | Execute C2-supplied ps1, shellcode, dll, or exe in memory, similar to the update command, however it doesn't release its mutex or exit. |
| start_hvnc | Hidden remote desktop allowing threat actors to control victim's machine |
| key_press | Inject keystrokes into the hidden desktop, supporting the HVNC feature, uses SendInput API |
| mouse_input | Simulate mouse movement, supporting the HVNC feature, uses SendInput API |
| mouse_wheel | Threat actors can simulate mouse scroll wheel, supporting the HVNC feature |
| switch_desktop | Switch between hidden desktops |
| paste | Allows paste functionality, supporting the HVNC feature |
| start_tunnel | Open socket on C2-specified host/port, connect_ok or connect_failed is sent back to the C2 to indicate success/failure. |
| tunnel_data | Tunnel data through an opened socket. On failure tunnel_eof is sent to the C2. |
| connection_lost | Clean up all desktop handles / close all HVNC channels |
| channel_closed | Clean up desktop handle / close a specific HVNC channel |
The malware supports various payload types delivered by the C2. Payloads are handled through numerous commands, e.g. update, run. Payload types supported by the malware are described below. The table below describes the id to string mapping of payload types and are internal identifiers used by the malware.
| ID | Payload Types | Description |
| 1 | bin, exe | VirtualAlloc (PAGE_EXECUTE_READWRITE) w/ stub that calls OEP |
| 2 | dll | x64 DLL reflective injection |
| 3 | shellcode | Calls CreateThread and passes it a routine that calls VirtualAlloc to allocate RWE buffer for the payload, then copies the payload to the buffer, flushes instruction cache, and calls the entrypoint. |
| 4 | ps1 | PowerShell execution via STDIN |
For the ps1 payload type specifically, the malware creates a pipe by calling CreatePipe and assigns it to a new PowerShell process's redirected STDIN handle. It then calls CreateProcessW to launch the process with its standard input bound to the pipe and calls WriteFile to write the C2-delivered PowerShell payload into the child process's STDIN. PowerShell reads the data from STDIN and executes it via Invoke-Expression, resulting in fileless, minimal command-line for executing PowerShell in memory. The minimal command line used in this process is shown in the code snippet below.
powershell.exe -Command "[Console]::In.ReadToEnd() | Invoke-Expression"


The list below described credentials targeted by the malware (non-exhaustive):
| Folder Name/Application | Behavior |
| FileZilla |
*.xml file contents are matched against:
|
| WinSCP |
Credentials stored in registry keys under the key shown below are harvested:
|
| Cyberduck/iterate_GmbH |
Matches file contents against regex patterns:
|

This section covers two commands (key_press and mouse_input) that enable HVNC keyboard/mouse injection into hidden desktops. When the RAT receives the key_press command, the routine shown below is responsible for converting the C2-supplied string into keyboard input via the SendInput API, effectively allowing threat actors to send keyboard input in hidden desktop windows.

The next figure displays the handler for the mouse_input command, which allows threat actors to move the mouse cursor in hidden desktops using C2 supplied coordinates and the SendInput API.

The malware uses several different techniques to persist on the infected host, some of which are described below.





eSentire has created a python script for IDA Pro available here that decrypts rolling-XOR and AES-128-CTR strings in the unpacked payload and sets comments. The script will also resolve APIs by parsing the Module/Export SHA-1/Salt tables and match them against exports in the JSON file exports_map.json (or the System directory).


rule STXRat
{
meta:
author = "YungBinary"
description = "Detection for unpacked STX RAT in memory"
strings:
// Lowercasing
$s1 = {
8D 51 BF
83 FA 19
8D 41 20
0F 47 C1
C2
}
// AMSI ghosting
$s2 = {
48 8D 05 ?? ?? ?? ??
66 C7 45 ?? 48 B8 [0-6]
48 89 45 ??
48 8D 55 ??
66 C7 45 ?? FF E0
}
// Debugger check
$s3 = {
65 48 8B 04 25 60 00 00 00
80 78 02 01
}
// Crypto string
$s4 = "Microsoft Enhanced RSA and AES Cryptographic Provider (Prototype)" ascii
// AES key/size/algo handling
$s5 = {
B9 10 66 00 00 [0-3]
0F 44 C1
B9 0F 66 00 00
41 81 ?? C0 00 00 00
0F 44 C1
B9 0E 66 00 00
}
// module name copying
$s6 = {
48 83 FB 5A
73 ??
88 84 1C ?? ?? ?? ??
48 FF C3
48 FF C1
8A 01
84 C0
75
}
// Sha1 initialization constants
$s7 = {
83 61 18 00
83 61 14 00
C7 01 01 23 45 67
C7 41 04 89 AB CD EF
C7 41 08 FE DC BA 98
C7 41 0C 76 54 32 10
C7 41 10 F0 E1 D2 C3
C3
}
// X25519 clamping
$s8 = {
80 61 1F 3F
80 49 1F 40
80 21 F8
}
condition:
uint16(0) == 0x5a4d and (4 of ($s*))
}
import "pe"
rule STXRatLoader
{
meta:
author = "YungBinary"
description = "Detection for STX RAT loader"
strings:
// Kernel32 ROR-14
$ror1 = { B9 4E 15 F5 1F E8 }
// VirtualProtect ROR-14
$ror2 = {
BA 35 EC 33 57
48 8B C8
48 8B D8
E8
}
// CreateThread ROR-14
$ror3 = {
BA 36 91 AC 32
}
// Ntdll ROR-14
$ror4 = {
BA 7E 91 90 5A
48 8B C8
E8
}
// XXTEA constant
$s1 = {
69 D0 47 86 C8 61
}
// Zlib
$s2 = {
B8 85 10 42 08
41 F7 E2
}
// ROR
$s3 = {
41 C1 C8 0E
0F BE C0
44 03 C0
}
condition:
uint16(0) == 0x5a4d and ((pe.exports("init") and pe.exports("run")) and 1 of ($ror*) and 1 of ($s*))
}
| Type | Value | Description |
| IPv4 | 147.45.178.61 | Download IP seen in initial stage |
| File | 799b29f409578c79639c37ea4c676475fd88f55251af28eb49f8199b904a51f3 | VBScript that loads STX RAT |
| IPv4 | 95.216.51.236 | STX RAT C2 |
| URL | yu7sbzk2tgm4vv56qgvsq44wnwgct6sven4akbb2n3onp46f42fcstid.onion | STX RAT C2 (onion) |
| Command Line | powershell.exe -Command "[Console]::In.ReadToEnd() | Invoke-Expression" | Fileless PowerShell execution |
| Command Line | "C:\Windows\System32\wscript.exe" "C:\Users\<Username>\AppData\Local\Temp\business-structure.xlsx.js" /elevated | Elevated JScript execution |
| File | a57683ae49dd24256dab0dd21ca83c4a08892fda92e83206447380a2b6c80221 | STX RAT unpacked |
| File | 0e9c8e5ce94641e0b07607647a55c162adb18048f9c1e1e3dbe859cd08b2a797 | STX RAT unpacked |
| File | 77eea991e5c11da46e10c208fb8920a08a9bbdd8ffd72d0d6548fd8e45aa4647 | STX RAT unpacked |
| File | 52862b538459c8faaf89cf2b5d79c2f0030f79f80a68f93d65ec91f046f05be6 | STX RAT unpacked |
| File | da65c30f4dee13d3c85c6a31386018d101d635e28eeb65ac73699787fecc20e0 | STX RAT unpacked |
| File | abd003ab1172cda83731dbe76d20a43c35a452d683d628a4e59eac8aadc68ffa | STX RAT unpacked |
| File | ca3bd6f8f4c8170c60896493b0bbfc4629bf94a3d0c5bd3f32397e869e98fb3d | STX RAT unpacked |
| File | b59ac3088a58ebafdcdf00a5597c0de156de667d498bb8eccdaad5c8ba380e99 | STX RAT unpacked |
| File | 2b0d8c8e86dd372b44b99f8be4e4a7cbbfe5ce78bc10b714fc0735c15b7ddb32 | STX RAT unpacked |
| File | 3511e2bb89f64555acdef3b486717fd517f500c8c630e02e9c6fa0ac5bed8950 | STX RAT unpacked |
| File | a1ac7046e99181fe46edd62c00ca53602e7cd4430365307d0b3a47ddd1e9e670 | STX RAT unpacked |
| File | 2d2073ee0404dba0de7e248dc50f60258ca85e493be9021657e325a9bbd7cb01 | STX RAT unpacked |
| File | f04b0c3a53e3af7699c30ab9adb4d60a71a7da6945cf0ae287a9f67675433a67 | STX RAT unpacked |
| File | d122d6c2ccc69594bbfbca82315aa0803b3b93972a6ab83699797812b35d9679 | STX RAT unpacked |
| File | f431ff7bc59df48c137ef63839a5a2af520e0d3b28429468398e3b291f30d1e6 | STX RAT unpacked |
| File | 3455ec49b8dc3743398a20c271194682eba40a67ee3b10549d3e6f837f7499ca | STX RAT unpacked |
| File | 64adf1715483f63fc47283393f89857f0545a45d9e7382417189b5084d19c37b | STX RAT unpacked |
| File | f74d052337110c6282f4d9738263b89056d0c89d131b329d5d4e3189b67206ae | STX RAT unpacked |
| File | 0a60ccf29f89019b1eebbbb8ad9bf0302dba399a26a62449078dda919bbd247b | STX RAT Loader |
| File | c4a5223bcf57b32e036c33c4d0e41aa44ff3eb4632c2fb4ed9c9bd593a04c3ee | STX RAT Loader |
| File | 17fb97a117cb684c82d522e65c0958c4c1267401317cda53c77035189546ebba | STX RAT Loader |
| File | f81e14ac7309019208529599a848c2287789f0ccbcd2f7609e9f239f52376763 | STX RAT Loader |
| File | 66a155f6672fbbb041cb754c143db91b30084f98e9102c280ba95ffda156123b | STX RAT Loader |
| File | 8c812fa14a4c5ac63dd1dce47232b45bea95f93dcc5cac40bb12fa6f1961e1bc | STX RAT Loader |
| File | 5168eae0ee183575b9a2d2c0c21a23400125502fb78f41b20db27a0bea58324d | STX RAT Loader |
| File | 3763b9e6eeb9a18875c45ba7d1a4f9fbfd6e80d1aea434e88ad99ee5b1bbd790 | STX RAT Loader |
| File | a2d703265d61b78837e86527aa2e31994a934c72b6c073db0c4d9c0c59a4e401 | STX RAT Loader |
| File | b3f21d0843fa7106b466c590c97b1b8b201a79ae82ed46b46d2422dd252d7836 | STX RAT Loader |
| File | 84c2f3b13f5251cf87d1a2c95ac7ca111238f61d56358b2c4228c84ef9ed1ae7 | STX RAT Loader |
| File | da65c30f4dee13d3c85c6a31386018d101d635e28eeb65ac73699787fecc20e0 | STX RAT Loader |
| File | ac97a49e17bf2a315205a30cf39a68c264b1dc4395b88e3997ec506c778159b0 | STX RAT Loader |
| File | 5c37b35929dac5c640d1d14e6dc74009c5072536d7fbe0c58822bf2387a8a22d | STX RAT Loader |
| File | ca3bd6f8f4c8170c60896493b0bbfc4629bf94a3d0c5bd3f32397e869e98fb3d | STX RAT Loader |
| File | aab1f1bdba7083a25d7c841cd2dc3588cc0f3e28e29260bea5c2fd5b033697fb | STX RAT Loader |
| File | af7a76820a42c4cadfc7ff5fd372c99e9c5fd96ee9d14e07bde0902fec1881ab | STX RAT Loader |
| File | 8b28c0568baa7da10200a012a70ff735ccec557678a40d1b3fb16f5c0a31f6b7 | STX RAT Loader |
| File | d32455fc430ffc13e8a89db9198f17184fd27001fc11a7e9531d6055932853db | STX RAT Loader |
| File | 9eeef204645391b9c9e3d5b54f3541b8e52440d2a288873749398741182ce7de | STX RAT Loader |
| File | b7d64d6a9c641855f400949c8c000e1b53b9355fbe3663e41f01e4b80f8eab60 | STX RAT Loader |
| File | 58460f8009df7ca5d2a9b2e9346d940388472cd4cd808ac6c797942824bde299 | STX RAT Loader |
| File | 3074a18a349d7e8022fbbdcfc059b7b729862488f1e23adcfe634bb94535fd61 | STX RAT Loader |
To learn how your organization can build cyber resilience and prevent business disruption with eSentire’s Next Level MDR, connect with an eSentire Security Specialist now.
GET STARTEDThe 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.
Cookies allow us to deliver the best possible experience for you on our website - by continuing to use our website or by closing this box, you are consenting to our use of cookies. Visit our Privacy Policy to learn more.
2026 eSentire, Inc. All Rights Reserved.