Packagist Supply-Chain Attack Hid Its Malware in package.json, Not composer.json

A coordinated attack on Packagist, the PHP package registry, poisoned eight Composer packages by hiding malicious code in package.json — the JavaScript manifest — instead of composer.json, exploiting the blind spot where PHP and JavaScript toolchains coexist but are reviewed separately.

Share
Line-art illustration of two stacked manifest cards, the upper one tagged with a git-branch mark and carrying a small hidden parcel; the parcel bears a red dot.

Key Takeaways

  • A coordinated supply-chain attack on Packagist, the PHP package registry, compromised eight Composer packages on or about May 22-23, 2026, with code designed to fetch and run a Linux binary.
  • Although every affected package is a Composer package, the attacker did not touch `composer.json` — the malicious code was hidden in `package.json`, the JavaScript manifest, a deliberate misdirection that targets the file PHP reviewers do not check.
  • PHP, Laravel, and Composer teams should check their `composer.lock` files for the eight named packages, audit every `package.json` in their PHP repositories for unexpected install hooks, and hunt affected hosts for a hidden executable at `/tmp/.sshd`.

This attack does not rely on a clever exploit — it relies on a clever hiding place, putting its payload in the one manifest a PHP package reviewer is least likely to open.

BERLIN, GERMANY — On or about May 22-23, 2026, researchers documented a coordinated supply-chain attack on Packagist, the PHP package registry, that compromised eight packages with malicious code designed to fetch and run a Linux binary. The technically distinctive detail is where the attacker put that code: although every affected package is a Composer (PHP) package, the malicious code was not placed in `composer.json`, the Composer manifest a PHP reviewer would inspect — it was hidden in `package.json`, the JavaScript and Node manifest, deliberately targeting PHP projects that ship JavaScript build tooling alongside their PHP code.

The campaign was documented across the May 22-23, 2026 coverage cycle, anchored by The Hacker News with research from the supply-chain security firm Socket. Packagist has removed the malicious versions. No public reporting has tied the attack to a named threat actor.

Disclosure Overview
FieldDetails
DisclosureA coordinated supply-chain attack on Packagist, the PHP package registry, documented across the May 22-23, 2026 coverage cycle
Reported ByThe Hacker News, anchored on research from the supply-chain security firm Socket
ScopeEight Composer (PHP) packages confirmed compromised on Packagist; more than 700 GitHub results tied to the same attacker infrastructure
TechniqueMalicious code inserted into package.json — the JavaScript and Node manifest — rather than composer.json, the Composer manifest a PHP reviewer would inspect
Payload DeliveryAn install hook downloaded a Linux binary from a GitHub Releases URL, saved it as /tmp/.sshd, made it executable, and ran it in the background
ResponsePackagist has removed the malicious versions
AttributionNone — no public reporting links the attack to a named threat actor

What Happened

Researchers documented a coordinated supply-chain attack on Packagist — the package registry for PHP, the language whose dependency manager is Composer — across the May 22-23, 2026 coverage cycle. Eight Composer packages were confirmed compromised with malicious code designed to fetch and run a Linux binary. The eight packages are devdojo/wave, devdojo/genesis, katanaui/katana, elitedevsquad/sidecar-laravel, r2luna/brain, baskarcm/tzi-chat-ui, moritz-sauer-13/silverstripe-cms-theme, and crosiersource/crosierlib-base. Researchers tied more than 700 GitHub results to the same attacker infrastructure. Packagist has removed the malicious versions.

The mechanics of the payload are consistent across the reporting. Every one of the eight affected packages is a Composer package — code installed into PHP projects when a developer runs composer install. But the attacker did not add the malicious code to composer.json, the manifest that defines a Composer package. The code was inserted instead into package.json, the manifest that belongs to JavaScript and Node — a file present in these projects only because they ship JavaScript build tooling alongside their PHP code. An install hook in that package.json downloaded a Linux binary from a GitHub Releases URL, saved it under the path /tmp/.sshd, made it executable, and ran it in the background. What that Linux binary does once running has not been established in the reporting, and this account does not characterize it.

Why composer.json Versus package.json Is the Whole Story

