Does this sound familiar? You install a third-party dependency for a local project through a standard package manager. Perhaps the project has no PHP at all. You are not especially worried about a malicious sub-dependency because you assume it cannot access your sensitive local files, and you certainly do not expect it to execute shell commands with your user privileges.
However, a scan of your local machine reveals an unpatched PHP process bound to the loopback interface (127.0.0.1). That process may be exploitable.
The limited-access dependency you were not too worried about only has to send a specially crafted HTTP request to your locally running PHP endpoint to trigger one of two PHP core memory-safety flaws. Depending on how the PHP process is configured, successful exploitation may allow code execution with the privileges of the PHP process or web-server worker. That could expose SSH keys, .env files, API tokens, or other sensitive information.
Note that this threat does not require an external service to connect directly to your machine. It can arise when malicious code is already running locally, or when a website opened in your browser sends requests to a local development server. In effect, a forgotten local PHP service can become a local attack escalation pathway.
This scenario highlights the risk—even on local environments—from PHP2Shell (tracked under Common Vulnerabilities and Exposures entries CVE-2026-109618 and CVE-2026-109619): two critical memory-safety remote code execution (RCE) vulnerabilities in the PHP core engine. One involves out-of-memory handling; the other involves use-after-free or stale global state.
The Risk to Local Development Workstations
Developers often assume that software listening only on 127.0.0.1 is immune to external attack because loopback traffic is not directly reachable from other machines.
That boundary is weaker than it appears. Malicious code already running on the host can send requests to local ports. A malicious website can also cause a victim’s browser to issue requests to http://127.0.0.1:port, even though browser cross-origin rules restrict what that site can read from the response.
The risk is most relevant when a PHP process is actually serving HTTP locally—for example through PHP’s built-in server (php -S), Laravel Valet or Herd, MAMP, Apache with mod_php, or PHP-FPM behind a local web server. A PHP CLI binary that is not listening on a socket is not remotely exploitable through this HTTP request vector.
Where a vulnerable PHP web runtime is reachable from a browser or another local process, PHP2Shell may allow an attacker to execute code as the PHP worker user. It does not automatically escape every sandbox or grant full user-level access, but it can still be a serious escalation path on a developer workstation.
Step 1: Audit All Installed PHP Versions
If you maintain local PHP projects, upgrade every installed PHP runtime to a patched release.
Updating only the primary system PHP binary is a partial fix if version-pinned binaries remain unpatched across projects.
macOS no longer includes PHP by default, but developers often maintain multiple concurrent versions using Homebrew formulas (php@8.2, php@8.3, php@8.4), version managers such as phpenv or asdf, or local stacks such as Laravel Herd, MAMP, and Docker.
Search your system paths for PHP executables:
# Check primary PATH resolution
which -a php
# List installed Homebrew PHP formulas
brew list --versions | grep php
# Audit common Homebrew binary locations
ls -l /opt/homebrew/opt/php*/bin/php /usr/local/opt/php*/bin/php 2>/dev/null
Inspect each discovered executable:
/opt/homebrew/opt/php@8.3/bin/php -v
/opt/homebrew/opt/php@8.4/bin/php -v
Step 2: Apply the Patch Across All Installed Versions
The vulnerabilities affect supported PHP releases before the emergency security patches. Ensure every installed supported branch meets or exceeds these versions:
- PHP 8.2: 8.2.35 or higher
- PHP 8.3: 8.3.36 or higher
- PHP 8.4: 8.4.27 or higher
- PHP 8.5: 8.5.12 or higher
PHP 8.1 and older branches are end-of-life and do not receive official upstream security patches from php.net. PHP versions from 5.4 through 8.1 are reported as affected, so legacy runtimes need containment or a distribution-provided backported fix where available.
Patching via Homebrew
Updating the default php formula alone will not patch version-pinned formulas. Upgrade all installed PHP formulas explicitly:
brew update
# Upgrade all Homebrew formulas
brew upgrade
# Or target specific PHP formulas
brew upgrade php php@8.2 php@8.3 php@8.4
Patching Environment Managers and Local Stacks
- Laravel Herd / MAMP / DDEV: Open the application’s version-management interface, refresh available versions, and install updated point releases for every PHP version installed within the application.
-
phpenvorasdf: Rebuild or reinstall the latest patched versions using the relevant plugin, for exampleasdf install php 8.3.36. -
Docker and containerized environments: Update base image tags in your
Dockerfileordocker-compose.ymlto patched point-release tags, then rebuild development containers:
docker compose build --no-cache
Dealing with Legacy PHP 5, 7, and 8.1 Projects
Many developers have older sites running PHP 8.1, PHP 7.x, or even legacy PHP 5 codebases. Because these branches are past end-of-life, official upstream security patches are not available.
Leaving unused, unpatched runtimes installed creates an avoidable exposure. If you do not need to run an old project, contain it:
Remove unused interpreters. If you no longer run a legacy PHP version, uninstall that binary from Homebrew or your version manager. Unused PHP source files pose little direct runtime risk, but remove or isolate them if any tooling may parse, serve, or execute them.
Quarantine archive folders. Move legacy projects out of active development paths, for example to
~/Archive/Legacy-PHP/. Ensure local development servers such as Valet or Herd are not configured to serve or scan that directory.Containerize execution when necessary. If you must occasionally run an old project, use an isolated Docker container with no host-volume bindings to sensitive directories, and shut it down after use.
Do not leave local servers running. Stop PHP built-in servers, Valet/Herd services, MAMP, and development containers when they are not needed.
Auditing every installed PHP runtime and isolating unsupported legacy code reduces the chance that a forgotten background process becomes an exposure window.
Defence in Depth
Some general tips for reducing your attack surface in general are worth keeping in mind, for the next uncovered flaw that will almost certainly be revealed in the future.
- Run local PHP development servers under a normal, least-privileged user account rather than granting them broad system permissions.
- Do not permanently grant Microphone, Camera, or Screen Recording permission to Terminal, iTerm, VS Code, or other broad development environments unless you specifically need it.
- Avoid running PHP development servers as launch daemons with inherited privacy permissions. This will help prevent an always-on attack vector.
- On Linux, avoid adding development users or service accounts to
audio/videounless required. - Treat any local service that executes shell commands as capable of using whatever hardware and permissions its process context already has.
Top comments (0)