Skip to content
Let’s talk
GREY WING SECURITY
All field notes
Application security

PHP2Shell and the LiteLLM Compromise: What to Check Now

PHP2Shell puts internet-facing PHP at risk, and a compromised LiteLLM release ran a credential stealer at Python startup. What to check this week.

11 min read

Two security stories broke on October 20 and 21, 2026, and both deserve an hour of your time this week. The first is PHP2Shell, a pair of critical flaws in PHP itself. If you run PHP on an internet-facing server, you are in scope. The second is a supply-chain attack on LiteLLM, a popular Python library for calling AI models. If a laptop, CI runner, or AI gateway pulled the bad release, treat it as exposed.

Neither story needs a new tool. Both need an inventory and a clear order of work. Here is what the primary sources say, and what to do.

PHP2Shell: two critical flaws in PHP itself

What happened

On October 20, the PHP security team gave notice of an emergency release set for October 21 at 14:00 UTC. That release has now shipped. PHP's security advisory says PHP 8.5.12, 8.4.27, 8.3.36 and 8.2.35 fix CVE-2026-109618 and CVE-2026-109619. PHP rates both 9.2 on CVSS v4.0 and says they "can allow unauthenticated remote code execution."

The flaws live in the PHP runtime itself. The Hacker News puts it plainly: a site is exposed "even when its own code is secure." One flaw is in how PHP cleans up after it runs out of memory. The other lets a worker reuse stale state from an earlier request. PHP's advisory for the second flaw also lists memory disclosure as a confirmed impact. Worker memory can hold passwords, tokens, and API keys from other requests.

Who is exposed

PHP says every install that serves web requests is at risk. That includes PHP-FPM, Apache with mod_php, php-cgi, FrankenPHP and LiteSpeed. PHP rates the risk highest for mod_php, where a demonstrated exploit fires before any application code runs. PHP-FPM is not safe either. PHP confirmed code execution on FPM for the second flaw, though it says that "depends on a suitable application operation."

Older branches are the hard part. PHP says 8.1 and older "no longer receive updates from the PHP project." Given the severity, its security team did publish minimal source backports for 7.4, 8.0 and 8.1. Those help teams that build PHP from source. They are not official releases, and PHP names no backport for anything older than 7.4. Those older versions are still affected. The Hacker News (THN) reports the flaws reach back to PHP 5.4.

Attackers are already going after the flaws. THN reports that watchTowr saw "active exploitation attempts" against both flaws, and that Cloudflare saw attack traffic. Amazon says PHP2Shell "is now being broadly exploited in the wild." It also logged an attempt on October 17, four days before disclosure, and advises starting any investigation from that date. As of THN's latest update, no source had reported a confirmed break-in. CISA had not added either CVE to its list of known exploited flaws as of its October 21 update.

What to do

On 8.2 or newer, update to the fixed release for your branch. Then restart Apache, PHP-FPM, and any long-running PHP workers. Updating WordPress or Laravel does not update the PHP runtime under it.

On 8.1 or older, you have three options:

  • Upgrade to a supported branch.
  • Take the host off the internet until you can.
  • Use a backported fix from your Linux distribution, host, or LTS vendor, if they offer one. If you build PHP 7.4, 8.0, or 8.1 from source, PHP's minimal source backports are an option while you plan the upgrade.

PHP says distributions got the patches in advance. A distro package can carry the fix under an old-looking version number. THN's advice is to check the package changelog for the CVE number, since the version string can mislead.

Config changes are a stopgap. PHP lists some, such as setting session.upload_progress.enabled=0. Set it in php.ini or the server config, not in app code, and restart every worker. PHP says they "reduce exposure to specific attack paths but do not make an installation safe."

LiteLLM 1.107.0: a compromised release

What happened

LiteLLM is the victim here. Its maintainers say the project's release pipeline was compromised on October 21 and used to push version 1.107.0 to PyPI and Docker. That version "contained compiled malware which steals credentials and loads remote-code execution tools."

The official v1.107.0 source tag on GitHub is clean. LiteLLM says the bad artifacts "were built from an attacker-controlled repository," a lookalike named Berri-AI/litellm. LiteLLM's current account is that the attacker used stolen GitHub CLI tokens tied to developer accounts. The investigation is still open.

The malware runs at Python startup. Each bad wheel drops three files into site-packages: cook.pth, cook.py, and sitecustomize.py. Python reads .pth files every time it starts, and cook.pth holds one line: import cook. So the stealer runs even if your code never imports LiteLLM. Running pip in that environment sets it off again.

The OSV entry lists what the stealer takes: browser passwords, SSH keys, AWS, GCP and Azure credentials, CI secrets, AI coding tool configs, and environment variables. On a LiteLLM proxy, it also tries to dump the provider keys stored in the proxy database. The third file, sitecustomize.py, starts a background process that checks a GitHub file every hour and runs code from the domain it finds there.

