Threat Intel

7 min read

PHP2Shell, a Breach and a Key Called 4: Are You Exposed?

Three security stories broke within days of each other. PHP2Shell is a critical flaw in PHP itself, a database vendor disclosed a breach, and a certificate authority turned out to be signing with keys that were tiny numbers. They look unrelated, but they teach the same thing: finding weaknesses is getting cheaper, so what matters now is how fast you fix things and whether you know what you run.

  • PHP2Shell: patch PHP now, and check logs back to 17 October.

  • Secrets in code: scan your repositories and rotate what turns up.

  • Certificates: check whether any of yours chain to eMudhra.

Last checked 22 October 2026. These stories are still moving.

PHP2Shell: a flaw in the code under much of the web

On 21 October PHP shipped emergency fixes for two flaws, CVE-2026-109618 and CVE-2026-109619. Both score 9.2 on CVSS v4.0, and PHP says either can give an attacker remote code execution without logging in. The bugs are in PHP core, not in a plugin, so any installation that serves web requests is at risk. That includes sites on WordPress, Drupal, Joomla and Magento, where researchers at Calif confirmed working attacks. The affected code goes back to PHP 5.4, released in 2012 (Tenable, BleepingComputer).

The part to plan around is timing. Amazon Threat Intelligence saw an exploitation attempt on 17 October, four days before the patch existed, and it advises starting any investigation from that date rather than from disclosure (Amazon).

The fixed versions are 8.5.12, 8.4.27, 8.3.36 and 8.2.35 (Tenable). Branches 7.4 through 8.1 get no official release, only source backports from the PHP security team. Branches 5.4 through 7.3 are end of life and get nothing. Cloudflare's CTO said the company rolled out WAF rules for the known attack routes to every customer, free plans included, but called them a stopgap and told people to patch (Tenable).

A database vendor breach, and the secrets problem

PingCAP, the company behind the TiDB database, disclosed on 19 October that an intruder had been in its systems between 9 September and 7 October. PingCAP blames stolen service account credentials. A group calling itself FulcrumSec says it found cloud keys sitting in PingCAP's own code repositories. The company hasn't confirmed that, so for now it is an allegation (Dark Reading).

Whatever happened at PingCAP, the wider pattern is well documented. GitGuardian counted about 29 million new hardcoded secrets on public GitHub in 2025, up 34% on 2024, and found that 32.2% of internal repositories held at least one (TechRadar). Those are the vendor's own measurements, but the direction is hard to dispute. A key in a repository works for anyone who can read that repository.

A signing key that was the number 4

Eight intermediate certificates from the Indian certificate authority eMudhra were protected by private keys so small that anyone could recover them from the public certificates in under a second. A proper key of this type is a random number more than 100 digits long. Scott Helme recovered one that was just 4, and others that were 56 and 115. Browsers and operating systems blocked the certificates within hours (Help Net Security).

One number going around is wrong. eMudhra has issued more than 60 million certificates in total, but that isn't how many are at risk. The company estimates about 30,000 customer certificates need replacing (Help Net Security). It traced the problem to a faulty script during a key ceremony in April 2025, which produced a second, weaker set of keys with labels similar to the good ones. Staff picked the wrong set. Nobody knows yet whether anyone used the keys before the report.

The lasting risk is outside the browser. Apps, servers and devices that never get revocation updates may still trust those certificates.

What links them

Two of the three were found by researchers who went looking, and looking keeps getting cheaper. The PostgreSQL project now plans to release version 19 on 19 November (postgresql.org). Committer Peter Eisentraut has said AI-assisted code review surfaced defects faster than developers could fix them, which led to several features being reverted (technology.org). Credit the AI or credit plain research, the effect on a small team is the same. More problems get disclosed, faster, and attacks sometimes start before a patch exists.

You can't read your way through that stream. You can shorten the gap between learning you're affected and being fixed, and you can keep an honest list of what you run. Patching on a schedule someone owns is much of what managed security covers.

Check whether you are affected

Most setups can get through this quickly, though scanning a large repository history takes longer. Every command below only reads information, so run them on servers you're allowed to inspect. If you're on managed hosting, jump to step 5.

Part 1: PHP2Shell

Step 1: find out whether you run PHP at all. Look at web servers, containers, hosting plans, and any WordPress, Drupal, Joomla or Magento site, including the old marketing site nobody owns anymore.

Step 2: get the version.

php -v
php-fpm -v            # may be php-fpm8.3 -v on Debian or Ubuntu
dpkg -l | grep -i php # Debian, Ubuntu
rpm -qa | grep -i php # RHEL, Alma, Amazon Linux
php -v
php-fpm -v            # may be php-fpm8.3 -v on Debian or Ubuntu
dpkg -l | grep -i php # Debian, Ubuntu
rpm -qa | grep -i php # RHEL, Alma, Amazon Linux
php -v
php-fpm -v            # may be php-fpm8.3 -v on Debian or Ubuntu
dpkg -l | grep -i php # Debian, Ubuntu
rpm -qa | grep -i php # RHEL, Alma, Amazon Linux

