This Week Hurt: PHP RCE, a Leaked CA Key, and Rebuilding Under Fire
Two critical security events hit at once: a Log4Shell-level PHP RCE and a leaked CA private key. How we handled both, plus the prompts that helped us move faster.
This week handed anyone running their own infrastructure two problems at once. Neither was minor. Here's what happened, how we handled it, and the prompts we used to move faster.
Hit One: The PHP Vulnerability Nobody Was Ready For
On October 21st, PHP released emergency patches for two critical vulnerabilities — CVE-2026-109618 and CVE-2026-109619. Both allow remote code execution. Both were already being exploited in the wild before the patch dropped.
Krebs called it Log4Shell-level. That's not hyperbole. Nearly 70% of websites run PHP. WordPress, Drupal, Joomla, Magento — all of them. The attack surface is enormous, the exploitation was immediate, and the most common payload being dropped was a webshell: a PHP file tucked somewhere in your web root that gives an attacker persistent shell access to your server.
Every PHP version from 5.4 onward is affected. The patched versions are:
- PHP 8.5.12
- PHP 8.4.27
- PHP 8.3.36
- PHP 8.2.35
If you're running anything older than those, you're exposed right now.
We found injected code and unfamiliar PHP files in the web root. The tell was recently modified files that had no business changing. Once you know what to look for, the scan is fast.
Check your PHP version across Docker containers:
docker ps --format "{{.Names}}" | xargs -I{} docker exec {} php -v 2>/dev/null | grep -E "PHP [0-9]"
# Or directly:
php -vPrompt to use with Claude or ChatGPT:
I'm running PHP [version] on a [Debian/Ubuntu/Alpine] Docker container. Walk me through patching to PHP [target version] — including how to update the package source, verify the upgrade didn't break my app, and check for signs of a webshell being dropped before the patch. Show me the specific files and directories I should inspect.
Hit Two: The CA That Handed Out Its Private Key
The second problem came from eMudhra, a certificate authority used by a lot of hosting providers and commercial sites. Their intermediate CA key leaked. Not "might have leaked." Leaked. Publicly. Verifiably.
How do we know? Because mitmproxy shipped a commit this week that embeds the actual private key directly in the tool:
Add --emudhra option to sign TLS certificates with a compromised eMudhra intermediate CA key.The commit includes the literal private key for eMudhra's "EM OV TLS CA - G3C" intermediate CA. Anyone running mitmproxy with --emudhra can now generate valid-looking certificates for any domain and intercept HTTPS traffic from any client that still trusts the eMudhra root.
certifi — the Python CA bundle used by practically every Python HTTP client — removed eMudhra from its precompiled binaries in the same release. Major browsers are pulling the roots too.
If your TLS cert was issued by eMudhra, replace it. It can be forged.
Check who issued your cert:
echo | openssl s_client -connect yourdomain.com:443 2>/dev/null | openssl x509 -noout -issuerLook for eMudhra, emSign, or emsign in the issuer field.
Prompt to use:
My TLS certificate was issued by eMudhra. I need to replace it with a new cert from Let's Encrypt. I'm running [nginx/Apache/Caddy] inside Docker on [Linux distro]. Walk me through revoking the old cert if needed, issuing a new one with certbot/acme.sh, mounting it into the container, and verifying the chain is clean. Zero downtime preferred.
The Docker Rebuild
Replacing the cert meant rebuilding the container. Our setup bakes the cert in at build time, so a new cert meant a fresh build. The process:
- Issue new cert from Let's Encrypt via certbot
- Update cert path references in
docker-compose.yml docker compose down && docker compose up -d --build- Verify with
openssl s_clientthat the new issuer is clean - Confirm the full chain resolves to a trusted root
Prompt for the rebuild:
I need to rotate a TLS certificate in a Docker Compose setup. My current cert is at [path], mounted as a volume into an nginx container. The new cert from Let's Encrypt is at [path]. Walk me through updating docker-compose.yml, restarting cleanly, and confirming the new cert is being served correctly. Also show me how to set up auto-renewal so I don't have to do this manually again.
What to Do Right Now
If you run any PHP-based infrastructure:
- Patch PHP immediately — upgrade to 8.3.36, 8.4.27, or 8.5.12 depending on your branch
- Audit for webshells — look for recently modified
.phpfiles in your web root, especially those containingeval,base64_decode,system, orpassthru - Check your cert issuer — if it says eMudhra or emSign, plan the replacement this week
Quick webshell scan:
# PHP files modified in the last 7 days
find /var/www -name "*.php" -mtime -7 -ls
# Common webshell patterns
grep -rn "eval(base64_decode" /var/www/
grep -rn "system\|passthru\|shell_exec\|exec" /var/www/ --include="*.php"Prompt for a full post-compromise audit:
I think my server may have been compromised via the PHP RCE vulnerability (CVE-2026-109618/109619). Walk me through a systematic post-compromise audit: how to find dropped webshells, check for new user accounts or cron jobs added, review auth logs for unusual access, and determine whether I need a full rebuild or can clean in place. I'm on [Ubuntu/Debian] running [nginx/Apache] with PHP-FPM.
Lessons
Two unrelated security events landing in the same week is bad luck. But the common thread is infrastructure that's easy to let drift — PHP versions that haven't been updated in months, certs from a CA that seemed fine until it wasn't.
The faster you can verify your own exposure (openssl, find, grep) and draft a remediation plan, the shorter the window attackers have. Neither of these required deep security expertise to handle — just knowing where to look and what to do when you find it.