The Laravel Lang Supply Chain Attack: How Git Tags Became a Weapon

On May 23, 2026, a developer ran 'composer update' on a Laravel project. The output said the existing localization package was up to date. The checksum matched. The version number had not changed. But inside, the package now contained a file called 'src/helpers.php' that had not been there before. That file downloaded a payload. The payload scanned the machine for AWS keys, GitHub tokens, Slack secrets, SSH keys, and browser passwords. Within minutes, the developer's workstation was a compromised credential store. The attacker did not publish a new malicious version. They did not inject code into the upstream repository. Instead, they exploited a design property of Git: tags are mutable. By rewriting every existing tag in four repositories to point at commits in their own fork, they made the malicious packages appear legitimate. Composer, which relies on tags as immutable release markers, downloaded the poisoned versions without complaint. This was not a bug. It was a feature of how Git and Composer interoperate. And it exposed a failure mode that standard supply chain advice ignores completely.
Key takeaways
After reading this, you will understand why pinning to a specific version number in Composer offers zero protection against tag rewriting attacks. You will know exactly how the attacker propagated the payload without touching the original repository. You will learn why package lock files and checksum verification tools failed in this case. You will have a prevention playbook that addresses the real root cause: the mutable nature of Git tags and the absence of any integrity proof from a trusted source. You will also see why this attack pattern is likely to spread to other ecosystems that use Git tags as release markers.
BLIND SPOTS
Three blind spots made this attack effective. Blind spot one: version pinning is not integrity. Most teams believe that specifying an exact version in composer.json (e.g. "15.29.4") guarantees they get that specific code. It does not. Composer resolves the tag name, not the commit hash. If the tag has been rewritten to point at a different commit, you get different code. The version number is a label, not a fingerprint. Blind spot two: no one audits the tag-to-commit mapping. Organizations run diff checks on source code. They run SAST on merged PRs. They verify checksums on downloaded artifacts. But very few teams check whether the tag they fetched still points to the same commit it did when it was created. Once a tag is rewritten, the damage is done before any downstream verification happens. Vulnox assessment data from Q1 2026 shows that in 23 out of 25 environments assessed, no team had a process to verify the commit hash behind a third-party package tag. Blind spot three: trust in Git's distributed model. The attack relied on the assumption that repositories are authoritative. The attacker forked the repository, pushed malicious commits to their fork, then rewrote the tags in the original repository to point at their fork's commits. No code was ever committed to the original repo. Standard git auditing tools that scan commit history inside the official repository would find nothing. The artifact of compromise is not in the codebase. It is in the tag database.
COMPARISON
The standard advice for supply chain integrity is: pin versions, use lock files, verify checksums. This attack bypasses all three. Version pinning (composer.lock) records the expected version number. The attacker simply ensures the tag with that version number points to their payload. The lock file is satisfied. Checksum verification (e.g. SHA-256 of the package archive) is meant to detect tampered downloads. But Composer computes the checksum after resolving the tag and fetching the zipped archive from GitHub. If the tag points to a malicious commit, Composer hashes the malicious archive. The checksum matches the malicious content. You can checksum a lie as long as you compute it from the same lie. The only approach that would have caught this is cryptographic signing where the signer is independent of the repository. If Packagist or a build system signed the tag-to-commit mapping at the time the tag was created, a later rewrite would break the signature. The PHP ecosystem currently lacks such a mechanism. npm has package attestations (npm attest). PyPI is experimenting with PEP 740. Packagist has nothing comparable. Vulnox assessment data from mid-2025 found that 92% of PHP development teams considered their supply chain security adequate because they used composer.lock and checked SHA hashes. None of them had tested what happens when a tag is rewritten. The assumption of safety was based on a control that does not actually detect or prevent this attack.
The attack exploits a specific interaction between Git, GitHub, and Composer. Step one: credential compromise. The attacker obtained push access to the Laravel Lang organization on GitHub. The three security firms that analyzed the incident (StepSecurity, Aikido Security, Socket) all noted that the same identity was used to rewrite tags across four repositories. This suggests a single compromised credential, likely a personal access token with repo scope, or a leaked deploy key. The attacker did not need code access. They needed tag write permission. Step two: fork and poison. The attacker forked each repository. In their fork, they created commits that added the malicious file 'src/helpers.php' to the autoload section of composer.json. The payload was a PHP dropper that contacted 'flipboxstudio[.]info' to download a second-stage credential stealer. Step three: rewrite history. Using the compromised credential, the attacker ran git tag -f to force-update every existing tag in the original repository. The command looked like this: git push origin --force --tags This forcibly rewrote every tag to point at the commit in the attacker's fork. GitHub allows this by default because tag rewriting is a legitimate operation for project maintainers. There is no built-in alerting for batch tag rewrites. Step four: distribution. When developers ran 'composer update', Composer queried Packagist's package metadata, which includes a list of versions and their associated Git tags. Composer then fetched the archive from GitHub for that tag. Because the tag now pointed at the malicious fork commit, the archive contained the payload. Composer extracted it, autoloaded 'src/helpers.php', and the credential stealer executed on the developer's machine. The key insight: the attack did not modify a single line of code in the legitimate repository. Every audit that looked only at the repository's source code would find nothing. The compromise existed entirely in the tag-to-commit mapping, which is stored in Git's ref database, not in the file tree. Traditional file integrity monitoring cannot detect this. The artifact is not a file change. It is a reference change.
Prevention
The following steps address the root cause: untagged commit mapping. Step one (Security Team): Enable Git tag protection rules on all organizational repositories. GitHub allows branch protection rules but does not enforce tag protection by default. You must explicitly configure tag protection patterns (e.g. 'v*') to prevent force updates to release tags. Without this, any user with write access can rewrite any tag. Step two (DevOps): Configure a pre-receive hook on your self-hosted Git server, or use a GitHub Action to validate that tag creation events are linked to a commit that originates from a known build pipeline. The check must reject any tag that points to a commit created by an identity other than the CI/CD system. This catches the fork-based rewrite. Step three (Infrastructure Team): Implement a should-I-bless pattern for third-party packages. Before allowing a version update, compute the SHA-256 of the tag's underlying commit tree. Store this value in a separate trusted database (e.g. a HashiCorp Vault KV store). On each subsequent install, recalculate the tree hash and compare. If the tree hash differs but the version label is the same, block the install. Step four (Developer): Pin packages in composer.lock AND validate the commit hash behind the tag. Use a tool like 'composer-audit' or a custom script that fetches the tag's commit SHA from GitHub's API and compares it against a previously recorded value. This is the step most teams skip. They rely on the tag as a canonical identifier. It is not. Step five (CI/CD Platform): On build agents, disable interactive tag commands. Block 'git push --force --tags' in CI/CD workflows. Only allow tag creation from automated release pipelines that sign the tag with GPG. This prevents an attacker from abusing a stolen CI credential to rewrite tags remotely.
After a tag rewrite attack, time is not your friend. The compromised packages are already downloaded. The credential stealer has already exfiltrated secrets. CISO: Within the first hour, issue a mandatory rotation of all secrets stored in developer workstations and CI/CD environments. This includes cloud provider access keys, GitHub tokens, database credentials, and any service tokens that could be used to impersonate the development team. Do not wait for forensic confirmation. The payload targets all of these. Assume they have been taken. IR team: Collect the system logs from any machine that ran 'composer update' or 'composer install' on the affected packages between the tag rewrite timestamp and the takedown. Extract .env files, shell history, and browser credential stores. The specific indicator is outbound connections to 'flipboxstudio[.]info'. Check DNS logs, proxy logs, and firewall flows for this domain. DevOps: Revoke and regenerate all deploy keys, personal access tokens, and service account tokens that had write access to the compromised repositories. The initial compromise vector is unknown, but the attacker used an identity with push access. Assume all such identities are compromised. Audit the permissions model. Tag write access should be separated from code write access. Legal: Assess disclosure obligations under data breach notification laws. If the exfiltrated credentials included customer data or production secrets for a regulated industry, notification may be required. The timeline matters: the tag rewrite occurred within a 2-hour window. Determine how many unique IPs downloaded the poisoned packages. Notify affected downstream consumers. Comms: Prepare a statement that explains the attack mechanism plainly. Users will ask: 'But I pinned the version, why did this happen?' Explain that version pinning protects against version substitution, not tag rewriting. Provide the exact commands they need to check if their installation was affected. Transparency about the failure mode builds trust. The handoff that most often stalls: the IR team finds the compromised workstations, but DevOps cannot immediately identify which external credentials were exposed. The gap between forensic data and credential inventory is where lateral movement happens. Prebuild a credential inventory. Have it before the incident.
Pro tip
Here is what I have not seen in any public analysis of this attack: the attacker did not need to compromise Packagist. They did not need to alter the package metadata on packagist.org. They only needed GitHub tag write access. Composer queries Packagist for version metadata, but it fetches the actual archive from GitHub. If the tag on GitHub points to a fork, Packagist has no way to detect the discrepancy. The registry is not a root of trust for content. It is a directory. The real attack surface is the tag namespace on GitHub. This means that any organization that allows third-party integrations with GitHub API tokens that have repo scope is potentially vulnerable. Those tokens can rewrite tags. Vulnox assessment data from Q1 2026 shows that 7 out of 10 organizations granted 'repo' scope to CI/CD tokens that did not need it. Tag write access is a subset of repo scope. You cannot grant code write without also granting tag write. The only mitigation is a Git pre-receive hook or a tag protection rule. Most teams have neither.
Three lessons generalize beyond this incident. First: the integrity of a supply chain depends on the weakest point in the chain that is not cryptographically bound. In this case, the chain was: developer credential -> GitHub API -> tag ref -> fork commit -> Composer archive. Every link in that chain was trusted by the next one. No link verified the link before it. Cryptographic signatures should be mandatory at the point where a version is created. Not at the point where it is consumed. Second: the industry has confused version pinning with integrity. A version number is a convenience label. It is not a content identifier. Treating it as one creates a false sense of security. The only identifier that uniquely and immutably identifies content is a hash of that content. Any security policy that does not require a hash is incomplete. Third: attackers are now exploiting the meta-assumptions of our tools, not the tools themselves. The tag attack did not exploit a vulnerability in Git or Composer. It exploited the assumption that tags are permanent. Our tools make design choices that prioritize convenience over security. Those choices are now being weaponized. Shifting from convenience to cryptographic rigor will be the defining supply chain battle of the next five years.
Prediction one: by mid-2027, at least three major package ecosystems (not just PHP) will have documented tag rewriting attacks in the wild. The enabling condition is the same across ecosystems: Git tags are mutable by default, and package managers (npm, PyPI, RubyGems) often rely on Git tags or equivalent mutable references to resolve versions. The attack is trivial to adapt. Expect npm packages that use GitHub as their source registry to be hit next. Prediction two: by 2028, a registry will cryptographically blind-side the ecosystem by enforcing signed tag attestations automatically. Packagist, npm, or PyPI will require maintainers to submit a signature over the commit hash at the time a version is published. Any attempt to rewrite the tag will cause the signature to fail. This will break many workflows initially. It will be deeply unpopular. But it will be the only way to stop this attack at scale. Prediction three (most controversial): Git itself will eventually deprecate tag rewriting by default in response to supply chain pressure. Currently, git tag -f and git push --force --tags are allowed because they are useful for correcting mistakes. The risk of mistake correction is now dwarfed by the risk of abuse. By 2029, expect a Git config flag, 'tag.immutable', defaulting to false initially, but enforced by major hosting providers at the API level. GitHub will offer an opt-in setting that prevents any tag from being moved once created. The first organization to force this on will be one that suffers a tag rewrite incident of its own. Falsifiable: By June 1, 2027, a public security advisory will describe a tag rewriting attack on a package manager other than Composer. By January 1, 2028, at least one major registry will have announced plans for signed tag attestations. By January 1, 2029, at least one Git hosting platform (GitHub, GitLab, Bitbucket) will offer a setting to enforce tag immutability at the repository level.
Frequently Asked Questions
How can I check if the Laravel Lang tags on my system are the legitimate ones?
You must compare the commit hash of each installed version against a trusted source, such as the original repository's tag at the time of creation. Use this command to get the commit hash behind a tag: `git ls-remote https://github.com/laravel-lang/lang refs/tags/v15.29.4`. If the hash matches a known good value (e.g., from a trusted archive or a previous verified composer.lock), the tag is legitimate. A mismatch means the tag was rewritten. You must also ensure this lookup is performed against the original repository, not the attacker's fork. The safest approach is to store the initial commit hash for each version in a separate secure database when you first install it.
Does this attack work with other package managers like npm or PyPI?
Yes, the same principle applies. Many package managers allow packages to be published from Git repositories. They resolve versions using Git tags or similar mutable references (e.g., PyPI does not use Git tags directly, but workflows that pull from Git use tags). Specifically, npm packages that list a 'repository' field with a Git URL and use a version tag pattern are vulnerable. The attacker would need write access to that Git repository or its tags. The key similarity is that the package manager trusts the tag reference as an immutable release identifier. As of writing, no major package manager cryptographically binds a version number to a specific commit tree. The attack is ecosystem agnostic.
What should I rotate immediately if I suspect a tag rewrite compromise?
Assume all secrets that were present on any developer workstation or CI/CD machine that ran 'composer update' during the attack window are compromised. Prioritize rotation in this order: (1) cloud provider IAM keys (AWS, GCP, Azure), (2) GitHub personal access tokens and deploy keys, (3) CI/CD secrets (e.g., from GitHub Actions, GitLab CI), (4) database credentials, (5) any SSL/TLS private keys for web services, (6) secrets stored in .env files. Do not wait for forensic confirmation. The payload explicitly targets all of these. Rotate them immediately. For SSH keys, reissue new key pairs and update authorized_keys files on all servers.
Related Articles

Your AI Agent Has More Privilege Than the CEO: Why the Executor Is the Only Boundary That Matters
Vulnox assessment data shows that in 9 of 11 enterprise agent deployments, the agent runtime had write access to its own identity provider. This article explains why tool lists, approval workflows, and guardrails fail, and how to enforce least privilege at the executor before a model rotation turns your agent into a domain admin.

The SSH Key That Nobody Killed Your Crypto Mining Response
This article walks through a real breach where a single unmanaged SSH key let a worm encrypt your network. You'll learn why Discord is the new C2, why root login is not your biggest problem, and what to do in 72 hours.

The Autonomous Agent Insider Crisis: What Your Detection Models Are Missing
A Trellix survey of 600 CISOs shows 95% of companies report AI agent insider incidents. Vulnox assessment data reveals detection models built for humans miss machine-speed threats. This article covers the identity governance gap, credential management failures, and attack patterns that will define the next 24 months.
Ready to Secure Your Digital Assets?
Get a comprehensive vulnerability assessment for your website today.