Fix Heap-Use-After-Free in sapi_read_post_data Processing in CLI SAPI Interface GHSA-4w77-75f9-2c8w Fix Configuring a proxy in a stream context might allow for CRLF injection in URIs CVE-2024-11234 Fix Single byte overread with convert.quoted-printable-decode filter CVE-2024-11233 Fix Leak partial content of the heap through heap buffer over-read CVE-2024-8929 Fix libxml streams use wrong `content-type` header when requesting a redirected resource CVE-2025-1219 Fix Stream HTTP wrapper header check might omit basic auth header CVE-2025-1736 Fix Stream HTTP wrapper truncate redirect location to 1024 bytes CVE-2025-1861 Fix Streams HTTP wrapper does not fail for headers without colon CVE-2025-1734 Fix Header parser of `http` stream wrapper does not handle folded headers CVE-2025-1217 Fix pgsql extension does not check for errors during escaping CVE-2025-1735 Fix NULL Pointer Dereference in PHP SOAP Extension via Large XML Namespace Prefix CVE-2025-6491 Fix Null byte termination in hostnames CVE-2025-1220 Fix Null byte termination in dns_get_record() GHSA-www2-q4fc-65wf Fix Heap buffer overflow in array_merge() CVE-2025-14178 Fix Information Leak of Memory in getimagesize CVE-2025-14177 Resolves: RHEL-141181
155 lines
5.0 KiB
Diff
155 lines
5.0 KiB
Diff
From d407d8a8735ebf43bee3e6b49fb013b8aa4b6bfc 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)
|
|
(cherry picked from commit 36150278addd8686a9899559241296094bd57282)
|
|
---
|
|
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 fe8fbea85ca..df6a74b078f 100644
|
|
--- a/ext/standard/fsock.c
|
|
+++ b/ext/standard/fsock.c
|
|
@@ -25,6 +25,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)
|
|
@@ -59,11 +81,12 @@ static void php_fsockopen_stream(INTERNAL_FUNCTION_PARAMETERS, int persistent)
|
|
ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);
|
|
|
|
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..e16d3fa9060
|
|
--- /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..bc1f34eaf58
|
|
--- /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 46b23b63ada..7a192ea6c0b 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
|
|
|