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 Resolves: RHEL-87151
242 lines
7.1 KiB
Diff
242 lines
7.1 KiB
Diff
From 8f65ef50929f6781f4973325f9b619f02cce19d8 Mon Sep 17 00:00:00 2001
|
|
From: Jakub Zelenka <bukka@php.net>
|
|
Date: Fri, 14 Feb 2025 19:17:22 +0100
|
|
Subject: [PATCH 04/11] Fix GHSA-hgf5-96fm-v528: http user header check of crlf
|
|
|
|
(cherry picked from commit 41d49abbd99dab06cdae4834db664435f8177174)
|
|
---
|
|
ext/standard/http_fopen_wrapper.c | 2 +-
|
|
.../tests/http/ghsa-hgf5-96fm-v528-001.phpt | 65 +++++++++++++++++++
|
|
.../tests/http/ghsa-hgf5-96fm-v528-002.phpt | 62 ++++++++++++++++++
|
|
.../tests/http/ghsa-hgf5-96fm-v528-003.phpt | 64 ++++++++++++++++++
|
|
4 files changed, 192 insertions(+), 1 deletion(-)
|
|
create mode 100644 ext/standard/tests/http/ghsa-hgf5-96fm-v528-001.phpt
|
|
create mode 100644 ext/standard/tests/http/ghsa-hgf5-96fm-v528-002.phpt
|
|
create mode 100644 ext/standard/tests/http/ghsa-hgf5-96fm-v528-003.phpt
|
|
|
|
diff --git a/ext/standard/http_fopen_wrapper.c b/ext/standard/http_fopen_wrapper.c
|
|
index e9b2486a7c9..64703c2f56b 100644
|
|
--- a/ext/standard/http_fopen_wrapper.c
|
|
+++ b/ext/standard/http_fopen_wrapper.c
|
|
@@ -107,7 +107,7 @@ static inline void strip_header(char *header_bag, char *lc_header_bag,
|
|
static zend_bool check_has_header(const char *headers, const char *header) {
|
|
const char *s = headers;
|
|
while ((s = strstr(s, header))) {
|
|
- if (s == headers || *(s-1) == '\n') {
|
|
+ if (s == headers || (*(s-1) == '\n' && *(s-2) == '\r')) {
|
|
return 1;
|
|
}
|
|
s++;
|
|
diff --git a/ext/standard/tests/http/ghsa-hgf5-96fm-v528-001.phpt b/ext/standard/tests/http/ghsa-hgf5-96fm-v528-001.phpt
|
|
new file mode 100644
|
|
index 00000000000..c40123560ef
|
|
--- /dev/null
|
|
+++ b/ext/standard/tests/http/ghsa-hgf5-96fm-v528-001.phpt
|
|
@@ -0,0 +1,65 @@
|
|
+--TEST--
|
|
+GHSA-hgf5-96fm-v528: Stream HTTP wrapper header check might omit basic auth header (incorrect inside pos)
|
|
+--FILE--
|
|
+<?php
|
|
+$serverCode = <<<'CODE'
|
|
+ $ctxt = stream_context_create([
|
|
+ "socket" => [
|
|
+ "tcp_nodelay" => true
|
|
+ ]
|
|
+ ]);
|
|
+
|
|
+ $server = stream_socket_server(
|
|
+ "tcp://127.0.0.1:0", $errno, $errstr, STREAM_SERVER_BIND | STREAM_SERVER_LISTEN, $ctxt);
|
|
+ phpt_notify_server_start($server);
|
|
+
|
|
+ $conn = stream_socket_accept($server);
|
|
+
|
|
+ phpt_notify(message:"server-accepted");
|
|
+
|
|
+ $result = fread($conn, 1024);
|
|
+ $encoded_result = base64_encode($result);
|
|
+
|
|
+ fwrite($conn, "HTTP/1.0 200 Ok\r\nContent-Type: text/html; charset=utf-8\r\n\r\n$encoded_result\r\n");
|
|
+
|
|
+CODE;
|
|
+
|
|
+$clientCode = <<<'CODE'
|
|
+ $opts = [
|
|
+ "http" => [
|
|
+ "method" => "GET",
|
|
+ "header" => "Cookie: foo=bar\nauthorization:x\r\n"
|
|
+ ]
|
|
+ ];
|
|
+ $ctx = stream_context_create($opts);
|
|
+ var_dump(explode("\r\n", base64_decode(file_get_contents("http://user:pwd@{{ ADDR }}", false, $ctx))));
|
|
+ var_dump($http_response_header);
|
|
+CODE;
|
|
+
|
|
+include sprintf("%s/../../../openssl/tests/ServerClientTestCase.inc", __DIR__);
|
|
+ServerClientTestCase::getInstance()->run($clientCode, $serverCode);
|
|
+?>
|
|
+--EXPECTF--
|
|
+array(7) {
|
|
+ [0]=>
|
|
+ string(14) "GET / HTTP/1.1"
|
|
+ [1]=>
|
|
+ string(33) "Authorization: Basic dXNlcjpwd2Q="
|
|
+ [2]=>
|
|
+ string(21) "Host: 127.0.0.1:%d"
|
|
+ [3]=>
|
|
+ string(17) "Connection: close"
|
|
+ [4]=>
|
|
+ string(31) "Cookie: foo=bar
|
|
+authorization:x"
|
|
+ [5]=>
|
|
+ string(0) ""
|
|
+ [6]=>
|
|
+ string(0) ""
|
|
+}
|
|
+array(2) {
|
|
+ [0]=>
|
|
+ string(15) "HTTP/1.0 200 Ok"
|
|
+ [1]=>
|
|
+ string(38) "Content-Type: text/html; charset=utf-8"
|
|
+}
|
|
diff --git a/ext/standard/tests/http/ghsa-hgf5-96fm-v528-002.phpt b/ext/standard/tests/http/ghsa-hgf5-96fm-v528-002.phpt
|
|
new file mode 100644
|
|
index 00000000000..37a47df060a
|
|
--- /dev/null
|
|
+++ b/ext/standard/tests/http/ghsa-hgf5-96fm-v528-002.phpt
|
|
@@ -0,0 +1,62 @@
|
|
+--TEST--
|
|
+GHSA-hgf5-96fm-v528: Header parser of http stream wrapper does not handle folded headers (correct start pos)
|
|
+--FILE--
|
|
+<?php
|
|
+$serverCode = <<<'CODE'
|
|
+ $ctxt = stream_context_create([
|
|
+ "socket" => [
|
|
+ "tcp_nodelay" => true
|
|
+ ]
|
|
+ ]);
|
|
+
|
|
+ $server = stream_socket_server(
|
|
+ "tcp://127.0.0.1:0", $errno, $errstr, STREAM_SERVER_BIND | STREAM_SERVER_LISTEN, $ctxt);
|
|
+ phpt_notify_server_start($server);
|
|
+
|
|
+ $conn = stream_socket_accept($server);
|
|
+
|
|
+ phpt_notify(message:"server-accepted");
|
|
+
|
|
+ $result = fread($conn, 1024);
|
|
+ $encoded_result = base64_encode($result);
|
|
+
|
|
+ fwrite($conn, "HTTP/1.0 200 Ok\r\nContent-Type: text/html; charset=utf-8\r\n\r\n$encoded_result\r\n");
|
|
+
|
|
+CODE;
|
|
+
|
|
+$clientCode = <<<'CODE'
|
|
+ $opts = [
|
|
+ "http" => [
|
|
+ "method" => "GET",
|
|
+ "header" => "Authorization: Bearer x\r\n"
|
|
+ ]
|
|
+ ];
|
|
+ $ctx = stream_context_create($opts);
|
|
+ var_dump(explode("\r\n", base64_decode(file_get_contents("http://user:pwd@{{ ADDR }}", false, $ctx))));
|
|
+ var_dump($http_response_header);
|
|
+CODE;
|
|
+
|
|
+include sprintf("%s/../../../openssl/tests/ServerClientTestCase.inc", __DIR__);
|
|
+ServerClientTestCase::getInstance()->run($clientCode, $serverCode);
|
|
+?>
|
|
+--EXPECTF--
|
|
+array(6) {
|
|
+ [0]=>
|
|
+ string(14) "GET / HTTP/1.1"
|
|
+ [1]=>
|
|
+ string(21) "Host: 127.0.0.1:%d"
|
|
+ [2]=>
|
|
+ string(17) "Connection: close"
|
|
+ [3]=>
|
|
+ string(23) "Authorization: Bearer x"
|
|
+ [4]=>
|
|
+ string(0) ""
|
|
+ [5]=>
|
|
+ string(0) ""
|
|
+}
|
|
+array(2) {
|
|
+ [0]=>
|
|
+ string(15) "HTTP/1.0 200 Ok"
|
|
+ [1]=>
|
|
+ string(38) "Content-Type: text/html; charset=utf-8"
|
|
+}
|
|
diff --git a/ext/standard/tests/http/ghsa-hgf5-96fm-v528-003.phpt b/ext/standard/tests/http/ghsa-hgf5-96fm-v528-003.phpt
|
|
new file mode 100644
|
|
index 00000000000..6c84679ff63
|
|
--- /dev/null
|
|
+++ b/ext/standard/tests/http/ghsa-hgf5-96fm-v528-003.phpt
|
|
@@ -0,0 +1,64 @@
|
|
+--TEST--
|
|
+GHSA-hgf5-96fm-v528: Header parser of http stream wrapper does not handle folded headers (correct middle pos)
|
|
+--FILE--
|
|
+<?php
|
|
+$serverCode = <<<'CODE'
|
|
+ $ctxt = stream_context_create([
|
|
+ "socket" => [
|
|
+ "tcp_nodelay" => true
|
|
+ ]
|
|
+ ]);
|
|
+
|
|
+ $server = stream_socket_server(
|
|
+ "tcp://127.0.0.1:0", $errno, $errstr, STREAM_SERVER_BIND | STREAM_SERVER_LISTEN, $ctxt);
|
|
+ phpt_notify_server_start($server);
|
|
+
|
|
+ $conn = stream_socket_accept($server);
|
|
+
|
|
+ phpt_notify(message:"server-accepted");
|
|
+
|
|
+ $result = fread($conn, 1024);
|
|
+ $encoded_result = base64_encode($result);
|
|
+
|
|
+ fwrite($conn, "HTTP/1.0 200 Ok\r\nContent-Type: text/html; charset=utf-8\r\n\r\n$encoded_result\r\n");
|
|
+
|
|
+CODE;
|
|
+
|
|
+$clientCode = <<<'CODE'
|
|
+ $opts = [
|
|
+ "http" => [
|
|
+ "method" => "GET",
|
|
+ "header" => "Cookie: x=y\r\nAuthorization: Bearer x\r\n"
|
|
+ ]
|
|
+ ];
|
|
+ $ctx = stream_context_create($opts);
|
|
+ var_dump(explode("\r\n", base64_decode(file_get_contents("http://user:pwd@{{ ADDR }}", false, $ctx))));
|
|
+ var_dump($http_response_header);
|
|
+CODE;
|
|
+
|
|
+include sprintf("%s/../../../openssl/tests/ServerClientTestCase.inc", __DIR__);
|
|
+ServerClientTestCase::getInstance()->run($clientCode, $serverCode);
|
|
+?>
|
|
+--EXPECTF--
|
|
+array(7) {
|
|
+ [0]=>
|
|
+ string(14) "GET / HTTP/1.1"
|
|
+ [1]=>
|
|
+ string(21) "Host: 127.0.0.1:%d"
|
|
+ [2]=>
|
|
+ string(17) "Connection: close"
|
|
+ [3]=>
|
|
+ string(11) "Cookie: x=y"
|
|
+ [4]=>
|
|
+ string(23) "Authorization: Bearer x"
|
|
+ [5]=>
|
|
+ string(0) ""
|
|
+ [6]=>
|
|
+ string(0) ""
|
|
+}
|
|
+array(2) {
|
|
+ [0]=>
|
|
+ string(15) "HTTP/1.0 200 Ok"
|
|
+ [1]=>
|
|
+ string(38) "Content-Type: text/html; charset=utf-8"
|
|
+}
|
|
--
|
|
2.48.1
|
|
|