Like a cracked pressure valve that only blows when one exact pipeline is carrying user traffic
Tenable plugin 158133 maps to upstream PHP 7.4.0 through 7.4.27 missing the fix in 7.4.28 for CVE-2021-21708, a CWE-416 use-after-free in the filter extension. The vulnerable path is not "any PHP request"; it is code that uses FILTER_VALIDATE_FLOAT with min_range or max_range on attacker-controlled input, where a failed validation can crash php-fpm and *potentially* corrupt memory further.
The vendor-style scoring is too hot for enterprise prioritization because reachability is app-specific, not runtime-global. A scanner sees an old PHP version and screams; reality is you still need an exposed application route, user-controlled numeric input, and the exact filter/options pattern before the bug even starts to matter, and there is no strong evidence this became a broadly weaponized internet RCE.
4 steps from start to impact.
Reach a PHP endpoint that accepts numeric user input
curl, Burp Suite, or an application-specific client against a PHP-backed route. This is only interesting if the endpoint forwards attacker-controlled values into server-side input validation logic rather than rejecting them at the framework or business-logic layer.- Internet or internal access to a PHP application endpoint
- Attacker-controlled parameter reaches PHP validation code
- Many PHP deployments are not internet-exposed
- A lot of applications never use this filter path for user data
- Reverse proxies, API gateways, and framework validators often normalize or reject input earlier
FILTER_VALIDATE_FLOAT with range options is actually used.Trigger the narrow filter bug
filter_var() or equivalent use of FILTER_VALIDATE_FLOAT with min_range/max_range. The public reproducer in bug #81708 shows a crafted request causing php-fpm to crash when the validation fails and the engine later touches freed memory.- Application code uses
FILTER_VALIDATE_FLOAT - The call includes
min_rangeormax_rangeoptions - The attacker can supply an input that fails validation in the right way
- This is a *specific* programming pattern, not a generic PHP request bug
- Some frameworks use custom validation layers instead of PHP's filter extension
- Input canonicalization can stop the exact failing condition
php-fpm restarts, and core dumps are better indicators than perimeter tooling.Convert crash into impact
502 responses, and pool restarts. Moving from crash to controlled memory corruption or RCE is materially harder and target-dependent, especially on hardened builds, modern allocators, and isolated php-fpm pools.- Vulnerable PHP worker processes attacker requests
- Memory layout is favorable enough for post-UAF control
- Public reporting shows a crash PoC more clearly than a reliable RCE exploit
- Exploit reliability depends on build flags, allocator behavior, and surrounding code
- EDR, crash monitoring, and service supervisors often surface repeated attempts quickly
php-fpm crashes, worker restarts, SIGSEGV, and abrupt 502 spikes. EDR and host telemetry usually catch the noisy failure mode even when they cannot label the CVE.Escalate from one app worker to broader compromise
- Successful memory-corruption exploitation beyond a crash
- Weak process isolation or over-privileged web runtime
- Per-pool identities and containerization sharply reduce follow-on value
- Secrets management, read-only containers, and noexec mounts limit post-exploitation
- Lateral movement still requires second-stage wins
The supporting signals.
| In-the-wild status | No authoritative active-exploitation signal found in reviewed sources. CVE-2021-21708 is not present in the current CISA KEV catalog. |
|---|---|
| Public PoC / exploit | Yes, but mostly as a crash reproducer, not a proven reliable internet RCE. Bug PHP #81708 links reporter dukk's repo MrdUkk/php-sigsegv and a patch gist by cmb. |
| EPSS | Bitsight currently shows EPSS at 0.26 with 49th percentile for this CVE, which is middle-of-the-pack rather than a breakout exploit favorite. Treat that as another sign this is not a hot internet exploitation priority. |
| KEV status | Not KEV-listed. No CISA date-added or due date applies. |
| CVSS and what it implies | NVD scores it 9.8 / CRITICAL with CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H, while the PHP CNA score is 8.2 / HIGH. Both assume network reachability, but neither score captures the biggest real-world limiter: the vulnerable call pattern is not universal across PHP apps. |
| Affected versions | Upstream affected ranges are 7.4.0-7.4.27, 8.0.0-8.0.15, and 8.1.0-8.1.2 per NVD. For this Tenable plugin, the slice you care about is PHP 7.4.x < 7.4.28. |
| Fixed versions and distro backports | Upstream fix is 7.4.28. Distro packages may be patched without showing 7.4.28; for example Ubuntu 20.04 fixed php7.4-* via 7.4.3-4ubuntu2.9 in USN-5303-1. Do not trust upstream-style version compares on vendor-maintained distro builds. |
| Scanning / exposure reality | Bitsight shows 502,306 total observations over the prior 30 days for this CVE context, which tells you the PHP footprint is large. But version fingerprinting is incomplete and noisy because many sites suppress or rewrite X-Powered-By, so exposure counts overstate certainty and understate reachability at the same time. |
| Disclosure timeline | Reported to PHP on 2022-01-30, fixed in source on 2022-02-14, released in 7.4.28 on 2022-02-17, and published in NVD on 2022-02-27. |
| Researcher / reporting org | Reported by dukk / softdev.online in PHP bug #81708; fix work credited in the bug and linked commits to PHP maintainers including cmb and smalyshev. |
noisgate verdict.
The decisive factor is code-path reachability: an attacker does not win by merely finding old PHP, they need an exposed application route that actually uses FILTER_VALIDATE_FLOAT with range options on attacker input. That narrows the exposed population so much that a runtime-level CRITICAL label is misleading for fleet-wide patch prioritization.
Why this verdict
- Specific code path required: this is not "send one HTTP request to any PHP box." The request has to land in app code using
FILTER_VALIDATE_FLOATplusmin_rangeormax_range. - Posture implies narrower exposure: requiring that exact validation pattern means many PHP installs flagged by the scanner are not truly reachable for this bug, which is a strong downward adjustment from a runtime-wide
CRITICALbaseline. - Crash is easy; reliable RCE is not: public material demonstrates a crash PoC cleanly, but not a mature, commonly used exploit chain that turns this into dependable remote code execution at scale.
- No KEV or strong exploitation telemetry: without CISA KEV inclusion or credible campaign reporting, there is no practical amplifier that would keep this in the top patch queue.
- Still not LOW: PHP is heavily deployed and internet-facing apps are common, so if you *do* have the bad call pattern on exposed routes, the impact can be real.
Why not higher?
Because the attack chain has compounding friction at the very first technical gate: the attacker needs more than a vulnerable PHP version, they need a reachable application endpoint that uses one specific filter/options combination. That requirement collapses the real exposed population and makes mass exploitation less plausible than the NVD vector suggests.
Why not lower?
Because this is still memory corruption in a ubiquitous runtime, and the easy failure mode is an externally triggerable service crash on exposed applications. If you run legacy PHP 7.4 apps with ad hoc input validation, you cannot dismiss it as pure scanner noise.
What to do — in priority order.
- Inventory real 7.4 exposure — Separate upstream/self-built PHP from distro-backported packages before you panic. For a
MEDIUMverdict there is no mitigation SLA — go straight to the 365-day remediation window, but do this scoping work now so you know which hosts are truly carrying unfixed upstream code. - Grep for the risky call pattern — Search codebases for
FILTER_VALIDATE_FLOATcombined withmin_rangeormax_range, then map those files to internet-facing routes. There is no mitigation SLA — go straight to the 365-day remediation window for this severity, but this is the fastest way to cut false positives and identify the handful of apps that deserve earlier change review. - Harden PHP-FPM isolation — Run separate pools or containers per app, use least-privilege service accounts, and keep secrets out of the web root so a worker crash or even partial compromise has less follow-on value. For
MEDIUM, there is no mitigation SLA — go straight to the 365-day remediation window, but pool isolation materially lowers blast radius immediately. - Reduce input attack surface — Where practical, replace this validation pattern with framework-native validators or strict type parsing that does not depend on the vulnerable filter path, especially in public APIs. There is no mitigation SLA — go straight to the 365-day remediation window, so fold this into normal app maintenance unless you confirm reachable call sites.
- Suppress version leakage — Disable unnecessary
X-Powered-Bydisclosure and other version banners so external scanners and opportunistic attackers get less free targeting data. This does not fix the bug, but it slightly raises attacker reconnaissance cost while you work through the remediation window.
Generic WAF signaturesdo not reliably help because the bug is triggered by an internal PHP filter/validation pattern, not by a stable exploit string you can match at the edge.Version-only triageis not enough because distro backports can be patched while still reporting older-looking package versions, and self-built php.net binaries can be unpatched even when the app stack is otherwise well-managed.Network ACLs aloneare weak compensation if the vulnerable route is already supposed to be internet-facing for customers or APIs.
Crowdsourced verification payload.
Run this on the target Linux host that actually executes PHP, not from your scanner. Invoke it as sudo bash verify-cve-2021-21708.sh /var/www/html (the docroot argument is optional but recommended); it needs read access to the PHP binary, package database, and app code if you want the reachability grep.
#!/usr/bin/env bash
# verify-cve-2021-21708.sh
# Check whether this host is likely vulnerable to CVE-2021-21708 (PHP FILTER_VALIDATE_FLOAT UAF)
# Exit codes: 0=PATCHED, 1=VULNERABLE, 2=UNKNOWN, 3=usage/runtime error
set -u
DOCROOT="${1:-}"
have_cmd() { command -v "$1" >/dev/null 2>&1; }
ver_lt() {
# returns 0 if $1 < $2 using sort -V
[ "$(printf '%s\n' "$1" "$2" | sort -V | head -n1)" != "$2" ]
}
ver_ge() {
# returns 0 if $1 >= $2
! ver_lt "$1" "$2"
}
php_ver=""
if have_cmd php; then
php_ver="$(php -r 'echo PHP_MAJOR_VERSION,".",PHP_MINOR_VERSION,".",PHP_RELEASE_VERSION;' 2>/dev/null || true)"
fi
if [ -z "$php_ver" ]; then
echo "UNKNOWN"
exit 2
fi
# Check for Ubuntu focal backport explicitly, because package versions may look older than upstream 7.4.28.
if have_cmd dpkg-query && dpkg-query -W -f='${Status}' php7.4-common 2>/dev/null | grep -q 'install ok installed'; then
pkg_ver="$(dpkg-query -W -f='${Version}' php7.4-common 2>/dev/null || true)"
if [ -n "$pkg_ver" ] && have_cmd dpkg; then
if dpkg --compare-versions "$pkg_ver" ge '7.4.3-4ubuntu2.9'; then
echo "PATCHED"
exit 0
fi
fi
fi
case "$php_ver" in
7.4.*)
if ver_ge "$php_ver" "7.4.28"; then
echo "PATCHED"
exit 0
fi
;;
8.0.*)
if ver_ge "$php_ver" "8.0.16"; then
echo "PATCHED"
exit 0
fi
;;
8.1.*)
if ver_ge "$php_ver" "8.1.3"; then
echo "PATCHED"
exit 0
fi
;;
*)
# Other branches are outside this CVE's affected ranges
echo "PATCHED"
exit 0
;;
esac
# If we get here, the runtime version is in an affected upstream range.
# Distros other than Ubuntu may have backports we cannot safely infer.
if [ -n "$DOCROOT" ] && [ -d "$DOCROOT" ]; then
tmp1="$(mktemp)"
tmp2="$(mktemp)"
trap 'rm -f "$tmp1" "$tmp2"' EXIT
grep -RIl --exclude-dir=.git --exclude-dir=vendor --exclude='*.min.*' 'FILTER_VALIDATE_FLOAT' "$DOCROOT" 2>/dev/null | sort -u > "$tmp1" || true
grep -RIl --exclude-dir=.git --exclude-dir=vendor --exclude='*.min.*' -E 'min_range|max_range' "$DOCROOT" 2>/dev/null | sort -u > "$tmp2" || true
if [ -s "$tmp1" ] && [ -s "$tmp2" ] && comm -12 "$tmp1" "$tmp2" | grep -q .; then
echo "VULNERABLE"
exit 1
fi
# Affected runtime, but no obvious reachable code pattern found in provided docroot.
echo "UNKNOWN"
exit 2
fi
# Affected runtime version, but without source inspection we cannot prove reachability.
echo "UNKNOWN"
exit 2
If you remember one thing.
PHP 7.4.x < 7.4.28 hit as equal: split your fleet into distro-backported packages versus self-built/upstream PHP, then grep exposed applications for FILTER_VALIDATE_FLOAT with min_range/max_range. This lands as MEDIUM, so there is noisgate mitigation SLA — go straight to the 365-day remediation window — but use that year intelligently: verify which apps are actually reachable, move any unsupported php.net 7.4 installs to a supported branch, and complete the actual vendor upgrade or equivalent maintained backport within the noisgate remediation SLA of 365 days.Sources
What defenders are saying.
Crowdsourced verification outputs.
Results submitted by users who ran the verification payload against their environment.