Summary
On May 22, 2026, a single threat actor compromised four popular Composer packages maintained by the Laravel-Lang organization. Rather than publishing a new malicious version, the attacker rewrote every existing git tag in each repository to point at a new malicious commit. The rewrites started at 22:32 UTC against laravel-lang/lang (the flagship Laravel translations package, with 502 tags) and finished by 00:00 UTC against laravel-lang/actions. All four repositories share the same fake author identity, the same modified files, and the same payload behavior, which makes them almost certainly the work of one actor using one compromised credential with org wide push access.
The malicious commits add src/helpers.php to the Composer autoload.files map. This means simply requiring vendor/autoload.php, which every Laravel and Symfony application does on startup, fires the payload. The payload reaches out to flipboxstudio.info, drops a hidden PHP loader and an ELF binary into /tmp, exfiltrates runner environment data, and then deletes its own artifacts from disk to frustrate forensics.
We confirmed end to end exploitation by detonating laravel-lang/http-statuses v3.4.5 in an isolated GitHub Actions runner protected by Harden-Runner in audit mode. The other three packages share identical commit structure but have not been detonated yet. We expect they behave the same way.
We have reported this to the Laravel-Lang maintainers by opening a security issue in each of the four affected repositories:
- Laravel-Lang/lang#8295
- Laravel-Lang/http-statuses#277
- Laravel-Lang/actions#1193
- Laravel-Lang/attributes#1085
Affected versions
All four packages have had every git tag rewritten. There is no safe version to pin to today other than a pre 2026-05-22 commit SHA that you can independently verify against a local clone or the Packagist mirror.

laravel-lang/lang (all 502 tags, the flagship Laravel translations package; this was the first and largest target in the campaign):
- 15.29.5 at commit
a5ea2e8fa92ccf29cdb1d2dadbeb27722b2bff37 - 15.29.1 at commit
50ac0db454d19234c835716f297bbc5363c0a25c - 2.0.4 at commit
c45764e70285146da37025cd8601a921ab8a7eda - 1.0.2 at commit
a9f8d88cf98e35988d3d0fd6d79547f980853041 - Every other tag follows the same pattern. Rewrites span 2026-05-22 22:32 UTC through 23:24 UTC.
laravel-lang/http-statuses (every tag from v1.0.0 through v3.4.5):
- v3.4.5 at commit
bba2e443dc7ff1f8704f52a5375383e3f4f643b8 - v3.4.0 at commit
26c233e1a0d4fd2331e8e0f175e18f8eed904aa3 - v3.0.0 at commit
db0c3ef246103fd0f6c318e0d48f26b5289044c3 - v2.0.0 at commit
9ee599d248cc322fa26054694a83a1f4558cc716 - v1.0.0 at commit
6b1d5782a8c8c199d070857802d39bfe609eb6f2 - Every other tag in between follows the same pattern.
laravel-lang/actions (all 46 tags from 1.0.0 through 1.12.2):
- 1.12.2 at commit
556d2b335d4d6d92139822017ee461b668afe375 - 1.10.0 at commit
722cee67326d932e7f71ba3438f62a255d779aa9 - 1.0.0 at commit
ad24b980db8f0dca50ccb3ba6badb3c2331e0ef4 - Every other tag follows the same pattern.
laravel-lang/attributes (all 86 tags):
- v2.4.1 at commit
d59561727927117e65b35f0183cae131baad19fe - 2.6.0 at commit
1713b19cbf609cb101ff5e216be41f7224269082 - 2.5.0 at commit
daa5212264bb73fb39fe7a36618b62717dc564a5 - Every other tag follows the same pattern.
Because the rewrites moved every tag rather than introducing a new version, projects that pin to a version range (^3.4, ~2.0, *, and so on) and run composer update will resolve to a poisoned tag. Projects whose composer.lock already pins a commit SHA from before 2026-05-22 are safe as long as they only run composer install against that lockfile, and as long as the locked SHA is not one of the imposter commits listed above.
Indicators of compromise
Network indicators:
- C2 domain:
flipboxstudio.info(a typosquat of the legitimateflipboxstudio.com) - Stage 1 fetch:
GET https://flipboxstudio.info/payload - Stage 2 exfiltration:
POST https://flipboxstudio.info/exfil
Filesystem indicators (these files self delete a few seconds after the payload runs, so they are most likely to be visible only on a live runner or in a filesystem snapshot taken during execution):
/tmp/.laravel_locale/<12 hex chars>.php, a hidden directory containing a hidden PHP loader/tmp/.<8 hex chars>, an ELF binary with no file extension
Process indicators (visible in ps even after the dropper files are deleted, because the binary continues to run from memory):
- An orphaned
phpprocess withppid=1 - An orphaned unnamed ELF process with
ppid=1, executing from a now deleted path under/tmp
Git indicators:
- Commit author name
Your Nameand emailyou@example.comon every tagged commit in the affected repositories - Commit timestamps spanning 2026-05-22 22:32 UTC through 2026-05-23 00:00 UTC
- Each commit modifies only two files:
composer.jsonandsrc/helpers.php
How the attack was carried out
Composer supports three autoload modes in composer.json. Two of them, psr-4 and classmap, are lazy: a class file only loads when its class is referenced. The third, files, is eager: every listed file is required the moment vendor/autoload.php is required.
The attacker added src/helpers.php to the autoload.files map in each compromised package. Because every Laravel application calls require __DIR__.'/vendor/autoload.php' on startup, and because Symfony, PHPUnit, and most other PHP frameworks do the same, the payload runs the moment any consumer of the package boots. No class instantiation, no method call, no special trigger is required.

