Contents

PHP Composer Command Injection CVE-2026-40261

 

Want to learn ethical hacking? I built a complete course. Have a look!
Learn penetration testing, web exploitation, network security, and the hacker mindset:
→ Master ethical hacking hands-on
Hacking is not a hobby but a way of life!

 
Contents

PHP Composer Has Two Flaws That Run Arbitrary Commands on Developer Machines PHP Composer, the package manager that almost every PHP developer uses to build websites and applications, has two serious vulnerabilities that allow an attacker to run arbitrary commands on any machine running a vulnerable version. Neither one requires Perforce to be installed, configured, or even known about. Patches came out on April 14, 2026, and many environments will still be running vulnerable versions. 😏

Composer is the tool that keeps PHP projects running. When you build a website with Laravel, when a WordPress plugin pulls in a library, Composer is what goes out and grabs everything. It handles which version of what goes where and makes sure all the pieces fit together. Almost every modern PHP project has it installed. Packagist, the main place Composer downloads packages from, has recorded 174 billion total downloads and currently has 447,000 packages on there. PHP itself runs on 71.7% of all websites that have a known backend language, and WordPress alone runs on 43% of every website on the internet.

Inside Composer, there is a driver for something called Perforce. Perforce is a version control system used mostly in big enterprise companies and game studios. Most PHP developers work with Git and have no reason to ever go near Perforce. The problem is that even if Perforce is nowhere near the project, the vulnerability is still there and still reachable.

CVE-2026-40261 is the worse of the two, with a CVSS score of 8.8. It sits inside a piece of Composer code called syncCodeBase(), which is responsible for fetching the actual files of a package. Inside that code, there is a variable called $sourceReference that gets dropped directly into a shell command without any checking or cleaning of the input first. Shell commands have special characters that tell the terminal to run additional commands on top of the original one, and if those characters end up in $sourceReference, whatever is attached to them gets executed on the machine. What makes this dangerous is that $sourceReference does not come from the developer typing something in manually. It comes from the package metadata that Composer reads when installing a package. Any Composer repository out there can serve a package where the metadata says Perforce is the source type and includes a crafted source reference containing those special characters. Composer reads that metadata, processes it, and runs whatever was injected into it.

When a developer installs a package that has a dev- prefix in the version name, Composer automatically uses source installation as its default behavior. That is not a flag anyone has to set manually, it is just how Composer works. Which means anyone working with dev packages in a Laravel or Symfony project is already running through the vulnerable code path without doing anything unusual or wrong.

CVE-2026-40176 scores a CVSS of 7.8 and hits a different part of the same driver, a method called generateP4Command(). Here the problem is that Composer takes Perforce connection details from the project file, things like the port number, the username, and the client name, and drops them straight into a shell command without checking them first. An attacker who controls a malicious composer.json file can put whatever they want into those fields and get it executed. This one only triggers when a developer runs Composer commands on an untrusted project, so it is a bit more limited in reach, but cloning a repository without checking its contents and running composer install is something that happens all the time.

Both vulnerabilities come from the same underlying mistake: building shell commands using raw input that was never validated, in a part of the codebase that apparently nobody had looked at closely in a long time.

PHP developers run composer install constantly: setting up a project locally, adding a new team member, deploying an update. But the bigger issue is that every modern CI/CD pipeline runs it automatically. GitHub Actions, GitLab CI, Jenkins, Semaphore, all of them trigger composer install whenever a developer pushes code, and none of that process involves a human reviewing what happens. The pipeline just executes.

Build servers are not ordinary machines. They hold SSH keys that connect directly to production servers. They carry cloud credentials and deploy tokens stored as environment variables. They have access to everything needed to push a new version of an application live. A machine that automatically runs composer install on every push to the main branch already has everything an attacker would want. Someone who can run commands on that machine can grab those credentials and use them to get into the rest of the organisation’s systems from there. One poisoned package in one repository is enough to turn an automated build pipeline into an entry point into production, with no unusual tools and no extra steps needed.

