Tactics, Techniques & Procedures
Attack patterns observed in the wild, mapped to MITRE ATT&CK where applicable.
Adversaries acquire domains for use in targeting, often re-registering expired domains with historical trust.
MITRE ATT&CK ↗Approaching open source maintainers with a fabricated acquisition proposal from a legitimate-looking company. The maintainer voluntarily transfers ownership after due diligence. Distinct from credential theft, phishing, or account recovery abuse.
Harvested credential-file contents are gzip-compressed in-process (Node zlib) before exfiltration, reducing beacon size and bypassing naive plaintext-content inspection.
MITRE ATT&CK ↗The C2 host is not stored as a resolvable string. It sits inside an embedded config blob wrapped in an RC4 layer over an XOR layer, so static string extraction and base64/alphabet remapping do not recover it. This defeats the static-decode recovery that succeeded against Waves 1-3 (which exposed oob.moika.tech in cleartext or via simple alphabet remapping). SafeDep recorded the Wave 4 C2 host as UNRESOLVED; no code was executed.
4.8 MB index.js payload obfuscated with a fixed ROT13 shift and executed via direct eval(). Wave 5 simplification vs Wave 4 (LeoPlatform): no AES-128-GCM decryption layer, no per-package key variation. ROT13 is statically recoverable without key material. Payload content post-eval is unconfirmed (analysis ongoing as of 2026-06-26).
Attacker places binding.gyp at package root with command expansion '<!(node index.js > /dev/null 2>&1 && echo stub.c)' in the sources field. npm falls back to node-gyp rebuild which executes the command before lifecycle script scanners fire. Used identically to Wave 2 (June 3) and Wave 4 (June 24).
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.
MITRE ATT&CK ↗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).
MITRE ATT&CK ↗setup.cjs self-deletes via fs.rmSync(__filename) after spawning the detached stage-2 process.
MITRE ATT&CK ↗Primary objective: theft of cryptocurrency via browser wallet-extension data harvesting.
MITRE ATT&CK ↗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/[email protected] 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.
worker.js execution fallback chain includes PowerShell Start-Process -WindowStyle Hidden.
MITRE ATT&CK ↗worker.js execution fallback chain includes cmd.exe /d /s /c start "" /min, plus curl.exe and bitsadmin invoked for download.
MITRE ATT&CK ↗bitsadmin BITS job used as a download fallback to fetch the payload.
MITRE ATT&CK ↗Writes a fake Zone.Identifier alternate data stream with ZoneId=0 to the dropped .exe to strip Mark-of-the-Web and defeat SmartScreen.
MITRE ATT&CK ↗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.
MITRE ATT&CK ↗Payload executed with windowsHide / start /min / -WindowStyle Hidden so no window appears.
MITRE ATT&CK ↗Fallback exfiltration via POST /upload to the public file-sharing service temp.sh over Tor when the primary C2 is unavailable.
MITRE ATT&CK ↗Secondary payload assessed as a cryptominer; detection hint is unexpected modification of /usr/bin/monero-wallet-gui.
MITRE ATT&CK ↗Cargo build script (build.rs) executes attacker code at compile time, before any library code or runtime, including in CI.
MITRE ATT&CK ↗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.
MITRE ATT&CK ↗Developer email addresses are harvested from git author metadata captured via 'git log -n 1'.
MITRE ATT&CK ↗Uses stolen GitHub credentials of the compromised owner (ocrybit) and the asteroiddao npm account to publish packages and poison repos.
MITRE ATT&CK ↗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.
MITRE ATT&CK ↗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.
MITRE ATT&CK ↗Conceals its own processes, sockets and on-host artifacts via the eBPF component and binary path masquerade.
MITRE ATT&CK ↗setup.cjs sets NODE_TLS_REJECT_UNAUTHORIZED=0 to disable TLS certificate verification for C2 over self-signed/expired certs.
MITRE ATT&CK ↗eBPF component kills ptrace (PTRACE_ATTACH/PTRACE_SEIZE) against hidden processes to frustrate debugging/analysis.
MITRE ATT&CK ↗eBPF/kernel-level persistence component loaded to survive and re-hook on the host. (Mapped from JFrog's eBPF persistence note.)
MITRE ATT&CK ↗Final-stage PE padded to ~86 MB to exceed scanner size limits and evade automated analysis.
MITRE ATT&CK ↗Attacker-added lib/core/eval.js carries a fake 1985 mtime to blend with the copied legitimate axios files.
MITRE ATT&CK ↗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.
MITRE ATT&CK ↗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.
MITRE ATT&CK ↗WebSocket RAT over wss:// Cloudflare quick-tunnel with auto-reconnect. Supports arbitrary cmd.exe and powershell execution with real-time stdout streaming to operator.
MITRE ATT&CK ↗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.
MITRE ATT&CK ↗Wave 3 @emcd-vue/[email protected] (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.
MITRE ATT&CK ↗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.
MITRE ATT&CK ↗Queries AWS IMDS (169.254.169.254), ECS (169.254.170.2), Azure managed identity, and GCP metadata for credentials.
MITRE ATT&CK ↗Exchanges the GitHub Actions OIDC token for npm publish tokens via the registry OIDC exchange endpoint.
MITRE ATT&CK ↗Attempts Docker socket container escape, mounting /etc/sudoers.d and granting the runner NOPASSWD sudo.
MITRE ATT&CK ↗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.
MITRE ATT&CK ↗Detects CrowdStrike, SentinelOne, Carbon Black, and StepSecurity Harden-Runner.
MITRE ATT&CK ↗Pushes oidc-<hex> branches that rewrite the trusted CI workflow, exchanges OIDC for npm publish tokens, repackages legit tarballs with a malicious preinstall, re-signs via Sigstore (Fulcio/Rekor), and republishes with valid provenance.
GitHub API calls use User-Agent python-requests/2.31.0 to blend with tooling traffic.
Wave 2 postinstall.js (~13 KB, entropy ~5.46) layers obfuscator.io output, a custom base64 alphabet, and an integer-shuffle string table. Statically recoverable by alphabet remapping; decodes to the same oob.moika.tech and X-Secret constants Wave 1 used in cleartext. Matches SafeDep YARA rules dynamic_require_double_obscured (critical), js_char_code_at_substitution, js_hex_obfuscation. Distinguishes Wave 2 from Wave 1's plaintext payload and represents an evasion upgrade by the same operator.
Reads Chromium Login Data and decrypts saved passwords with aes-256-gcm (v10/v11 schemes). ATTRIBUTED to the convicted @apexcraft/nano-key root lineage from prior SafeDep triage and external triage c1896; not decoded in this static pass.
MITRE ATT&CK ↗Windows persistence via schtasks and registry Run key.
MITRE ATT&CK ↗macOS persistence via LaunchAgent plist (com.launchkeeper.MicrosoftSystem64).
MITRE ATT&CK ↗Poisoned PKGBUILD build/prepare steps run shell commands (npm install atomic-lockfile / bun install js-digest) under the building user's privileges via yay/paru/makepkg.
MITRE ATT&CK ↗Malware payload bundled as a Node.js SEA (Single Executable Application), producing a standalone binary (81 MB) that embeds the Node.js v20.18.2 runtime. Avoids dependency on victim having Node.js installed and obscures the JavaScript payload within a stripped ELF/PE binary.
XOR encryption with 8-byte key [90, 60, 126, 18, 159, 75, 109, 138] used to encrypt embedded configuration including C2 address and HuggingFace token.
MITRE ATT&CK ↗Account rotation across multiple npm accounts (jpeek868, jpeek886, jpeek895, pvnd3540749, yggedd817513, toskypi) to sustain publishing after takedowns.
MITRE ATT&CK ↗Wave 2 adds a functional T_IN_ONE_NO_TELEMETRY kill switch honored in code and a run-once de-duplication guard (~/.cache/._t-in-one_init/), reducing repeat beacons and analysis surface. Wave 1's telemetry opt-out env vars were README-only social engineering.
MITRE ATT&CK ↗Package README declares a fake telemetry feature with a fabricated opt-out environment variable and plausible internal telemetry domain. This normalizes expected outbound network activity at install time. Developers and security reviewers see a disclosure with an opt-out — standard practice for legitimate telemetry — and do not investigate the actual exfiltration POST to oob.moika.tech. The telemetry domains are not functional C2; they exist only in README text as social engineering artifacts. Fake changelog entries with plausible version history further present the package as a mature, ongoing project rather than a fresh first-and-only publish.
Staging web content (proxy pages with injected ad scripts) on infrastructure to target users who visit the proxy.
MITRE ATT&CK ↗Mass-publishing packages to npm via automated shell scripts (auto-publish.sh) to rapidly deploy adware infrastructure across many package names.
Service worker intercepts all fetch events in the web proxy, injecting tab-interception scripts into proxied HTML responses to enable popunder ad monetization.
Typosquat of axios published to npm as turbo-axios and faster-axios, copying legitimate axios source and metadata to appear trustworthy while smuggling a postinstall payload.
MITRE ATT&CK ↗Copies binary to %LOCALAPPDATA%\Microsoft\Windows\0\svchost.exe and sets HKCU\Software\Microsoft\Windows\CurrentVersion\Run\svchost for boot persistence.
MITRE ATT&CK ↗macOS persistence via LaunchAgent plist.
MITRE ATT&CK ↗Rust infostealer installs a systemd USER service masquerading as a benign daemon (observed colord, haveged) at ~/.config/systemd/user/<daemon>.service pointing at ~/.local/bin/<daemon> for persistence (Amazon Inspector).
MITRE ATT&CK ↗Injects JavaScript into the Exodus desktop wallet to capture the wallet password and BIP-39 seed mnemonic as the user enters them, POSTed to the local listener on 127.0.0.1:8738.
MITRE ATT&CK ↗Clipboard monitoring with 1-second polling interval.
MITRE ATT&CK ↗Screenshot capture at 60-second intervals, uploaded to HuggingFace datasets.
MITRE ATT&CK ↗Collection of .env files, shell history, and host inventory.
MITRE ATT&CK ↗Exfiltration of screenshots and stolen credential archives to HuggingFace Hub repositories using embedded API token.
MITRE ATT&CK ↗Host inventory collection including installed applications.
MITRE ATT&CK ↗Enumerates 27+ browser variant profiles and live developer accounts via api.github.com (/user, top repos), registry.npmjs.org (/-/whoami, maintainer search) and api.openai.com.
MITRE ATT&CK ↗Scheduled scans and automatic upload of collected data.
MITRE ATT&CK ↗Malicious package content delivers exploit code intended to execute in a client application context.
MITRE ATT&CK ↗Hardcoded 2FA password and recovery email installed on victim accounts via Telegram updateTwoFaSettings, with the operator's IMAP mailbox auto-submitting the confirmation code.
MITRE ATT&CK ↗Listens for messages on Telegram's official OTP sender chat 777000 and forwards every login code to operator-controlled bot channels.
Malware propagates or executes across additional systems by abusing remote management channels.
MITRE ATT&CK ↗Malware modifies account state, access paths, sessions, or authorization material to expand or preserve access.
MITRE ATT&CK ↗Wave 4 payload is an obfuscator.io-style single-line ~160 KB script using a custom lowercase-first base64 alphabet (abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/=) PLUS a per-string RC4 layer. Static base64 decode of the string table only recovers primitives such as charCodeAt/fromCharCode; sensitive strings (C2, headers, file list) remain RC4-protected. This differs from Wave 3's WaCk/JScrambler string-array obfuscation.
MITRE ATT&CK ↗Abuses npm OIDC Trusted Publishing token exchange to mint package-scoped automation tokens for self-replication; also steals SCM/registry/CI and AI-provider API tokens.
MITRE ATT&CK ↗Malware deletes, corrupts, or otherwise destroys local data or system state.
MITRE ATT&CK ↗Abuses legitimate web services for staging and C2: gofile.io anonymous file hosting, Cloudflare quick-tunnel (trycloudflare.com) for multiple distinct tunnels (delivery, exfil API, secondary download, WebSocket RAT, shellcode), ipinfo.io for geolocation, Discord API for token validation.
MITRE ATT&CK ↗Python used as the execution trigger: modified __init__.py spawns a daemon thread that imports and calls the stager module at package import time.
MITRE ATT&CK ↗Wave 4 shifts from full-process.env capture (Waves 1-3) to targeted on-disk credential/secret harvesting. The postinstall reads ~20 files including ~/.ssh, ~/.aws/credentials, ~/.kube/config, ~/.docker/config.json, ~/.npmrc, ~/.netrc, ~/.pgpass, ~/.git-credentials, ~/.env, and ~/.bash_history.
MITRE ATT&CK ↗Collected data is exfiltrated as a gzip-compressed HTTPS POST to the path /api/v1/events, gated by a custom X-Secret header. The destination C2 host is RC4+XOR-concealed in the payload and was not statically resolved.
MITRE ATT&CK ↗Malware targets SSH keys, wallet private keys, or other private key material.
MITRE ATT&CK ↗In addition to the HTTPS POST, Wave 4 issues a DNS resolver beacon, providing a low-profile secondary signalling/exfil channel that survives HTTP egress filtering.
MITRE ATT&CK ↗Harvests browser cookies (Network/Cookies, encrypted cookie values) and Local Storage/leveldb across 27+ Chromium/Firefox variants including Flatpak/Snap, plus Slack/Teams/Discord/Telegram stores.
MITRE ATT&CK ↗Web-protocol exfiltration. EARLIER static hypothesis: HTTPS exfil with spoofed Mozilla/5.0 UA from a self-contained browser stealer. CORRECTED (Amazon Inspector): the Linux/macOS Rust infostealer second stage POSTs stolen data as multipart/form-data over HTTP using the minreq library, gated behind anti-VM checks.
MITRE ATT&CK ↗Module import event triggers daemon thread execution. Any code path that imports the core.telemetry package — application startup, CI test runs, IDE background processes — silently triggers the stager.
MITRE ATT&CK ↗Packages masquerade as an internal 'Platform Engineering Team' library set. Constant scope-parameterized metadata fingerprint: README marker 'Internal package — Platform Engineering Team'; author '<Scope> Platform Engineering <platform@<scope>.io>'; repository git+https://github.<scope>.io/platform/<pkg>.git; bugs https://jira.<scope>.io/projects/PLATFORM; homepage https://docs.<scope>.io/platform/<pkg>; fake internal registry lure registry=https://npm.<scope>.io; telemetry cover story to telemetry.<scope>.io; descriptions from a fixed pool (e.g. 'Internal structured logger ... remote drain support', 'Internal configuration loader with env, vault and remote config support'). A decoy dist/index.js require()s a ../src/index.js absent from the tarball, so the library is non-functional and only the postinstall executes.
MITRE ATT&CK ↗25 malicious @marketfront packages published at version 7.0.0 to win dependency-confusion resolution against an internal @marketfront scope. Execution is via a postinstall lifecycle hook ('node scripts/postinstall.js') that fires automatically on npm install without any developer import.
MITRE ATT&CK ↗Obfuscated JavaScript payload executed at install time via npm postinstall hook (and, for @petitcode/eb-retry, additionally at require-time on first call to exported retry()).
MITRE ATT&CK ↗First-stage obfuscator.io downloader fetches a ~10.6MB Rust-compiled infostealer second stage from GitHub Releases (github.com/angelmaybeth21-oss/test/releases/download/v1.0.0/{linux,mac,win.js}) and executes it via a detached child process (Amazon Inspector).
MITRE ATT&CK ↗