diff options
Diffstat (limited to 'php-cve-2025-1220.patch')
-rw-r--r-- | php-cve-2025-1220.patch | 153 |
1 files changed, 153 insertions, 0 deletions
diff --git a/php-cve-2025-1220.patch b/php-cve-2025-1220.patch new file mode 100644 index 0000000..dad45e0 --- /dev/null +++ b/php-cve-2025-1220.patch @@ -0,0 +1,153 @@ +From 36150278addd8686a9899559241296094bd57282 Mon Sep 17 00:00:00 2001 +From: Jakub Zelenka <bukka@php.net> +Date: Thu, 10 Apr 2025 15:15:36 +0200 +Subject: [PATCH 2/4] Fix GHSA-3cr5-j632-f35r: Null byte in hostnames + +This fixes stream_socket_client() and fsockopen(). + +Specifically it adds a check to parse_ip_address_ex and it also makes +sure that the \0 is not ignored in fsockopen() hostname formatting. + +(cherry picked from commit cac8f7f1cf4939f55f06b68120040f057682d89c) +--- + ext/standard/fsock.c | 27 +++++++++++++++++-- + .../tests/network/ghsa-3cr5-j632-f35r.phpt | 21 +++++++++++++++ + .../tests/streams/ghsa-3cr5-j632-f35r.phpt | 26 ++++++++++++++++++ + main/streams/xp_socket.c | 9 ++++--- + 4 files changed, 78 insertions(+), 5 deletions(-) + create mode 100644 ext/standard/tests/network/ghsa-3cr5-j632-f35r.phpt + create mode 100644 ext/standard/tests/streams/ghsa-3cr5-j632-f35r.phpt + +diff --git a/ext/standard/fsock.c b/ext/standard/fsock.c +index a9c3cb0bf5d..636dbb6e359 100644 +--- a/ext/standard/fsock.c ++++ b/ext/standard/fsock.c +@@ -23,6 +23,28 @@ + #include "php_network.h" + #include "file.h" + ++static size_t php_fsockopen_format_host_port(char **message, const char *prefix, size_t prefix_len, ++ const char *host, size_t host_len, zend_long port) ++{ ++ char portbuf[32]; ++ int portlen = snprintf(portbuf, sizeof(portbuf), ":" ZEND_LONG_FMT, port); ++ size_t total_len = prefix_len + host_len + portlen; ++ ++ char *result = emalloc(total_len + 1); ++ ++ if (prefix_len > 0) { ++ memcpy(result, prefix, prefix_len); ++ } ++ memcpy(result + prefix_len, host, host_len); ++ memcpy(result + prefix_len + host_len, portbuf, portlen); ++ ++ result[total_len] = '\0'; ++ ++ *message = result; ++ ++ return total_len; ++} ++ + /* {{{ php_fsockopen() */ + + static void php_fsockopen_stream(INTERNAL_FUNCTION_PARAMETERS, int persistent) +@@ -62,11 +84,12 @@ static void php_fsockopen_stream(INTERNAL_FUNCTION_PARAMETERS, int persistent) + } + + if (persistent) { +- spprintf(&hashkey, 0, "pfsockopen__%s:" ZEND_LONG_FMT, host, port); ++ php_fsockopen_format_host_port(&hashkey, "pfsockopen__", strlen("pfsockopen__"), host, ++ host_len, port); + } + + if (port > 0) { +- hostname_len = spprintf(&hostname, 0, "%s:" ZEND_LONG_FMT, host, port); ++ hostname_len = php_fsockopen_format_host_port(&hostname, "", 0, host, host_len, port); + } else { + hostname_len = host_len; + hostname = host; +diff --git a/ext/standard/tests/network/ghsa-3cr5-j632-f35r.phpt b/ext/standard/tests/network/ghsa-3cr5-j632-f35r.phpt +new file mode 100644 +index 00000000000..7556c3be94c +--- /dev/null ++++ b/ext/standard/tests/network/ghsa-3cr5-j632-f35r.phpt +@@ -0,0 +1,21 @@ ++--TEST-- ++GHSA-3cr5-j632-f35r: Null byte termination in fsockopen() ++--FILE-- ++<?php ++ ++$server = stream_socket_server("tcp://localhost:0"); ++ ++if (preg_match('/:(\d+)$/', stream_socket_get_name($server, false), $m)) { ++ $client = fsockopen("localhost\0.example.com", intval($m[1])); ++ var_dump($client); ++ if ($client) { ++ fclose($client); ++ } ++} ++fclose($server); ++ ++?> ++--EXPECTF-- ++ ++Warning: fsockopen(): Unable to connect to localhost:%d (The hostname must not contain null bytes) in %s ++bool(false) +diff --git a/ext/standard/tests/streams/ghsa-3cr5-j632-f35r.phpt b/ext/standard/tests/streams/ghsa-3cr5-j632-f35r.phpt +new file mode 100644 +index 00000000000..52f9263c99a +--- /dev/null ++++ b/ext/standard/tests/streams/ghsa-3cr5-j632-f35r.phpt +@@ -0,0 +1,26 @@ ++--TEST-- ++GHSA-3cr5-j632-f35r: Null byte termination in stream_socket_client() ++--FILE-- ++<?php ++ ++$server = stream_socket_server("tcp://localhost:0"); ++$socket_name = stream_socket_get_name($server, false); ++ ++if (preg_match('/:(\d+)$/', $socket_name, $m)) { ++ $port = $m[1]; ++ $client = stream_socket_client("tcp://localhost\0.example.com:$port"); ++ var_dump($client); ++ if ($client) { ++ fclose($client); ++ } ++} else { ++ echo "Could not extract port from socket name: $socket_name\n"; ++} ++ ++fclose($server); ++ ++?> ++--EXPECTF-- ++ ++Warning: stream_socket_client(): Unable to connect to tcp://localhost\0.example.com:%d (The hostname must not contain null bytes) in %s ++bool(false) +diff --git a/main/streams/xp_socket.c b/main/streams/xp_socket.c +index 68df3366340..47f1bdd4b1d 100644 +--- a/main/streams/xp_socket.c ++++ b/main/streams/xp_socket.c +@@ -580,12 +580,15 @@ static inline char *parse_ip_address_ex(const char *str, size_t str_len, int *po + char *colon; + char *host = NULL; + +-#ifdef HAVE_IPV6 +- char *p; ++ if (memchr(str, '\0', str_len)) { ++ *err = strpprintf(0, "The hostname must not contain null bytes"); ++ return NULL; ++ } + ++#ifdef HAVE_IPV6 + if (*(str) == '[' && str_len > 1) { + /* IPV6 notation to specify raw address with port (i.e. [fe80::1]:80) */ +- p = memchr(str + 1, ']', str_len - 2); ++ char *p = memchr(str + 1, ']', str_len - 2); + if (!p || *(p + 1) != ':') { + if (get_err) { + *err = strpprintf(0, "Failed to parse IPv6 address \"%s\"", str); +-- +2.50.0 + |