CVE-2026-6735
Patch Available.
This Vulnerability has been fixed in the Never-Ending Support (NES) version offered by HeroDevs.
Overview
PHP is a general-purpose scripting language used mostly for web development. It runs behind web servers through server APIs such as Apache mod_php and PHP-FPM, and powers content management systems and frameworks such as WordPress, Drupal, Moodle and Laravel. PHP-FPM (FastCGI Process Manager) runs PHP for web servers, and its optional status page, enabled with pm.status_path, shows the state of each worker in the pool, including the request it is serving.
A Cross-Site Scripting (XSS) vulnerability (CVE-2026-6735) has been identified in the full status page of PHP-FPM, which allows an attacker who can send a request to the pool to place markup in the request URI and so run script in the browser of whoever views the status page in HTML or XML format. PHP tracks this issue as GHSA-7qg2-v9fj-4mwv.
Per OWASP: Cross-Site Scripting (XSS) attacks occur when: Data enters a Web application through an untrusted source, most frequently a web request. The data is included in dynamic content that is sent to a web user without being validated for malicious content.
This issue affects PHP before 8.2.31, PHP 8.3 before 8.3.31, PHP 8.4 before 8.4.21 and PHP 8.5 before 8.5.6, which includes every End-of-Life release line from PHP 5.6 through 8.1, each through its final release.
Details
Module Info
- Product: PHP
- Affected packages:
php-src(sapi/fpm) - Affected versions: <8.2.31, >=8.3.0 <8.3.31, >=8.4.0 <8.4.21, >=8.5.0 <8.5.6
- GitHub repository: https://github.com/php/php-src
- Published packages: https://www.php.net/downloads
- Package manager: N/A
- Fixed in:
- OSS PHP 8.2.31, 8.3.31, 8.4.21, 8.5.6
- NES for PHP 5.6.1 (based on PHP 5.6.40)
- NES for PHP 7.2.1 (based on PHP 7.2.34)
- NES for PHP 7.4.1 (based on PHP 7.4.33)
Vulnerability Info
This High-severity vulnerability is found in the FPM SAPI of PHP before 8.2.31, 8.3 before 8.3.31, 8.4 before 8.4.21 and 8.5 before 8.5.6, including every End-of-Life release line from PHP 5.6 through 8.1, which never received the upstream fix. The PHP project scores it 7.3 under CVSS 4.0 (CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:P/VC:H/VI:H/VA:N/SC:H/SI:H/SA:N/E:P/S:P/AU:Y/RE:L/U:Amber).
With ?full, the status page lists every worker of the pool with the method, URI and query string of the request it is serving or last served. In the HTML (?full&html) and XML (?full&xml) formats, PHP escaped the query string, although with flags that leave quotes alone, but inserted the request URI unchanged. In PHP 7.4.33 the code reads:
if (proc.query_string[0] != '\0') {
if (!encode) {
query_string = proc.query_string;
} else {
tmp_query_string = php_escape_html_entities_ex((unsigned char *)proc.query_string, strlen(proc.query_string), 1, ENT_HTML_IGNORE_ERRORS & ENT_COMPAT, NULL, 1);
query_string = ZSTR_VAL(tmp_query_string);
}
}
The request URI is then passed to the page template as proc.request_uri with no escaping. An attacker who sends a request whose URI contains markup to any script in the pool gets that markup written into the status page while the request is recorded on the worker. When an administrator or a monitoring tool opens the full HTML or XML status page in a browser, the injected script runs with the origin of the status page, which is stored cross-site scripting. HeroDevs' analysis found the same unescaped output in the PHP 5.6, 7.2 and 7.4 lines.
Exploitation needs php-fpm with pm.status_path set and a victim who views the full status page in HTML or XML format in a browser. The attacker only needs to be able to send a request to the pool. Other SAPIs, FPM pools without a status path, and status pages viewed only in plain text are not affected. Upstream also escapes these fields in the JSON format. The NES release for PHP 5.6 does the same, while the NES releases for PHP 7.2 and 7.4 leave the JSON output unescaped, as those versions always produced it; a browser does not render that output as HTML.
The fix escapes the request URI as well as the query string, with flags that also encode quotes and characters not allowed in the document type:
if (encode_html) {
tmp_request_uri_string = php_escape_html_entities_ex(
(const unsigned char *) proc->request_uri,
strlen(proc->request_uri), 1, ENT_DISALLOWED | ENT_HTML_DOC_XML1 | ENT_COMPAT,
NULL, /* double_encode */ 1, /* quiet */ 0);
request_uri_string = ZSTR_VAL(tmp_request_uri_string);
}
Mitigation
The PHP project publishes security fixes only for its supported release lines. PHP 5.6, 7.0, 7.1, 7.2, 7.3, 7.4, 8.0 and 8.1 are End-of-Life and will not receive any public updates to address this issue.
Until an upgrade is possible, disable pm.status_path if the status page is not needed, restrict access to it to trusted monitoring hosts, or read it only in plain text format rather than in a browser.
Users of the affected components should apply one of the following mitigations:
- Upgrade to PHP 8.2.31, 8.3.31, 8.4.21, 8.5.6, or later.
- Leverage a commercial support partner like HeroDevs (NES for PHP) for post-EOL security support.
Credits
- conradfd (finder)
- Jakub Zelenka (remediation)