This is a crate of old glassware, not a single primed grenade
Tenable plugin 121602 fires when a remote banner shows PHP 5.6.x earlier than 5.6.40. The bundle covers six different flaws fixed in the 5.6.40 security release, including memory-safety bugs in gd, xmlrpc, PHAR, and mbstring/oniguruma. Affected upstream range for this finding is straightforward: PHP 5.6.0 through 5.6.39.
The vendor scoring is too hot for most enterprise reality. Tenable currently labels the plugin Critical because it inherits a 9.8 vector from CVE-2019-9023, but the actual attack surface is fragmented: an attacker generally needs a reachable vulnerable code path in a specific extension and attacker-controlled malformed input, not just a socket to a PHP web server. That is still bad on public legacy web stacks, but it is not the same thing as a broadly reliable pre-auth RCE like a modern edge appliance bug.
4 steps from start to impact.
Find a host still exposing pre-5.6.40 PHP
PHP/5.6.39 or older, the host lands in scope. Internet telemetry shows this branch still exists in the wild, which means the population is not hypothetical. Tooling here is basic recon: curl, Shodan-style header collection, or the Nessus plugin itself.- Target exposes PHP version headers or another remotely readable banner
- Server is actually running upstream PHP 5.6.0-5.6.39 rather than a backported distro build masked behind packaging
- Many hardened stacks suppress
X-Powered-Byand PHP banners - Banner/version checks cannot prove that a vulnerable extension is enabled or reachable
- Distro backports can make version-only findings noisy if the package naming/versioning is non-upstream
Reach a vulnerable parser or extension path
xmlrpc_decode(), PHAR filename parsing, mb_ereg* processing with invalid multibyte regex data, or a GD image-processing path. Weaponization would typically use a custom HTTP client plus crafted payloads derived from the public bug reports and patches.- The application uses one of the affected extensions (
xmlrpc,phar,mbstring,gd) - Untrusted attacker input reaches that extension's parsing function
- The app does not sanitize, reject, or normalize the malformed payload first
- Many PHP apps never expose XML-RPC decoding or attacker-controlled PHAR parsing
- Some affected pieces are optional or workload-specific rather than universal
- Modern WAFs, upload validation, and application-layer checks often block malformed payloads before PHP reaches the vulnerable code
Trigger memory-safety failure
C:H/I:H/A:H, but in the field these bugs often collapse into crash, info leak, or unstable behavior unless the attacker has a carefully matched exploit chain for the exact build and memory layout.- Precise malformed input is accepted by the target function
- Target build characteristics make the bug reliably triggerable
- Process isolation and hardening do not blunt post-crash impact
- Memory corruption reliability on old PHP stacks is highly environment-specific
- FPM pool isolation, seccomp/chrooting, and container boundaries can limit blast radius
- Several bugs in this bundle are over-reads, which are materially less useful than turnkey RCE
Turn crash or read primitive into meaningful impact
9.8 overstates reality for defenders scheduling patches across thousands of hosts.- Useful secrets or execution context are exposed in the PHP worker
- The vulnerable web app has access to sensitive data or adjacent internal systems
- Host controls do not stop payload execution or follow-on actions
- Many PHP workers run with constrained service accounts and limited local privileges
- No strong evidence surfaced for mass exploitation campaigns against this exact 5.6.40 bundle
- Even a successful app-tier hit may stay confined to one site or pool rather than the whole estate
The supporting signals.
| Vendor label | Tenable currently shows plugin 121602 as Critical, with CVSS v3 9.8 and VPR 6.7 / Medium. Your note of 'HIGH (?)' likely reflects older CVSSv2 handling (7.5 / High) rather than the plugin's present top-line label. |
|---|---|
| In-the-wild status | I found no strong evidence of active exploitation campaigns tied specifically to this 5.6.40 vulnerability bundle, and I did not find a CISA KEV entry for the listed member CVEs during this review. Confidence is medium, because absence-of-evidence on old library bugs is not the same as proof of safety. |
| Proof-of-concept availability | Public bug reports and patches are available via bugs.php.net, and Tenable marks the plugin as Exploit Available: true. That said, I did not find a mainstream, widely used exploit chain equivalent to a Metasploit-grade internet worm for this bundle. |
| Representative EPSS | The bundle is mixed. Tenable's CVE page shows CVE-2019-9021 at EPSS 0.25106 (~25.1%), while public trackers put CVE-2019-9021 around 30.03% / ~97th percentile; by contrast CVE-2019-9023 has been shown publicly around 0.37% / 69.5th percentile. Read that as 'some curiosity, not mass exploitation gravity.' |
| KEV status | Not KEV-listed in this assessment. No KEV due date pressure was found for the component CVEs. |
| CVSS vector reality check | The inherited vector is CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H, which assumes worst-case impact from a remotely reachable primitive. In practice, reachability is the missing variable: you need the right extension, the right app behavior, and attacker-controlled malformed input. |
| Affected versions | For this Tenable finding: PHP 5.6.x < 5.6.40. The underlying CVEs also affected early 7.0/7.1/7.2/7.3 builds, but that is adjacent context, not the exact banner rule here. |
| Fixed versions | Upstream fix for this finding is PHP 5.6.40. Ubuntu's advisory also shows backported fixes for maintained distro branches such as php7.0 7.0.33-0ubuntu0.16.04.2 and php7.2 7.2.15-0ubuntu0.18.10.1; distro package status can therefore diverge from naive upstream version logic. |
| Exposure data | Bitsight's Groma page for PHP 5.6.39 reports 13,809 total public observations over the prior 30 days, with the US at 2,669 observations. So the branch is still visible on the internet, but telemetry is for a version family, not proof that your specific app exposes the vulnerable function path. |
| Disclosure and release timing | PHP 5.6.40 was released as a security release and marked as the last scheduled release of the PHP 5.6 branch. Tenable lists patch publication and vulnerability publication for this plugin as 2019-01-10. |
noisgate verdict.
The decisive factor is code-path reachability: this is not one generic, reliably weaponized pre-auth RCE against every PHP site that answers on port 80. It stays HIGH because the affected branch is internet-exposed in the real world and multiple parser bugs sit in common web-stack extensions, but it drops below Critical because the exploit chain narrows hard once you ask whether the application actually reaches xmlrpc, PHAR, mbstring, or gd with attacker-controlled malformed input.
Why this verdict
- Start at 9.8, then subtract reachability debt: the vendor score inherits worst-case
CVE-2019-9023, but an attacker still needs a live vulnerable parser path, not just network access to a PHP host. - Extension and app prerequisites matter:
xmlrpc,PHAR,mbstring, andgdare not equally exposed across real estates; each missing extension or unreachable function is compounding downward pressure. - No strong exploitation amplifier: I found no KEV listing and no compelling evidence of broad in-the-wild campaigns for this exact bundle, which keeps it out of Critical even though the branch is old and public-facing.
Why not higher?
Because this is not a turnkey edge exploit with a universal request pattern. The highest score in the bundle assumes best-case attacker reachability and worst-case impact, but the actual attack path usually requires a specific feature, a specific input sink, and often fragile memory-corruption behavior.
Why not lower?
Because the population is still externally exposed and the branch is legacy. If you do have public PHP 5.6.39 or older serving real traffic, the odds are non-trivial that at least one app path accepts attacker-controlled data into a risky extension, and the consequence sits on an internet-facing web tier.
What to do — in priority order.
- Constrain exposure — Put affected sites behind a reverse proxy, VPN, allowlist, or authenticated gateway wherever business permits. For a HIGH verdict, deploy this exposure reduction within 30 days if patching is not immediate, because the main risk amplifier here is internet reachability.
- Disable unused PHP extensions — Turn off
xmlrpc, unnecessary image-processing modules, and any unused archive or multibyte features on affected pools where the application does not need them. This directly removes the vulnerable code paths and should be completed within 30 days on systems that must temporarily remain on the old branch. - Tighten untrusted input handling — Block or heavily validate hostile uploads, malformed XML-RPC payloads, and unexpected multibyte regex input at the application and reverse-proxy layers. This is a compensating reduction, not a fix, and it belongs within 30 days for internet-facing workloads.
- Isolate PHP workers — Run affected pools with least-privilege service accounts, separate FPM pools per app, and filesystem/network segmentation so a parser fault cannot pivot cleanly. That containment work should land within 30 days where legacy PHP cannot yet be retired.
expose_php = Offor hidingX-Powered-Bydoes not fix the vulnerable code; it only makes banner-based discovery noisier.- A generic perimeter WAF does not reliably stop PHAR, regex, or image-parser memory bugs unless you have very specific blocking logic for the exact app path.
- Upgrading only the web application while leaving the PHP runtime on
5.6.39or older does not remove these engine/extension flaws.
Crowdsourced verification payload.
Run this on the target Linux host that serves the PHP application, not from your auditor workstation. Invoke it as bash verify_php_121602.sh /usr/bin/php; it needs only local shell access and no root, though package-path access and accurate php binary selection matter if multiple versions are installed.
#!/usr/bin/env bash
# verify_php_121602.sh
# Check for Tenable plugin 121602 condition: PHP 5.6.x < 5.6.40
# Output: VULNERABLE / PATCHED / UNKNOWN
# Exit codes: 0=PATCHED, 1=VULNERABLE, 2=UNKNOWN
set -u
PHP_BIN="${1:-php}"
if ! command -v "$PHP_BIN" >/dev/null 2>&1; then
echo "UNKNOWN - PHP binary not found: $PHP_BIN"
exit 2
fi
RAW_VER="$($PHP_BIN -r 'echo PHP_VERSION;' 2>/dev/null)"
if [ -z "$RAW_VER" ]; then
echo "UNKNOWN - unable to read PHP_VERSION from $PHP_BIN"
exit 2
fi
# Normalize to first x.y.z occurrence
VER="$(printf '%s' "$RAW_VER" | grep -Eo '^[0-9]+\.[0-9]+\.[0-9]+' || true)"
if [ -z "$VER" ]; then
echo "UNKNOWN - unparsable PHP version: $RAW_VER"
exit 2
fi
# version_ge A B => true if A >= B
version_ge() {
[ "$(printf '%s\n%s\n' "$1" "$2" | sort -V | tail -n1)" = "$1" ]
}
MAJOR="${VER%%.*}"
REST="${VER#*.}"
MINOR="${REST%%.*}"
PATCH="${VER##*.}"
# Primary logic for this exact Tenable finding
if [ "$MAJOR" = "5" ] && [ "$MINOR" = "6" ]; then
if version_ge "$VER" "5.6.40"; then
echo "PATCHED - PHP version $VER is not affected by plugin 121602's upstream rule (5.6.x < 5.6.40)"
exit 0
else
echo "VULNERABLE - PHP version $VER matches plugin 121602 condition (5.6.x < 5.6.40)"
exit 1
fi
fi
# If version is newer than 5.6.x, this specific plugin rule does not apply.
if version_ge "$VER" "5.6.40"; then
echo "PATCHED - PHP version $VER is outside plugin 121602's affected upstream range"
exit 0
fi
# Older non-5.6 branches are ambiguous for this exact plugin signature.
echo "UNKNOWN - PHP version $VER does not match the exact 5.6.x rule; verify distro backports and branch-specific advisories separately"
exit 2
If you remember one thing.
PHP 5.6.39 or older, apply exposure reduction or extension-level compensating controls under the noisgate mitigation SLA within 30 days, and plan the actual runtime upgrade under the noisgate remediation SLA within 180 days; in practice, don't stop at 5.6.40 if you can avoid it, because 5.6.40 was the last scheduled release of the branch and long-term risk remains lifecycle-driven even after this specific Tenable finding is cleared.Sources
What defenders are saying.
Crowdsourced verification outputs.
Results submitted by users who ran the verification payload against their environment.