Who is exposed

LiteLLM says you are affected if you installed an unpinned version with pip between 11:39 and 13:23 UTC on October 21. You are also affected if you ran the Docker image tagged v1.107.0, latest, main-latest, or main-stable between 11:46 and 14:20 UTC. LiteLLM says customers on its private enterprise registry were not affected.

Think beyond production. LiteLLM runs on developer laptops, in CI pipelines, and on AI gateways that hold provider keys. Any of them could have pulled the release.

The bad release is gone. LiteLLM says the PyPI release was pulled, and the affected Docker tags were removed or pointed at clean images.

Removing the release does nothing about keys already taken. Wiz reports that between 16:20 and 17:01 UTC on October 21, the attacker validated AWS role credentials taken from compromised CI runners and Kubernetes nodes. Each check was followed by calls to list the AI models those credentials could reach.

What to do

LiteLLM says to pin 1.106.2 "or any explicit version published prior to now." THN adds that you should pin 1.106.2 by digest.

If a host pulled 1.107.0, treat it as compromised. LiteLLM's steps open with a warning: do not start with pip install. Our order: contain the host first, then clean it from the shell, rebuild, and only then rotate keys from a clean machine.

What to check this week

  1. Find internet-facing PHP. Run php -v on each host. That shows the command-line PHP, which can be a different build from the PHP-FPM or Apache module serving requests. So also check the web runtime's own version or package, for example with php-fpm8.3 -v, since Debian and Ubuntu name the binary by version. Flag anything below the fixed release for its branch, unless it is a distro package whose tracker lists both CVEs as fixed (step 2). Include PHP inside vendor software and container images.
  2. Check your distro tracker. For hosts on distro packages, look up both CVE numbers in your distro's security tracker, such as Debian's. Then confirm the installed package changelog names them.
  3. Restart after you patch. Restart Apache, PHP-FPM, and workers. Rebuild PHP container images.
  4. Take 8.1 and older off the internet until you have an upgrade or a vendor backport.
  5. Find LiteLLM installs. Search lockfiles, CI logs, and image build logs for litellm 1.107.0. Also list every unpinned litellm requirement, including where it comes in as a dependency of another package. Unpinned installs are the ones affected. LiteLLM suggests pip show litellm, but don't run pip or any Python on a host that may have 1.107.0. Starting Python runs the malware again. Search the disk for litellm-1.107.0.dist-info instead.
  6. Check image tags and digests. List LiteLLM containers pulled or started on October 21. Treat any container that ran a v1.107.0 image as compromised. That includes the litellm-database and litellm-non_root variants, whose digests differ from the one LiteLLM spells out in full. Check internal registry mirrors and CI caches that may still hold it.
  7. Look for the IOC files. Search every site-packages on the host for cook.pth, cook.py, and sitecustomize.py. That means system Python, each venv and conda env, user installs, and container images. Check any sitecustomize.py against the published hashes, since some systems ship a real one. On Windows the /tmp markers won't exist, but cook.py still runs. Search /tmp for remote_code_loader.lock and .pc_once_72129118c59f5636c0bb. Finding one confirms exposure. Finding none does not prove the host is clean.
  8. Contain, rebuild, then rotate. Work through these steps in order.
    • Cut the host's outbound network access. If you can, snapshot it for evidence.
    • From the shell, not Python, kill the process holding /tmp/remote_code_loader.lock. Then delete the three files and any cook or sitecustomize .pyc files under pycache.
    • Rebuild the venv or image from scratch, pinned to 1.106.2 (by digest for images). Wipe and rebuild CI runners and servers rather than cleaning them in place.
    • Only once the old host or container is stopped or rebuilt, work from a clean machine. Revoke the instance or task role's active sessions. Rotate every AI provider key in the LiteLLM proxy database, plus its master and virtual keys. Rotate the database password and any secret in the environment of any process on the host.
    • Review AWS CloudTrail now, not last. LiteLLM says to look for GetCallerIdentity calls by the instance role around install time. GetCallerIdentity, Bedrock, or IAM list calls using that role from addresses outside your network are a sign someone tested the stolen credentials. Wiz attributes these IPs to the attacker: 168.100.8[.]57 and 165.227.201[.]8 (remove the brackets to search). A match in your logs gives you a lead. A clean result does not clear a host.
  9. Keep the evidence. Save the host list, the versions you found, patch dates, and rotation records. Your next customer security review will ask.

Where we fit

We help Security and IT teams put these controls in place, set up alerts, and keep the evidence current. We are a readiness helper. We do not audit or certify. If you need hands-on incident response, it comes through our partner MDR, delivered by At-Bay. Our own team does not run incident response.

If you want help working out which hosts to check first, Let’s talk.


Sources

Not sure where to start?

Tell us about your systems and deadline. We’ll reply within one business day.

Keep reading

More on application security

All field notes