From 8f65ef50929f6781f4973325f9b619f02cce19d8 Mon Sep 17 00:00:00 2001 From: Jakub Zelenka 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-- + [ + "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-- + [ + "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-- + [ + "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