In early March 2026, Socket’s Threat Research Team discovered and reported several malicious packages on Packagist that had been sitting there since mid-2024, published by a threat actor using the handle “nhattuanbl”. The packages looked exactly like normal Laravel utility libraries but had been quietly installing a remote access trojan on developer machines, build servers, and production environments for months before anyone caught them. The attack worked through exactly the same trust in automated pipelines that makes these CVEs dangerous.

Packagist noticed the problem before the public disclosure. On April 10, 2026, the ability to publish Perforce source metadata on Packagist.org was quietly switched off, and a full scan of the repository found no sign of anyone having tried to exploit this. Private Packagist was handled the same way. The public advisory and the patched versions came out on April 14.

CVE-2026-40261 was found and reported by a researcher named Koda Reef. CVE-2026-40176 was found by a researcher named saku0512. Both went through responsible disclosure before anything went public, and as far as the Composer team knows, neither flaw was used in an actual attack before the patches came out.

The vulnerable versions are Composer 2.0 through 2.9.5 on the main track and anything below 2.2.27 on the LTS track. The safe versions are 2.9.6 and 2.2.27. Running composer --version shows exactly which one is installed. Worth knowing: version 2.9.6 fixes more than just these two CVEs. The release notes also include a fix for git credentials that were being left behind in .git/config after a failed clone or update, a fix for insecure 3DES ciphers when ext-curl is missing, and hardened identifier validation across git, Perforce, Mercurial and Fossil. That is four separate security improvements in one update, which makes updating even more worthwhile.

Here is what to do right now.

  • → Check the installed version with composer --version. Anything below 2.9.6 on the main track or below 2.2.27 on the LTS track is vulnerable and needs updating.
  • → Update with composer self-update and the new version pulls in automatically.
  • → If an immediate update is not possible for any reason, add --prefer-dist to every composer install and composer update command. This tells Composer to download the pre-built package archive instead of fetching from source, which avoids the code path where CVE-2026-40261 triggers.
  • → Before running Composer on any project that was not written in-house, open composer.json and look for any repository entry with the type set to perforce. Unfamiliar or suspicious values in those fields should be treated as a red flag until someone has checked them properly.
  • → Go through every CI/CD pipeline that runs composer install. Every GitHub Actions workflow, every GitLab CI configuration, every automated deployment script. These run without anyone watching, and that automated trust is exactly what this kind of attack depends on.
  • → Organisations running Private Packagist Self-Hosted should watch for the upcoming release that includes verification steps to confirm no malicious Perforce metadata is sitting in the installation.

What makes this worth paying attention to beyond just patching Composer is that the flaw sits in the tool that WordPress, Laravel, Symfony and practically every other PHP project depends on to install its dependencies. That means every project and every automated pipeline that touches Composer is affected, regardless of what the application itself does.

Privilege escalation, post-exploitation, network pivoting, and how attackers move laterally through systems after getting a foothold are all covered in my ethical hacking course.

Hacking is not a hobby but a way of life.

Sources: Packagist Blog: Composer 2.9.6 Security Advisory · GitHub Advisory CVE-2026-40261 · GitHub Advisory CVE-2026-40176 · Composer Changelog 2.9.6

 

→ Stay updated!

Get the latest posts in your inbox every week. Ethical hacking, security news, tutorials, and everything that catches my attention. If that sounds useful, drop your email below.

By Bulls Eye

Jolanda de koff • emaildonate

My name is Jolanda de Koff and on the internet, I'm also known as Bulls Eye. Ethical Hacker, Penetration tester, Researcher, Programmer, Self Learner, and forever n00b. Not necessarily in that order. Like to make my own hacking tools and I sometimes share them with you. "You can create art & beauty with a computer and Hacking is not a hobby but a way of life ...

I ♥ open-source and Linux