# laravel-lang tag rewrite teardown: old versions, new code

> How the existing tags of four laravel-lang Composer packages were re-pointed at a credential stealer on 22 May 2026, and whether Chainsaw refuses it on day 0.

Source: https://chain305.com/teardowns/laravel-lang-tag-rewrite/

---

# Teardown: the laravel-lang tag rewrite

No new version to spot: existing tags re-pointed at a credential stealer.

What happened on 22 May 2026, what ran when Composer's autoloader loaded, and where Chainsaw would and would not have stopped it.

By [Muhammad Zeeshan Sultan](https://chain305.com/about/#founder), founder of Chain305 · October 2026

Questions and answers

## What happened?

On 22 May 2026, an attacker re-pointed the existing git tags of four Laravel-Lang Composer packages, `laravel-lang/lang`, `laravel-lang/http-statuses`, `laravel-lang/actions` and `laravel-lang/attributes`, at new commits that added a credential stealer. No new version number was published. A Composer version is a git tag, so versions that had been safe for years started resolving to the attacker’s code under the same name. [Socket](https://socket.dev/blog/laravel-lang-compromise) notes these are third-party localisation packages, not part of the official Laravel framework.

[StepSecurity](https://www.stepsecurity.io/blog/laravel-lang-supply-chain-attack) dates the rewrites from 22:32 UTC on 22 May, starting with `laravel-lang/lang`, to 00:00 UTC on 23 May, ending with `laravel-lang/actions`. Its [issue in the lang repository](https://github.com/Laravel-Lang/lang/issues/8295) puts all of lang’s rewrites between 22:32 and 23:24 UTC. None of the sources we read says when the tags were restored. [Aikido](https://www.aikido.dev/blog/supply-chain-attack-targets-laravel-lang-packages-with-credential-stealer) reported the issue to the maintainers, and Packagist removed the malicious versions and temporarily unlisted the packages.

Sources count the affected versions differently:

Source

Count

[StepSecurity](https://www.stepsecurity.io/blog/laravel-lang-supply-chain-attack)

All 502 tags of `lang`, all 86 of `attributes`, all 46 of `actions` (1.0.0 to 1.12.2), and every tag of `http-statuses` from v1.0.0 to v3.4.5

[Socket](https://socket.dev/blog/laravel-lang-compromise)

More than 700 historical versions across the four packages

[Aikido](https://www.aikido.dev/blog/supply-chain-attack-targets-laravel-lang-packages-with-credential-stealer)

233 versions across three repositories (`actions` not included)

None of these sources gives download counts for the affected versions.

## How did the attacker get in?

The Laravel-Lang attacker almost certainly got in with a single compromised credential that had push access across the Laravel-Lang organisation, according to StepSecurity, which does not say how the credential was obtained before 22 May 2026. The sources disagree on where the malicious commits lived:

-   **StepSecurity** says the commits were force-pushed to the official repositories as tag targets only, unreachable from any branch. Each one changed two files, `composer.json` and `src/helpers.php`, and was authored as “Your Name” with the email `you@example.com`.
-   **Aikido** says the code was never committed to the official repositories, and that the tags pointed at commits in a fork the attacker controlled, which GitHub allows.
-   **Socket** does not confirm the method, and says the attacker may have had access to organisation-level credentials, repository automation or release infrastructure.

The [isotope13 compendium entry](https://isotope13.ai/compendium/2026/laravel-lang/) notes that StepSecurity ruled out the fork explanation and that its git evidence points to direct pushes.

Either way, the exposure was the same. Any `composer update` against a version range such as `^3.4`, or a `composer install` without a lockfile from before the attack, resolved to a poisoned tag. A lockfile that pinned a pre-attack commit was safe until it was refreshed.

## What ran on load?

The Laravel-Lang payload ran on load through Composer’s autoloader: the 22 May 2026 tags changed `composer.json` to add `src/helpers.php` to `autoload.files`. Composer loads those files eagerly, so the code ran whenever an application required `vendor/autoload.php`, with no install hook and no class or function call needed. According to Socket, that means every PHP request the application handled.

-   **Hid its server.** Socket and Aikido describe the host `flipboxstudio[.]info`, a lookalike of `flipboxstudio.com`, built at runtime from an array of character codes so it never appears as a plain string.
-   **Ran once per machine.** Socket describes an MD5 marker built from the host’s path, architecture and inode.
-   **Fetched and started a second stage.** It downloaded `https://flipboxstudio[.]info/payload` with TLS verification turned off and a fake Mozilla User-Agent, wrote it under the temp directory and started it detached. StepSecurity shows it run as `php "$f" > /dev/null 2>&1 &` on Linux and macOS; on Windows, Socket and Aikido describe a generated `.vbs` launcher run with `cscript`.
-   **Cleaned up.** StepSecurity detonated `http-statuses` v3.4.5 on an isolated GitHub Actions runner: it saw `GET /payload`, then `POST /exfil` about a second later, a hidden PHP loader at `/tmp/.laravel_locale/<12 hex>.php` and an ELF binary at `/tmp/.<8 hex>`, both deleted within about two seconds, and two orphaned processes left running under PID 1. The whole run took 3.16 seconds.

The second stage is a cross-platform PHP stealer that sends its results to `https://flipboxstudio[.]info/exfil`. Socket lists 17 collectors covering AWS, Azure and GCP credentials and the EC2 metadata service, Kubernetes and Vault tokens, Jenkins, GitLab Runner, GitHub Actions, CircleCI, Travis CI and ArgoCD secrets, SSH keys, Git and Docker credentials, `.env` files, `/proc/<pid>/environ`, browser data, password managers and crypto wallets. On Windows it drops `DebugChromium.exe` to get past Chrome’s app-bound encryption and decrypt saved passwords. Aikido counts fifteen collector modules in about 5,900 lines. The two disagree on encryption: Socket says the data is XOR-encrypted with the hardcoded key `k9X2mP7vL4nQ8wR1`, Aikido says AES-256.

## Would Chainsaw have stopped it?

Yes. Chainsaw would have stopped the Laravel-Lang tag rewrite for installs through its proxy, on day 0 and without a malware feed listing. The evidence is a re-run of 58 attacks from 2026 against production Chainsaw (v0.22.118) on 19 October 2026. Two controls apply, depending on whether Chainsaw already had the package’s metadata:

-   **If Chainsaw had cached the metadata before the attack, Chainsaw keeps the old commits.** Chainsaw’s Composer support compares fresh Packagist metadata with the cached copy. When existing tags point at new commits, Chainsaw keeps the cached commit on every revalidation. The re-run tests this with every tag moved and with one tag left untouched. The pin is built into the Composer proxy, not a policy rule.
-   **If the first fetch happens during the attack, a default rule refuses the package.** The metadata then points at the attacker’s commits. The default policy rule “Block import-time execution of fetched or decoded code” refuses packages whose code runs fetched or decoded content when it is loaded. In the re-run, a reconstruction of `src/helpers.php` that fetches content and runs it from `autoload.files` was refused by the rule, so the download fails.
-   **The rule has to be in your policy.** The rule is part of the default policy set. An organisation whose policies do not include it keeps the metadata pin but has no refusal on a first fetch during the window.
-   **The rule was tested against a reconstruction.** The re-run’s test file builds the host from character codes, fetches content and runs it, following the published write-ups. The test file is not the original `helpers.php`, and the write-ups describe the original writing the payload to a temp file and running it with `php`.
-   **The code reading is shallow.** Chainsaw follows fetched content through one named variable. A variant that passes the payload through a second variable or a function return is missed.
-   **The malware feed does not help here.** The production feed had no record for any of the four packages at the re-run, and a feed keyed on version numbers cannot describe an old version whose commit changed.
-   **The release hold is not reliable here.** These are not new versions. The hold depends on the date Packagist gives the version, which follows the commit date the attacker controls.
-   **Chainsaw never sees the second stage.** The second stage comes from `flipboxstudio[.]info` at run time, not from Packagist.
-   **Installs that bypass the proxy get the malware.** A laptop or CI runner pointed straight at Packagist or GitHub is not protected. Chainsaw therefore also gates CI, pins laptops through MDM and blocks direct registry traffic at the network.

## What should you do?

For the Laravel-Lang tag rewrite, the most urgent step is the one StepSecurity and Socket recommend: if any machine, container build or CI runner installed or updated `laravel-lang/lang`, `laravel-lang/http-statuses`, `laravel-lang/actions` or `laravel-lang/attributes` from 22:32 UTC on 22 May 2026, treat the host as compromised. Then:

1.  **Check your lockfiles.** Look in `composer.lock` for the four packages, for the imposter commit SHAs StepSecurity lists, and for lockfiles regenerated on or after 22:32 UTC on 22 May.
2.  **Look for what the dropper left.** Check for `.laravel_locale` under the temp directory, hidden `/tmp/.<8 hex>` files, and orphaned `php` or unnamed processes with parent PID 1.
3.  **Search network and DNS logs** for `flipboxstudio[.]info`, and block it at egress.
4.  **Rotate every secret the host could reach**: cloud credentials, Kubernetes and Vault tokens, CI secrets, `GITHUB_TOKEN` and personal access tokens, SSH keys, registry and database credentials, and Laravel’s `APP_KEY`. Rebuild affected hosts, containers and runners from known-good images.
5.  **Keep evidence before cleaning up.** Socket advises preserving `composer.lock`, the Composer cache, process, DNS and cloud audit logs, and temp directory contents.

For the next one:

1.  **Commit `composer.lock` and install from it** with `composer install` in CI. A lock that pinned a pre-attack commit was safe here.
2.  **Treat a moved tag as an incident.** A version number does not identify code when the tag behind it can move.
3.  **Restrict who can push tags.** StepSecurity advises maintainers to audit every account, app and token with push access and enforce 2FA.

## Sources

-   StepSecurity, [Laravel-Lang supply chain attack](https://www.stepsecurity.io/blog/laravel-lang-supply-chain-attack), 22 May 2026.
-   Laravel-Lang/lang, [issue #8295: compromised version tags](https://github.com/Laravel-Lang/lang/issues/8295), opened 23 May 2026.
-   Socket, [Laravel Lang supply chain compromise](https://socket.dev/blog/laravel-lang-compromise), 23 May 2026.
-   Aikido, [Supply chain attack targets Laravel-Lang packages with credential stealer](https://www.aikido.dev/blog/supply-chain-attack-targets-laravel-lang-packages-with-credential-stealer), 23 May 2026.
-   isotope13, [laravel-lang Packagist compromise](https://isotope13.ai/compendium/2026/laravel-lang/).

One teardown per notable attack

## Get the next teardown by email

What happened, the install-path angle most coverage misses, and the rule that refuses it.

[Subscribe to teardowns](https://chain305.com/teardowns/) [Read what a package firewall catches →](https://chain305.com/what-is-a-package-firewall/)
