Skip to content
Supply Chaincredential-theftCI/CDopen-source-security

Laravel-Lang Supply Chain Attack: Git Tag Rewrites and a 5,900-Line PHP Credential Stealer

6 min read
Share

Laravel-Lang attackers rewrote 233 Git tags to deliver a 5,900-line PHP credential stealer. The Composer ecosystem is the new supply-chain delivery surface.

On May 22, a coordinated supply chain attack hit Laravel-Lang, one of the most widely used internationalization library suites for the Laravel PHP framework. The attacker rewrote Git tags across at least four repositories, pointing existing version tags to commits in an attacker-controlled fork. The result was that every PHP developer who pinned to one of those versions and ran composer install or update pulled a 5,900-line PHP credential stealer.

StepSecurity, Aikido, and Snyk each published technical writeups within 24 hours. The numbers vary slightly by methodology. Aikido counts 233 affected versions across three repos. Socket's read puts the historical scope at roughly 700 versions touched, reflecting different scope definitions. The repos hit are laravel-lang/lang, laravel-lang/http-statuses, laravel-lang/attributes, and possibly laravel-lang/actions.

This is the first time the Composer ecosystem has seen a coordinated wave-attack of this scale in 2026. It is also the first time the Git-tag-rewrite mechanism has been demonstrated at scale against PHP, and the lesson it teaches generalizes well beyond Composer.

How the attack worked

The attacker did not compromise a maintainer's GitHub account in the classical sense. There is no evidence of credential theft against the Laravel-Lang maintainers. Instead, the attacker exploited a property of how Packagist (the PHP package index that Composer pulls from) resolves Git tags.

When a Laravel project declares a Composer dependency on laravel-lang/lang ^9.0, Composer asks Packagist to resolve which version to install. Packagist queries the upstream GitHub repository and pulls down the commit pointed to by the matching tag. If the tag pointer changes, the commit pulled changes. The tag pointer is mutable in Git, and Packagist's resolution model trusts the tag pointer as it exists at install time.

The attacker pointed existing tags (released months or years ago) to commits in an attacker-controlled fork that contained the credential-stealer payload alongside the legitimate Laravel-Lang code. Composer's autoloader pulls the payload into every PHP process that loads the library. There is no entry-point that the developer has to call. The payload runs automatically because PHP autoloading runs on first use.

The damage radius is every Laravel project that pinned to one of the rewritten tags and ran composer install or composer update during the attack window. Because the attack targeted historical tags rather than only new releases, projects that thought they had locked their dependency tree to a known-good version were also exposed.

What the payload does

The payload is a 5,900-line PHP credential stealer organized into 15 specialist collector modules. The breakdown across the three primary research writeups gives the picture:

The collectors target cloud credentials (AWS, Azure, GCP), Kubernetes secrets (kubeconfig files, service-account tokens), HashiCorp Vault tokens, Git credentials (SSH keys, .git-credentials files, GitHub tokens), CI/CD secrets (environment variables, build-system tokens), SSH keys (id_rsa, id_ed25519, authorized_keys), browser data (saved passwords, cookies, autofill), cryptocurrency wallets (Bitcoin, Ethereum, Solana wallet files), password manager files (KeePass, 1Password local databases), VPN configurations (OpenVPN, WireGuard configs), and local .env configuration files.

That last category is the most consequential for PHP shops. Laravel projects conventionally ship application secrets via .env files at the project root. Every Laravel app pulling laravel-lang would have given the payload first-class read access to the application's database credentials, third-party API keys, and any other production secrets stored in environment files.

The C2 channel is hidden inside an integer array decoded at runtime to evade static scanners. It resolves to flipboxstudio.info. The runtime decoding pattern is sophisticated enough to bypass most string-pattern scanners but trivial to detect with behavioral analysis or with any DNS-monitoring tool watching for resolution of unexpected external domains from PHP web processes.

What to do this week if you run PHP

Five actions in priority order.

First, audit composer.lock and composer.json across every PHP project in your environment for any Laravel-Lang dependency. The package names to grep for are laravel-lang/lang, laravel-lang/http-statuses, laravel-lang/attributes, and laravel-lang/actions. If you find any of them, the project is in scope.

Second, for any in-scope project, identify whether composer install or update ran between roughly midday UTC on May 22 and the publication of this attack on May 23. Any install or update during that window pulled the malicious tag, regardless of which version was pinned. Treat the host that ran the install as credential-compromised.

Third, rotate every credential class on the collector module list above. For Laravel apps specifically, this means rotating database credentials, third-party API keys, mail server credentials, queue worker credentials, and any other secret stored in .env. For the build environment, rotate CI tokens, deployment SSH keys, and any cloud-provider service account credentials that the build host has access to.

Fourth, replace tag-based Composer pinning with commit-hash pinning for laravel-lang dependencies going forward. Composer supports the dist-reference and source-reference fields in composer.lock to pin to a specific commit SHA rather than a tag. This is more verbose to maintain but immune to the Git-tag-rewrite class of attack.

Fifth, treat this as a forcing function to audit the rest of your Composer dependency tree for high-trust libraries that you pin by tag rather than by commit hash. Anything that ships via Packagist is in scope for the same attack pattern. The Laravel-Lang attacker found one mechanism; others will follow.

The cross-ecosystem pattern

The Composer ecosystem is now the fourth major package ecosystem in the last six months to demonstrate this attack class, after npm (TanStack wave 1, May 11), PyPI (Lightning Mini Shai-Hulud, May 6), and the VS Code Marketplace plus OpenVSX (Nx Console wave 5, May 20). The mechanism varies by ecosystem. The structural pattern is identical.

Every package ecosystem operates on a trust model where the package index resolves a human-friendly identifier (tag, version name, latest, marketplace listing) into actual code at install time. Every ecosystem allows the identifier-to-code mapping to be mutated by some actor (the maintainer, the index admin, in some cases an attacker who can manipulate metadata without touching the original publishing account). Every ecosystem has been or is currently being attacked through this mechanism.

The defender response across every ecosystem is the same. Pin to immutable identifiers. Use commit hashes for Composer, integrity hashes (npm shasum or npm-lockfile) for npm, content addresses (PyPI file hashes) for Python, and explicit version-and-publisher pairs for marketplace extensions. Audit dependency trees regularly for mutable pins and replace them with immutable ones. Treat any wave-attack disclosure in any package ecosystem as a forcing function to do this hygiene for your own organization.

For Tashkent-region PHP shops running Laravel-based fintech or government modernization stacks, this is patch-level urgency this week. The Laravel-Lang library family is in the dependency tree of effectively every modern Laravel project that does any kind of internationalization, which is most of them. Audit, rotate, repin.

The Composer ecosystem has now joined the live supply-chain attack surface. The defender baseline updates accordingly.

Gigia Tsiklauri is a Security Architect and founder of Infosec.ge. Get in touch if you want to talk through Composer pinning strategy or supply-chain hygiene for your PHP stack.