Black October: what one week of PHP2Shell, LiteLLM and eMudhra says about who scans your code

AI-found PHP RCEs, a backdoored LiteLLM release and weak CA keys in one week. Finding bugs got cheap; fixing still waits.

PUBLISHED AUTHOR NurstarREAD 9 MIN
Ink illustration of falling dominoes marked with a cracked padlock, a key and a wax seal, caught by a lantern robot

Between Friday 17 October and Friday 24 October 2026, the PHP interpreter shipped emergency patches for two pre-auth RCEs found with frontier AI models. The fix date was forced forward by a leaked embargo. A popular AI proxy shipped a backdoored release to PyPI and Docker Hub, and an Indian certificate authority turned out to have eight intermediate keys that anyone could derive from the public certificates. Node.js pushed ten security fixes on the same day as PHP. Government websites in several European countries went dark on purpose while operators patched.

None of these is an exotic attack. Two are old bugs that sat in plain sight until a model went looking. One is a leaked credential that nobody revoked for 136 days. The pattern behind all of them is that finding a vulnerability is now cheap, and fixing it still waits on a human noticing.

1. PHP2Shell: a decade-old bug, found in a week, leaked a day early

What it is. CVE-2026-109618 and CVE-2026-109619, both CVSS v4.0 9.2. The first is memory corruption in PHP's out-of-memory handling, reachable with crafted multipart requests. The second is a use-after-free from state that survives between requests on a reused worker. Both give unauthenticated code execution as the web worker. Every SAPI that serves web requests is in scope: PHP-FPM, Apache mod_php, php-cgi, FrankenPHP and LiteSpeed. Rapid7 calls mod_php the worst case because "exploitation can succeed before any application code runs." Wiz says every PHP version from 5.4 up is hit by at least one of the two.

Fixed in 8.5.12, 8.4.27, 8.3.36 and 8.2.35. Branches 7.4 to 8.1 get limited source backports only. Rapid7: "an update containing both complete fixes is the only complete remediation."

How it was found. Three groups converged on the same root cause. Calif researcher Ryan Austin found it while auditing PHP under OpenAI's "Patch the Planet" program. OpenAI reported the same root cause independently on 11 August, the same day as Calif. Wiz researchers used Gemini 4 Argon, Wiz Atlas and GPT-5.6 Sol, and say a single researcher did in a week what used to take a strong team months.

The drama. The coordinated release was set for Thursday 22 October. Then a "Tell HN" post on Hacker News, since removed, claimed a "reliable source" and announced two PHP RCEs rated CVSS 10.0. Screenshots spread on X and Mastodon. On 20 October watchTowr confirmed the embargo had broken ("Information has leaked wrt zero-day embargoed RCE vulns in PHP"), while noting it had not found the bugs itself. The PHP security team posted an advance notice the same day, moved the release up to Wednesday 21 October at 14:00 UTC, and said it reserved the right to ship earlier if details went public. The final score came in at 9.2, not the leaked 10.0.

The fallout. The NSA told PHP operators to update, or take the system offline until they could. Reuters reported government sites across Europe going dark on Wednesday. French prefecture websites were pulled while waiting for the fix, and Germany's federal IT provider ITZBund took several sites down as a precaution. Citing Censys data, Reuters reported that roughly 1.5 million of 2.6 million hosts it saw were running a PHP version with no fix available at all. Palo Alto Networks confirmed PAN-OS and Panorama are affected.

The honest part. The CVSS v4 vector is AC:H/AT:P, meaning high attack complexity with preconditions. Rapid7 says the public PoCs each work against one specific combination of OS, PHP version and web server, none is broadly reliable, and as of its last update it had seen exploitation attempts but no confirmed in-the-wild compromise. Cloudflare and watchTowr honeypots logged attempts. That is a narrower window than the headlines suggest, and it is closing fast because many people are now reproducing the research.

And it isn't over. On 23 October the PHP Foundation published a third issue, CVE-2026-115560. A mitigation exists, no patch yet, and exploitation needs a non-standard configuration.

2. LiteLLM 1.107.0: one unrevoked key, 136 days later

On 21 October, litellm==1.107.0 landed on PyPI with three extra files (cook.pth, cook.py, sitecustomize.py). A .pth file runs whenever the Python interpreter starts, so the payload executed on any host that installed the wheel, whether or not anything imported LiteLLM. Datadog and StepSecurity describe a credential stealer plus a remote loader. The bad wheels were live on PyPI for roughly 11:39 to 13:23 UTC. Docker Hub and GHCR images, including latest, served the bad build until about 14:20 UTC. It is LiteLLM's second supply-chain compromise this year, after 1.82.7 and 1.82.8 in March.

LiteLLM's own attack-chain write-up is the most useful document of the week, because the root cause is so ordinary:

  • 27 May 2026. A file committed to a public repo, LiteLLM-Labs/lite-harness, contained the master key to an internal LiteLLM Agent Platform deployment. The commit was later force-pushed over. Per the post, "key revocation did not occur for the master key."
  • 10 October. The attacker used that key. One API response included a GitHub CLI OAuth token belonging to an organization owner.
  • 10 to 12 October. Using the owner token, the attacker pushed throwaway CI branches whose workflows posted repository secrets, including two more maintainers' tokens, to a webhook endpoint, then deleted the runs.
  • 21 October. The release workflow was pointed at a lookalike Berri-AI/litellm fork, branch protection was overridden with the owner token, the production gate was approved with a stolen maintainer token, the wheels shipped, and the run logs and main history were scrubbed.

