From cc2960e782eb5cc262d7bd572a7d18979a811954 Mon Sep 17 00:00:00 2001 From: Jakub Zelenka Date: Sun, 3 May 2026 20:01:41 +0200 Subject: [PATCH 04/10] GHSA-7qg2-v9fj-4mwv: [fpm] XSS within status endpoint Fixes GHSA-7qg2-v9fj-4mwv Fixes CVE-2026-6735 (cherry picked from commit 99a5ad7441de9914246c7863adb6997396008b9d) --- sapi/fpm/fpm/fpm_status.c | 34 +++++++++++-- .../tests/ghsa-7qg2-v9fj-4mwv-status-xss.phpt | 48 +++++++++++++++++++ 2 files changed, 78 insertions(+), 4 deletions(-) create mode 100644 sapi/fpm/tests/ghsa-7qg2-v9fj-4mwv-status-xss.phpt diff --git a/sapi/fpm/fpm/fpm_status.c b/sapi/fpm/fpm/fpm_status.c index f698753cf4c..81292aa3bf8 100644 --- a/sapi/fpm/fpm/fpm_status.c +++ b/sapi/fpm/fpm/fpm_status.c @@ -525,8 +525,8 @@ int fpm_status_handle_request(void) /* {{{ */ if (full_syntax) { unsigned int i; int first; - zend_string *tmp_query_string; - char *query_string; + zend_string *tmp_query_string, *tmp_request_uri_string; + char *query_string, *request_uri_string; struct timeval duration, now; float cpu; @@ -551,13 +551,36 @@ int fpm_status_handle_request(void) /* {{{ */ } } + request_uri_string = NULL; + tmp_request_uri_string = NULL; + if (proc->request_uri[0] != '\0') { + 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); + } else if (encode_json) { + tmp_request_uri_string = php_json_encode_string(proc->request_uri, + strlen(proc->request_uri), PHP_JSON_INVALID_UTF8_IGNORE); + request_uri_string = ZSTR_VAL(tmp_request_uri_string); + /* remove quotes around the string */ + if (ZSTR_LEN(tmp_request_uri_string) >= 2) { + request_uri_string[ZSTR_LEN(tmp_request_uri_string) - 1] = '\0'; + ++request_uri_string; + } + } else { + request_uri_string = proc->request_uri; + } + } + query_string = NULL; tmp_query_string = NULL; if (proc->query_string[0] != '\0') { if (encode_html) { tmp_query_string = php_escape_html_entities_ex( (const unsigned char *) proc->query_string, - strlen(proc->query_string), 1, ENT_HTML_IGNORE_ERRORS & ENT_COMPAT, + strlen(proc->query_string), 1, ENT_DISALLOWED | ENT_HTML_DOC_XML1 | ENT_COMPAT, NULL, /* double_encode */ 1, /* quiet */ 0); } else if (encode_json) { tmp_query_string = php_json_encode_string(proc->query_string, @@ -596,7 +619,7 @@ int fpm_status_handle_request(void) /* {{{ */ proc->requests, duration.tv_sec * 1000000UL + duration.tv_usec, proc->request_method[0] != '\0' ? proc->request_method : "-", - proc->request_uri[0] != '\0' ? proc->request_uri : "-", + request_uri_string ? request_uri_string : "-", query_string ? "?" : "", query_string ? query_string : "", proc->content_length, @@ -607,6 +630,9 @@ int fpm_status_handle_request(void) /* {{{ */ PUTS(buffer); efree(buffer); + if (tmp_request_uri_string) { + zend_string_free(tmp_request_uri_string); + } if (tmp_query_string) { zend_string_free(tmp_query_string); } diff --git a/sapi/fpm/tests/ghsa-7qg2-v9fj-4mwv-status-xss.phpt b/sapi/fpm/tests/ghsa-7qg2-v9fj-4mwv-status-xss.phpt new file mode 100644 index 00000000000..475bc130a42 --- /dev/null +++ b/sapi/fpm/tests/ghsa-7qg2-v9fj-4mwv-status-xss.phpt @@ -0,0 +1,48 @@ +--TEST-- +FPM: GHSA-7qg2-v9fj-4mwv - status xss +--SKIPIF-- + +--FILE-- +start(); +$tester->expectLogStartNotices(); +$responses = $tester + ->multiRequest([ + ['uri' => '/', 'query' => ''], + ['uri' => '/status', 'query' => 'full&html', 'delay' => 100000], + ]); +var_dump(strpos($responses[1]->getBody(), '