The post Laravel-Lang Composer tag-rewrite Supply Chain Attack appeared first on Mend.
On 2026-05-22, an attacker rewrote every repository tag across four Composer packages in the Laravel-Lang ecosystem to point at malicious commits. The affected packages are laravel-lang/lang, laravel-lang/attributes, laravel-lang/http-statuses, and laravel-lang/actions. The rewrite took place on 2026-05-22 into the early hours of 2026-05-23. Every malicious commit makes the same two-file change: one entry added to composer.json, and one new file at src/helpers[.]php. This article walks through both files as they actually exist in a poisoned tree.
The malicious commits in all four repositories share the same git author identity, Your Name <[email protected]>. The commits are not reachable from any branch in the upstream repositories; only the rewritten tags point to them. The combination of the shared author identity, the orphan-commit topology, and the compressed rewrite window across four repositories is consistent with one operator running through the four targets in sequence.
The poisoned manifest’s autoload block contains two entries:
"autoload": {
"psr-4": {
"LaravelLang\\Lang\\": "src/"
},
"files": [
"src/helpers.php"
]
}
The PSR-4 entry maps the package’s LaravelLang\Lang\ namespace to src/. The "files" entry is the attack vector.
Composer’s autoload.files directive is documented to load every listed file immediately when an application requires vendor/autoload.php. Unlike PSR-4 entries, which are lazy and only load classes on first reference, autoload.files entries are eager: they execute the moment the autoloader is bootstrapped. In a Laravel application this happens on every HTTP request boot. In a Composer-using CLI tool it happens on every command invocation. In a CI workflow it happens the first time any step touches the project’s autoload.
The dropper opens with two decoy functions, laravel_lang_locale() and laravel_lang_fallback(), that wrap Laravel’s config() and return the application locale. The malicious behavior lives in an anonymous closure guarded by a LARAVEL_LANG_HELPERS define-check. The closure runs once per host, and the once-enforcement is a marker file:

The marker filename is an MD5 over three values: the install directory, the host’s network name returned by php_uname('n'), and the inode of helpers.php itself. The closure returns early on a second invocation, so the dropper fires exactly once per vendor/ tree per host.
The C2 host is kept out of plain-text scans by reconstructing it from a byte array:

The byte array spells flipboxstudio[.]info.
The fetcher tries file_get_contents first with a custom stream context, then falls back to libcurl if the first returns less than 50 bytes. Both transports set verify_peer => false, CURLOPT_SSL_VERIFYPEER => false, and a spoofed Mozilla/5.0 User-Agent. The 50-byte minimum is the dropper’s signal-quality check: it tolerates a transport failure but rejects an empty or near-empty response.
If the fetch returns content, the dropper writes the response to a random filename under sys_get_temp_dir()/.laravel_locale/ (bin2hex(random_bytes(6)) produces a 12-hex-character name) and branches on OS for launch. On Linux and macOS the launch is exec("php \"$f\" > /dev/null 2>&1 &"). On Windows the dropper writes a 4-byte-randomized .vbs shim that calls WScript.Shell.Run with a non-blocking flag, invoked from cscript //nologo //b. Every filesystem and network call in the closure uses PHP’s @ error-suppression operator, so no warning or stack trace reaches PHP-FPM, the web server, or error_log.
The full closure is roughly fifty lines of PHP. By the time control returns from vendor/autoload.php, the stage-two PHP is already running detached in the background.
Analysis of the stage-two payload served from hxxps://flipboxstudio[.]info/payload shows a PHP credential stealer targeting cloud, container, and developer-machine credentials: EC2 instance metadata at 169.254.169.254, Kubernetes service-account tokens at /var/run/secrets/, HashiCorp Vault, Jenkins master.key and credentials.xml, Linux /proc/<pid>/environ and /cmdline, and Chrome v127+ App-Bound Encryption via a dropped DebugChromium.exe. The harvested data is XOR-encrypted with the key k9X2mP7vL4nQ8wR1 and POSTed to hxxps://flipboxstudio[.]info/exfil.
| Category | Indicator |
|---|---|
| C2 domain | flipboxstudio[.]info |
| Stage-one URL | hxxps://flipboxstudio[.]info/payload |
| Stage-two exfil URL | hxxps://flipboxstudio[.]info/exfil |
| Drop directory | <sys_get_temp_dir>/.laravel_locale/ |
| Stage-two PHP filename | ^[0-9a-f]{12}\.php$ |
| Windows launcher filename | ^[0-9a-f]{8}\.vbs$ |
| Windows stage-three binary | DebugChromium.exe |
| Cloud-metadata egress | Outbound to 169.254.169.254 from a PHP process |
| In-process constant | LARAVEL_LANG_HELPERS |
composer.json indicator |
"files": ["src/helpers.php"] entry under autoload |
| Affected packages | laravel-lang/lang, laravel-lang/attributes, laravel-lang/http-statuses, laravel-lang/actions |
| Rewrite window | 2026-05-22 into 2026-05-23 |
| Malicious commit author | Your Name <[email protected]> |
flipboxstudio[.]info at DNS and HTTPS proxy layers, then investigate.composer.lock against upstream history. For each source.reference SHA recorded under laravel-lang/lang, laravel-lang/attributes, laravel-lang/http-statuses, or laravel-lang/actions, look the SHA up in the upstream repository and check the commit author. Any commit authored by Your Name <[email protected]> is a poisoned install. Lockfiles whose pinned SHAs pre-date 2026-05-22 and are reachable from upstream branch history are safe, provided no composer update has run since.composer.json resolve to whatever the rewritten tag now points at, so re-running composer install against a tag constraint is not sufficient. Pin each affected laravel-lang/* dependency to a commit SHA that pre-dates 2026-05-22 and is reachable from upstream branch history, then run composer update <package> to refetch. Confirm afterwards that vendor/laravel-lang/*/composer.json no longer contains the "files": ["src/helpers.php"] autoload entry and that src/helpers.php is no longer in the package tree.<sys_get_temp_dir>/.laravel_locale/ on every affected host.Mend.io has issued an MSC covering all four affected Laravel-Lang Composer packages:
https://socket.dev/blog/laravel-lang-compromise
Aikido Security: https://www.aikido.dev/blog/supply-chain-attack-targets-laravel-lang-packages-with-credential-stealer
Laravel-Lang/attributes issue #1085: https://github.com/Laravel-Lang/attributes/issues/1085
StepSecurity: https://www.stepsecurity.io/blog/laravel-lang-supply-chain-attack
*** This is a Security Bloggers Network syndicated blog from Mend authored by Alina Podoba. Read the original post at: https://www.mend.io/blog/laravel-lang-composer-tag-rewrite-supply-chain-attack/