Merge branch 'PHP-8.2' into PHP-8.3

This commit is contained in:
Ben Ramsey 2024-06-05 01:11:30 -05:00
commit e4453dcbd2
No known key found for this signature in database
GPG Key ID: F9C39DC0B9698544
8 changed files with 819 additions and 51 deletions

View File

@ -89,7 +89,7 @@
#define FORMAT_IPV4 4
#define FORMAT_IPV6 6
static int _php_filter_validate_ipv6(char *str, size_t str_len, int ip[8]);
static int _php_filter_validate_ipv6(const char *str, size_t str_len, int ip[8]);
static int php_filter_parse_int(const char *str, size_t str_len, zend_long *ret) { /* {{{ */
zend_long ctx_value;
@ -580,6 +580,14 @@ static int is_userinfo_valid(zend_string *str)
return 1;
}
static bool php_filter_is_valid_ipv6_hostname(const char *s, size_t l)
{
const char *e = s + l;
const char *t = e - 1;
return *s == '[' && *t == ']' && _php_filter_validate_ipv6(s + 1, l - 2, NULL);
}
void php_filter_validate_url(PHP_INPUT_FILTER_PARAM_DECL) /* {{{ */
{
php_url *url;
@ -600,7 +608,7 @@ void php_filter_validate_url(PHP_INPUT_FILTER_PARAM_DECL) /* {{{ */
if (url->scheme != NULL &&
(zend_string_equals_literal_ci(url->scheme, "http") || zend_string_equals_literal_ci(url->scheme, "https"))) {
char *e, *s, *t;
const char *s;
size_t l;
if (url->host == NULL) {
@ -609,17 +617,14 @@ void php_filter_validate_url(PHP_INPUT_FILTER_PARAM_DECL) /* {{{ */
s = ZSTR_VAL(url->host);
l = ZSTR_LEN(url->host);
e = s + l;
t = e - 1;
/* An IPv6 enclosed by square brackets is a valid hostname */
if (*s == '[' && *t == ']' && _php_filter_validate_ipv6((s + 1), l - 2, NULL)) {
php_url_free(url);
return;
}
// Validate domain
if (!_php_filter_validate_domain(ZSTR_VAL(url->host), l, FILTER_FLAG_HOSTNAME)) {
if (
/* An IPv6 enclosed by square brackets is a valid hostname.*/
!php_filter_is_valid_ipv6_hostname(s, l) &&
/* Validate domain.
* This includes a loose check for an IPv4 address. */
!_php_filter_validate_domain(ZSTR_VAL(url->host), l, FILTER_FLAG_HOSTNAME)
) {
php_url_free(url);
RETURN_VALIDATION_FAILED
}
@ -753,15 +758,15 @@ static int _php_filter_validate_ipv4(char *str, size_t str_len, int *ip) /* {{{
}
/* }}} */
static int _php_filter_validate_ipv6(char *str, size_t str_len, int ip[8]) /* {{{ */
static int _php_filter_validate_ipv6(const char *str, size_t str_len, int ip[8]) /* {{{ */
{
int compressed_pos = -1;
int blocks = 0;
int num, n, i;
char *ipv4;
char *end;
const char *end;
int ip4elm[4];
char *s = str;
const char *s = str;
if (!memchr(str, ':', str_len)) {
return 0;

View File

@ -0,0 +1,41 @@
--TEST--
GHSA-w8qr-v226-r27w
--EXTENSIONS--
filter
--FILE--
<?php
function test(string $input) {
var_dump(filter_var($input, FILTER_VALIDATE_URL));
}
echo "--- These ones should fail ---\n";
test("http://t[est@127.0.0.1");
test("http://t[est@[::1]");
test("http://t[est@[::1");
test("http://t[est@::1]");
test("http://php.net\\@aliyun.com/aaa.do");
test("http://test[@2001:db8:3333:4444:5555:6666:1.2.3.4]");
test("http://te[st@2001:db8:3333:4444:5555:6666:1.2.3.4]");
test("http://te[st@2001:db8:3333:4444:5555:6666:1.2.3.4");
echo "--- These ones should work ---\n";
test("http://test@127.0.0.1");
test("http://test@[2001:db8:3333:4444:5555:6666:1.2.3.4]");
test("http://test@[::1]");
?>
--EXPECT--
--- These ones should fail ---
bool(false)
bool(false)
bool(false)
bool(false)
bool(false)
bool(false)
bool(false)
bool(false)
--- These ones should work ---
string(21) "http://test@127.0.0.1"
string(50) "http://test@[2001:db8:3333:4444:5555:6666:1.2.3.4]"
string(17) "http://test@[::1]"

View File

@ -592,48 +592,39 @@ static void append_win_escaped_arg(smart_str *str, zend_string *arg, bool is_cmd
smart_str_appendc(str, '"');
}
static inline int stricmp_end(const char* suffix, const char* str) {
size_t suffix_len = strlen(suffix);
size_t str_len = strlen(str);
if (suffix_len > str_len) {
return -1; /* Suffix is longer than string, cannot match. */
}
/* Compare the end of the string with the suffix, ignoring case. */
return _stricmp(str + (str_len - suffix_len), suffix);
}
static bool is_executed_by_cmd(const char *prog_name)
static bool is_executed_by_cmd(const char *prog_name, size_t prog_name_length)
{
/* If program name is cmd.exe, then return true. */
if (_stricmp("cmd.exe", prog_name) == 0 || _stricmp("cmd", prog_name) == 0
|| stricmp_end("\\cmd.exe", prog_name) == 0 || stricmp_end("\\cmd", prog_name) == 0) {
return true;
}
size_t out_len;
WCHAR long_name[MAX_PATH];
WCHAR full_name[MAX_PATH];
LPWSTR file_part = NULL;
/* Find the last occurrence of the directory separator (backslash or forward slash). */
char *last_separator = strrchr(prog_name, '\\');
char *last_separator_fwd = strrchr(prog_name, '/');
if (last_separator_fwd && (!last_separator || last_separator < last_separator_fwd)) {
last_separator = last_separator_fwd;
wchar_t *prog_name_wide = php_win32_cp_conv_any_to_w(prog_name, prog_name_length, &out_len);
if (GetLongPathNameW(prog_name_wide, long_name, MAX_PATH) == 0) {
/* This can fail for example with ERROR_FILE_NOT_FOUND (short path resolution only works for existing files)
* in which case we'll pass the path verbatim to the FullPath transformation. */
lstrcpynW(long_name, prog_name_wide, MAX_PATH);
}
/* Find the last dot in the filename after the last directory separator. */
char *extension = NULL;
if (last_separator != NULL) {
extension = strrchr(last_separator, '.');
} else {
extension = strrchr(prog_name, '.');
}
free(prog_name_wide);
prog_name_wide = NULL;
if (extension == NULL || extension == prog_name) {
/* No file extension found, it is not batch file. */
if (GetFullPathNameW(long_name, MAX_PATH, full_name, &file_part) == 0 || file_part == NULL) {
return false;
}
/* Check if the file extension is ".bat" or ".cmd" which is always executed by cmd.exe. */
return _stricmp(extension, ".bat") == 0 || _stricmp(extension, ".cmd") == 0;
bool uses_cmd = false;
if (_wcsicmp(file_part, L"cmd.exe") == 0 || _wcsicmp(file_part, L"cmd") == 0) {
uses_cmd = true;
} else {
const WCHAR *extension_dot = wcsrchr(file_part, L'.');
if (extension_dot && (_wcsicmp(extension_dot, L".bat") == 0 || _wcsicmp(extension_dot, L".cmd") == 0)) {
uses_cmd = true;
}
}
return uses_cmd;
}
static zend_string *create_win_command_from_args(HashTable *args)
@ -652,7 +643,7 @@ static zend_string *create_win_command_from_args(HashTable *args)
}
if (is_prog_name) {
is_cmd_execution = is_executed_by_cmd(ZSTR_VAL(arg_str));
is_cmd_execution = is_executed_by_cmd(ZSTR_VAL(arg_str), ZSTR_LEN(arg_str));
} else {
smart_str_appendc(&str, ' ');
}

View File

@ -0,0 +1,56 @@
--TEST--
GHSA-9fcc-425m-g385 - bypass CVE-2024-1874 - batch file variation
--SKIPIF--
<?php
if( substr(PHP_OS, 0, 3) != "WIN" )
die('skip Run only on Windows');
if (getenv("SKIP_SLOW_TESTS")) die("skip slow test");
?>
--FILE--
<?php
$batch_file_content = <<<EOT
@echo off
powershell -Command "Write-Output '%0%'"
powershell -Command "Write-Output '%1%'"
EOT;
$batch_file_path = __DIR__ . '/ghsa-9fcc-425m-g385_001.bat';
file_put_contents($batch_file_path, $batch_file_content);
$descriptorspec = [STDIN, STDOUT, STDOUT];
$proc = proc_open([$batch_file_path . ".", "\"&notepad.exe"], $descriptorspec, $pipes);
proc_close($proc);
$proc = proc_open([$batch_file_path . " ", "\"&notepad.exe"], $descriptorspec, $pipes);
proc_close($proc);
$proc = proc_open([$batch_file_path . ". ", "\"&notepad.exe"], $descriptorspec, $pipes);
proc_close($proc);
$proc = proc_open([$batch_file_path . ". ... ", "\"&notepad.exe"], $descriptorspec, $pipes);
proc_close($proc);
$proc = proc_open([$batch_file_path . ". ... . ", "\"&notepad.exe"], $descriptorspec, $pipes);
proc_close($proc);
$proc = proc_open([$batch_file_path . ". ... . .", "\"&notepad.exe"], $descriptorspec, $pipes);
proc_close($proc);
proc_open([$batch_file_path . ". .\\.. . .", "\"&notepad.exe"], $descriptorspec, $pipes);
?>
--EXPECTF--
'"%sghsa-9fcc-425m-g385_001.bat."' is not recognized as an internal or external command,
operable program or batch file.
%sghsa-9fcc-425m-g385_001.bat
"&notepad.exe
%sghsa-9fcc-425m-g385_001.bat.
"&notepad.exe
%sghsa-9fcc-425m-g385_001.bat. ...
"&notepad.exe
%sghsa-9fcc-425m-g385_001.bat. ... .
"&notepad.exe
'"%sghsa-9fcc-425m-g385_001.bat. ... . ."' is not recognized as an internal or external command,
operable program or batch file.
Warning: proc_open(): CreateProcess failed, error code: 2 in %s on line %d
--CLEAN--
<?php
@unlink(__DIR__ . '/ghsa-9fcc-425m-g385_001.bat');
?>

View File

@ -0,0 +1,66 @@
--TEST--
GHSA-9fcc-425m-g385 - bypass CVE-2024-1874 - cmd.exe variation
--SKIPIF--
<?php
if( substr(PHP_OS, 0, 3) != "WIN" )
die('skip Run only on Windows');
if (getenv("SKIP_SLOW_TESTS")) die("skip slow test");
?>
--FILE--
<?php
$batch_file_content = <<<EOT
@echo off
powershell -Command "Write-Output '%0%'"
powershell -Command "Write-Output '%1%'"
EOT;
$batch_file_path = __DIR__ . '/ghsa-9fcc-425m-g385_002.bat';
file_put_contents($batch_file_path, $batch_file_content);
$descriptorspec = [STDIN, STDOUT, STDOUT];
$proc = proc_open(["cmd.exe", "/c", $batch_file_path, "\"&notepad.exe"], $descriptorspec, $pipes);
proc_close($proc);
$proc = proc_open(["cmd.exe ", "/c", $batch_file_path, "\"&notepad.exe"], $descriptorspec, $pipes);
proc_close($proc);
$proc = proc_open(["cmd.exe. ", "/c", $batch_file_path, "\"&notepad.exe"], $descriptorspec, $pipes);
proc_close($proc);
$proc = proc_open(["cmd.exe. ... ", "/c", $batch_file_path, "\"&notepad.exe"], $descriptorspec, $pipes);
proc_close($proc);
$proc = proc_open(["\\cmd.exe. ... ", "/c", $batch_file_path, "\"&notepad.exe"], $descriptorspec, $pipes);
$proc = proc_open(["cmd", "/c", $batch_file_path, "\"&notepad.exe"], $descriptorspec, $pipes);
proc_close($proc);
$proc = proc_open(["cmd ", "/c", $batch_file_path, "\"&notepad.exe"], $descriptorspec, $pipes);
proc_close($proc);
$proc = proc_open(["cmd. ", "/c", $batch_file_path, "\"&notepad.exe"], $descriptorspec, $pipes);
$proc = proc_open(["cmd. ... ", "/c", $batch_file_path, "\"&notepad.exe"], $descriptorspec, $pipes);
$proc = proc_open(["\\cmd. ... ", "/c", $batch_file_path, "\"&notepad.exe"], $descriptorspec, $pipes);
?>
--EXPECTF--
%sghsa-9fcc-425m-g385_002.bat
"&notepad.exe
%sghsa-9fcc-425m-g385_002.bat
"&notepad.exe
%sghsa-9fcc-425m-g385_002.bat
"&notepad.exe
%sghsa-9fcc-425m-g385_002.bat
"&notepad.exe
Warning: proc_open(): CreateProcess failed, error code: 2 in %s on line %d
%sghsa-9fcc-425m-g385_002.bat
"&notepad.exe
%sghsa-9fcc-425m-g385_002.bat
"&notepad.exe
Warning: proc_open(): CreateProcess failed, error code: 2 in %s on line %d
Warning: proc_open(): CreateProcess failed, error code: 2 in %s on line %d
Warning: proc_open(): CreateProcess failed, error code: 2 in %s on line %d
--CLEAN--
<?php
@unlink(__DIR__ . '/ghsa-9fcc-425m-g385_002.bat');
?>

View File

@ -0,0 +1,550 @@
--TEST--
GHSA-9fcc-425m-g385 - bypass CVE-2024-1874 - exhaustive suffix test
--SKIPIF--
<?php
if( substr(PHP_OS, 0, 3) != "WIN" )
die('skip Run only on Windows');
?>
--FILE--
<?php
$batch_file_content = <<<EOT
@echo off
powershell -Command "Write-Output '%0%'"
powershell -Command "Write-Output '%1%'"
EOT;
$batch_file_path = __DIR__ . '/ghsa-9fcc-425m-g385_003.bat';
file_put_contents($batch_file_path, $batch_file_content);
$descriptorspec = [STDIN, STDOUT, STDOUT];
for ($i = 1; $i <= 255; $i++) {
echo "Testing $i\n";
try {
$proc = @proc_open([$batch_file_path . chr($i), "\"&notepad.exe"], $descriptorspec, $pipes, null, null, array("bypass_shell" => true));
var_dump($proc);
proc_close($proc);
} catch (Error) {}
}
?>
--EXPECTF--
Testing 1
bool(false)
Testing 2
bool(false)
Testing 3
bool(false)
Testing 4
bool(false)
Testing 5
bool(false)
Testing 6
bool(false)
Testing 7
bool(false)
Testing 8
bool(false)
Testing 9
bool(false)
Testing 10
bool(false)
Testing 11
bool(false)
Testing 12
bool(false)
Testing 13
bool(false)
Testing 14
bool(false)
Testing 15
bool(false)
Testing 16
bool(false)
Testing 17
bool(false)
Testing 18
bool(false)
Testing 19
bool(false)
Testing 20
bool(false)
Testing 21
bool(false)
Testing 22
bool(false)
Testing 23
bool(false)
Testing 24
bool(false)
Testing 25
bool(false)
Testing 26
bool(false)
Testing 27
bool(false)
Testing 28
bool(false)
Testing 29
bool(false)
Testing 30
bool(false)
Testing 31
bool(false)
Testing 32
resource(%d) of type (process)
%s.bat
"&notepad.exe
Testing 33
bool(false)
Testing 34
bool(false)
Testing 35
bool(false)
Testing 36
bool(false)
Testing 37
bool(false)
Testing 38
bool(false)
Testing 39
bool(false)
Testing 40
bool(false)
Testing 41
bool(false)
Testing 42
bool(false)
Testing 43
bool(false)
Testing 44
bool(false)
Testing 45
bool(false)
Testing 46
resource(%d) of type (process)
'"%s.bat."' is not recognized as an internal or external command,
operable program or batch file.
Testing 47
bool(false)
Testing 48
bool(false)
Testing 49
bool(false)
Testing 50
bool(false)
Testing 51
bool(false)
Testing 52
bool(false)
Testing 53
bool(false)
Testing 54
bool(false)
Testing 55
bool(false)
Testing 56
bool(false)
Testing 57
bool(false)
Testing 58
bool(false)
Testing 59
bool(false)
Testing 60
bool(false)
Testing 61
bool(false)
Testing 62
bool(false)
Testing 63
bool(false)
Testing 64
bool(false)
Testing 65
bool(false)
Testing 66
bool(false)
Testing 67
bool(false)
Testing 68
bool(false)
Testing 69
bool(false)
Testing 70
bool(false)
Testing 71
bool(false)
Testing 72
bool(false)
Testing 73
bool(false)
Testing 74
bool(false)
Testing 75
bool(false)
Testing 76
bool(false)
Testing 77
bool(false)
Testing 78
bool(false)
Testing 79
bool(false)
Testing 80
bool(false)
Testing 81
bool(false)
Testing 82
bool(false)
Testing 83
bool(false)
Testing 84
bool(false)
Testing 85
bool(false)
Testing 86
bool(false)
Testing 87
bool(false)
Testing 88
bool(false)
Testing 89
bool(false)
Testing 90
bool(false)
Testing 91
bool(false)
Testing 92
bool(false)
Testing 93
bool(false)
Testing 94
bool(false)
Testing 95
bool(false)
Testing 96
bool(false)
Testing 97
bool(false)
Testing 98
bool(false)
Testing 99
bool(false)
Testing 100
bool(false)
Testing 101
bool(false)
Testing 102
bool(false)
Testing 103
bool(false)
Testing 104
bool(false)
Testing 105
bool(false)
Testing 106
bool(false)
Testing 107
bool(false)
Testing 108
bool(false)
Testing 109
bool(false)
Testing 110
bool(false)
Testing 111
bool(false)
Testing 112
bool(false)
Testing 113
bool(false)
Testing 114
bool(false)
Testing 115
bool(false)
Testing 116
bool(false)
Testing 117
bool(false)
Testing 118
bool(false)
Testing 119
bool(false)
Testing 120
bool(false)
Testing 121
bool(false)
Testing 122
bool(false)
Testing 123
bool(false)
Testing 124
bool(false)
Testing 125
bool(false)
Testing 126
bool(false)
Testing 127
bool(false)
Testing 128
bool(false)
Testing 129
bool(false)
Testing 130
bool(false)
Testing 131
bool(false)
Testing 132
bool(false)
Testing 133
bool(false)
Testing 134
bool(false)
Testing 135
bool(false)
Testing 136
bool(false)
Testing 137
bool(false)
Testing 138
bool(false)
Testing 139
bool(false)
Testing 140
bool(false)
Testing 141
bool(false)
Testing 142
bool(false)
Testing 143
bool(false)
Testing 144
bool(false)
Testing 145
bool(false)
Testing 146
bool(false)
Testing 147
bool(false)
Testing 148
bool(false)
Testing 149
bool(false)
Testing 150
bool(false)
Testing 151
bool(false)
Testing 152
bool(false)
Testing 153
bool(false)
Testing 154
bool(false)
Testing 155
bool(false)
Testing 156
bool(false)
Testing 157
bool(false)
Testing 158
bool(false)
Testing 159
bool(false)
Testing 160
bool(false)
Testing 161
bool(false)
Testing 162
bool(false)
Testing 163
bool(false)
Testing 164
bool(false)
Testing 165
bool(false)
Testing 166
bool(false)
Testing 167
bool(false)
Testing 168
bool(false)
Testing 169
bool(false)
Testing 170
bool(false)
Testing 171
bool(false)
Testing 172
bool(false)
Testing 173
bool(false)
Testing 174
bool(false)
Testing 175
bool(false)
Testing 176
bool(false)
Testing 177
bool(false)
Testing 178
bool(false)
Testing 179
bool(false)
Testing 180
bool(false)
Testing 181
bool(false)
Testing 182
bool(false)
Testing 183
bool(false)
Testing 184
bool(false)
Testing 185
bool(false)
Testing 186
bool(false)
Testing 187
bool(false)
Testing 188
bool(false)
Testing 189
bool(false)
Testing 190
bool(false)
Testing 191
bool(false)
Testing 192
bool(false)
Testing 193
bool(false)
Testing 194
bool(false)
Testing 195
bool(false)
Testing 196
bool(false)
Testing 197
bool(false)
Testing 198
bool(false)
Testing 199
bool(false)
Testing 200
bool(false)
Testing 201
bool(false)
Testing 202
bool(false)
Testing 203
bool(false)
Testing 204
bool(false)
Testing 205
bool(false)
Testing 206
bool(false)
Testing 207
bool(false)
Testing 208
bool(false)
Testing 209
bool(false)
Testing 210
bool(false)
Testing 211
bool(false)
Testing 212
bool(false)
Testing 213
bool(false)
Testing 214
bool(false)
Testing 215
bool(false)
Testing 216
bool(false)
Testing 217
bool(false)
Testing 218
bool(false)
Testing 219
bool(false)
Testing 220
bool(false)
Testing 221
bool(false)
Testing 222
bool(false)
Testing 223
bool(false)
Testing 224
bool(false)
Testing 225
bool(false)
Testing 226
bool(false)
Testing 227
bool(false)
Testing 228
bool(false)
Testing 229
bool(false)
Testing 230
bool(false)
Testing 231
bool(false)
Testing 232
bool(false)
Testing 233
bool(false)
Testing 234
bool(false)
Testing 235
bool(false)
Testing 236
bool(false)
Testing 237
bool(false)
Testing 238
bool(false)
Testing 239
bool(false)
Testing 240
bool(false)
Testing 241
bool(false)
Testing 242
bool(false)
Testing 243
bool(false)
Testing 244
bool(false)
Testing 245
bool(false)
Testing 246
bool(false)
Testing 247
bool(false)
Testing 248
bool(false)
Testing 249
bool(false)
Testing 250
bool(false)
Testing 251
bool(false)
Testing 252
bool(false)
Testing 253
bool(false)
Testing 254
bool(false)
Testing 255
bool(false)
--CLEAN--
<?php
@unlink(__DIR__ . '/ghsa-9fcc-425m-g385_003.bat');
?>

View File

@ -1796,8 +1796,13 @@ int main(int argc, char *argv[])
}
}
/* Apache CGI will pass the query string to the command line if it doesn't contain a '='.
* This can create an issue where a malicious request can pass command line arguments to
* the executable. Ideally we skip argument parsing when we're in cgi or fastcgi mode,
* but that breaks PHP scripts on Linux with a hashbang: `#!/php-cgi -d option=value`.
* Therefore, this code only prevents passing arguments if the query string starts with a '-'.
* Similarly, scripts spawned in subprocesses on Windows may have the same issue. */
if((query_string = getenv("QUERY_STRING")) != NULL && strchr(query_string, '=') == NULL) {
/* we've got query string that has no = - apache CGI will pass it to command line */
unsigned char *p;
decoded_query_string = strdup(query_string);
php_url_decode(decoded_query_string, strlen(decoded_query_string));
@ -1807,6 +1812,22 @@ int main(int argc, char *argv[])
if(*p == '-') {
skip_getopt = 1;
}
/* On Windows we have to take into account the "best fit" mapping behaviour. */
#ifdef PHP_WIN32
if (*p >= 0x80) {
wchar_t wide_buf[1];
wide_buf[0] = *p;
char char_buf[4];
size_t wide_buf_len = sizeof(wide_buf) / sizeof(wide_buf[0]);
size_t char_buf_len = sizeof(char_buf) / sizeof(char_buf[0]);
if (WideCharToMultiByte(CP_ACP, 0, wide_buf, wide_buf_len, char_buf, char_buf_len, NULL, NULL) == 0
|| char_buf[0] == '-') {
skip_getopt = 1;
}
}
#endif
free(decoded_query_string);
}

View File

@ -0,0 +1,38 @@
--TEST--
GHSA-3qgc-jrrr-25jv
--SKIPIF--
<?php
include 'skipif.inc';
if (PHP_OS_FAMILY !== "Windows") die("skip Only for Windows");
$codepage = trim(shell_exec("powershell Get-ItemPropertyValue HKLM:\\SYSTEM\\CurrentControlSet\\Control\\Nls\\CodePage ACP"));
if ($codepage !== '932' && $codepage !== '936' && $codepage !== '950') die("skip Wrong codepage");
?>
--FILE--
<?php
include 'include.inc';
$filename = __DIR__."/GHSA-3qgc-jrrr-25jv_tmp.php";
$script = '<?php echo "hello "; echo "world"; ?>';
file_put_contents($filename, $script);
$php = get_cgi_path();
reset_env_vars();
putenv("SERVER_NAME=Test");
putenv("SCRIPT_FILENAME=$filename");
putenv("QUERY_STRING=%ads");
putenv("REDIRECT_STATUS=1");
passthru("$php -s");
?>
--CLEAN--
<?php
@unlink(__DIR__."/GHSA-3qgc-jrrr-25jv_tmp.php");
?>
--EXPECTF--
X-Powered-By: PHP/%s
Content-type: %s
hello world