A community member opened the issue that caught it at 12:06 UTC. The official v1.107.0 git tag is clean. The fix is to pin 1.106.2 and rotate every credential on any host that installed 1.107.0 or pulled the image.

The lesson is the gap between May and October. Rewriting git history does not un-leak a key, and a live credential in a public repo is the kind of thing that verified-secret scanners exist to flag.

3. eMudhra: eight CA keys anyone could compute

On 17 October a researcher reported that eight eMudhra intermediate CA keys, from a batch generated on 30 April 2025, were weak enough to derive from the public certificates. Six could issue TLS certificates for any website a browser would accept, and two were for code signing. eMudhra says a flawed key ceremony produced a second, weak set of keys, and that set went into the root stores.

Chrome, Mozilla, Apple and Microsoft blocked the certificates between 17 and 20 October. Mozilla removed the emSign ECC Root CA - G3. As of 21 October no malicious use was confirmed, and no website certificate chaining to the six TLS intermediates had shown up in Certificate Transparency before Chrome's block. The long tail is everything that isn't a browser: API clients, containers, build pipelines and embedded devices that don't check revocation or CT. Go's crypto/x509 got CVE-2026-111943, and Node.js shipped an updated CA store in its 21 October release, which also fixed ten vulnerabilities, four rated high.

What connects them

Strip away the names and the week has three parts:

  • Discovery got cheap. A bug class that lived in PHP core for over a decade fell to one researcher with frontier models in a week. The same models are available to anyone, and the PHP release was pulled forward because people were already reproducing the work from a leak.
  • The oldest failures are still the ones that land. A key in a public commit. A dependency that runs code at install. A trust store nobody updates.
  • The response is human-paced. LiteLLM's key sat live for 136 days. 1.5 million hosts are on PHP branches with no upstream fix. Governments chose to switch websites off because that was faster than being sure.

If attackers can point a model at your code whenever they like, checking it once a quarter, or whenever someone remembers, leaves the advantage with them.

Where an autonomous agent fits

I contribute to Aeon, an open-source agent harness created by Aaron Elijah Mars. It runs skills on a schedule through GitHub Actions in your own repo. One of those skills is vuln-scanner. Each scan run:

  • runs Semgrep with the security-audit, owasp-top-ten and secrets rulesets,
  • runs TruffleHog with --only-verified over both the working tree and the git history, so it reports credentials that actually authenticate, not every string that looks like a key,
  • runs osv-scanner across every lockfile it finds, which covers npm, PyPI, Go, Cargo, Composer and the other ecosystems in the OSV database,
  • has the model triage the raw output down to findings that are actually exploitable, then routes each one by disclosure channel: private vulnerability reporting for code flaws, and a public dependency-bump PR only when the CVE is already public.

A companion skill, vuln-tracker, polls daily what the scanner opened: PR and advisory status, private-report triage state, and disclosures that are aging without a response.

By default the scanner picks a trending repo to audit and follows a responsible-disclosure process. Point its var at your own owner/repo and the same pipeline runs against your code on a cron you set. The shipped default is weekly, Saturdays at 16:00 UTC, and it can run nightly if you want it to.

Mapped to this week:

  • LiteLLM. A verified-secret scan of git history, run on a schedule across every repo an org publishes, is designed to catch a live master key in a public commit. Running it continuously matters: once history is force-pushed, a fresh clone may no longer contain the commit, while the key stays valid until someone revokes it.
  • Malicious dependencies. Known-malicious package versions get OSV entries. A scheduled osv-scanner pass over your lockfiles is how you find out that a pinned version you shipped last month has since been flagged.
  • PHP2Shell. This is where the limits are. The bug is in the interpreter, not your code. osv-scanner reads lockfiles, not the PHP binary your server runs, so this scan would not have caught it. What closes that gap is runtime inventory and patching. The case for continuous scanning here is indirect: the next bug a model finds may well be in your own application code, and a scanner that checks that code every week gives you a chance to see it first.

Scanners don't replace patching, key rotation or someone owning the incident. What an agent changes is the cadence. The scans happen on schedule whether or not anyone is paying attention that week, and the output arrives as triaged findings instead of raw tool dumps.

Checklist for this week

  • PHP: move to 8.5.12, 8.4.27, 8.3.36 or 8.2.35. Until then, apply the temporary mitigations from the PHP advisory (session.upload_progress.enabled=0, an administratively enforced memory_limit, fatal_error_backtraces=0 on 8.5) and restart every worker. Watch CVE-2026-115560.
  • LiteLLM: if anything installed 1.107.0 or pulled the image between 21 October 11:39 and 14:20 UTC, treat the host as compromised and rotate everything it could reach.
  • eMudhra: update the CA bundles in containers, build images and language runtimes, not only browsers. Update Node.js and Go.
  • Any credential that has ever appeared in a commit: revoke it. Rewriting history does not revoke anything.

Sources: Rapid7, Threat Landscape, Fortune, CNN, NSA, Reuters via Investing.com, LiteLLM, Help Net Security, Cloudflare, TechCrunch.