← Back to Feed CACHED · 2026-05-17 09:42:19 · cache_key CVE-2025-29912
tenable:158133 · CWE-416 · Disclosed 2022-02-17

PHP 7

ASSESSED — NOISGATE V0.5
Vendor
Reassessed
Verdict:
01 · The Real Story

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.

"This is a real memory bug, but it only matters where app code feeds attacker input into one narrow PHP filter path."
02 · The Attack Path

4 steps from start to impact.

STEP 01

Reach a PHP endpoint that accepts numeric user input

The attacker starts with a normal web request using 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.
Conditions required:
  • Internet or internal access to a PHP application endpoint
  • Attacker-controlled parameter reaches PHP validation code
Where this breaks in practice:
  • 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
Detection/coverage: Network scanners can usually detect the PHP version, but they cannot prove the vulnerable code path is reachable. DAST may hit the endpoint, yet still miss whether FILTER_VALIDATE_FLOAT with range options is actually used.
STEP 02

Trigger the narrow filter bug

The vulnerable code path is 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.
Conditions required:
  • Application code uses FILTER_VALIDATE_FLOAT
  • The call includes min_range or max_range options
  • The attacker can supply an input that fails validation in the right way
Where this breaks in practice:
  • 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
Detection/coverage: SAST or code grep can find likely call sites. Runtime crash telemetry, php-fpm restarts, and core dumps are better indicators than perimeter tooling.
STEP 03

Convert crash into impact

The easy outcome is denial of service: worker crash, 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.
Conditions required:
  • Vulnerable PHP worker processes attacker requests
  • Memory layout is favorable enough for post-UAF control
Where this breaks in practice:
  • 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
Detection/coverage: Expect 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.
STEP 04

Escalate from one app worker to broader compromise

Even with code execution in the PHP worker, the attacker still lands in the confinement of that web app's account, pool, and filesystem permissions. Real enterprise blast radius depends on whether the app pool is isolated, whether secrets sit on disk, and whether the host runs multiple sites under the same runtime identity.
Conditions required:
  • Successful memory-corruption exploitation beyond a crash
  • Weak process isolation or over-privileged web runtime
Where this breaks in practice:
  • 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
Detection/coverage: Post-exploitation looks like generic webshell or process-abuse activity; EDR, FIM, and credential-access detections matter more than a CVE-specific signature.
03 · Intelligence Metadata

The supporting signals.

In-the-wild statusNo authoritative active-exploitation signal found in reviewed sources. CVE-2021-21708 is not present in the current CISA KEV catalog.
Public PoC / exploitYes, 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.
EPSSBitsight 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 statusNot KEV-listed. No CISA date-added or due date applies.
CVSS and what it impliesNVD 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 versionsUpstream 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 backportsUpstream 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 realityBitsight 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 timelineReported 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 orgReported by dukk / softdev.online in PHP bug #81708; fix work credited in the bug and linked commits to PHP maintainers including cmb and smalyshev.
04 · The Call

noisgate verdict.

Final Verdict
= UNCHANGED to MEDIUM (5.9/10)

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.

HIGH Version mapping from Tenable plugin `158133` to upstream PHP `7.4.x < 7.4.28`
MEDIUM Reassessment that practical severity is lower than vendor/NVD scoring because app-level reachability dominates risk
MEDIUM Absence of broad in-the-wild exploitation evidence in reviewed public sources

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_FLOAT plus min_range or max_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 CRITICAL baseline.
  • 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.

05 · Compensating Control

What to do — in priority order.

  1. Inventory real 7.4 exposure — Separate upstream/self-built PHP from distro-backported packages before you panic. For a MEDIUM verdict 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.
  2. Grep for the risky call pattern — Search codebases for FILTER_VALIDATE_FLOAT combined with min_range or max_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.
  3. 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.
  4. 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.
  5. Suppress version leakage — Disable unnecessary X-Powered-By disclosure 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.
What doesn't work
  • Generic WAF signatures do 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 triage is 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 alone are weak compensation if the vulnerable route is already supposed to be internet-facing for customers or APIs.
06 · Verification

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.

noisgate-verify.sh
BASHREAD-ONLYSAFE
#!/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
07 · Bottom Line

If you remember one thing.

TL;DR
By Monday morning, stop treating every 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

  1. Tenable Nessus Plugin 158133
  2. PHP 7.4.28 release announcement
  3. PHP 7 changelog entry for 7.4.28
  4. NVD CVE-2021-21708
  5. PHP bug #81708
  6. Ubuntu USN-5303-1
  7. CISA Known Exploited Vulnerabilities Catalog
  8. Bitsight observation footprint for CVE-2021-21708
Peer Review

What defenders are saying.

Submit a review attribution: handle + country only
0 flags selected · stored anonymously
Validation Results

Crowdsourced verification outputs.

Results submitted by users who ran the verification payload against their environment.