Ferrox: Dissecting a Modern Rust-Based Infostealer


Research Context

This analysis documents the Ferrox infostealer project — a Windows-targeted credential harvester I built for security research and educational purposes. All testing was conducted in isolated virtual machine environments against Windows Defender only.

This is a research project. The goal was to understand modern stealer techniques, explore how Rust can be weaponized for offensive tooling, and demonstrate the current state of AV evasion. Critical components have been intentionally removed from the public codebase.

The full (redacted) source is available at github.com/vibheksoni/ferrox.

Why This Matters

Infostealers are one of the most prevalent malware categories today. They’re sold as malware-as-a-service, used in initial access operations, and responsible for massive credential leaks. Understanding how they work — from the evasion techniques to the data exfiltration methods — is essential for building better defenses.

Most modern stealers are written in Go or C++. Ferrox is written in Rust, which is unusual but increasingly relevant as offensive tooling diversifies. The project demonstrates:

  • Hell’s Gate syscalls for EDR bypass
  • Polymorphic code generation producing unique binaries per build
  • Compile-time string encryption with AES-256-GCM
  • Chrome App-Bound Encryption bypass for credential theft
  • Process injection into SYSTEM processes to disable Defender
  • Anti-VM, anti-debugger, and sandbox evasion techniques

This post breaks down each technique, explains the implementation, and discusses detection strategies.

Execution Pipeline