A modern PHP project is rarely only PHP. A Laravel application, for example, routinely ships a JavaScript front-end build pipeline next to its PHP back end, and that pipeline carries its own package.json — the JavaScript and Node manifest — sitting in the same repository as the project's composer.json, the Composer manifest. The two files belong to two different toolchains: composer.json is read by Composer when PHP dependencies are installed, package.json is read by Node tooling when JavaScript dependencies are installed. The attack turns that coexistence into a hiding place. A security reviewer auditing a Composer package opens composer.json, because that is the file that defines the package — and finds it clean. The malicious install hook is one file over, in package.json, a file the PHP reviewer has no particular reason to open. The misdirection works because the PHP and JavaScript toolchains live in the same repository but are usually reviewed by different people, with different tools, against different manifests.

A Linux Binary Disguised as a System Daemon

The payload's hiding place has a second layer. The install hook did not download the Linux binary to an obvious location under an obvious name — it saved the file as /tmp/.sshd. Two deliberate choices are packed into that path. The leading dot makes the file hidden: a plain directory listing will not show it, and a developer glancing at the contents of /tmp will not see it without explicitly asking for hidden entries. And the name sshd is the name of the SSH daemon, one of the most familiar and most trusted process names on a Linux system. An administrator who does notice the file, or a process by that name, is primed to read it as a legitimate part of the operating system rather than as something an install hook dropped there minutes earlier. A hidden executable in /tmp masquerading as the SSH daemon is not an accident of naming; it is camouflage, and it is also — for exactly that reason — a high-fidelity indicator for defenders who know to look for it.

The Second PHP Supply-Chain Campaign in a Single Week

This is not the first PHP and Composer supply-chain attack of the week — it is the second. Days earlier, The CyberSignal covered the Laravel-Lang compromise, in which a popular PHP localization package was poisoned with a credential stealer. The two campaigns are distinct and should not be conflated: Laravel-Lang and the Packagist eight are different packages, and the techniques differ — Laravel-Lang turned on the gap between a Git tag and the commit it pointed to, while this campaign turns on cross-manifest misdirection inside package.json. What they share is an ecosystem and a week. For years the supply-chain wave was discussed mostly in terms of npm and PyPI; the message of this week is that PHP and Composer are now squarely inside it. That wave has also reached VS Code extensions, in the GitHub breach attributed to the actor TeamPCP, and GitHub CI/CD workflows, in the automated Megalodon campaign — and the registries themselves are starting to respond, as in npm's move to staged publishing with 2FA-gated release approval.

The Eight Compromised Packagist Packages
FieldDetails
Package 1devdojo/wave
Package 2devdojo/genesis
Package 3katanaui/katana
Package 4elitedevsquad/sidecar-laravel
Package 5r2luna/brain
Package 6baskarcm/tzi-chat-ui
Package 7moritz-sauer-13/silverstripe-cms-theme
Package 8crosiersource/crosierlib-base

Scope and Impact

The confirmed scope is eight compromised Composer packages on Packagist, and Packagist has removed the malicious versions. Beyond that, much of the picture is genuinely unknown, and this account does not fill the gaps. Researchers tied more than 700 GitHub results to the same attacker infrastructure, but whether those results represent 700 additional compromised repositories or simply search hits that reference the attacker's infrastructure has not been established. How the attacker gained the ability to publish malicious versions of the eight packages has not been reported. The number of developers or servers that installed a compromised version is not known, and neither is whether the GitHub Releases URL hosting the Linux binary has been taken down. The reporting also does not say what the binary does once it is running — so it should not be described as a credential stealer, a cryptominer, a backdoor, or a botnet agent without evidence.

What is clear is the shape of the exposure. Any PHP project that installed one of the eight packages during the May 22-23 window pulled in a package.json with an install hook that fetched and executed a Linux binary on the host running the install — a developer workstation or a CI/CD runner. The blast radius therefore is not the registry but every machine that ran the install. This is the same lesson the Verizon DBIR 2026 underlined when it found vulnerability exploitation had overtaken credential theft as the top initial-access method: attackers are systematically targeting the least-scrutinized trust surface, and in 2026 the package manifest — especially the wrong package manifest — is one of them.