The contents of the added src/helpers.php are short and self contained. The file fetches a second stage from $sh/payload (where $sh resolves to https://flipboxstudio.info), writes it to a hidden filename under the system cache directory using bin2hex(random_bytes(6)) as the filename, and executes it with a backgrounded php "$f" > /dev/null 2>&1 &. The Windows branch in the same file uses a WSH WScript.Shell object to achieve the equivalent detached execution.

Harden Runner Detections
The remainder of this section is the raw evidence captured by Harden Runner during the detonation of laravel-lang/http-statuses v3.4.5 in run 26318135547. Times are UTC. PIDs come from the runner's process table.
Process tree. The autoload step we ran is at the top. Each subsequent sh wrapper exists only to background its child and detach it. The result is two implant processes adopted by init, while the dropper files on disk are removed within two seconds of first execution.
00:17:45.972 php (pid 2804) [workflow step: Autoload package]
invoked: php -r "require 'vendor/autoload.php'; echo 'autoload complete';"
00:17:47.012 └─ sh (pid 2805)
invoked: sh -c "php /tmp/.laravel_locale/f3e2c293172f.php > /dev/null 2>&1 &"
00:17:47.013 └─ php (pid 2806, ppid=1) [reparented to init]
invoked: php /tmp/.laravel_locale/f3e2c293172f.php
00:17:48.129 └─ sh (pid 2813)
invoked: sh -c "nohup /tmp/.480dc608 > /dev/null 2>&1 &"
00:17:48.130 └─ nohup -> /tmp/.480dc608 (pid 2814, ppid=1) [reparented to init]
00:17:49.134 ├─ rm (pid 2816)
│ invoked: rm -f /tmp/.laravel_locale/f3e2c293172f.php
00:17:49.135 └─ rm (pid 2817)
invoked: rm -f /tmp/.480dc608
Two facts in this tree are worth highlighting. First, PIDs 2806 and 2814 both end up with ppid=1, which means they were reparented to init when their immediate shell parent exited. That is what makes them survive the workflow step boundary and what causes Harden Runner to attribute their network calls to infra rather than to the runner user. Second, the entire compromise (initial autoload to self deletion of both artifacts) takes 3.16 seconds.
Network calls. Two HTTPS requests to the attacker C2 domain, attributed to the correct PIDs:
00:17:46.055 pid 2804 (workflow php) GET https://flipboxstudio.info/payload
00:17:47.156 pid 2806 (orphan loader) POST https://flipboxstudio.info/exfil
The GET /payload call is what fetched the two staged files written to /tmp. The POST /exfil call carried whatever the loader harvested from the runner environment (most likely $GITHUB_TOKEN, the contents of $GITHUB_ENV, secrets from $RUNNER_TEMP/_runner_file_commands/, and anything readable from /proc/<runner pid>/environ). We did not capture the request body because Harden Runner was in audit mode for this run.
What an unprotected runner sees. Outside of Harden Runner, a defender investigating this incident after the fact has very little to work with. The two dropped files are gone within 3 seconds. The two implant processes have no parent in the workflow's process tree because they were reparented to init. The only durable artifact is the outbound DNS and TLS traffic to flipboxstudio.info, which on a hosted GitHub Actions runner is usually not captured anywhere. This is why egress monitoring is the most reliable detection for this class of payload.
You can view the Harden Runner insights here:

Recovery steps
If you depend on any of the four affected packages:
- Stop running
composer update, and stop runningcomposer installwithout a known good lockfile, for any project that depends onlaravel-lang/lang,laravel-lang/http-statuses,laravel-lang/actions, orlaravel-lang/attributes.laravel-lang/langis the most widely used of the four (the core Laravel translations package) and is the highest priority to triage. - Inspect
composer.lockfor these packages. If the locked commit SHA is one of the imposter commits listed above, or if the lockfile was regenerated on or after 2026-05-22 22:32 UTC, treat the project as compromised. - If you ran a Composer install of these packages on or after 2026-05-22 22:32 UTC, treat every secret accessible to that environment as compromised. This includes CI provider tokens, cloud provider credentials (AWS, GCP, Azure),
GITHUB_TOKENand any other GitHub PAT, container registry credentials, deploy keys, database credentials, and any application secret available in environment variables. Rotate all of them. - Audit the runners and developer machines where the install ran. Check
ps auxffor orphanedphpand unnamed ELF processes withppid=1. Check/tmpfor the artifacts listed above. They may still be present if you check soon enough after the install. - If you use Harden-Runner, search your detections for HTTPS calls to
flipboxstudio.infoacross your tenant. Any hit confirms a compromised workflow run. - Add
flipboxstudio.infoto any egress blocklist, firewall rule, or DNS sinkhole you control.
If you are a Laravel-Lang maintainer, or you operate a similar org:
- Force update every tag in the four affected repositories back to its original commit. If the local reflog is gone, the Packagist dist mirror retains the original tarballs, and any user with a clone made before 2026-05-22 has the original SHAs.
- Audit every account, app, and personal access token with push access to the Laravel-Lang organization. Revoke and rotate everything. Re enable 2FA enforcement.
- Notify Packagist so the affected versions can be flagged or yanked.
- Audit the rest of the organization for similar rewrites. The attacker had the access and the time to compromise more than four repos, and we may only have spotted the loudest cases.



