This is a cracked peephole, not a kicked-in front door
CVE-2025-14177 is an uninitialized heap-memory disclosure in PHP getimagesize() affecting 8.1.x < 8.1.34, 8.2.x < 8.2.30, 8.3.x < 8.3.29, 8.4.x < 8.4.16, and 8.5.x < 8.5.1. The bug sits in php_read_stream_all_chunks(): during multi-chunk reads, the buffer pointer is not advanced, so later chunks overwrite the beginning while the tail remains uninitialized. In the vulnerable path, that stale heap content can land in returned JPEG APPn metadata such as APP1.
Vendor/NVD 7.5 HIGH overstates enterprise urgency because the real exploit chain is much narrower than a raw network CVSS suggests. An attacker does not get code execution, integrity loss, or a crash; they need an application that parses attacker-controlled images with getimagesize(..., $info), hits a multi-chunk read condition, and then exposes the returned metadata back to them or into another readable channel. That's a real bug and worth patching, but for most fleets it is backlog-worthy rather than fire-drill-worthy.
4 steps from start to impact.
Reach an upload or image-inspection code path
curl; the bug lives in the server-side PHP runtime, not in a network-facing daemon with its own banner.- Unauthenticated or authenticated access to an app feature that accepts user-controlled image content
- Backend is running a vulnerable PHP branch
- Application actually invokes
getimagesize()on attacker data
- Many PHP apps only check MIME types or use other libraries
- A large share of internal PHP services never process untrusted images at all
- Internet scanning cannot tell you whether a host reaches this exact function
Force the narrow vulnerable call shape
getimagesize(..., $info) collects APPn metadata and the read happens in multi-chunk mode. Upstream's PoC uses php://filter to guarantee chunking and demonstrate the bug, while the advisory notes exploitation on normal images is possible if the attacker can predict stream chunk behavior. The practical weapon is the upstream poc.php pattern plus a JPEG with a large APP1 segment.- The app requests the second
$infooutput parameter or otherwise consumes APPn metadata - Input is processed in a multi-chunk read scenario
- JPEG/APPn metadata survives to the vulnerable parsing path
- Code that only asks for width/height without using
$infodramatically reduces value - Multi-chunk reproduction is not guaranteed on every deployment
- CISA ADP marks exploitation as
pocandautomatable: no
getimagesize( with a second argument is more useful than perimeter scanning. Runtime telemetry rarely has a signature unless you log suspicious oversized APP segments.Harvest leaked heap bytes from returned metadata
APP1 or similar APPn content containing uninitialized heap bytes from the worker process. That can expose fragments of prior in-memory data such as tokens, request material, or other application residue. The tool here is the application's own response path: JSON, logs, previews, admin panels, or stored metadata that echoes attacker-controlled image metadata back out.- Application returns, stores, logs, or otherwise exposes the APPn metadata to an attacker-readable channel
- Leaked bytes contain something valuable in that worker's heap at that moment
- Many apps never return raw APPn metadata to end users
- Leak quality is inconsistent and partial, not a guaranteed secret dump
- Short-lived FPM workers and memory churn reduce repeatability
php://filter or repeated crafted JPEG uploads during testing.Use the leak as a chain helper, not a standalone takeover
- A second objective exists that benefits from partial memory disclosure
- The leaked data includes something actionable
- No built-in RCE primitive
- No integrity or availability impact
- Post-leak operationalization is highly environment-specific
The supporting signals.
| In-the-wild status | No authoritative public evidence of active exploitation found. Not listed in CISA KEV as of the catalog pages reviewed. |
|---|---|
| PoC availability | Yes. The upstream GitHub advisory includes a minimal PoC (poc.php) and the fix commit adds regression test gh20584.phpt demonstrating the vulnerable path. |
| EPSS | 0.00025 from your intel — effectively background-noise probability, which fits the narrow and non-automatable chain. |
| KEV / exploitation metadata | KEV: No. CISA ADP/Vulnrichment on the CVE record labels exploitation as poc, automatable: no, technical impact: partial. |
| CVSS reality check | NVD shows CVSS v3.1 7.5 HIGH, but the PHP CNA's CVSS v4.0 is only 6.3 MEDIUM because it recognizes high complexity and attack requirements present. |
| Affected versions | 8.1.x < 8.1.34, 8.2.x < 8.2.30, 8.3.x < 8.3.29, 8.4.x < 8.4.16, 8.5.x < 8.5.1. |
| Fixed versions / backports | Upstream fixed in 8.1.34, 8.2.30, 8.3.29, 8.4.16, 8.5.1. Distros backport: Ubuntu 24.04 fixed php8.3 8.3.6-0ubuntu0.24.04.6; Ubuntu 25.04 fixed php8.4 8.4.5-1ubuntu1.2; Debian bookworm fixed php8.2 8.2.30-1~deb12u1; Debian trixie fixed php8.4 8.4.16-1~deb13u1. |
| Exposure / scanning reality | There is no decision-grade internet fingerprint for this bug. Shodan/Censys-style counts for 'PHP' do not prove vulnerable runtime version, app reachability, getimagesize(..., $info) use, or metadata exposure. |
| Disclosure timeline | Public advisory and releases landed 2025-12-18; CVE/NVD publication followed on 2025-12-27; NVD analysis update shows 2026-01-08. |
| Reporter | Reported by Nikita Sveshnikov (Positive Technologies). |
noisgate verdict.
The decisive factor is that exploitability depends on a very specific application behavior: attacker-controlled image parsing through getimagesize(..., $info) with a multi-chunk read and an attacker-readable metadata sink. That collapses the reachable population well below what a generic AV:N/PR:N/UI:N CVSS line implies, and the impact remains confidentiality-only and partial even when hit.
Why this verdict
- Downgrade for app-path dependence: the attacker needs more than 'a PHP server on the internet' — they need a reachable code path that passes attacker content into
getimagesize(). - Downgrade again for narrow API usage: meaningful leakage depends on the
$infometadata path, not just width/height probing, which materially shrinks the exposed population. - Downgrade again for exploit friction: multi-chunk behavior is a prerequisite; the CVE record itself reflects this with CVSS v4
AC:HandAT:P, and CISA ADP marks itautomatable: no. - Downgrade again for blast radius: this is an info leak, not RCE; no integrity or availability impact, and the attacker still needs a way to read the leaked APPn data back out.
- Do not downgrade to ignore: upstream published a PoC, the bug is in a ubiquitous runtime, and any public-facing upload workflow that reflects metadata turns this into a real confidentiality issue.
Why not higher?
There is no native code-execution, overwrite, or denial-of-service primitive here. More importantly, the attack chain stacks multiple real-world prerequisites — untrusted image handling, the right getimagesize() usage, multi-chunk conditions, and an exposed metadata sink — which means most vulnerable PHP installs are not directly exploitable in a useful way.
Why not lower?
This is still a genuine memory disclosure in a widely deployed runtime, not a theoretical lint finding. If you run internet-facing PHP applications that parse user uploads and surface EXIF/IPTC or APP metadata, an unauthenticated attacker may be able to extract process-memory residue without any prior foothold.
What to do — in priority order.
- Inventory
getimagesize()callers — Identify applications that callgetimagesize(on user-controlled content, especially code that supplies the second$infoparameter or stores/returns image metadata. For a LOW verdict there is no mitigation SLA, so do this as backlog hygiene while preparing the patch path. - Stop reflecting raw APP/EXIF/IPTC metadata — If your app returns or stores raw metadata from uploaded images, strip or discard it before user-visible output. This directly breaks the attacker-readable sink that makes the leak useful; with LOW severity, handle during normal engineering work before the remediation window closes.
- Constrain upload pathways — Gate image-processing endpoints behind auth where feasible, rate-limit anonymous uploads, and prefer transcoding or metadata stripping before downstream handling. This reduces reachable population and dampens repeated probing, but because the verdict is LOW, treat it as routine hardening rather than emergency mitigation.
- Patch to distro-fixed builds — Prefer vendor-backported distro packages over raw upstream comparisons when you are on Ubuntu/Debian/SUSE platform builds. This is the cleanest durable fix; for LOW, there is no mitigation SLA and the patch belongs in backlog remediation.
- A generic WAF will not reliably stop this because the bug is triggered inside normal image-processing logic, often on legitimate multipart uploads.
- EDR on the host may see the PHP process but usually cannot tell that returned APP1 bytes contain uninitialized heap data.
- Blocking obvious
php://filterstrings alone is insufficient; upstream states exploitation on normal images can still be possible with known chunk behavior. - Simply hiding the PHP version banner does nothing; the hard part for the attacker is the application code path, not the banner.
Crowdsourced verification payload.
Run this on the target PHP host as a local auditor or via your config-management/remote-exec tooling. Invoke it as bash check-cve-2025-14177.sh or bash check-cve-2025-14177.sh /usr/bin/php; no root is required for the basic version check, but package-manager backport detection works best with normal local package-query access.
#!/usr/bin/env bash
# check-cve-2025-14177.sh
# Detect likely exposure to CVE-2025-14177 on Linux hosts.
# Output: VULNERABLE / PATCHED / UNKNOWN
# Exit codes: 0=PATCHED, 1=VULNERABLE, 2=UNKNOWN
set -u
PHP_BIN="${1:-php}"
have() { command -v "$1" >/dev/null 2>&1; }
ver_lt() { [ "$(printf '%s\n%s\n' "$1" "$2" | sort -V | head -n1)" != "$2" ]; }
ver_ge() { ! ver_lt "$1" "$2"; }
emit() {
local status="$1"; shift
echo "$status: $*"
case "$status" in
PATCHED) exit 0 ;;
VULNERABLE) exit 1 ;;
*) exit 2 ;;
esac
}
if ! have "$PHP_BIN"; then
emit UNKNOWN "PHP binary '$PHP_BIN' not found"
fi
raw_ver="$($PHP_BIN -v 2>/dev/null | head -n1 || true)"
if [ -z "$raw_ver" ]; then
emit UNKNOWN "Unable to read PHP version from '$PHP_BIN -v'"
fi
# Extract first version-looking token, e.g. 8.3.6 or 8.3.6-0ubuntu0.24.04.6
full_token="$(printf '%s' "$raw_ver" | grep -Eo '[0-9]+\.[0-9]+\.[0-9]+([.-][A-Za-z0-9:+~._-]+)?' | head -n1)"
base_ver="$(printf '%s' "$full_token" | grep -Eo '^[0-9]+\.[0-9]+\.[0-9]+' | head -n1)"
if [ -z "$base_ver" ]; then
emit UNKNOWN "Could not parse version from: $raw_ver"
fi
major_minor="$(printf '%s' "$base_ver" | cut -d. -f1,2)"
# Distro backport checks first when package metadata is available.
check_dpkg() {
have dpkg-query || return 1
local pkg ver
for pkg in php php8.1 php8.2 php8.3 php8.4; do
ver="$(dpkg-query -W -f='${Version}' "$pkg" 2>/dev/null | head -n1)"
[ -n "$ver" ] || continue
case "$pkg:$ver" in
php8.3:8.3.6-0ubuntu0.24.04.6*|php8.4:8.4.5-1ubuntu1.2*|php8.4:8.4.11-1ubuntu1.1*|php8.2:8.2.30-1~deb12u1*|php8.2:8.2.31-1~deb12u1*|php8.4:8.4.16-1~deb13u1*|php8.4:8.4.21-1~deb13u1*|php7.4:7.4.33-1+deb11u11*)
emit PATCHED "Detected distro-fixed package $pkg $ver for CVE-2025-14177"
;;
esac
done
return 1
}
check_rpm() {
have rpm || return 1
local q
q="$(rpm -qa 2>/dev/null | grep -E '^php(|8\.[0-9]+)(-|$)' | sort | head -n20 || true)"
[ -n "$q" ] || return 1
# Conservative RPM logic: if the installed runtime reports an upstream-fixed base version, trust it.
return 1
}
check_dpkg || true
check_rpm || true
case "$major_minor" in
8.1)
if ver_lt "$base_ver" "8.1.34"; then
emit VULNERABLE "PHP $base_ver is in affected range for CVE-2025-14177 (< 8.1.34)"
else
emit PATCHED "PHP $base_ver is at or above upstream fix 8.1.34"
fi
;;
8.2)
if ver_lt "$base_ver" "8.2.30"; then
emit VULNERABLE "PHP $base_ver is in affected range for CVE-2025-14177 (< 8.2.30)"
else
emit PATCHED "PHP $base_ver is at or above upstream fix 8.2.30"
fi
;;
8.3)
if ver_lt "$base_ver" "8.3.29"; then
emit VULNERABLE "PHP $base_ver is in affected range for CVE-2025-14177 (< 8.3.29)"
else
emit PATCHED "PHP $base_ver is at or above upstream fix 8.3.29"
fi
;;
8.4)
if ver_lt "$base_ver" "8.4.16"; then
emit VULNERABLE "PHP $base_ver is in affected range for CVE-2025-14177 (< 8.4.16)"
else
emit PATCHED "PHP $base_ver is at or above upstream fix 8.4.16"
fi
;;
8.5)
if ver_lt "$base_ver" "8.5.1"; then
emit VULNERABLE "PHP $base_ver is in affected range for CVE-2025-14177 (< 8.5.1)"
else
emit PATCHED "PHP $base_ver is at or above upstream fix 8.5.1"
fi
;;
7.4|8.0)
emit PATCHED "Installed branch $major_minor is not in the affected upstream version ranges for this CVE"
;;
*)
emit UNKNOWN "Parsed PHP version $base_ver; branch not covered by this checker"
;;
esac
If you remember one thing.
getimagesize() metadata paths. Because this is a LOW reassessment, there is no noisgate mitigation SLA — treat it as backlog hygiene unless your app explicitly reflects image metadata, and work to complete patching within the noisgate remediation SLA for LOW issues by folding the vendor fix into normal maintenance rather than burning an emergency window.Sources
What defenders are saying.
Crowdsourced verification outputs.
Results submitted by users who ran the verification payload against their environment.