In WordPress, open Tools, then Site Health, then Info, then Server. For containers, check the image tag you deploy from, then run php -v inside a running one.

Step 3: compare it with the fixed releases.

  • PHP 8.5 before 8.5.12, 8.4 before 8.4.27, 8.3 before 8.3.36, or 8.2 before 8.2.35: vulnerable. Upgrade to the fixed release.

  • PHP 7.4 to 8.1: no official release, only source backports from the PHP security team. Move to a supported branch.

  • PHP 5.4 to 7.3: end of life, no fix from the PHP project.

Step 4: check for a distribution backport before you panic. Linux distributions often patch a flaw without changing the version number, so an old-looking version may already be fixed. Search the package changelog for the CVE IDs.

apt changelog php8.3-fpm | grep -E "CVE-2026-109618|CVE-2026-109619"
rpm -q --changelog php | grep -E "CVE-2026-109618|CVE-2026-109619"
apt changelog php8.3-fpm | grep -E "CVE-2026-109618|CVE-2026-109619"
rpm -q --changelog php | grep -E "CVE-2026-109618|CVE-2026-109619"
apt changelog php8.3-fpm | grep -E "CVE-2026-109618|CVE-2026-109619"
rpm -q --changelog php | grep -E "CVE-2026-109618|CVE-2026-109619"

If the IDs show up, that package has the fix. If they don't and you're on one of the vulnerable versions in the list above, treat it as unpatched. Change the package name to match what step 2 showed.

Step 5: on managed or shared hosting you can't see the runtime, so ask your provider in writing which PHP versions are patched for CVE-2026-109618 and CVE-2026-109619, and when. Then switch your site to a patched version in the control panel.

Step 6: look for signs that someone got there first. Attempts began on 17 October, so search from then.

find /var/www -name "*.php" -type f -newermt 2026-10-17
find /var/www -path "*uploads*" -name "*.php"
find /var/www -name "*.php" -type f -newermt 2026-10-17
find /var/www -path "*uploads*" -name "*.php"
find /var/www -name "*.php" -type f -newermt 2026-10-17
find /var/www -path "*uploads*" -name "*.php"

The first command also lists files you deployed since then, so compare it against your deploy history and look for files you didn't deploy. The second should normally return nothing, since PHP files in upload folders are a classic sign of a planted web shell. Also scan web server logs for odd POST requests, and check for outbound connections from the web server that you don't recognise. A WAF rule, including the free Cloudflare one, is a stopgap and doesn't prove you weren't hit. If you find something you can't explain, treat it as an incident and isolate the server before you clean it up.

How to read the result: if you don't run PHP, this one doesn't apply to you. If you run a fixed version, or a package whose changelog names both CVEs, you're patched from here on, but do step 6 anyway. Anything else is exposed until you upgrade.

Part 2: secrets in your code

Run a scanner over your repositories and their full history, because a deleted key still sits in old commits.

gitleaks git .          # newer releases; older ones use: gitleaks detect --source .
trufflehog git file://. --only-verified
gitleaks git .          # newer releases; older ones use: gitleaks detect --source .
trufflehog git file://. --only-verified
gitleaks git .          # newer releases; older ones use: gitleaks detect --source .
trufflehog git file://. --only-verified

Rotate anything it finds first and clean up second. Then move credentials into a secrets manager so they stop turning up in code. Catching secrets before they are committed is a standard part of a DevSecOps pipeline.

Part 3: certificates

The weak keys belong to eMudhra, so the question is whether any of your certificates chain to it. These two commands show what a live site presents and what has ever been issued for your domain.

openssl s_client -connect example.com:443 -servername example.com </dev/null 2>/dev/null | openssl x509 -noout -issuer
curl -s "https://crt.sh/?q=%25.example.com&output=json" | jq -r ".[].issuer_name" | sort | uniq -c | grep -i -E "emudhra|emsign"
openssl s_client -connect example.com:443 -servername example.com </dev/null 2>/dev/null | openssl x509 -noout -issuer
curl -s "https://crt.sh/?q=%25.example.com&output=json" | jq -r ".[].issuer_name" | sort | uniq -c | grep -i -E "emudhra|emsign"
openssl s_client -connect example.com:443 -servername example.com </dev/null 2>/dev/null | openssl x509 -noout -issuer
curl -s "https://crt.sh/?q=%25.example.com&output=json" | jq -r ".[].issuer_name" | sort | uniq -c | grep -i -E "emudhra|emsign"

Swap example.com for your domain. No match means none of your certificates in the public logs come from that issuer. A match means you should ask your certificate provider about replacement. Also look at devices and apps that rarely get updates, such as embedded gear and older Java applications, since those may still trust the revoked certificates.

Where we can help

None of this needs a big team, but someone has to own it. In one engagement with a venture-backed technology company that had no dedicated security hire, we surfaced cloud findings and then verified the fixes, because finding an issue and closing it are separate jobs. If you want a second pair of eyes on your PHP estate, your secrets exposure or your certificate inventory, start with our free security review.

Want this looked at for real?

Get a free security review and we will show you where you actually stand.