
On May 22, someone with push access to the Laravel-Lang GitHub organization rewrote every version tag across four widely used Composer packages — pointing them at malicious fork commits that nobody was watching. Within a 15-minute window, 233 package versions were poisoned and reached 700+ downstream repositories. The payload: a 5,900-line PHP credential stealer that fires automatically on every app startup. If you ran composer install with any laravel-lang package after 22:32 UTC that day, assume every secret on that machine is gone.
A Git Tag Is Not What You Think
The novel part of this attack is the mechanism. The attacker did not push malicious commits to the official Laravel-Lang repos. The main branch history looks completely clean. What changed were the tags.
GitHub allows version tags to point to commits from a fork of the same repository. The attacker created a malicious fork, added backdoored commits, then rewrote all existing official version tags to point at those fork commits instead of the original ones. A developer running git log on the Laravel-Lang repo would see nothing suspicious. But when Composer resolves laravel-lang/lang:14.3.7, it fetches the commit that tag points to — which is now the attacker’s commit.
This is not a GitHub vulnerability. It is a design gap in how Composer and Packagist establish trust: Packagist validates publishers, not individual commit hashes. Version tags are assumed to be stable. That assumption is now provably false.
What Actually Runs on Your Server
Every malicious commit added two files: an entry in composer.json under autoload.files, and a new src/helpers.php. In Composer, files listed under autoload.files are loaded on every request — no explicit call required. The moment the Composer autoloader initializes, the backdoor runs.
The dropper in helpers.php uses a marker file (keyed to the install path, hostname, and inode) to fire exactly once per host per vendor/ install. It reconstructs the C2 hostname from a byte array to evade static scanning, fetches a ~5,900-line second-stage payload, writes it to a random temp file under sys_get_temp_dir()/.laravel_locale/, and launches it in the background. On Linux and macOS it does this with a direct exec() call. On Windows, it drops a VBS shim.
The second stage has 15 credential-collection modules. It targets AWS, GCP, Azure, and DigitalOcean credentials; Kubernetes service account tokens and kubeconfig; HashiCorp Vault tokens; CI secrets including GITHUB_TOKEN, GitLab and CircleCI tokens; SSH private keys; Laravel .env files (including APP_KEY and database passwords); npm auth tokens; shell history; and browser password databases. Everything is AES-256 encrypted before exfiltration to flipboxstudio[.]info/exfil.
Check If You’re Affected
The four affected packages are laravel-lang/lang, laravel-lang/attributes, laravel-lang/http-statuses, and laravel-lang/actions. If any of these appear in your composer.lock and you ran an install or update after 2026-05-22 22:32 UTC, you need to act now.
The fastest check is looking for the dropper file itself — it should not exist in any legitimate installation:
grep -E "laravel-lang/(lang|attributes|http-statuses|actions)" composer.lock
find vendor/laravel-lang -name "helpers.php" 2>/dev/null
If helpers.php shows up anywhere under vendor/laravel-lang/, the dropper was installed. Treat the environment as fully compromised regardless of whether you see suspicious network activity — the credential stealer runs silently and leaves minimal traces by design.
Four Steps to Respond
Step 1: Remove the packages. Do this before anything else, to stop future installs from pulling malicious versions.
composer remove laravel-lang/lang laravel-lang/attributes laravel-lang/http-statuses laravel-lang/actions
Step 2: Rotate every secret that was accessible on affected machines. This is not optional. The stealer grabbed cloud provider credentials, CI tokens, SSH keys, database passwords, and anything in .env. Rotate all of it: AWS access keys, GCP service account keys, Azure credentials, Kubernetes service account tokens, Vault tokens, GITHUB_TOKEN and any personal access tokens, SSH keys, and your Laravel APP_KEY. If it was on the machine, it is in the attacker’s hands.
Step 3: Check for persistence. Look for unexpected files in sys_get_temp_dir()/.laravel_locale/ and for PHP processes that should not be running. On macOS, check LaunchAgents for anything unusual.
Step 4: Do not reinstall until clean versions are confirmed. Packagist has temporarily unlisted the affected packages. Monitor the official GitHub issue tracking the compromise for updates on which versions are clean.
The Composer Trust Gap
The broader issue here is a structural one that will outlast this incident. The PHP community trusts Packagist to verify publishers. Developers trust git tags to be stable pointers to known-good commits. Neither assumption protected anyone here. The attacker did not need to exploit a vulnerability — they just needed push access to the GitHub org, which they apparently obtained.
This is the PHP ecosystem’s version of what happened to TanStack two weeks ago — a different mechanism, the same outcome: legitimate packaging infrastructure weaponized to deliver malware to developers who did everything right. The supply chain attack surface is not shrinking.
Lock your Composer packages to exact commit hashes where possible. Treat composer.lock as a security artifact and review it on every dependency update. For a broader breakdown of the attack vector, Socket’s technical analysis is the most thorough account published so far, and Aikido’s discovery report has the initial disclosure timeline. The Hacker News write-up has a solid summary of the affected package list.













