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 1 of 3
A script or build backend that runs early in a GitHub Actions job appends BASH_ENV=<attacker script> to $GITHUB_ENV, so bash sources the attacker script before every later run step, including the publish step that holds the registry token.
Locale and region profiling.
Encrypted C2 messages.
Faking npm popularity by simulating bulk installs (or otherwise inflating download counts) to gain false credibility and defeat popularity-based risk scoring.
Gems published to RubyGems carried a .yardopts file whose '--load <path>' directive is applied as a CLI argument during RubyDoc.info's automated YARD documentation build, executing an attacker-supplied Ruby script (evil.rb, hack.rb, ssrf.rb, inject.rb, exploit.rb) on RubyDoc's own infrastructure and network egress. Over 100 packages in the campaign used this path. Confirmed exact syntax in yardxabc889@0.0.1: '--load ./evil.rb'.
IDE hook commits carry valid GitHub API-verified signatures with author field spoofed to github-actions[bot]. Upgrade from prior waves which used unsigned imposter commits. Detected in jaredwray/keyv (d8c850c7) and jaredwray/ecto (a4e7dd4f, c392617e, 5687c7ba).
C2 domain configuration stored in an Ethereum smart contract. Malware calls a read-only contract function via public RPC endpoints to retrieve active C2 domains. Censorship-resistant: no registrar or CDN can take down the configuration. Domain rotation via a single on-chain transaction. eth_call is free (no gas), unauthenticated, and traffic to public RPC providers is indistinguishable from legitimate Web3 application usage.
CI runner process memory scraping via sudo python3 reading /proc/{pid}/mem to extract GitHub Actions masked secrets. Targets the Runner.Worker internal JSON structure where secret values are stored in plaintext.
Adversary checks the host's internet connectivity and public IP address.
A .pth file whose line begins with 'import' is executed by the CPython site module at every interpreter startup. Malware drops such a .pth into site-packages to launch a payload on every Python invocation, package-independent and surviving pip uninstall of the dropper.
macOS payload is a 707-line AppleScript/JXA stage decrypted from payload.enc and executed via osascript, never written to disk as a separate file.
Fake native macOS password dialog ('Please enter your password to continue', titled 'macOS' with system lock icon) loops until the entry validates against the real account via dscl . authonly, with a 150s per-attempt timeout.
Reuses the phished login password to unlock the login Keychain and extract Safe Storage keys that decrypt Chromium-family cookies and saved passwords; also exfiltrates all *.keychain-db files.
File grabber automatically scans Desktop/Documents/Downloads for document, wallet, key, and cert files (.pdf/.docx/.doc/.wallet/.key/.pem/.ovpn), capped at 10MB per file, alongside 20+ desktop wallets and 100+ wallet browser extensions.
The dropper sets http.verify_mode = OpenSSL::SSL::VERIFY_NONE when fetching the payload, disabling TLS certificate validation so the download succeeds through interception, self-signed, or misconfigured TLS without warnings.
A GitHub Actions workflow triggered by pull_request_target (which runs with repository secrets) is misconfigured to check out the untrusted PR head instead of the base branch. Attacker-controlled PR code therefore executes in a privileged context and reads/exfiltrates workflow secrets. Here it stole the org-wide asyncapi-bot Personal Access Token via PR #2155.
Attacker opened 37 pull requests, 36 of them near-identical decoys (adding a fake charity donation page), to camouflage the single malicious PR that exploited the CI misconfiguration among routine-looking contribution noise.
Malicious obfuscated JavaScript placed on the first line of a legitimate source file after ~1,000 bytes of whitespace padding, so it scrolls off-screen in most editors and renders as an apparently blank/normal line.
A ChannelOrchestrator with tiered automatic failover across multiple independent C2 channels: HTTP (primary), Nostr relays, Ethereum smart contracts, IPFS, BitTorrent DHT, and a libp2p mesh network, designed to survive takedown of any single channel. Payload self-identifies as 'M-RED-TEAM v6.4'.
The stolen asyncapi-bot PAT was exfiltrated from the CI runner to a rentry.co dead-drop URL (slug 'elzotebo') before automated review flagged the malicious PR.
8 of 11 builds wrap gtaobus.pyc with pyarmor_runtime_015050; payload delivered as a PyInstaller bundle.
8 PyArmor payloads reroute Google Sheets traffic through a hardcoded authenticated HTTP proxy (196.16.3.71:9528) when direct access fails.
Resolves the installing host public egress IP by requesting Cloudflare /cdn-cgi/trace with a spoofed desktop-browser User-Agent to bypass bot challenges.
Installs a LaunchAgent at ~/Library/LaunchAgents/com.system.notifications.agent.plist with a 60s StartInterval that polls the C2 for commands, turning the infection into a standing backdoor.
The typosquat interposes on the SDK's own gateway methods and property setters: CardOperationLogger hooks CreditCardGateway.Create/transaction/payment-method/card-verification calls to skim PAN/CVV/expiry before the legit HTTP request, and the BraintreeGateway.PrivateKey setter side-effects to leak MerchantId+PublicKey+PrivateKey, all without the developer invoking any obviously malicious API.
A skip_install? guard scans roughly 30 CI-platform environment variables (GitHub Actions, GitLab CI, CircleCI, Travis, Jenkins, Vercel among them) and does nothing if any are present, constraining execution to non-CI developer laptops to avoid build servers and analysis environments.
Two [ModuleInitializer]-attributed static methods (Braintree.DependencyInjectorLoader.Load in Braintree.dll and DependencyInjector.Core.AutoInitializer.Initialize in DependencyInjector.Core.dll, the latter Interlocked.Exchange-guarded) fire automatically on assembly load, running CodebaseAnalyzer.AnalyzeAndPrint() with no explicit call from the developer. Requires no install/postinstall hook and runs even under --ignore-scripts equivalents.
120 empty placeholder versions of Braintree.Net (0.0.1-0.0.120), each .nuspec-only with no DLL, were published in a single day (2025-10-09) recording ~93,300 downloads each. This squats the package name and inflates the total download count (~11M of ~14M reported) to lend false credibility before functional malicious payloads shipped 9 months later.
Victims build/run the fake scanner or lure-repo cheats/tools, triggering the loader chain.
L.ps1 resolves its payload URL from a LastW marker + encrypted blob hidden across Pastebin/Rlim/YouTube/Instagram/Telegram/Google Docs/GitCode.
Defender/security-tool tampering reported across the post-execution chain (ATT&CK v19 Defense Impairment technique; Socket's mapping).
Telegram RAT exit handler deletes HKLM Windows\Installer DisableLogging/DisableMSI registry values.
UAC bypass reported in the post-execution chain.
Windows service persistence reported.
Quixo.7z payload staged as a GitHub release asset (tb78/expresso); malware committed to lure-repo source trees and releases.
WriteProcessMemory / SetThreadContext / privilege-adjustment API usage across the chain.
Reported by Socket among post-execution capability signals.
Registry querying reported in the post-execution chain.
Exfiltration deferred behind an ~11768 ms setTimeout so short-lived sandboxes tear down before the callback fires.
DependencyInjector.Core harvests every process environment variable (name+value), categorized by cloud-provider prefix (AWS_/AZURE_/GOOGLE_/GCP_/KUBERNETES_/HEROKU_/RAILWAY_/FLY_/VERCEL_/RENDER_/DIGITALOCEAN_/ASPNETCORE_ etc.).
C2 fronted by an ngrok free-tier tunnel (caliber-spinner-finishing.ngrok-free.dev), hiding the backing host behind a trusted tunneling provider.
Adversaries acquire domains for use in targeting, often re-registering expired domains with historical trust.
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.
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).
Adversary installs a cron job for recurring execution and persistence.