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:
- Single instance check — Creates a global mutex to prevent multiple instances
- Anti-analysis checks — VM detection, debugger detection, sandbox evasion
- Legitimate traffic generation — Blends in with normal network activity
- Device fingerprinting — Generates unique device ID from hardware serials
- System reconnaissance — Collects IP, WiFi passwords, Windows key, installed AV
- Data harvesting — Extracts credentials from browsers, wallets, apps, games
- Compression and encryption — ChaCha20 encryption of stolen data
- Exfiltration — Uploads encrypted blob to Telegram Bot API
- 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:
- Locating NTDLL — Walks the PEB (Process Environment Block) via
gs:[0x60]to find ntdll.dll without calling any APIs - Parsing PE exports — Manually parses the PE export table to find syscall functions
- Extracting SSNs — Reads the
mov eax, <SSN>instruction from function prologues to extract System Service Numbers - Executing raw syscalls — Uses inline assembly to execute
syscallinstructions directly
The syscall numbers are cached in a global OnceLock singleton for performance. Resolved syscalls include:
NtAllocateVirtualMemoryNtWriteVirtualMemoryNtProtectVirtualMemoryNtCreateThreadExNtOpenProcessNtCloseNtQuerySystemInformationNtCreateFileNtReadFileNtWriteFile
This technique is well-documented in the offensive security community. Key references:
- Hell’s Gate original paper by am0nsec and RtlMateusz
- RedOps: Exploring Hell’s Gate by Daniel Feichter
- Alice Climent-Pommeret’s EDR Bypass guide
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:
- Walk PEB — Access
gs:[0x60]to get PEB, then walkLdr->InMemoryOrderModuleListto find loaded modules - Parse PE exports — For each module, parse the export table to enumerate function names
- Hash and compare — Hash each function name with DJB2 and compare against target hash
- 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
sendDocumentendpoint - 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
Resulttype forces explicit error handling, reducing crashes during execution - Tooling —
cargoprovides 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 🐝