Response and Attribution

For PHP, Laravel, and Composer teams the first action is an inventory check. Search your composer.lock files for any of the eight named packages — devdojo/wave, devdojo/genesis, katanaui/katana, elitedevsquad/sidecar-laravel, r2luna/brain, baskarcm/tzi-chat-ui, moritz-sauer-13/silverstripe-cms-theme, and crosiersource/crosierlib-base — and, where any appear, identify the installed version and whether it was pulled on May 22-23. If a compromised version was installed, treat the host as compromised: hunt for the file /tmp/.sshd and any other hidden executable in /tmp, look for unexpected background processes and outbound connections to GitHub Releases URLs and unknown destinations, and rotate every credential reachable from that host. Separately, audit every package.json inside your PHP repositories — if your PHP project ships a package.json for build tooling, that file is now a confirmed payload-hiding location, and its scripts and install hooks deserve a direct read.

For DevSecOps and security-review teams the structural lesson is the cross-manifest blind spot. Reviewing a PHP package means reviewing package.json as well as composer.json, and software-composition-analysis tooling should be configured to parse every manifest in a repository, not only the one that matches the registry the package came from. Detection content should flag an install hook that downloads a binary from a GitHub Releases URL and writes it to /tmp, and threat hunters should treat a hidden executable in /tmp that mimics a system daemon — /tmp/.sshd and names like it — as something that should never exist legitimately. For SOC and incident-response teams, any developer machine or CI/CD runner that ran composer install against a compromised version should be handled as a credential-compromise incident. On attribution the honest position is that there is none: no public reporting links this campaign to a named threat actor, and it has not been established whether it is operationally connected to the Laravel-Lang compromise.


The CyberSignal Analysis

Signal 01 — The Attack Hides Where Defenders Do Not Look

Most coverage will lead with the eight packages, and the count matters. But the detail that should hold the spotlight is the choice of manifest. The attacker put the payload in package.json inside packages whose identity is entirely defined by composer.json, and that choice is the attack. A security reviewer vetting a Composer package opens the Composer manifest — it is the correct, disciplined thing to do — and the malicious code is one file away, in a manifest that belongs to a different toolchain. This is not an exploit of a software flaw; it is an exploit of a review process. It works precisely because organizations review by language: the PHP people read PHP files, the JavaScript people read JavaScript files, and the package.json sitting inside a PHP project falls into the seam between them. You cannot patch that seam with a software update — closing it is procedural work.

Signal 02 — Package Review Must Cover Every Manifest in the Repository

The concrete takeaway for engineering and security leaders is narrow and actionable: a repository can carry more than one package manifest, and a security review that inspects only the one matching the registry is structurally incomplete. A Composer package can contain a package.json; an npm package can contain a composer.json; a single repository can carry several manifests for several toolchains. Each one is an install-time execution surface, because each one can declare hooks that run code on the machine doing the install. Software-composition-analysis tooling has to be pointed at all of them, and human review has to be scoped to the repository rather than to the language. The default assumption should flip: an unexpected manifest, or unexpected scripts inside an expected one, is suspicious until shown otherwise.

Signal 03 — The Supply-Chain Wave Is Now Fully Cross-Ecosystem

Two distinct PHP and Composer supply-chain campaigns in a single week — Laravel-Lang and this Packagist attack — settle a question that was still arguable a year ago. The supply-chain wave is not an npm problem, or a PyPI problem, that PHP teams can watch from a safe distance. It is cross-ecosystem, and within a single week it reached the PHP registry twice. Set against the node-ipc npm packages that hid a stealer behind a maintainer's lapsed domain and the Shai-Hulud worm generating valid Sigstore provenance for its malicious npm packages, the pattern is consistent: attackers keep moving to whatever trust surface is least examined. The defensive response cannot be language-siloed. Supply-chain review has to be comprehensive — every registry a team draws from, and every manifest in every repository it ships — because the attacker's whole method is to find the one file nobody on the team has been told to read.


Sources

TypeSource
PrimaryThe Hacker News — Packagist Supply Chain Attack Infects 8 PHP Packages
PrimarySocket — Packagist Supply-Chain Campaign Research