Tactics, Techniques & Procedures
Attack patterns observed in the wild, mapped to MITRE ATT&CK where applicable.
Attack patterns observed in the wild, mapped to MITRE ATT&CK where applicable.
149 techniques · page 2 of 3
Adversary appends payload invocation to shell rc files so it runs on new shells.
psutil-based network-connection counts and admin check; WMI keyboard/mouse enumeration.
Registering a plausible but unclaimed npm scope that mimics a trusted vendor's domain or brand. Unlike typosquatting (misspelling), scope squatting exploits the gap between a vendor's actual npm scope and other plausible scope names derived from their domains, product names, or brand variations. npm scope registration is first-come-first-served with no trademark verification.
Multi-platform persistence: macOS LaunchAgent (com.nvm.protocal.plist), Linux systemd user unit (nvmconf.service), Windows ProgramData\NodePackages, disguised as Node/nvm tooling.
Rust infostealer reads 30+ crypto wallet stores (MetaMask, Phantom, Trust Wallet, Solflare, Keplr, Ledger, Trezor, ...) and browser password stores (Chrome/Brave/Firefox/Edge passwords, cookies, history, autofill, cards), plus Discord tokens and Telegram session data (Amazon Inspector).
CleanupMeiFolders removes PyInstaller _MEI dirs; exitadaptive cleanup can delete every non-EXE file and all subdirs of the app dir; /realtime batch written then deleted.
Primary objective: theft of cryptocurrency via browser wallet-extension data harvesting.
Legitimate @mastra releases publish via npm OIDC trusted publisher with SLSA provenance/attestations; malicious versions were published from a personal token with dist.attestations=null. The missing attestation (baseline @mastra/core@1.42.0 OIDC vs 1.42.1 ehindero) is the primary compromise signal.
setup.cjs spawns the stage-2 RAT as a detached, stdio-ignored, windowsHide child node process and unref()s it so it survives npm install exit.
10 of 11 downloaders spawn hidden powershell.exe via runas to run Start-Service w32time; w32tm /resync (clock resync). Cleanup branch uses PowerShell.
worker.js execution fallback chain includes cmd.exe /d /s /c start "" /min, plus curl.exe and bitsadmin invoked for download.
bitsadmin BITS job used as a download fallback to fetch the payload.
Writes a fake Zone.Identifier alternate data stream with ZoneId=0 to the dropped .exe to strip Mark-of-the-Web and defeat SmartScreen.
Payload written under random filenames masquerading as msedge_update / chrome_installer / dotnet_host / onedrive_setup / teams_update + hex + .exe; download requests use UA Microsoft-Delivery-Optimization/10.0.
Payload executed with windowsHide / start /min / -WindowStyle Hidden so no window appears.
Fallback exfiltration via POST /upload to the public file-sharing service temp.sh over Tor when the primary C2 is unavailable.
Secondary payload assessed as a cryptominer; detection hint is unexpected modification of /usr/bin/monero-wallet-gui.
Cargo build script (build.rs) executes attacker code at compile time, before any library code or runtime, including in CI.
build.rs walks up from OUT_DIR until it finds the parent of a 'target' directory (the consuming Cargo project root) and runs 'git diff HEAD^ HEAD' to harvest proprietary source code from the victim's local repository, plus 'git log -n 1 --pretty=format:{...}' for commit metadata.
Developer email addresses are harvested from git author metadata captured via 'git log -n 1'.
Initial access via takeover of a trusted GitHub contributor account with an established history of legitimate contributions to InjectiveLabs/injective-ts (not a sockpuppet). Attacker first pushed a test-backdoor-check branch to validate access.
C2 and exfiltration routed through Tor (local SOCKS-style transport on 127.0.0.1, external TCP/80 and TCP/8080) to a .onion service.
eBPF kernel rootkit (q2.bpf.c) hides processes via /proc rewriting and TCP sockets via netlink filtering; requires root and absent kernel lockdown for full effect.
Conceals its own processes, sockets and on-host artifacts via the eBPF component and binary path masquerade.
setup.cjs sets NODE_TLS_REJECT_UNAUTHORIZED=0 to disable TLS certificate verification for C2 over self-signed/expired certs.
Defense-evasion behaviors around stage-2 launch: detached child spawn with stdio:ignore + unref (fire-and-forget, no output), and console.log saved then nulled to suppress traces.
eBPF/kernel-level persistence component loaded to survive and re-hook on the host. (Mapped from JFrog's eBPF persistence note.)
Final-stage PE padded to ~86 MB to exceed scanner size limits and evade automated analysis.
Attacker-added lib/core/eval.js carries a fake 1985 mtime to blend with the copied legitimate axios files.
Extensive use of native APIs via koffi FFI library from Electron: Crypt32.dll CryptUnprotectData for DPAPI browser credential decryption, kernel32.dll VirtualAllocEx/WriteProcessMemory/CreateRemoteThread for process injection, and child_process.execFile for PE execution.
Downloads shellcode from C2, XOR-decodes with key 0xAA, spawns suspended dllhost.exe, injects via VirtualAllocEx + WriteProcessMemory + CreateRemoteThread. All Win32 API calls made through koffi FFI from Electron/Node.js runtime.
WebSocket RAT over wss:// Cloudflare quick-tunnel with auto-reconnect. Supports arbitrary cmd.exe and powershell execution with real-time stdout streaming to operator.
Hardcoded blacklists of known sandbox IPs and hostnames. Checks victim IP via ipinfo.io/json and local hostname against lists before proceeding with theft operations.
Wave 3 @emcd-vue/auth@6.4.8 (137.5 KB, entropy 5.04) uses WaCk/JScrambler-style obfuscation: 811-element string array (a0c()) backed by a custom lowercase-first base64 alphabet, integer arithmetic to compute array indices, 5-argument helper proxy functions, and an anti-debug self-checking loop. The 6.4.9 re-publish (13.3 KB) applies the same structure more lightly, enabling static recovery. This is a step beyond the obfuscator.io + custom-alphabet layers used in Wave 2 of the same campaign.
Wave 3 passes six structured FUSION_ environment variables to the spawned second-stage process: FUSION_RECON_ONLY (reconnaissance-only mode flag), FUSION_DEP_CON, FUSION_PKG (@emcd-vue/auth), FUSION_VER (6.4.9), FUSION_SECRET (the X-Secret value), and FUSION_PAYLOAD (payload data). This enables the operator to configure second-stage behavior without modifying the first-stage dropper. FUSION_RECON_ONLY=1 puts the second stage in a passive mode.
Wave 3 writes the second-stage payload to ~/.emcd-vue_init.js (dot-hidden file in the user home directory) rather than os.tmpdir(). Home directories are not cleared by OS or standard cleanup tools, making this a persistence upgrade. The run-once dedup cache is also stored in the home directory (~/.emcd-vue_init/).
Wave 3 README advertises EMCD_VUE_8D440FE1_NO_TEL=1 as the telemetry opt-out env var. The postinstall code checks EMCD_VUE_NO_TELEMETRY. These differ; setting the README variable does nothing. Developers who follow the documented opt-out are still exfiltrated. The hex fragment 8D440FE1 in the README var name indicates deliberate construction. Extends the campaign's README social engineering pattern (fake telemetry disclosure) with an additional deception layer.
Wave 3 packages use version numbers that resemble genuine mature-project releases (6.4.8, 6.4.9, 7.1.7) rather than anomalous strings (99.99.99, 5.7.1) used in Waves 1 and 2. The packages jump directly to these versions with no prior history, which remains a weak signal, but the version numbers themselves do not trigger version-anomaly heuristics common in OSS security tooling.
Abuse of npm GitHub Actions trusted publishing, which binds trust to repository + workflow filename rather than branch/ref/environment.
XOR-decoding the C2 URL from endpointmap's _ep/_p byte arrays using a key derived from the helper package's own manifest name (Buffer.from('endpointmap').slice(0,8) = 'endpoint'); no key constant is stored.
Probes AWS/Azure/GCP instance metadata endpoints (and Docker cgroup info) to harvest cloud role credentials and instance metadata.
Exchanges the GitHub Actions OIDC token for npm publish tokens via the registry OIDC exchange endpoint.
Attempts Docker socket container escape, mounting /etc/sudoers.d and granting the runner NOPASSWD sudo.
Run-once lock file (core/telemetry/.loader.lock) prevents double-execution. Silent exception catch makes the stager inert in clean environments where _runtime.bin is absent.