1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
|
From aeaf48ca0bceba42b9595dff30d9e96029c54613 Mon Sep 17 00:00:00 2001
From: Jakub Zelenka <bukka@php.net>
Date: Sun, 3 May 2026 20:01:41 +0200
Subject: [PATCH 4/9] GHSA-7qg2-v9fj-4mwv: [fpm] XSS within status endpoint
Fixes GHSA-7qg2-v9fj-4mwv
Fixes CVE-2026-6735
(cherry picked from commit 99a5ad7441de9914246c7863adb6997396008b9d)
(cherry picked from commit cc2960e782eb5cc262d7bd572a7d18979a811954)
(cherry picked from commit 62daef7b73108ceda2545862cde0673f252ba2d2)
---
sapi/fpm/fpm/fpm_status.c | 28 +++++++++--
.../tests/ghsa-7qg2-v9fj-4mwv-status-xss.phpt | 48 +++++++++++++++++++
2 files changed, 72 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 de8db9d61a2..9926ebd6b27 100644
--- a/sapi/fpm/fpm/fpm_status.c
+++ b/sapi/fpm/fpm/fpm_status.c
@@ -483,8 +483,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;
#ifdef HAVE_FPM_LQ
float cpu;
@@ -511,13 +511,30 @@ int fpm_status_handle_request(void) /* {{{ */
}
}
+ request_uri_string = NULL;
+ tmp_request_uri_string = NULL;
+ if (proc.request_uri[0] != '\0') {
+ if (encode) {
+ tmp_request_uri_string = php_escape_html_entities_ex(
+ (unsigned char*)proc.request_uri,
+ strlen(proc.request_uri), 1, ENT_DISALLOWED | ENT_HTML_DOC_XML1 | ENT_COMPAT,
+ NULL, /* double_encode */ 1);
+ request_uri_string = ZSTR_VAL(tmp_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) {
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);
+ tmp_query_string = php_escape_html_entities_ex(
+ (unsigned char*)proc.query_string,
+ strlen(proc.query_string), 1, ENT_DISALLOWED | ENT_HTML_DOC_XML1 | ENT_COMPAT,
+ NULL, /* double_encode */ 1);
query_string = ZSTR_VAL(tmp_query_string);
}
}
@@ -545,7 +562,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,
@@ -558,6 +575,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--
+<?php include "skipif.inc"; ?>
+--FILE--
+<?php
+
+require_once "tester.inc";
+
+$cfg = <<<EOT
+[global]
+error_log = {{FILE:LOG}}
+[unconfined]
+listen = {{ADDR}}
+pm = static
+pm.max_children = 2
+pm.status_path = /status
+catch_workers_output = yes
+EOT;
+
+$code = <<<EOT
+<?php
+usleep(200000);
+EOT;
+
+$tester = new FPM\Tester($cfg, $code);
+$tester->start();
+$tester->expectLogStartNotices();
+$responses = $tester
+ ->multiRequest([
+ ['uri' => '/<script>alert(1)</script>', 'query' => '<script>alert(2)</script>'],
+ ['uri' => '/status', 'query' => 'full&html', 'delay' => 100000],
+ ]);
+var_dump(strpos($responses[1]->getBody(), '<script>'));
+$tester->terminate();
+$tester->expectLogTerminatingNotices();
+$tester->close();
+
+?>
+Done
+--EXPECT--
+bool(false)
+Done
+--CLEAN--
+<?php
+require_once "tester.inc";
+FPM\Tester::clean();
+?>
--
2.54.0
|