Ferrox runs silently (no console window via #![windows_subsystem = "windows"]) and follows a structured execution flow:

  1. Single instance check — Creates a global mutex to prevent multiple instances
  2. Anti-analysis checks — VM detection, debugger detection, sandbox evasion
  3. Legitimate traffic generation — Blends in with normal network activity
  4. Device fingerprinting — Generates unique device ID from hardware serials
  5. System reconnaissance — Collects IP, WiFi passwords, Windows key, installed AV
  6. Data harvesting — Extracts credentials from browsers, wallets, apps, games
  7. Compression and encryption — ChaCha20 encryption of stolen data
  8. Exfiltration — Uploads encrypted blob to Telegram Bot API
  9. Cleanup and self-deletion — Removes traces and exits

If any anti-analysis check fails, the malware silently exits or self-corrupts to avoid detection.

Harvest Targets

Browsers

All Chromium-based browsers (Chrome, Edge, Brave, Opera, Vivaldi) and Gecko-based browsers (Firefox, Waterfox, Pale Moon). Extracts:

  • Saved passwords — Including Chrome App-Bound Encryption decryption (credit: xaitax/Chrome-App-Bound-Encryption-Decryption)
  • Cookies — Session tokens for account takeover
  • Autofill data — Names, addresses, phone numbers
  • Credit cards — Encrypted card data
  • Browser extensions — Crypto wallet extensions targeted specifically

Cryptocurrency Wallets

Desktop apps: Exodus, Electrum, Atomic, Coinomi, Guarda, Bitcoin Core, Litecoin Core, Ethereum (Geth), Monero, Zcash, Dash, Dogecoin, Wasabi Wallet, Jaxx, MultiBit, Armory.

Browser extensions: MetaMask, Phantom, Trust Wallet, Coinbase Wallet.

Messaging & Gaming

Messaging: Discord (+ Canary, PTB, Lightcord), Telegram Desktop (tdata, session files), WhatsApp, Signal, Viber, Element (Matrix), Skype, ICQ, Pidgin, Tox, TeamSpeak, Slack, Zoom.

Gaming: Steam (SSFN auth files, config, userdata), Minecraft sessions, Epic Games Store, Riot Games (League/Valorant), Roblox, Battle.net, Ubisoft Connect, Origin/EA.

Documents & Keys

SSH keys, SSL certificates, PGP keys (.gnupg), VPN configs (OpenVPN, NordVPN, ProtonVPN, ExpressVPN), crypto wallet files, keystore/seed/mnemonic/recovery files from Documents/Desktop/Downloads.

Anti-Analysis Techniques

Anti-VM Detection

The malware employs multiple layers of VM detection to avoid analysis in sandboxes. All detection strings are XOR-encrypted with a runtime random key to prevent static analysis:

pub fn cpuid_vm_check() -> bool {
    unsafe {
        let key = get_runtime_key();
        let cpuid1 = __cpuid(1);

        // Check ECX bit 31 - Hypervisor Present bit
        if (cpuid1.ecx & (1 << 31)) != 0 {
            let cpuid_hv = __cpuid_count(0x40000000, 0);
            let brand = extract_hypervisor_brand(cpuid_hv);

            // XOR-encrypted VM vendor strings
            let vmware_enc = [86^key, 77^key, 119^key, 97^key, 114^key, 101^key];
            let kvm_enc = [75^key, 86^key, 77^key];
            let vbox_enc = [86^key, 98^key, 111^key, 120^key];

            // Check against known hypervisors
            if brand.contains(&xor_decrypt(&vmware_enc, key)) { return true; }
        }
    }
}

Detection methods:

  • CPUID hypervisor bit — Checks ECX bit 31 and brand string matching (VMware, KVM, VirtualBox, Hyper-V, QEMU, Xen)
  • MAC address prefixes — Matches against known VM vendor OUIs
  • WMI hardware queries — BIOS manufacturer, computer model, disk model
  • Registry artifacts — VM-specific registry keys
  • Process scanning — Looks for vmtoolsd, vboxservice, qemu-ga, etc.

Anti-Debugger

Two primary techniques detect debugger presence:

pub fn peb_check() -> bool {
    unsafe {
        HashedAPIs::is_debugger_present() != 0
    }
}

pub fn timing_check() -> bool {
    unsafe {
        _mm_lfence();
        let start = _rdtsc();

        for _ in 0..10 {
            black_box(key.wrapping_mul(7).wrapping_add(13));
        }

        _mm_lfence();
        let end = _rdtsc();
        let cycles = end.wrapping_sub(start);

        cycles > threshold
    }
}
  • PEB IsDebuggerPresent — Resolved via hashed API to avoid import table detection
  • RDTSC timing check — Measures CPU cycles for trivial operations; single-stepping causes massive cycle count increases

Sandbox Evasion

Multiple techniques detect automated analysis environments:

  • Suspicious parent processes — Checks for python, wireshark, fiddler, procmon, x64dbg, ollydbg, ida
  • Low resource detection — RAM < 4GB, CPU < 2 cores
  • Recent boot detection — Uptime < 10 minutes
  • Time acceleration detection — Compares expected vs actual sleep duration to catch sandbox fast-forwarding
  • Execution inflation — Performs 15-30 rounds of legitimate-looking operations (reading system DLLs, registry queries, prime calculations) to waste sandbox analysis time

Hell’s Gate: Direct Syscall Execution

One of the most sophisticated evasion techniques in Ferrox is the implementation of Hell’s Gate — a method for bypassing userland API hooks installed by EDR/AV products.

The Problem with Normal API Calls

When you call a Windows API function like VirtualAlloc, the call chain looks like this:

Your Code → kernel32.dll!VirtualAlloc → ntdll.dll!NtAllocateVirtualMemory → syscall → kernel

EDR products hook functions in ntdll.dll by modifying the first few bytes to redirect execution to their monitoring code. This allows them to inspect parameters, block malicious operations, and log activity.

The Hell’s Gate Solution

Hell’s Gate bypasses these hooks by:

  1. Locating NTDLL — Walks the PEB (Process Environment Block) via gs:[0x60] to find ntdll.dll without calling any APIs
  2. Parsing PE exports — Manually parses the PE export table to find syscall functions
  3. Extracting SSNs — Reads the mov eax, <SSN> instruction from function prologues to extract System Service Numbers
  4. Executing raw syscalls — Uses inline assembly to execute syscall instructions directly

The syscall numbers are cached in a global OnceLock singleton for performance. Resolved syscalls include:

  • NtAllocateVirtualMemory
  • NtWriteVirtualMemory
  • NtProtectVirtualMemory
  • NtCreateThreadEx
  • NtOpenProcess
  • NtClose
  • NtQuerySystemInformation
  • NtCreateFile
  • NtReadFile
  • NtWriteFile

This technique is well-documented in the offensive security community. Key references:

API Hashing

To avoid suspicious import table entries, Ferrox uses API hashing with DJB2 algorithm and a randomized key per build.

Zero import table entries for sensitive APIs means static analysis tools can’t see what functions the malware uses. The resolution process:

  1. Walk PEB — Access gs:[0x60] to get PEB, then walk Ldr->InMemoryOrderModuleList to find loaded modules
  2. Parse PE exports — For each module, parse the export table to enumerate function names
  3. Hash and compare — Hash each function name with DJB2 and compare against target hash
  4. Return address — When match found, return function address

The api_hash!() proc macro generates hashes at compile time, and api_resolve!() resolves at runtime. The hash key is randomized per build (1000-10000 range), making each binary unique.

Compile-Time String Encryption

Every sensitive string in the binary is encrypted at compile time using AES-256-GCM:

lazy_static::lazy_static! {
    pub static ref EXE_NAME: String = sprotect!("SystemDriverUpdate.exe");
}

The sprotect!() proc macro encrypts with AES-256-GCM during compilation. Key derived from 32 constants disguised as innocent names, unique random nonce per string, decrypted at runtime on first access.

The VolatileString wrapper zeros memory on drop using write_volatile + random overwrite + zero again to prevent memory forensics.

Polymorphic Builds

Every compilation produces a unique binary. The polymorph.py script mutates source code before building:

  • Injects random dead functions into source files (70% probability per file)
  • Randomizes timing constants - sleep/jitter durations (80% probability)
  • Mutates numeric constants (50% probability)
  • Generates unique AES-256-GCM key for string encryption
  • Generates unique DJB2 hash key for API hashing
  • Restores original source files after build completes

Marker system — source files contain comment markers that the builder transforms:

//#ultraprotect(name="API_KEY", value="default_value")
// Builder generates: lazy_static! { pub static ref API_KEY: String = sprotect!("randomized"); }
//#endultra()

//#junk(name="my_junk")
// Builder generates random dead code functions here
//#endjunk()

//#jcall(name="my_junk")
// Builder inserts call to generated junk function

Runtime polymorphism (polymorph.rs):

  • NOP sleds using various x86 NOP-equivalent instructions
  • Opaque predicates - always-true conditions using volatile reads
  • Stack pollution with random values
  • Polymorphic sleep using randomly chosen methods

Process Injection (Defender Killer)

Injects an encrypted DLL into SYSTEM-level processes to disable Windows Defender:

  • Embedded AES-256-GCM encrypted DLL payload at compile time
  • Enables SeDebugPrivilege to access privileged processes
  • Targets: winlogon.exe, services.exe, lsass.exe, csrss.exe
  • Opens target with PROCESS_ALL_ACCESS via NtOpenProcess syscall
  • Allocates RWX memory via NtAllocateVirtualMemory syscall
  • Writes decrypted DLL via NtWriteVirtualMemory syscall
  • Creates remote thread via NtCreateThreadEx syscall
  • All injection steps use direct syscalls to bypass EDR hooks

Chrome App-Bound Encryption Bypass

Chrome v127 introduced App-Bound Encryption to protect cookies and passwords from infostealers. The protection relies on an elevated service that verifies the calling application is legitimate Chrome.

Ferrox bypasses this by decrypting the App-Bound key using the same technique as xaitax’s research - accessing the IElevator COM interface to retrieve the encryption key, then decrypting the DPAPI-protected data.

This allows full access to:

  • Saved passwords in Login Data
  • Session cookies
  • Credit card data
  • Autofill information

Exfiltration

All stolen data is exfiltrated over Telegram Bot API:

  • Data collected to C:\temp\extract\
  • ZIP compressed, then encrypted with ChaCha20
  • Wrapped in custom binary blob format with embedded key/nonce/password
  • Random filenames like update_Xk3mP9qR.dat
  • Uploaded via Telegram Bot API sendDocument endpoint
  • Supports chunked uploads (45MB chunks) for large datasets
  • Randomized User-Agent strings
  • Retry logic with exponential backoff and rate limit handling
  • Startup notification disguised as “Windows Update Service”
  • Harvest summary sent with password/cookie/card counts

Anti-Forensics

Multiple techniques prevent forensic analysis:

  • Self-deletion — Spawns temp .bat file that loops killing the process and deleting the exe
  • File overwriting — Writes null bytes before deletion to prevent recovery
  • Self-corruption on VM detection — Spawns hidden PowerShell to overwrite exe with random bytes
  • Handle manipulation — Uses NtQuerySystemInformation to unlock own executable while running
  • Volatile strings — Memory zeroed on drop to prevent memory forensics

Detection Strategies

While Ferrox evades signature-based detection, behavioral analysis can still catch it:

Network indicators:

  • Telegram Bot API traffic patterns (api.telegram.org)
  • Large file uploads with suspicious User-Agent rotation
  • Encrypted blob uploads with random filenames

Behavioral indicators:

  • Process accessing multiple browser profile directories
  • Reading Login Data, Cookies, Web Data files from AppData
  • Accessing crypto wallet directories (Exodus, Electrum, etc.)
  • Querying WiFi passwords via netsh
  • Suspicious registry access patterns

Memory indicators:

  • Direct syscall execution patterns
  • PEB walking without API calls
  • Encrypted strings decrypted at runtime
  • RWX memory allocations in SYSTEM processes

EDR/XDR detection:

  • Monitor for processes walking PEB structures
  • Detect direct syscall execution via kernel callbacks
  • Flag processes accessing multiple credential stores
  • Alert on SeDebugPrivilege escalation + SYSTEM process access

Why Rust for Malware?

Most stealers are written in Go or C++. Rust is unusual but offers some advantages:

  • Error handling — The Result type forces explicit error handling, reducing crashes during execution
  • Toolingcargo provides excellent dependency management and build system
  • FFI — Clean interop with Windows APIs and native libraries
  • Performance — Zero-cost abstractions and aggressive optimization

The tradeoff is that most code is unsafe by nature when doing DMA operations, API hooking, and process injection. Memory safety isn’t the selling point here - it’s the developer experience and tooling.

Conclusion

Ferrox demonstrates the current state of infostealer sophistication. The combination of Hell’s Gate syscalls, polymorphic builds, compile-time encryption, and anti-analysis techniques makes detection challenging for signature-based AV.

However, behavioral analysis remains effective. EDR products that monitor syscall patterns, credential access, and process behavior can still detect these techniques.

Key takeaways for defenders:

  • Signature-based detection is insufficient against polymorphic malware
  • Behavioral analysis and EDR are essential
  • Monitor for direct syscall execution patterns
  • Flag processes accessing multiple credential stores
  • Network monitoring for C2 traffic patterns (Telegram Bot API)

The full (redacted) source is available at github.com/vibheksoni/ferrox for security researchers and malware analysts.

Disclaimer

This project is for educational and research purposes only. All testing was conducted in isolated virtual machine environments. The techniques demonstrated here are well-documented in the security research community.

Use responsibly and legally. Unauthorized access to computer systems is illegal. Understanding how modern threats operate helps build better defenses - that’s the goal of this research.

Enjoyed this post? Buy me a coffee ☕ to support my work.

Need a project done? Hire DevHive Studios 🐝