fix low/moderate CVEs

Resolves: RHEL-66589
This commit is contained in:
Remi Collet 2024-11-13 08:57:02 +01:00
parent eb5b3533a9
commit ad6da4ad66
16 changed files with 2611 additions and 29 deletions

16
.gitignore vendored
View File

@ -1,17 +1,5 @@
clog
php*.bz2
php-5*.xz
php-7.0*.xz
php-7.1*.xz
php-7.2*.xz
php-7.3*.xz
php-8.0*.xz
php-8.0*.xz.asc
/php-7.4.6.tar.xz
/php-7.4.6.tar.xz.asc
/php-7.4.19.tar.xz
/php-7.4.19.tar.xz.asc
/php-7.4.30.tar.xz
/php-7.4.30.tar.xz.asc
php-8.*.xz
php-8.*.xz.asc
/php-7.4.33.tar.xz
/php-7.4.33.tar.xz.asc

View File

@ -1,7 +1,7 @@
From 7cb160efe19d3dfb8b92629805733ea186b55050 Mon Sep 17 00:00:00 2001
From: "Christoph M. Becker" <cmbecker69@gmx.de>
Date: Mon, 31 Oct 2022 17:20:23 +0100
Subject: [PATCH] Fix #81740: PDO::quote() may return unquoted string
Subject: [PATCH 1/2] Fix #81740: PDO::quote() may return unquoted string
`sqlite3_snprintf()` expects its first parameter to be `int`; we need
to avoid overflow.
@ -14,7 +14,7 @@ to avoid overflow.
create mode 100644 ext/pdo_sqlite/tests/bug81740.phpt
diff --git a/ext/pdo_sqlite/sqlite_driver.c b/ext/pdo_sqlite/sqlite_driver.c
index 0595bd09fe..54f9d05e1e 100644
index 0595bd09feb..54f9d05e1e2 100644
--- a/ext/pdo_sqlite/sqlite_driver.c
+++ b/ext/pdo_sqlite/sqlite_driver.c
@@ -233,6 +233,9 @@ static char *pdo_sqlite_last_insert_id(pdo_dbh_t *dbh, const char *name, size_t
@ -29,7 +29,7 @@ index 0595bd09fe..54f9d05e1e 100644
*quotedlen = strlen(*quoted);
diff --git a/ext/pdo_sqlite/tests/bug81740.phpt b/ext/pdo_sqlite/tests/bug81740.phpt
new file mode 100644
index 0000000000..99fb07c304
index 00000000000..99fb07c3048
--- /dev/null
+++ b/ext/pdo_sqlite/tests/bug81740.phpt
@@ -0,0 +1,17 @@
@ -50,3 +50,35 @@ index 0000000000..99fb07c304
+?>
+--EXPECT--
+bool(false)
--
2.38.1
From 7328f3a0344806b846bd05657bdce96e47810bf0 Mon Sep 17 00:00:00 2001
From: Remi Collet <remi@remirepo.net>
Date: Mon, 19 Dec 2022 09:24:02 +0100
Subject: [PATCH 2/2] NEWS
---
NEWS | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/NEWS b/NEWS
index 8a8c0c9285d..03e8c839c77 100644
--- a/NEWS
+++ b/NEWS
@@ -1,5 +1,12 @@
PHP NEWS
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
+
+Backported from 8.0.27
+
+- PDO/SQLite:
+ . Fixed bug #81740 (PDO::quote() may return unquoted string).
+ (CVE-2022-31631) (cmb)
+
03 Nov 2022, PHP 7.4.33
- GD:
--
2.38.1

188
php-cve-2023-0567.patch Normal file
View File

@ -0,0 +1,188 @@
From 7437aaae38cf4b3357e7580f9e22fd4a403b6c23 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Tim=20D=C3=BCsterhus?= <tim@bastelstu.be>
Date: Mon, 23 Jan 2023 21:15:24 +0100
Subject: [PATCH 1/7] crypt: Fix validation of malformed BCrypt hashes
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
PHPs implementation of crypt_blowfish differs from the upstream Openwall
version by adding a “PHP Hack”, which allows one to cut short the BCrypt salt
by including a `$` character within the characters that represent the salt.
Hashes that are affected by the “PHP Hack” may erroneously validate any
password as valid when used with `password_verify` and when comparing the
return value of `crypt()` against the input.
The PHP Hack exists since the first version of PHPs own crypt_blowfish
implementation that was added in 1e820eca02dcf322b41fd2fe4ed2a6b8309f8ab5.
No clear reason is given for the PHP Hacks existence. This commit removes it,
because BCrypt hashes containing a `$` character in their salt are not valid
BCrypt hashes.
(cherry picked from commit c840f71524067aa474c00c3eacfb83bd860bfc8a)
---
ext/standard/crypt_blowfish.c | 8 --
.../tests/crypt/bcrypt_salt_dollar.phpt | 82 +++++++++++++++++++
2 files changed, 82 insertions(+), 8 deletions(-)
create mode 100644 ext/standard/tests/crypt/bcrypt_salt_dollar.phpt
diff --git a/ext/standard/crypt_blowfish.c b/ext/standard/crypt_blowfish.c
index c1f945f29ed..aa7e1bc2e68 100644
--- a/ext/standard/crypt_blowfish.c
+++ b/ext/standard/crypt_blowfish.c
@@ -376,7 +376,6 @@ static unsigned char BF_atoi64[0x60] = {
#define BF_safe_atoi64(dst, src) \
{ \
tmp = (unsigned char)(src); \
- if (tmp == '$') break; /* PHP hack */ \
if ((unsigned int)(tmp -= 0x20) >= 0x60) return -1; \
tmp = BF_atoi64[tmp]; \
if (tmp > 63) return -1; \
@@ -404,13 +403,6 @@ static int BF_decode(BF_word *dst, const char *src, int size)
*dptr++ = ((c3 & 0x03) << 6) | c4;
} while (dptr < end);
- if (end - dptr == size) {
- return -1;
- }
-
- while (dptr < end) /* PHP hack */
- *dptr++ = 0;
-
return 0;
}
diff --git a/ext/standard/tests/crypt/bcrypt_salt_dollar.phpt b/ext/standard/tests/crypt/bcrypt_salt_dollar.phpt
new file mode 100644
index 00000000000..32e335f4b08
--- /dev/null
+++ b/ext/standard/tests/crypt/bcrypt_salt_dollar.phpt
@@ -0,0 +1,82 @@
+--TEST--
+bcrypt correctly rejects salts containing $
+--FILE--
+<?php
+for ($i = 0; $i < 23; $i++) {
+ $salt = '$2y$04$' . str_repeat('0', $i) . '$';
+ $result = crypt("foo", $salt);
+ var_dump($salt);
+ var_dump($result);
+ var_dump($result === $salt);
+}
+?>
+--EXPECT--
+string(8) "$2y$04$$"
+string(2) "*0"
+bool(false)
+string(9) "$2y$04$0$"
+string(2) "*0"
+bool(false)
+string(10) "$2y$04$00$"
+string(2) "*0"
+bool(false)
+string(11) "$2y$04$000$"
+string(2) "*0"
+bool(false)
+string(12) "$2y$04$0000$"
+string(2) "*0"
+bool(false)
+string(13) "$2y$04$00000$"
+string(2) "*0"
+bool(false)
+string(14) "$2y$04$000000$"
+string(2) "*0"
+bool(false)
+string(15) "$2y$04$0000000$"
+string(2) "*0"
+bool(false)
+string(16) "$2y$04$00000000$"
+string(2) "*0"
+bool(false)
+string(17) "$2y$04$000000000$"
+string(2) "*0"
+bool(false)
+string(18) "$2y$04$0000000000$"
+string(2) "*0"
+bool(false)
+string(19) "$2y$04$00000000000$"
+string(2) "*0"
+bool(false)
+string(20) "$2y$04$000000000000$"
+string(2) "*0"
+bool(false)
+string(21) "$2y$04$0000000000000$"
+string(2) "*0"
+bool(false)
+string(22) "$2y$04$00000000000000$"
+string(2) "*0"
+bool(false)
+string(23) "$2y$04$000000000000000$"
+string(2) "*0"
+bool(false)
+string(24) "$2y$04$0000000000000000$"
+string(2) "*0"
+bool(false)
+string(25) "$2y$04$00000000000000000$"
+string(2) "*0"
+bool(false)
+string(26) "$2y$04$000000000000000000$"
+string(2) "*0"
+bool(false)
+string(27) "$2y$04$0000000000000000000$"
+string(2) "*0"
+bool(false)
+string(28) "$2y$04$00000000000000000000$"
+string(2) "*0"
+bool(false)
+string(29) "$2y$04$000000000000000000000$"
+string(2) "*0"
+bool(false)
+string(30) "$2y$04$0000000000000000000000$"
+string(60) "$2y$04$000000000000000000000u2a2UpVexIt9k3FMJeAVr3c04F5tcI8K"
+bool(false)
--
2.39.1
From ed0281b588a6840cb95f3134a4e68847a3be5bb7 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Tim=20D=C3=BCsterhus?= <tim@bastelstu.be>
Date: Mon, 23 Jan 2023 22:13:57 +0100
Subject: [PATCH 2/7] crypt: Fix possible buffer overread in php_crypt()
(cherry picked from commit a92acbad873a05470af1a47cb785a18eadd827b5)
---
ext/standard/crypt.c | 1 +
ext/standard/tests/password/password_bcrypt_short.phpt | 8 ++++++++
2 files changed, 9 insertions(+)
create mode 100644 ext/standard/tests/password/password_bcrypt_short.phpt
diff --git a/ext/standard/crypt.c b/ext/standard/crypt.c
index 92430b69f77..04487f3fe5a 100644
--- a/ext/standard/crypt.c
+++ b/ext/standard/crypt.c
@@ -151,6 +151,7 @@ PHPAPI zend_string *php_crypt(const char *password, const int pass_len, const ch
} else if (
salt[0] == '$' &&
salt[1] == '2' &&
+ salt[2] != 0 &&
salt[3] == '$') {
char output[PHP_MAX_SALT_LEN + 1];
diff --git a/ext/standard/tests/password/password_bcrypt_short.phpt b/ext/standard/tests/password/password_bcrypt_short.phpt
new file mode 100644
index 00000000000..085bc8a2390
--- /dev/null
+++ b/ext/standard/tests/password/password_bcrypt_short.phpt
@@ -0,0 +1,8 @@
+--TEST--
+Test that password_hash() does not overread buffers when a short hash is passed
+--FILE--
+<?php
+var_dump(password_verify("foo", '$2'));
+?>
+--EXPECT--
+bool(false)
--
2.39.1

98
php-cve-2023-0568.patch Normal file
View File

@ -0,0 +1,98 @@
From 887cd0710ad856a0d22c329b6ea6c71ebd8621ae Mon Sep 17 00:00:00 2001
From: Niels Dossche <7771979+nielsdos@users.noreply.github.com>
Date: Fri, 27 Jan 2023 19:28:27 +0100
Subject: [PATCH 3/7] Fix array overrun when appending slash to paths
Fix it by extending the array sizes by one character. As the input is
limited to the maximum path length, there will always be place to append
the slash. As the php_check_specific_open_basedir() simply uses the
strings to compare against each other, no new failures related to too
long paths are introduced.
We'll let the DOM and XML case handle a potentially too long path in the
library code.
(cherry picked from commit ec10b28d64decbc54aa1e585dce580f0bd7a5953)
---
ext/dom/document.c | 2 +-
ext/xmlreader/php_xmlreader.c | 2 +-
main/fopen_wrappers.c | 6 +++---
3 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/ext/dom/document.c b/ext/dom/document.c
index b478e1a1aab..e683eb8f701 100644
--- a/ext/dom/document.c
+++ b/ext/dom/document.c
@@ -1380,7 +1380,7 @@ static xmlDocPtr dom_document_parser(zval *id, int mode, char *source, size_t so
int validate, recover, resolve_externals, keep_blanks, substitute_ent;
int resolved_path_len;
int old_error_reporting = 0;
- char *directory=NULL, resolved_path[MAXPATHLEN];
+ char *directory=NULL, resolved_path[MAXPATHLEN + 1];
if (id != NULL) {
intern = Z_DOMOBJ_P(id);
diff --git a/ext/xmlreader/php_xmlreader.c b/ext/xmlreader/php_xmlreader.c
index 06f569949ce..ecc81ad1470 100644
--- a/ext/xmlreader/php_xmlreader.c
+++ b/ext/xmlreader/php_xmlreader.c
@@ -1038,7 +1038,7 @@ PHP_METHOD(xmlreader, XML)
xmlreader_object *intern = NULL;
char *source, *uri = NULL, *encoding = NULL;
int resolved_path_len, ret = 0;
- char *directory=NULL, resolved_path[MAXPATHLEN];
+ char *directory=NULL, resolved_path[MAXPATHLEN + 1];
xmlParserInputBufferPtr inputbfr;
xmlTextReaderPtr reader;
diff --git a/main/fopen_wrappers.c b/main/fopen_wrappers.c
index 27135020fa3..90de040a218 100644
--- a/main/fopen_wrappers.c
+++ b/main/fopen_wrappers.c
@@ -138,10 +138,10 @@ PHPAPI ZEND_INI_MH(OnUpdateBaseDir)
*/
PHPAPI int php_check_specific_open_basedir(const char *basedir, const char *path)
{
- char resolved_name[MAXPATHLEN];
- char resolved_basedir[MAXPATHLEN];
+ char resolved_name[MAXPATHLEN + 1];
+ char resolved_basedir[MAXPATHLEN + 1];
char local_open_basedir[MAXPATHLEN];
- char path_tmp[MAXPATHLEN];
+ char path_tmp[MAXPATHLEN + 1];
char *path_file;
size_t resolved_basedir_len;
size_t resolved_name_len;
--
2.39.1
From 614468ce4056c0ef93aae09532dcffdf65b594b5 Mon Sep 17 00:00:00 2001
From: Remi Collet <remi@remirepo.net>
Date: Mon, 13 Feb 2023 11:46:47 +0100
Subject: [PATCH 4/7] NEWS
---
NEWS | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/NEWS b/NEWS
index 03e8c839c77..8157a20d4b3 100644
--- a/NEWS
+++ b/NEWS
@@ -1,6 +1,14 @@
PHP NEWS
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
+Backported from 8.0.28
+
+- Core:
+ . Fixed bug #81744 (Password_verify() always return true with some hash).
+ (CVE-2023-0567). (Tim Düsterhus)
+ . Fixed bug #81746 (1-byte array overrun in common path resolve code).
+ (CVE-2023-0568). (Niels Dossche)
+
Backported from 8.0.27
- PDO/SQLite:
--
2.39.1

143
php-cve-2023-0662.patch Normal file
View File

@ -0,0 +1,143 @@
From 3a2fdef1ae38881110006616ee1f0534b082ca45 Mon Sep 17 00:00:00 2001
From: Jakub Zelenka <bukka@php.net>
Date: Thu, 19 Jan 2023 14:11:18 +0000
Subject: [PATCH 5/7] Fix repeated warning for file uploads limit exceeding
---
main/rfc1867.c | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/main/rfc1867.c b/main/rfc1867.c
index edef19c16d6..4931b9aeefb 100644
--- a/main/rfc1867.c
+++ b/main/rfc1867.c
@@ -922,7 +922,10 @@ SAPI_API SAPI_POST_HANDLER_FUNC(rfc1867_post_handler) /* {{{ */
skip_upload = 1;
} else if (upload_cnt <= 0) {
skip_upload = 1;
- sapi_module.sapi_error(E_WARNING, "Maximum number of allowable file uploads has been exceeded");
+ if (upload_cnt == 0) {
+ --upload_cnt;
+ sapi_module.sapi_error(E_WARNING, "Maximum number of allowable file uploads has been exceeded");
+ }
}
/* Return with an error if the posted data is garbled */
--
2.39.1
From 8ec78d28d20c82c75c4747f44c52601cfdb22516 Mon Sep 17 00:00:00 2001
From: Jakub Zelenka <bukka@php.net>
Date: Thu, 19 Jan 2023 14:31:25 +0000
Subject: [PATCH 6/7] Introduce max_multipart_body_parts INI
This fixes GHSA-54hq-v5wp-fqgv DOS vulnerabality by limitting number of
parsed multipart body parts as currently all parts were always parsed.
---
main/main.c | 1 +
main/rfc1867.c | 11 +++++++++++
2 files changed, 12 insertions(+)
diff --git a/main/main.c b/main/main.c
index 0b33b2b56c9..d8c465988cc 100644
--- a/main/main.c
+++ b/main/main.c
@@ -836,6 +836,7 @@ PHP_INI_BEGIN()
PHP_INI_ENTRY("disable_functions", "", PHP_INI_SYSTEM, NULL)
PHP_INI_ENTRY("disable_classes", "", PHP_INI_SYSTEM, NULL)
PHP_INI_ENTRY("max_file_uploads", "20", PHP_INI_SYSTEM|PHP_INI_PERDIR, NULL)
+ PHP_INI_ENTRY("max_multipart_body_parts", "-1", PHP_INI_SYSTEM|PHP_INI_PERDIR, NULL)
STD_PHP_INI_BOOLEAN("allow_url_fopen", "1", PHP_INI_SYSTEM, OnUpdateBool, allow_url_fopen, php_core_globals, core_globals)
STD_PHP_INI_BOOLEAN("allow_url_include", "0", PHP_INI_SYSTEM, OnUpdateBool, allow_url_include, php_core_globals, core_globals)
diff --git a/main/rfc1867.c b/main/rfc1867.c
index 4931b9aeefb..1b212c93325 100644
--- a/main/rfc1867.c
+++ b/main/rfc1867.c
@@ -694,6 +694,7 @@ SAPI_API SAPI_POST_HANDLER_FUNC(rfc1867_post_handler) /* {{{ */
void *event_extra_data = NULL;
unsigned int llen = 0;
int upload_cnt = INI_INT("max_file_uploads");
+ int body_parts_cnt = INI_INT("max_multipart_body_parts");
const zend_encoding *internal_encoding = zend_multibyte_get_internal_encoding();
php_rfc1867_getword_t getword;
php_rfc1867_getword_conf_t getword_conf;
@@ -715,6 +716,11 @@ SAPI_API SAPI_POST_HANDLER_FUNC(rfc1867_post_handler) /* {{{ */
return;
}
+ if (body_parts_cnt < 0) {
+ body_parts_cnt = PG(max_input_vars) + upload_cnt;
+ }
+ int body_parts_limit = body_parts_cnt;
+
/* Get the boundary */
boundary = strstr(content_type_dup, "boundary");
if (!boundary) {
@@ -799,6 +805,11 @@ SAPI_API SAPI_POST_HANDLER_FUNC(rfc1867_post_handler) /* {{{ */
char *pair = NULL;
int end = 0;
+ if (--body_parts_cnt < 0) {
+ php_error_docref(NULL, E_WARNING, "Multipart body parts limit exceeded %d. To increase the limit change max_multipart_body_parts in php.ini.", body_parts_limit);
+ goto fileupload_done;
+ }
+
while (isspace(*cd)) {
++cd;
}
--
2.39.1
From 472db3ee3a00ac00d36019eee0b3b7362334481c Mon Sep 17 00:00:00 2001
From: Remi Collet <remi@remirepo.net>
Date: Tue, 14 Feb 2023 09:14:47 +0100
Subject: [PATCH 7/7] NEWS
---
NEWS | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/NEWS b/NEWS
index 8157a20d4b3..c1668368818 100644
--- a/NEWS
+++ b/NEWS
@@ -9,6 +9,10 @@ Backported from 8.0.28
. Fixed bug #81746 (1-byte array overrun in common path resolve code).
(CVE-2023-0568). (Niels Dossche)
+- FPM:
+ . Fixed bug GHSA-54hq-v5wp-fqgv (DOS vulnerability when parsing multipart
+ request body). (CVE-2023-0662) (Jakub Zelenka)
+
Backported from 8.0.27
- PDO/SQLite:
--
2.39.1
From c04f310440a906fc4ca885f4ecf6e3e4cd36edc7 Mon Sep 17 00:00:00 2001
From: Remi Collet <remi@remirepo.net>
Date: Tue, 14 Feb 2023 11:47:22 +0100
Subject: [PATCH] fix NEWS, not FPM specific
---
NEWS | 2 --
1 file changed, 2 deletions(-)
diff --git a/NEWS b/NEWS
index c1668368818..3f8739eae78 100644
--- a/NEWS
+++ b/NEWS
@@ -8,8 +8,6 @@ Backported from 8.0.28
(CVE-2023-0567). (Tim Düsterhus)
. Fixed bug #81746 (1-byte array overrun in common path resolve code).
(CVE-2023-0568). (Niels Dossche)
-
-- FPM:
. Fixed bug GHSA-54hq-v5wp-fqgv (DOS vulnerability when parsing multipart
request body). (CVE-2023-0662) (Jakub Zelenka)
--
2.39.1

152
php-cve-2023-3247.patch Normal file
View File

@ -0,0 +1,152 @@
From 0cfca9aa1395271833848daec0bace51d965531d Mon Sep 17 00:00:00 2001
From: Niels Dossche <7771979+nielsdos@users.noreply.github.com>
Date: Sun, 16 Apr 2023 15:05:03 +0200
Subject: [PATCH] Fix missing randomness check and insufficient random bytes
for SOAP HTTP Digest
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
If php_random_bytes_throw fails, the nonce will be uninitialized, but
still sent to the server. The client nonce is intended to protect
against a malicious server. See section 5.10 and 5.12 of RFC 7616 [1],
and bullet point 2 below.
Tim pointed out that even though it's the MD5 of the nonce that gets sent,
enumerating 31 bits is trivial. So we have still a stack information leak
of 31 bits.
Furthermore, Tim found the following issues:
* The small size of cnonce might cause the server to erroneously reject
a request due to a repeated (cnonce, nc) pair. As per the birthday
problem 31 bits of randomness will return a duplication with 50%
chance after less than 55000 requests and nc always starts counting at 1.
* The cnonce is intended to protect the client and password against a
malicious server that returns a constant server nonce where the server
precomputed a rainbow table between passwords and correct client response.
As storage is fairly cheap, a server could precompute the client responses
for (a subset of) client nonces and still have a chance of reversing the
client response with the same probability as the cnonce duplication.
Precomputing the rainbow table for all 2^31 cnonces increases the rainbow
table size by factor 2 billion, which is infeasible. But precomputing it
for 2^14 cnonces only increases the table size by factor 16k and the server
would still have a 10% chance of successfully reversing a password with a
single client request.
This patch fixes the issues by increasing the nonce size, and checking
the return value of php_random_bytes_throw(). In the process we also get
rid of the MD5 hashing of the nonce.
[1] RFC 7616: https://www.rfc-editor.org/rfc/rfc7616
Co-authored-by: Tim Düsterhus <timwolla@php.net>
(cherry picked from commit 126d517ce240e9f638d9a5eaa509eaca49ef562a)
---
NEWS | 6 ++++++
ext/soap/php_http.c | 21 +++++++++++++--------
2 files changed, 19 insertions(+), 8 deletions(-)
diff --git a/NEWS b/NEWS
index 3f8739eae7..7c07635cad 100644
--- a/NEWS
+++ b/NEWS
@@ -1,6 +1,12 @@
PHP NEWS
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
+Backported from 8.0.29
+
+- Soap:
+ . Fixed bug GHSA-76gg-c692-v2mw (Missing error check and insufficient random
+ bytes in HTTP Digest authentication for SOAP). (nielsdos, timwolla)
+
Backported from 8.0.28
- Core:
diff --git a/ext/soap/php_http.c b/ext/soap/php_http.c
index ee3dcbdc9a..e3a9afdbe9 100644
--- a/ext/soap/php_http.c
+++ b/ext/soap/php_http.c
@@ -666,18 +666,23 @@ int make_http_soap_request(zval *this_ptr,
if ((digest = zend_hash_str_find(Z_OBJPROP_P(this_ptr), "_digest", sizeof("_digest")-1)) != NULL) {
if (Z_TYPE_P(digest) == IS_ARRAY) {
char HA1[33], HA2[33], response[33], cnonce[33], nc[9];
- zend_long nonce;
+ unsigned char nonce[16];
PHP_MD5_CTX md5ctx;
unsigned char hash[16];
- php_random_bytes_throw(&nonce, sizeof(nonce));
- nonce &= 0x7fffffff;
+ if (UNEXPECTED(php_random_bytes_throw(&nonce, sizeof(nonce)) != SUCCESS)) {
+ ZEND_ASSERT(EG(exception));
+ php_stream_close(stream);
+ zend_hash_str_del(Z_OBJPROP_P(this_ptr), "httpurl", sizeof("httpurl")-1);
+ zend_hash_str_del(Z_OBJPROP_P(this_ptr), "httpsocket", sizeof("httpsocket")-1);
+ zend_hash_str_del(Z_OBJPROP_P(this_ptr), "_use_proxy", sizeof("_use_proxy")-1);
+ smart_str_free(&soap_headers_z);
+ smart_str_free(&soap_headers);
+ return FALSE;
+ }
- PHP_MD5Init(&md5ctx);
- snprintf(cnonce, sizeof(cnonce), ZEND_LONG_FMT, nonce);
- PHP_MD5Update(&md5ctx, (unsigned char*)cnonce, strlen(cnonce));
- PHP_MD5Final(hash, &md5ctx);
- make_digest(cnonce, hash);
+ php_hash_bin2hex(cnonce, nonce, sizeof(nonce));
+ cnonce[32] = 0;
if ((tmp = zend_hash_str_find(Z_ARRVAL_P(digest), "nc", sizeof("nc")-1)) != NULL &&
Z_TYPE_P(tmp) == IS_LONG) {
From 40439039c224bb8cdebd1b7b3d03b8cc11e7cce7 Mon Sep 17 00:00:00 2001
From: Remi Collet <remi@remirepo.net>
Date: Tue, 6 Jun 2023 18:05:22 +0200
Subject: [PATCH] Fix GH-11382 add missing hash header for bin2hex
---
ext/soap/php_http.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/ext/soap/php_http.c b/ext/soap/php_http.c
index e3a9afdbe9f..912b8e341d8 100644
--- a/ext/soap/php_http.c
+++ b/ext/soap/php_http.c
@@ -22,6 +22,7 @@
#include "ext/standard/base64.h"
#include "ext/standard/md5.h"
#include "ext/standard/php_random.h"
+#include "ext/hash/php_hash.h"
static char *get_http_header_value_nodup(char *headers, char *type, size_t *len);
static char *get_http_header_value(char *headers, char *type);
--
2.40.1
From f3021d66d7bb42d2578530cc94f9bde47e58eb10 Mon Sep 17 00:00:00 2001
From: Remi Collet <remi@remirepo.net>
Date: Thu, 15 Jun 2023 08:47:55 +0200
Subject: [PATCH] add cve
---
NEWS | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/NEWS b/NEWS
index 7c07635cade..899644b3d63 100644
--- a/NEWS
+++ b/NEWS
@@ -5,7 +5,8 @@ Backported from 8.0.29
- Soap:
. Fixed bug GHSA-76gg-c692-v2mw (Missing error check and insufficient random
- bytes in HTTP Digest authentication for SOAP). (nielsdos, timwolla)
+ bytes in HTTP Digest authentication for SOAP).
+ (CVE-2023-3247) (nielsdos, timwolla)
Backported from 8.0.28
--
2.40.1

89
php-cve-2023-3823.patch Normal file
View File

@ -0,0 +1,89 @@
From c398fe98c044c8e7c23135acdc38d4ef7bedc983 Mon Sep 17 00:00:00 2001
From: Niels Dossche <7771979+nielsdos@users.noreply.github.com>
Date: Mon, 10 Jul 2023 13:25:34 +0200
Subject: [PATCH 1/4] Fix buffer mismanagement in phar_dir_read()
Fixes GHSA-jqcx-ccgc-xwhv.
(cherry picked from commit 80316123f3e9dcce8ac419bd9dd43546e2ccb5ef)
---
ext/phar/dirstream.c | 15 ++++++++------
ext/phar/tests/GHSA-jqcx-ccgc-xwhv.phpt | 27 +++++++++++++++++++++++++
2 files changed, 36 insertions(+), 6 deletions(-)
create mode 100644 ext/phar/tests/GHSA-jqcx-ccgc-xwhv.phpt
diff --git a/ext/phar/dirstream.c b/ext/phar/dirstream.c
index 4710703c70e..490b14528f1 100644
--- a/ext/phar/dirstream.c
+++ b/ext/phar/dirstream.c
@@ -91,25 +91,28 @@ static int phar_dir_seek(php_stream *stream, zend_off_t offset, int whence, zend
*/
static ssize_t phar_dir_read(php_stream *stream, char *buf, size_t count) /* {{{ */
{
- size_t to_read;
HashTable *data = (HashTable *)stream->abstract;
zend_string *str_key;
zend_ulong unused;
+ if (count != sizeof(php_stream_dirent)) {
+ return -1;
+ }
+
if (HASH_KEY_NON_EXISTENT == zend_hash_get_current_key(data, &str_key, &unused)) {
return 0;
}
zend_hash_move_forward(data);
- to_read = MIN(ZSTR_LEN(str_key), count);
- if (to_read == 0 || count < ZSTR_LEN(str_key)) {
+ php_stream_dirent *dirent = (php_stream_dirent *) buf;
+
+ if (sizeof(dirent->d_name) <= ZSTR_LEN(str_key)) {
return 0;
}
- memset(buf, 0, sizeof(php_stream_dirent));
- memcpy(((php_stream_dirent *) buf)->d_name, ZSTR_VAL(str_key), to_read);
- ((php_stream_dirent *) buf)->d_name[to_read + 1] = '\0';
+ memset(dirent, 0, sizeof(php_stream_dirent));
+ PHP_STRLCPY(dirent->d_name, ZSTR_VAL(str_key), sizeof(dirent->d_name), ZSTR_LEN(str_key));
return sizeof(php_stream_dirent);
}
diff --git a/ext/phar/tests/GHSA-jqcx-ccgc-xwhv.phpt b/ext/phar/tests/GHSA-jqcx-ccgc-xwhv.phpt
new file mode 100644
index 00000000000..4e12f05fb62
--- /dev/null
+++ b/ext/phar/tests/GHSA-jqcx-ccgc-xwhv.phpt
@@ -0,0 +1,27 @@
+--TEST--
+GHSA-jqcx-ccgc-xwhv (Buffer overflow and overread in phar_dir_read())
+--SKIPIF--
+<?php if (!extension_loaded("phar")) die("skip"); ?>
+--INI--
+phar.readonly=0
+--FILE--
+<?php
+$phar = new Phar(__DIR__. '/GHSA-jqcx-ccgc-xwhv.phar');
+$phar->startBuffering();
+$phar->addFromString(str_repeat('A', PHP_MAXPATHLEN - 1), 'This is the content of file 1.');
+$phar->addFromString(str_repeat('B', PHP_MAXPATHLEN - 1).'C', 'This is the content of file 2.');
+$phar->stopBuffering();
+
+$handle = opendir('phar://' . __DIR__ . '/GHSA-jqcx-ccgc-xwhv.phar');
+var_dump(strlen(readdir($handle)));
+// Must not be a string of length PHP_MAXPATHLEN+1
+var_dump(readdir($handle));
+closedir($handle);
+?>
+--CLEAN--
+<?php
+unlink(__DIR__. '/GHSA-jqcx-ccgc-xwhv.phar');
+?>
+--EXPECTF--
+int(%d)
+bool(false)
--
2.41.0

644
php-cve-2023-3824.patch Normal file
View File

@ -0,0 +1,644 @@
From b3758bd21223b97c042cae7bd26a66cde081ea98 Mon Sep 17 00:00:00 2001
From: Niels Dossche <7771979+nielsdos@users.noreply.github.com>
Date: Sat, 15 Jul 2023 17:33:52 +0200
Subject: [PATCH 2/4] Sanitize libxml2 globals before parsing
Fixes GHSA-3qrf-m4j2-pcrr.
To parse a document with libxml2, you first need to create a parsing context.
The parsing context contains parsing options (e.g. XML_NOENT to substitute
entities) that the application (in this case PHP) can set.
Unfortunately, libxml2 also supports providing default set options.
For example, if you call xmlSubstituteEntitiesDefault(1) then the XML_NOENT
option will be added to the parsing options every time you create a parsing
context **even if the application never requested XML_NOENT**.
Third party extensions can override these globals, in particular the
substitute entity global. This causes entity substitution to be
unexpectedly active.
Fix it by setting the parsing options to a sane known value.
For API calls that depend on global state we introduce
PHP_LIBXML_SANITIZE_GLOBALS() and PHP_LIBXML_RESTORE_GLOBALS().
For other APIs that work directly with a context we introduce
php_libxml_sanitize_parse_ctxt_options().
(cherry picked from commit c283c3ab0ba45d21b2b8745c1f9c7cbfe771c975)
---
ext/dom/document.c | 15 ++++++++
ext/dom/documentfragment.c | 2 ++
...xml_global_state_entity_loader_bypass.phpt | 36 +++++++++++++++++++
ext/libxml/php_libxml.h | 36 +++++++++++++++++++
ext/simplexml/simplexml.c | 6 ++++
...xml_global_state_entity_loader_bypass.phpt | 36 +++++++++++++++++++
ext/soap/php_xml.c | 2 ++
ext/xml/compat.c | 2 ++
ext/xmlreader/php_xmlreader.c | 9 +++++
...xml_global_state_entity_loader_bypass.phpt | 35 ++++++++++++++++++
ext/xsl/xsltprocessor.c | 9 +++--
11 files changed, 183 insertions(+), 5 deletions(-)
create mode 100644 ext/dom/tests/libxml_global_state_entity_loader_bypass.phpt
create mode 100644 ext/simplexml/tests/libxml_global_state_entity_loader_bypass.phpt
create mode 100644 ext/xmlreader/tests/libxml_global_state_entity_loader_bypass.phpt
diff --git a/ext/dom/document.c b/ext/dom/document.c
index e683eb8f701..989b5b3dd24 100644
--- a/ext/dom/document.c
+++ b/ext/dom/document.c
@@ -1459,6 +1459,7 @@ static xmlDocPtr dom_document_parser(zval *id, int mode, char *source, size_t so
options |= XML_PARSE_NOBLANKS;
}
+ php_libxml_sanitize_parse_ctxt_options(ctxt);
xmlCtxtUseOptions(ctxt, options);
ctxt->recovery = recover;
@@ -1759,7 +1760,9 @@ PHP_FUNCTION(dom_document_xinclude)
DOM_GET_OBJ(docp, id, xmlDocPtr, intern);
+ PHP_LIBXML_SANITIZE_GLOBALS(xinclude);
err = xmlXIncludeProcessFlags(docp, (int)flags);
+ PHP_LIBXML_RESTORE_GLOBALS(xinclude);
/* XML_XINCLUDE_START and XML_XINCLUDE_END nodes need to be removed as these
are added via xmlXIncludeProcess to mark beginning and ending of xincluded document
@@ -1799,6 +1802,7 @@ PHP_FUNCTION(dom_document_validate)
DOM_GET_OBJ(docp, id, xmlDocPtr, intern);
+ PHP_LIBXML_SANITIZE_GLOBALS(validate);
cvp = xmlNewValidCtxt();
cvp->userData = NULL;
@@ -1810,6 +1814,7 @@ PHP_FUNCTION(dom_document_validate)
} else {
RETVAL_FALSE;
}
+ PHP_LIBXML_RESTORE_GLOBALS(validate);
xmlFreeValidCtxt(cvp);
@@ -1844,14 +1849,18 @@ static void _dom_document_schema_validate(INTERNAL_FUNCTION_PARAMETERS, int type
DOM_GET_OBJ(docp, id, xmlDocPtr, intern);
+ PHP_LIBXML_SANITIZE_GLOBALS(new_parser_ctxt);
+
switch (type) {
case DOM_LOAD_FILE:
if (CHECK_NULL_PATH(source, source_len)) {
+ PHP_LIBXML_RESTORE_GLOBALS(new_parser_ctxt);
php_error_docref(NULL, E_WARNING, "Invalid Schema file source");
RETURN_FALSE;
}
valid_file = _dom_get_valid_file_path(source, resolved_path, MAXPATHLEN);
if (!valid_file) {
+ PHP_LIBXML_RESTORE_GLOBALS(new_parser_ctxt);
php_error_docref(NULL, E_WARNING, "Invalid Schema file source");
RETURN_FALSE;
}
@@ -1872,6 +1881,7 @@ static void _dom_document_schema_validate(INTERNAL_FUNCTION_PARAMETERS, int type
parser);
sptr = xmlSchemaParse(parser);
xmlSchemaFreeParserCtxt(parser);
+ PHP_LIBXML_RESTORE_GLOBALS(new_parser_ctxt);
if (!sptr) {
php_error_docref(NULL, E_WARNING, "Invalid Schema");
RETURN_FALSE;
@@ -1890,11 +1900,13 @@ static void _dom_document_schema_validate(INTERNAL_FUNCTION_PARAMETERS, int type
valid_opts |= XML_SCHEMA_VAL_VC_I_CREATE;
}
+ PHP_LIBXML_SANITIZE_GLOBALS(validate);
xmlSchemaSetValidOptions(vptr, valid_opts);
xmlSchemaSetValidErrors(vptr, php_libxml_error_handler, php_libxml_error_handler, vptr);
is_valid = xmlSchemaValidateDoc(vptr, docp);
xmlSchemaFree(sptr);
xmlSchemaFreeValidCtxt(vptr);
+ PHP_LIBXML_RESTORE_GLOBALS(validate);
if (is_valid == 0) {
RETURN_TRUE;
@@ -1965,12 +1977,14 @@ static void _dom_document_relaxNG_validate(INTERNAL_FUNCTION_PARAMETERS, int typ
return;
}
+ PHP_LIBXML_SANITIZE_GLOBALS(parse);
xmlRelaxNGSetParserErrors(parser,
(xmlRelaxNGValidityErrorFunc) php_libxml_error_handler,
(xmlRelaxNGValidityWarningFunc) php_libxml_error_handler,
parser);
sptr = xmlRelaxNGParse(parser);
xmlRelaxNGFreeParserCtxt(parser);
+ PHP_LIBXML_RESTORE_GLOBALS(parse);
if (!sptr) {
php_error_docref(NULL, E_WARNING, "Invalid RelaxNG");
RETURN_FALSE;
@@ -2069,6 +2083,7 @@ static void dom_load_html(INTERNAL_FUNCTION_PARAMETERS, int mode) /* {{{ */
ctxt->sax->error = php_libxml_ctx_error;
ctxt->sax->warning = php_libxml_ctx_warning;
}
+ php_libxml_sanitize_parse_ctxt_options(ctxt);
if (options) {
htmlCtxtUseOptions(ctxt, (int)options);
}
diff --git a/ext/dom/documentfragment.c b/ext/dom/documentfragment.c
index 9b222586ac5..711c42f939d 100644
--- a/ext/dom/documentfragment.c
+++ b/ext/dom/documentfragment.c
@@ -131,7 +131,9 @@ PHP_METHOD(domdocumentfragment, appendXML) {
}
if (data) {
+ PHP_LIBXML_SANITIZE_GLOBALS(parse);
err = xmlParseBalancedChunkMemory(nodep->doc, NULL, NULL, 0, (xmlChar *) data, &lst);
+ PHP_LIBXML_RESTORE_GLOBALS(parse);
if (err != 0) {
RETURN_FALSE;
}
diff --git a/ext/dom/tests/libxml_global_state_entity_loader_bypass.phpt b/ext/dom/tests/libxml_global_state_entity_loader_bypass.phpt
new file mode 100644
index 00000000000..b28afd4694e
--- /dev/null
+++ b/ext/dom/tests/libxml_global_state_entity_loader_bypass.phpt
@@ -0,0 +1,36 @@
+--TEST--
+GHSA-3qrf-m4j2-pcrr (libxml global state entity loader bypass)
+--SKIPIF--
+<?php
+if (!extension_loaded('libxml')) die('skip libxml extension not available');
+if (!extension_loaded('dom')) die('skip dom extension not available');
+if (!extension_loaded('zend-test')) die('skip zend-test extension not available');
+?>
+--FILE--
+<?php
+
+$xml = "<?xml version='1.0'?><!DOCTYPE root [<!ENTITY % bork SYSTEM \"php://nope\"> %bork;]><nothing/>";
+
+libxml_use_internal_errors(true);
+
+function parseXML($xml) {
+ $doc = new DOMDocument();
+ @$doc->loadXML($xml);
+ $doc->createDocumentFragment()->appendXML("&bork;");
+ foreach (libxml_get_errors() as $error) {
+ var_dump(trim($error->message));
+ }
+}
+
+parseXML($xml);
+zend_test_override_libxml_global_state();
+parseXML($xml);
+
+echo "Done\n";
+
+?>
+--EXPECT--
+string(25) "Entity 'bork' not defined"
+string(25) "Entity 'bork' not defined"
+string(25) "Entity 'bork' not defined"
+Done
diff --git a/ext/libxml/php_libxml.h b/ext/libxml/php_libxml.h
index cf936e95de1..92028d5703e 100644
--- a/ext/libxml/php_libxml.h
+++ b/ext/libxml/php_libxml.h
@@ -121,6 +121,42 @@ PHP_LIBXML_API void php_libxml_shutdown(void);
ZEND_TSRMLS_CACHE_EXTERN()
#endif
+/* Other extension may override the global state options, these global options
+ * are copied initially to ctxt->options. Set the options to a known good value.
+ * See libxml2 globals.c and parserInternals.c.
+ * The unique_name argument allows multiple sanitizes and restores within the
+ * same function, even nested is necessary. */
+#define PHP_LIBXML_SANITIZE_GLOBALS(unique_name) \
+ int xml_old_loadsubset_##unique_name = xmlLoadExtDtdDefaultValue; \
+ xmlLoadExtDtdDefaultValue = 0; \
+ int xml_old_validate_##unique_name = xmlDoValidityCheckingDefaultValue; \
+ xmlDoValidityCheckingDefaultValue = 0; \
+ int xml_old_pedantic_##unique_name = xmlPedanticParserDefault(0); \
+ int xml_old_substitute_##unique_name = xmlSubstituteEntitiesDefault(0); \
+ int xml_old_linenrs_##unique_name = xmlLineNumbersDefault(0); \
+ int xml_old_blanks_##unique_name = xmlKeepBlanksDefault(1);
+
+#define PHP_LIBXML_RESTORE_GLOBALS(unique_name) \
+ xmlLoadExtDtdDefaultValue = xml_old_loadsubset_##unique_name; \
+ xmlDoValidityCheckingDefaultValue = xml_old_validate_##unique_name; \
+ (void) xmlPedanticParserDefault(xml_old_pedantic_##unique_name); \
+ (void) xmlSubstituteEntitiesDefault(xml_old_substitute_##unique_name); \
+ (void) xmlLineNumbersDefault(xml_old_linenrs_##unique_name); \
+ (void) xmlKeepBlanksDefault(xml_old_blanks_##unique_name);
+
+/* Alternative for above, working directly on the context and not setting globals.
+ * Generally faster because no locking is involved, and this has the advantage that it sets the options to a known good value. */
+static zend_always_inline void php_libxml_sanitize_parse_ctxt_options(xmlParserCtxtPtr ctxt)
+{
+ ctxt->loadsubset = 0;
+ ctxt->validate = 0;
+ ctxt->pedantic = 0;
+ ctxt->replaceEntities = 0;
+ ctxt->linenumbers = 0;
+ ctxt->keepBlanks = 1;
+ ctxt->options = 0;
+}
+
#else /* HAVE_LIBXML */
#define libxml_module_ptr NULL
#endif
diff --git a/ext/simplexml/simplexml.c b/ext/simplexml/simplexml.c
index 2cdff0e648d..101a9d8fd8c 100644
--- a/ext/simplexml/simplexml.c
+++ b/ext/simplexml/simplexml.c
@@ -2194,7 +2194,9 @@ PHP_FUNCTION(simplexml_load_file)
RETURN_FALSE;
}
+ PHP_LIBXML_SANITIZE_GLOBALS(read_file);
docp = xmlReadFile(filename, NULL, (int)options);
+ PHP_LIBXML_RESTORE_GLOBALS(read_file);
if (!docp) {
RETURN_FALSE;
@@ -2248,7 +2250,9 @@ PHP_FUNCTION(simplexml_load_string)
RETURN_FALSE;
}
+ PHP_LIBXML_SANITIZE_GLOBALS(read_memory);
docp = xmlReadMemory(data, (int)data_len, NULL, NULL, (int)options);
+ PHP_LIBXML_RESTORE_GLOBALS(read_memory);
if (!docp) {
RETURN_FALSE;
@@ -2298,7 +2302,9 @@ SXE_METHOD(__construct)
return;
}
+ PHP_LIBXML_SANITIZE_GLOBALS(read_file_or_memory);
docp = is_url ? xmlReadFile(data, NULL, (int)options) : xmlReadMemory(data, (int)data_len, NULL, NULL, (int)options);
+ PHP_LIBXML_RESTORE_GLOBALS(read_file_or_memory);
if (!docp) {
((php_libxml_node_object *)sxe)->document = NULL;
diff --git a/ext/simplexml/tests/libxml_global_state_entity_loader_bypass.phpt b/ext/simplexml/tests/libxml_global_state_entity_loader_bypass.phpt
new file mode 100644
index 00000000000..2152e012328
--- /dev/null
+++ b/ext/simplexml/tests/libxml_global_state_entity_loader_bypass.phpt
@@ -0,0 +1,36 @@
+--TEST--
+GHSA-3qrf-m4j2-pcrr (libxml global state entity loader bypass)
+--SKIPIF--
+<?php
+if (!extension_loaded('libxml')) die('skip libxml extension not available');
+if (!extension_loaded('simplexml')) die('skip simplexml extension not available');
+if (!extension_loaded('zend-test')) die('skip zend-test extension not available');
+?>
+--FILE--
+<?php
+
+$xml = "<?xml version='1.0'?><!DOCTYPE root [<!ENTITY % bork SYSTEM \"php://nope\"> %bork;]><nothing/>";
+
+libxml_use_internal_errors(true);
+zend_test_override_libxml_global_state();
+
+echo "--- String test ---\n";
+simplexml_load_string($xml);
+echo "--- Constructor test ---\n";
+new SimpleXMLElement($xml);
+echo "--- File test ---\n";
+file_put_contents("libxml_global_state_entity_loader_bypass.tmp", $xml);
+simplexml_load_file("libxml_global_state_entity_loader_bypass.tmp");
+
+echo "Done\n";
+
+?>
+--CLEAN--
+<?php
+@unlink("libxml_global_state_entity_loader_bypass.tmp");
+?>
+--EXPECT--
+--- String test ---
+--- Constructor test ---
+--- File test ---
+Done
diff --git a/ext/soap/php_xml.c b/ext/soap/php_xml.c
index 18a266179b7..1bb7fa00a37 100644
--- a/ext/soap/php_xml.c
+++ b/ext/soap/php_xml.c
@@ -93,6 +93,7 @@ xmlDocPtr soap_xmlParseFile(const char *filename)
if (ctxt) {
zend_bool old;
+ php_libxml_sanitize_parse_ctxt_options(ctxt);
ctxt->keepBlanks = 0;
ctxt->sax->ignorableWhitespace = soap_ignorableWhitespace;
ctxt->sax->comment = soap_Comment;
@@ -141,6 +142,7 @@ xmlDocPtr soap_xmlParseMemory(const void *buf, size_t buf_size)
if (ctxt) {
zend_bool old;
+ php_libxml_sanitize_parse_ctxt_options(ctxt);
ctxt->sax->ignorableWhitespace = soap_ignorableWhitespace;
ctxt->sax->comment = soap_Comment;
ctxt->sax->warning = NULL;
diff --git a/ext/xml/compat.c b/ext/xml/compat.c
index fc4525650fc..57eb00dd429 100644
--- a/ext/xml/compat.c
+++ b/ext/xml/compat.c
@@ -19,6 +19,7 @@
#include "php.h"
#if defined(HAVE_LIBXML) && (defined(HAVE_XML) || defined(HAVE_XMLRPC)) && !defined(HAVE_LIBEXPAT)
#include "expat_compat.h"
+#include "ext/libxml/php_libxml.h"
typedef struct _php_xml_ns {
xmlNsPtr nsptr;
@@ -471,6 +472,7 @@ XML_ParserCreate_MM(const XML_Char *encoding, const XML_Memory_Handling_Suite *m
return NULL;
}
+ php_libxml_sanitize_parse_ctxt_options(parser->parser);
xmlCtxtUseOptions(parser->parser, XML_PARSE_OLDSAX);
parser->parser->replaceEntities = 1;
diff --git a/ext/xmlreader/php_xmlreader.c b/ext/xmlreader/php_xmlreader.c
index ecc81ad1470..51d6bb9c9f2 100644
--- a/ext/xmlreader/php_xmlreader.c
+++ b/ext/xmlreader/php_xmlreader.c
@@ -304,6 +304,7 @@ static xmlRelaxNGPtr _xmlreader_get_relaxNG(char *source, size_t source_len, siz
return NULL;
}
+ PHP_LIBXML_SANITIZE_GLOBALS(parse);
if (error_func || warn_func) {
xmlRelaxNGSetParserErrors(parser,
(xmlRelaxNGValidityErrorFunc) error_func,
@@ -312,6 +313,7 @@ static xmlRelaxNGPtr _xmlreader_get_relaxNG(char *source, size_t source_len, siz
}
sptr = xmlRelaxNGParse(parser);
xmlRelaxNGFreeParserCtxt(parser);
+ PHP_LIBXML_RESTORE_GLOBALS(parse);
return sptr;
}
@@ -881,7 +883,9 @@ PHP_METHOD(xmlreader, open)
valid_file = _xmlreader_get_valid_file_path(source, resolved_path, MAXPATHLEN );
if (valid_file) {
+ PHP_LIBXML_SANITIZE_GLOBALS(reader_for_file);
reader = xmlReaderForFile(valid_file, encoding, options);
+ PHP_LIBXML_RESTORE_GLOBALS(reader_for_file);
}
if (reader == NULL) {
@@ -958,7 +962,9 @@ PHP_METHOD(xmlreader, setSchema)
intern = Z_XMLREADER_P(id);
if (intern && intern->ptr) {
+ PHP_LIBXML_SANITIZE_GLOBALS(schema);
retval = xmlTextReaderSchemaValidate(intern->ptr, source);
+ PHP_LIBXML_RESTORE_GLOBALS(schema);
if (retval == 0) {
RETURN_TRUE;
@@ -1082,6 +1088,7 @@ PHP_METHOD(xmlreader, XML)
}
uri = (char *) xmlCanonicPath((const xmlChar *) resolved_path);
}
+ PHP_LIBXML_SANITIZE_GLOBALS(text_reader);
reader = xmlNewTextReader(inputbfr, uri);
if (reader != NULL) {
@@ -1100,9 +1107,11 @@ PHP_METHOD(xmlreader, XML)
xmlFree(uri);
}
+ PHP_LIBXML_RESTORE_GLOBALS(text_reader);
return;
}
}
+ PHP_LIBXML_RESTORE_GLOBALS(text_reader);
}
if (uri) {
diff --git a/ext/xmlreader/tests/libxml_global_state_entity_loader_bypass.phpt b/ext/xmlreader/tests/libxml_global_state_entity_loader_bypass.phpt
new file mode 100644
index 00000000000..e9ffb04c2bb
--- /dev/null
+++ b/ext/xmlreader/tests/libxml_global_state_entity_loader_bypass.phpt
@@ -0,0 +1,35 @@
+--TEST--
+GHSA-3qrf-m4j2-pcrr (libxml global state entity loader bypass)
+--SKIPIF--
+<?php
+if (!extension_loaded('libxml')) die('skip libxml extension not available');
+if (!extension_loaded('xmlreader')) die('skip xmlreader extension not available');
+if (!extension_loaded('zend-test')) die('skip zend-test extension not available');
+?>
+--FILE--
+<?php
+
+$xml = "<?xml version='1.0'?><!DOCTYPE root [<!ENTITY % bork SYSTEM \"php://nope\"> %bork;]><nothing/>";
+
+libxml_use_internal_errors(true);
+zend_test_override_libxml_global_state();
+
+echo "--- String test ---\n";
+$reader = XMLReader::xml($xml);
+$reader->read();
+echo "--- File test ---\n";
+file_put_contents("libxml_global_state_entity_loader_bypass.tmp", $xml);
+$reader = XMLReader::open("libxml_global_state_entity_loader_bypass.tmp");
+$reader->read();
+
+echo "Done\n";
+
+?>
+--CLEAN--
+<?php
+@unlink("libxml_global_state_entity_loader_bypass.tmp");
+?>
+--EXPECT--
+--- String test ---
+--- File test ---
+Done
diff --git a/ext/xsl/xsltprocessor.c b/ext/xsl/xsltprocessor.c
index 079920d0ffa..2d95b2ff4bb 100644
--- a/ext/xsl/xsltprocessor.c
+++ b/ext/xsl/xsltprocessor.c
@@ -398,7 +398,7 @@ PHP_FUNCTION(xsl_xsltprocessor_import_stylesheet)
xmlDoc *doc = NULL, *newdoc = NULL;
xsltStylesheetPtr sheetp, oldsheetp;
xsl_object *intern;
- int prevSubstValue, prevExtDtdValue, clone_docu = 0;
+ int clone_docu = 0;
xmlNode *nodep = NULL;
zval *cloneDocu, member, rv;
@@ -421,13 +421,12 @@ PHP_FUNCTION(xsl_xsltprocessor_import_stylesheet)
stylesheet document otherwise the node proxies will be a mess */
newdoc = xmlCopyDoc(doc, 1);
xmlNodeSetBase((xmlNodePtr) newdoc, (xmlChar *)doc->URL);
- prevSubstValue = xmlSubstituteEntitiesDefault(1);
- prevExtDtdValue = xmlLoadExtDtdDefaultValue;
+ PHP_LIBXML_SANITIZE_GLOBALS(parse);
+ xmlSubstituteEntitiesDefault(1);
xmlLoadExtDtdDefaultValue = XML_DETECT_IDS | XML_COMPLETE_ATTRS;
sheetp = xsltParseStylesheetDoc(newdoc);
- xmlSubstituteEntitiesDefault(prevSubstValue);
- xmlLoadExtDtdDefaultValue = prevExtDtdValue;
+ PHP_LIBXML_RESTORE_GLOBALS(parse);
if (!sheetp) {
xmlFreeDoc(newdoc);
--
2.41.0
From ef1d507acf7be23d7624dc3c891683b2218feb51 Mon Sep 17 00:00:00 2001
From: Remi Collet <remi@remirepo.net>
Date: Tue, 1 Aug 2023 07:22:33 +0200
Subject: [PATCH 3/4] NEWS
---
NEWS | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/NEWS b/NEWS
index 899644b3d63..4f88029a7d6 100644
--- a/NEWS
+++ b/NEWS
@@ -1,6 +1,16 @@
PHP NEWS
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
+Backported from 8.0.30
+
+- Libxml:
+ . Fixed bug GHSA-3qrf-m4j2-pcrr (Security issue with external entity loading
+ in XML without enabling it). (CVE-2023-3823) (nielsdos, ilutov)
+
+- Phar:
+ . Fixed bug GHSA-jqcx-ccgc-xwhv (Buffer mismanagement in phar_dir_read()).
+ (CVE-2023-3824) (nielsdos)
+
Backported from 8.0.29
- Soap:
--
2.41.0
From 24e669e790e6aebd219c9a9fa19017455c8646b4 Mon Sep 17 00:00:00 2001
From: Remi Collet <remi@remirepo.net>
Date: Tue, 1 Aug 2023 07:37:25 +0200
Subject: [PATCH 4/4] backport zend_test changes
(zend_test_override_libxml_global_state)
---
...xml_global_state_entity_loader_bypass.phpt | 1 +
...xml_global_state_entity_loader_bypass.phpt | 1 +
...xml_global_state_entity_loader_bypass.phpt | 5 +++--
ext/zend_test/test.c | 22 +++++++++++++++++++
4 files changed, 27 insertions(+), 2 deletions(-)
diff --git a/ext/dom/tests/libxml_global_state_entity_loader_bypass.phpt b/ext/dom/tests/libxml_global_state_entity_loader_bypass.phpt
index b28afd4694e..7fc2a249ac7 100644
--- a/ext/dom/tests/libxml_global_state_entity_loader_bypass.phpt
+++ b/ext/dom/tests/libxml_global_state_entity_loader_bypass.phpt
@@ -5,6 +5,7 @@ GHSA-3qrf-m4j2-pcrr (libxml global state entity loader bypass)
if (!extension_loaded('libxml')) die('skip libxml extension not available');
if (!extension_loaded('dom')) die('skip dom extension not available');
if (!extension_loaded('zend-test')) die('skip zend-test extension not available');
+if (!function_exists('zend_test_override_libxml_global_state')) die('skip not for Windows');
?>
--FILE--
<?php
diff --git a/ext/simplexml/tests/libxml_global_state_entity_loader_bypass.phpt b/ext/simplexml/tests/libxml_global_state_entity_loader_bypass.phpt
index 2152e012328..54f9d4941eb 100644
--- a/ext/simplexml/tests/libxml_global_state_entity_loader_bypass.phpt
+++ b/ext/simplexml/tests/libxml_global_state_entity_loader_bypass.phpt
@@ -5,6 +5,7 @@ GHSA-3qrf-m4j2-pcrr (libxml global state entity loader bypass)
if (!extension_loaded('libxml')) die('skip libxml extension not available');
if (!extension_loaded('simplexml')) die('skip simplexml extension not available');
if (!extension_loaded('zend-test')) die('skip zend-test extension not available');
+if (!function_exists('zend_test_override_libxml_global_state')) die('skip not for Windows');
?>
--FILE--
<?php
diff --git a/ext/xmlreader/tests/libxml_global_state_entity_loader_bypass.phpt b/ext/xmlreader/tests/libxml_global_state_entity_loader_bypass.phpt
index e9ffb04c2bb..b0120b325ef 100644
--- a/ext/xmlreader/tests/libxml_global_state_entity_loader_bypass.phpt
+++ b/ext/xmlreader/tests/libxml_global_state_entity_loader_bypass.phpt
@@ -5,6 +5,7 @@ GHSA-3qrf-m4j2-pcrr (libxml global state entity loader bypass)
if (!extension_loaded('libxml')) die('skip libxml extension not available');
if (!extension_loaded('xmlreader')) die('skip xmlreader extension not available');
if (!extension_loaded('zend-test')) die('skip zend-test extension not available');
+if (!function_exists('zend_test_override_libxml_global_state')) die('skip not for Windows');
?>
--FILE--
<?php
@@ -15,11 +16,11 @@ libxml_use_internal_errors(true);
zend_test_override_libxml_global_state();
echo "--- String test ---\n";
-$reader = XMLReader::xml($xml);
+$reader = @XMLReader::xml($xml);
$reader->read();
echo "--- File test ---\n";
file_put_contents("libxml_global_state_entity_loader_bypass.tmp", $xml);
-$reader = XMLReader::open("libxml_global_state_entity_loader_bypass.tmp");
+$reader = @XMLReader::open("libxml_global_state_entity_loader_bypass.tmp");
$reader->read();
echo "Done\n";
diff --git a/ext/zend_test/test.c b/ext/zend_test/test.c
index 4f81adc6ac1..cdfc15571c0 100644
--- a/ext/zend_test/test.c
+++ b/ext/zend_test/test.c
@@ -25,6 +25,11 @@
#include "ext/standard/info.h"
#include "php_test.h"
+#if defined(HAVE_LIBXML) && !defined(PHP_WIN32)
+# include <libxml/globals.h>
+# include <libxml/parser.h>
+#endif
+
static zend_class_entry *zend_test_interface;
static zend_class_entry *zend_test_class;
static zend_class_entry *zend_test_child_class;
@@ -48,6 +53,20 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_zend_leak_variable, 0, 0, 1)
ZEND_ARG_INFO(0, variable)
ZEND_END_ARG_INFO()
+#if defined(HAVE_LIBXML) && !defined(PHP_WIN32)
+static ZEND_FUNCTION(zend_test_override_libxml_global_state)
+{
+ ZEND_PARSE_PARAMETERS_NONE();
+
+ xmlLoadExtDtdDefaultValue = 1;
+ xmlDoValidityCheckingDefaultValue = 1;
+ (void) xmlPedanticParserDefault(1);
+ (void) xmlSubstituteEntitiesDefault(1);
+ (void) xmlLineNumbersDefault(1);
+ (void) xmlKeepBlanksDefault(0);
+}
+#endif
+
ZEND_FUNCTION(zend_test_func)
{
/* dummy */
@@ -297,6 +316,9 @@ static const zend_function_entry zend_test_functions[] = {
ZEND_FE(zend_terminate_string, arginfo_zend_terminate_string)
ZEND_FE(zend_leak_bytes, NULL)
ZEND_FE(zend_leak_variable, arginfo_zend_leak_variable)
+#if defined(HAVE_LIBXML) && !defined(PHP_WIN32)
+ ZEND_FE(zend_test_override_libxml_global_state, NULL)
+#endif
ZEND_FE_END
};
--
2.41.0

193
php-cve-2024-2756.patch Normal file
View File

@ -0,0 +1,193 @@
From a6c1c62a25ac23b08a86af11d68f0e2eaafc102b Mon Sep 17 00:00:00 2001
From: Niels Dossche <7771979+nielsdos@users.noreply.github.com>
Date: Sun, 17 Mar 2024 21:04:47 +0100
Subject: [PATCH 1/4] Fix GHSA-wpj3-hf5j-x4v4: __Host-/__Secure- cookie bypass
due to partial CVE-2022-31629 fix
The check happened too early as later code paths may perform more
mangling rules. Move the check downwards right before adding the actual
variable.
(cherry picked from commit 093c08af25fb323efa0c8e6154aa9fdeae3d3b53)
(cherry picked from commit 2e07a3acd7a6b53c55325b94bed97748d7697b53)
---
ext/standard/tests/ghsa-wpj3-hf5j-x4v4.phpt | 63 +++++++++++++++++++++
main/php_variables.c | 41 +++++++++-----
2 files changed, 90 insertions(+), 14 deletions(-)
create mode 100644 ext/standard/tests/ghsa-wpj3-hf5j-x4v4.phpt
diff --git a/ext/standard/tests/ghsa-wpj3-hf5j-x4v4.phpt b/ext/standard/tests/ghsa-wpj3-hf5j-x4v4.phpt
new file mode 100644
index 00000000000..77fcb680894
--- /dev/null
+++ b/ext/standard/tests/ghsa-wpj3-hf5j-x4v4.phpt
@@ -0,0 +1,63 @@
+--TEST--
+ghsa-wpj3-hf5j-x4v4 (__Host-/__Secure- cookie bypass due to partial CVE-2022-31629 fix)
+--COOKIE--
+..Host-test=ignore_1;
+._Host-test=ignore_2;
+.[Host-test=ignore_3;
+_.Host-test=ignore_4;
+__Host-test=ignore_5;
+_[Host-test=ignore_6;
+[.Host-test=ignore_7;
+[_Host-test=ignore_8;
+[[Host-test=ignore_9;
+..Host-test[]=ignore_10;
+._Host-test[]=ignore_11;
+.[Host-test[]=ignore_12;
+_.Host-test[]=ignore_13;
+__Host-test[]=legitimate_14;
+_[Host-test[]=legitimate_15;
+[.Host-test[]=ignore_16;
+[_Host-test[]=ignore_17;
+[[Host-test[]=ignore_18;
+..Secure-test=ignore_1;
+._Secure-test=ignore_2;
+.[Secure-test=ignore_3;
+_.Secure-test=ignore_4;
+__Secure-test=ignore_5;
+_[Secure-test=ignore_6;
+[.Secure-test=ignore_7;
+[_Secure-test=ignore_8;
+[[Secure-test=ignore_9;
+..Secure-test[]=ignore_10;
+._Secure-test[]=ignore_11;
+.[Secure-test[]=ignore_12;
+_.Secure-test[]=ignore_13;
+__Secure-test[]=legitimate_14;
+_[Secure-test[]=legitimate_15;
+[.Secure-test[]=ignore_16;
+[_Secure-test[]=ignore_17;
+[[Secure-test[]=ignore_18;
+--FILE--
+<?php
+var_dump($_COOKIE);
+?>
+--EXPECT--
+array(3) {
+ ["__Host-test"]=>
+ array(1) {
+ [0]=>
+ string(13) "legitimate_14"
+ }
+ ["_"]=>
+ array(2) {
+ ["Host-test["]=>
+ string(13) "legitimate_15"
+ ["Secure-test["]=>
+ string(13) "legitimate_15"
+ }
+ ["__Secure-test"]=>
+ array(1) {
+ [0]=>
+ string(13) "legitimate_14"
+ }
+}
diff --git a/main/php_variables.c b/main/php_variables.c
index 18f6b65a6c5..e971d497337 100644
--- a/main/php_variables.c
+++ b/main/php_variables.c
@@ -65,6 +65,21 @@ static zend_always_inline void php_register_variable_quick(const char *name, siz
zend_string_release_ex(key, 0);
}
+/* Discard variable if mangling made it start with __Host-, where pre-mangling it did not start with __Host-
+ * Discard variable if mangling made it start with __Secure-, where pre-mangling it did not start with __Secure- */
+static zend_bool php_is_forbidden_variable_name(const char *mangled_name, size_t mangled_name_len, const char *pre_mangled_name)
+{
+ if (mangled_name_len >= sizeof("__Host-")-1 && strncmp(mangled_name, "__Host-", sizeof("__Host-")-1) == 0 && strncmp(pre_mangled_name, "__Host-", sizeof("__Host-")-1) != 0) {
+ return 1;
+ }
+
+ if (mangled_name_len >= sizeof("__Secure-")-1 && strncmp(mangled_name, "__Secure-", sizeof("__Secure-")-1) == 0 && strncmp(pre_mangled_name, "__Secure-", sizeof("__Secure-")-1) != 0) {
+ return 1;
+ }
+
+ return 0;
+}
+
PHPAPI void php_register_variable_ex(char *var_name, zval *val, zval *track_vars_array)
{
char *p = NULL;
@@ -115,20 +130,6 @@ PHPAPI void php_register_variable_ex(char *var_name, zval *val, zval *track_vars
}
var_len = p - var;
- /* Discard variable if mangling made it start with __Host-, where pre-mangling it did not start with __Host- */
- if (strncmp(var, "__Host-", sizeof("__Host-")-1) == 0 && strncmp(var_name, "__Host-", sizeof("__Host-")-1) != 0) {
- zval_ptr_dtor_nogc(val);
- free_alloca(var_orig, use_heap);
- return;
- }
-
- /* Discard variable if mangling made it start with __Secure-, where pre-mangling it did not start with __Secure- */
- if (strncmp(var, "__Secure-", sizeof("__Secure-")-1) == 0 && strncmp(var_name, "__Secure-", sizeof("__Secure-")-1) != 0) {
- zval_ptr_dtor_nogc(val);
- free_alloca(var_orig, use_heap);
- return;
- }
-
if (var_len==0) { /* empty variable name, or variable name with a space in it */
zval_ptr_dtor_nogc(val);
free_alloca(var_orig, use_heap);
@@ -226,6 +227,12 @@ PHPAPI void php_register_variable_ex(char *var_name, zval *val, zval *track_vars
return;
}
} else {
+ if (php_is_forbidden_variable_name(index, index_len, var_name)) {
+ zval_ptr_dtor_nogc(val);
+ free_alloca(var_orig, use_heap);
+ return;
+ }
+
gpc_element_p = zend_symtable_str_find(symtable1, index, index_len);
if (!gpc_element_p) {
zval tmp;
@@ -263,6 +270,12 @@ plain_var:
zval_ptr_dtor_nogc(val);
}
} else {
+ if (php_is_forbidden_variable_name(index, index_len, var_name)) {
+ zval_ptr_dtor_nogc(val);
+ free_alloca(var_orig, use_heap);
+ return;
+ }
+
zend_ulong idx;
/*
--
2.44.0
From dcdd49ef3bfbd8ccc778850d6a0f9b98adf625d4 Mon Sep 17 00:00:00 2001
From: Remi Collet <remi@remirepo.net>
Date: Wed, 10 Apr 2024 08:59:32 +0200
Subject: [PATCH 2/4] NEWS
(cherry picked from commit 366cc249b7d54707572beb7096e8f6c65ee79719)
---
NEWS | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/NEWS b/NEWS
index 4f88029a7d6..d63aadc6851 100644
--- a/NEWS
+++ b/NEWS
@@ -1,6 +1,12 @@
PHP NEWS
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
+Backported from 8.1.28
+
+- Standard:
+ . Fixed bug GHSA-wpj3-hf5j-x4v4 (__Host-/__Secure- cookie bypass due to
+ partial CVE-2022-31629 fix). (CVE-2024-2756) (nielsdos)
+
Backported from 8.0.30
- Libxml:
--
2.44.0

81
php-cve-2024-3096.patch Normal file
View File

@ -0,0 +1,81 @@
From 4a7ceb9d6427f8d368f1a8739267b1f8310ec201 Mon Sep 17 00:00:00 2001
From: Jakub Zelenka <bukka@php.net>
Date: Fri, 29 Mar 2024 15:27:59 +0000
Subject: [PATCH 3/4] Fix bug GHSA-q6x7-frmf-grcw: password_verify can
erroneously return true
Disallow null character in bcrypt password
(cherry picked from commit 0ba5229a3f7572846e91c8f5382e87785f543826)
(cherry picked from commit 81794c73068d9a44bf109bbcc9793e7b56a1c051)
---
ext/standard/password.c | 5 +++++
ext/standard/tests/password/password_bcrypt_errors.phpt | 6 ++++++
2 files changed, 11 insertions(+)
diff --git a/ext/standard/password.c b/ext/standard/password.c
index 9fe7fb1a422..af80670246a 100644
--- a/ext/standard/password.c
+++ b/ext/standard/password.c
@@ -260,6 +260,11 @@ static zend_string* php_password_bcrypt_hash(const zend_string *password, zend_a
zval *zcost;
zend_long cost = PHP_PASSWORD_BCRYPT_COST;
+ if (memchr(ZSTR_VAL(password), '\0', ZSTR_LEN(password))) {
+ php_error_docref(NULL, E_WARNING, "Bcrypt password must not contain null character");
+ return NULL;
+ }
+
if (options && (zcost = zend_hash_str_find(options, "cost", sizeof("cost")-1)) != NULL) {
cost = zval_get_long(zcost);
}
diff --git a/ext/standard/tests/password/password_bcrypt_errors.phpt b/ext/standard/tests/password/password_bcrypt_errors.phpt
index a0826080e62..f95b72670ae 100644
--- a/ext/standard/tests/password/password_bcrypt_errors.phpt
+++ b/ext/standard/tests/password/password_bcrypt_errors.phpt
@@ -16,6 +16,8 @@ var_dump(password_hash("foo", PASSWORD_BCRYPT, array("salt" => 123)));
var_dump(password_hash("foo", PASSWORD_BCRYPT, array("cost" => "foo")));
+var_dump(password_hash("null\0password", PASSWORD_BCRYPT));
+
?>
--EXPECTF--
Warning: password_hash(): Invalid bcrypt cost parameter specified: 3 in %s on line %d
@@ -41,3 +43,7 @@ NULL
Warning: password_hash(): Invalid bcrypt cost parameter specified: 0 in %s on line %d
NULL
+
+Warning: password_hash(): Bcrypt password must not contain null character in %s on line %d
+NULL
+
--
2.44.0
From 027bdbc636632be49ecfad8d4191509faacb34ac Mon Sep 17 00:00:00 2001
From: Remi Collet <remi@remirepo.net>
Date: Wed, 10 Apr 2024 09:01:09 +0200
Subject: [PATCH 4/4] NEWS
(cherry picked from commit 24f77904ee2259d722559f129f96a1f145a2367b)
---
NEWS | 2 ++
1 file changed, 2 insertions(+)
diff --git a/NEWS b/NEWS
index d63aadc6851..96a33c21637 100644
--- a/NEWS
+++ b/NEWS
@@ -6,6 +6,8 @@ Backported from 8.1.28
- Standard:
. Fixed bug GHSA-wpj3-hf5j-x4v4 (__Host-/__Secure- cookie bypass due to
partial CVE-2022-31629 fix). (CVE-2024-2756) (nielsdos)
+ . Fixed bug GHSA-h746-cjrr-wfmr (password_verify can erroneously return true,
+ opening ATO risk). (CVE-2024-3096) (Jakub Zelenka)
Backported from 8.0.30
--
2.44.0

180
php-cve-2024-5458.patch Normal file
View File

@ -0,0 +1,180 @@
From 08be64e40197fc12dca5f802d16748d9c3cb4cb4 Mon Sep 17 00:00:00 2001
From: Niels Dossche <7771979+nielsdos@users.noreply.github.com>
Date: Wed, 22 May 2024 22:25:02 +0200
Subject: [PATCH 1/2] Fix GHSA-w8qr-v226-r27w
We should not early-out with success status if we found an ipv6
hostname, we should keep checking the rest of the conditions.
Because integrating the if-check of the ipv6 hostname in the
"Validate domain" if-check made the code hard to read, I extracted the
condition out to a separate function. This also required to make
a few pointers const in order to have some clean code.
(cherry picked from commit 4066610b47e22c24cbee91be434a94357056a479)
---
ext/filter/logical_filters.c | 35 ++++++++++---------
ext/filter/tests/ghsa-w8qr-v226-r27w.phpt | 41 +++++++++++++++++++++++
2 files changed, 61 insertions(+), 15 deletions(-)
create mode 100644 ext/filter/tests/ghsa-w8qr-v226-r27w.phpt
diff --git a/ext/filter/logical_filters.c b/ext/filter/logical_filters.c
index e5e87c01568..9c86ad072cc 100644
--- a/ext/filter/logical_filters.c
+++ b/ext/filter/logical_filters.c
@@ -91,7 +91,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;
@@ -571,6 +571,14 @@ static int is_userinfo_valid(zend_string *str)
return 1;
}
+static zend_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;
@@ -596,7 +604,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) {
@@ -605,17 +613,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
}
@@ -749,15 +754,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;
diff --git a/ext/filter/tests/ghsa-w8qr-v226-r27w.phpt b/ext/filter/tests/ghsa-w8qr-v226-r27w.phpt
new file mode 100644
index 00000000000..0092408ee5a
--- /dev/null
+++ b/ext/filter/tests/ghsa-w8qr-v226-r27w.phpt
@@ -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]"
--
2.45.1
From ec1d5e6468479e64acc7fb8cb955f053b64ea9a0 Mon Sep 17 00:00:00 2001
From: Remi Collet <remi@remirepo.net>
Date: Tue, 4 Jun 2024 16:48:08 +0200
Subject: [PATCH 2/2] NEWS
(cherry picked from commit a1ff81b786bd519597e770795be114f5171f0648)
---
NEWS | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/NEWS b/NEWS
index 8058eff0256..34ad33cf5c4 100644
--- a/NEWS
+++ b/NEWS
@@ -1,6 +1,12 @@
PHP NEWS
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
+Backported from 8.1.29
+
+- Filter:
+ . Fixed bug GHSA-w8qr-v226-r27w (Filter bypass in filter_var FILTER_VALIDATE_URL).
+ (CVE-2024-5458) (nielsdos)
+
Backported from 8.1.28
- Standard:
--
2.45.1

227
php-cve-2024-8925.patch Normal file
View File

@ -0,0 +1,227 @@
From a24ac172f52e75101913f3946cfa5515f723c99f Mon Sep 17 00:00:00 2001
From: Arnaud Le Blanc <arnaud.lb@gmail.com>
Date: Mon, 9 Sep 2024 15:22:07 +0200
Subject: [PATCH 04/11] Fix GHSA-9pqp-7h25-4f32
multipart/form-data boundaries larger than the read buffer result in erroneous
parsing, which violates data integrity.
Limit boundary size, as allowed by RFC 1521:
Encapsulation boundaries [...] must be no longer than 70 characters, not
counting the two leading hyphens.
We correctly parse payloads with boundaries of length up to
FILLUNIT-strlen("\r\n--") bytes, so allow this for BC.
(cherry picked from commit 19b49258d0c5a61398d395d8afde1123e8d161e0)
(cherry picked from commit 2b0daf421c162376892832588eccdfa9a286ed09)
---
main/rfc1867.c | 7 ++
tests/basic/GHSA-9pqp-7h25-4f32.inc | 3 +
tests/basic/GHSA-9pqp-7h25-4f32.phpt | 100 +++++++++++++++++++++++++++
3 files changed, 110 insertions(+)
create mode 100644 tests/basic/GHSA-9pqp-7h25-4f32.inc
create mode 100644 tests/basic/GHSA-9pqp-7h25-4f32.phpt
diff --git a/main/rfc1867.c b/main/rfc1867.c
index 1b212c93325..43ccce120c3 100644
--- a/main/rfc1867.c
+++ b/main/rfc1867.c
@@ -759,6 +759,13 @@ SAPI_API SAPI_POST_HANDLER_FUNC(rfc1867_post_handler) /* {{{ */
boundary_len = boundary_end-boundary;
}
+ /* Boundaries larger than FILLUNIT-strlen("\r\n--") characters lead to
+ * erroneous parsing */
+ if (boundary_len > FILLUNIT-strlen("\r\n--")) {
+ sapi_module.sapi_error(E_WARNING, "Boundary too large in multipart/form-data POST data");
+ return;
+ }
+
/* Initialize the buffer */
if (!(mbuff = multipart_buffer_new(boundary, boundary_len))) {
sapi_module.sapi_error(E_WARNING, "Unable to initialize the input buffer");
diff --git a/tests/basic/GHSA-9pqp-7h25-4f32.inc b/tests/basic/GHSA-9pqp-7h25-4f32.inc
new file mode 100644
index 00000000000..adf72a361a2
--- /dev/null
+++ b/tests/basic/GHSA-9pqp-7h25-4f32.inc
@@ -0,0 +1,3 @@
+<?php
+print "Hello world\n";
+var_dump($_POST);
diff --git a/tests/basic/GHSA-9pqp-7h25-4f32.phpt b/tests/basic/GHSA-9pqp-7h25-4f32.phpt
new file mode 100644
index 00000000000..af819163705
--- /dev/null
+++ b/tests/basic/GHSA-9pqp-7h25-4f32.phpt
@@ -0,0 +1,100 @@
+--TEST--
+GHSA-9pqp-7h25-4f32
+--SKIPIF--
+<?php
+if (!getenv('TEST_PHP_CGI_EXECUTABLE')) {
+ die("skip php-cgi not available");
+}
+?>
+--FILE--
+<?php
+
+const FILLUNIT = 5 * 1024;
+
+function test($boundaryLen) {
+ printf("Boundary len: %d\n", $boundaryLen);
+
+ $cmd = [
+ getenv('TEST_PHP_CGI_EXECUTABLE'),
+ '-C',
+ '-n',
+ __DIR__ . '/GHSA-9pqp-7h25-4f32.inc',
+ ];
+
+ $boundary = str_repeat('A', $boundaryLen);
+ $body = ""
+ . "--$boundary\r\n"
+ . "Content-Disposition: form-data; name=\"koko\"\r\n"
+ . "\r\n"
+ . "BBB\r\n--" . substr($boundary, 0, -1) . "CCC\r\n"
+ . "--$boundary--\r\n"
+ ;
+
+ $env = array_merge($_ENV, [
+ 'REDIRECT_STATUS' => '1',
+ 'CONTENT_TYPE' => "multipart/form-data; boundary=$boundary",
+ 'CONTENT_LENGTH' => strlen($body),
+ 'REQUEST_METHOD' => 'POST',
+ 'SCRIPT_FILENAME' => __DIR__ . '/GHSA-9pqp-7h25-4f32.inc',
+ ]);
+
+ $spec = [
+ 0 => ['pipe', 'r'],
+ 1 => STDOUT,
+ 2 => STDOUT,
+ ];
+
+ $pipes = [];
+
+ print "Starting...\n";
+
+ $handle = proc_open($cmd, $spec, $pipes, getcwd(), $env);
+
+ fwrite($pipes[0], $body);
+
+ $status = proc_close($handle);
+
+ print "\n";
+}
+
+for ($offset = -1; $offset <= 1; $offset++) {
+ test(FILLUNIT - strlen("\r\n--") + $offset);
+}
+
+?>
+--EXPECTF--
+Boundary len: 5115
+Starting...
+X-Powered-By: %s
+Content-type: text/html; charset=UTF-8
+
+Hello world
+array(1) {
+ ["koko"]=>
+ string(5124) "BBB
+--AAA%sCCC"
+}
+
+Boundary len: 5116
+Starting...
+X-Powered-By: %s
+Content-type: text/html; charset=UTF-8
+
+Hello world
+array(1) {
+ ["koko"]=>
+ string(5125) "BBB
+--AAA%sCCC"
+}
+
+Boundary len: 5117
+Starting...
+X-Powered-By: %s
+Content-type: text/html; charset=UTF-8
+
+<br />
+<b>Warning</b>: Boundary too large in multipart/form-data POST data in <b>Unknown</b> on line <b>0</b><br />
+Hello world
+array(0) {
+}
+
--
2.46.1
From 2fd1b83817d20523e72bef3ad524cd5797f51acf Mon Sep 17 00:00:00 2001
From: Jakub Zelenka <bukka@php.net>
Date: Mon, 23 Sep 2024 18:54:31 +0100
Subject: [PATCH 08/11] Skip GHSA-9pqp-7h25-4f32 test on Windows
(cherry picked from commit c70e25630832fa10d421328eed2b8e1a36af7a64)
(cherry picked from commit c75683864f6e4188439e8ca2adbb05824918be12)
---
tests/basic/GHSA-9pqp-7h25-4f32.phpt | 3 +++
1 file changed, 3 insertions(+)
diff --git a/tests/basic/GHSA-9pqp-7h25-4f32.phpt b/tests/basic/GHSA-9pqp-7h25-4f32.phpt
index af819163705..29bcb6557d5 100644
--- a/tests/basic/GHSA-9pqp-7h25-4f32.phpt
+++ b/tests/basic/GHSA-9pqp-7h25-4f32.phpt
@@ -5,6 +5,9 @@ GHSA-9pqp-7h25-4f32
if (!getenv('TEST_PHP_CGI_EXECUTABLE')) {
die("skip php-cgi not available");
}
+if (substr(PHP_OS, 0, 3) == 'WIN') {
+ die("skip not for Windows in CI - probably resource issue");
+}
?>
--FILE--
<?php
--
2.46.1
From 29065f33f37f99ba33254cb23c941647bcd7372c Mon Sep 17 00:00:00 2001
From: Remi Collet <remi@remirepo.net>
Date: Thu, 26 Sep 2024 15:49:03 +0200
Subject: [PATCH 11/11] adapt GHSA-9pqp-7h25-4f32 test for 7.x
---
tests/basic/GHSA-9pqp-7h25-4f32.phpt | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/tests/basic/GHSA-9pqp-7h25-4f32.phpt b/tests/basic/GHSA-9pqp-7h25-4f32.phpt
index 29bcb6557d5..a1ead918ff3 100644
--- a/tests/basic/GHSA-9pqp-7h25-4f32.phpt
+++ b/tests/basic/GHSA-9pqp-7h25-4f32.phpt
@@ -21,6 +21,7 @@ function test($boundaryLen) {
getenv('TEST_PHP_CGI_EXECUTABLE'),
'-C',
'-n',
+ '-dlog_errors=1',
__DIR__ . '/GHSA-9pqp-7h25-4f32.inc',
];
@@ -92,11 +93,10 @@ array(1) {
Boundary len: 5117
Starting...
+PHP Warning: Boundary too large in multipart/form-data POST data in Unknown on line 0
X-Powered-By: %s
Content-type: text/html; charset=UTF-8
-<br />
-<b>Warning</b>: Boundary too large in multipart/form-data POST data in <b>Unknown</b> on line <b>0</b><br />
Hello world
array(0) {
}
--
2.46.1

210
php-cve-2024-8926.patch Normal file
View File

@ -0,0 +1,210 @@
From fb718aa6f2117933566bb7bb2f70b2b0d9a9c08f Mon Sep 17 00:00:00 2001
From: Jan Ehrhardt <github@ehrhardt.nl>
Date: Wed, 5 Jun 2024 20:24:52 +0200
Subject: [PATCH 01/11] Fix GHSA-3qgc-jrrr-25jv
---
sapi/cgi/cgi_main.c | 23 ++++++++++++++-
sapi/cgi/tests/ghsa-3qgc-jrrr-25jv.phpt | 38 +++++++++++++++++++++++++
2 files changed, 60 insertions(+), 1 deletion(-)
create mode 100644 sapi/cgi/tests/ghsa-3qgc-jrrr-25jv.phpt
diff --git a/sapi/cgi/cgi_main.c b/sapi/cgi/cgi_main.c
index a36f426d266..8d1342727dc 100644
--- a/sapi/cgi/cgi_main.c
+++ b/sapi/cgi/cgi_main.c
@@ -1827,8 +1827,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));
@@ -1838,6 +1843,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);
}
diff --git a/sapi/cgi/tests/ghsa-3qgc-jrrr-25jv.phpt b/sapi/cgi/tests/ghsa-3qgc-jrrr-25jv.phpt
new file mode 100644
index 00000000000..fd2fcdfbf89
--- /dev/null
+++ b/sapi/cgi/tests/ghsa-3qgc-jrrr-25jv.phpt
@@ -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
--
2.46.1
From a634d3f5169c884715d9e26ac213ecf2a25c3666 Mon Sep 17 00:00:00 2001
From: Jan Ehrhardt <github@ehrhardt.nl>
Date: Sun, 9 Jun 2024 20:09:02 +0200
Subject: [PATCH 03/11] NEWS: Add backports from 8.1.29
---
NEWS | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/NEWS b/NEWS
index 34ad33cf5c4..a96518695fb 100644
--- a/NEWS
+++ b/NEWS
@@ -3,10 +3,18 @@ PHP NEWS
Backported from 8.1.29
+- CGI:
+ . Fixed bug GHSA-3qgc-jrrr-25jv (Bypass of CVE-2012-1823, Argument Injection
+ in PHP-CGI). (CVE-2024-4577) (nielsdos)
+
- Filter:
. Fixed bug GHSA-w8qr-v226-r27w (Filter bypass in filter_var FILTER_VALIDATE_URL).
(CVE-2024-5458) (nielsdos)
+- Standard:
+ . Fixed bug GHSA-9fcc-425m-g385 (Bypass of CVE-2024-1874).
+ (CVE-2024-5585) (nielsdos)
+
Backported from 8.1.28
- Standard:
--
2.46.1
From 1158d06f0b20532ab7309cb20f0be843f9662e3c Mon Sep 17 00:00:00 2001
From: Niels Dossche <7771979+nielsdos@users.noreply.github.com>
Date: Fri, 14 Jun 2024 19:49:22 +0200
Subject: [PATCH 05/11] Fix GHSA-p99j-rfp4-xqvq
It's no use trying to work around whatever the operating system and Apache
do because we'll be fighting that until eternity.
Change the skip_getopt condition such that when we're running in
CGI or FastCGI mode we always skip the argument parsing.
This is a BC break, but this seems to be the only way to get rid of this
class of issues.
(cherry picked from commit abcfd980bfa03298792fd3aba051c78d52f10642)
(cherry picked from commit 2d2552e092b6ff32cd823692d512f126ee629842)
---
sapi/cgi/cgi_main.c | 26 ++++++++------------------
1 file changed, 8 insertions(+), 18 deletions(-)
diff --git a/sapi/cgi/cgi_main.c b/sapi/cgi/cgi_main.c
index 8d1342727dc..a2761aafd7b 100644
--- a/sapi/cgi/cgi_main.c
+++ b/sapi/cgi/cgi_main.c
@@ -1777,7 +1777,6 @@ int main(int argc, char *argv[])
int status = 0;
#endif
char *query_string;
- char *decoded_query_string;
int skip_getopt = 0;
#if defined(SIGPIPE) && defined(SIG_IGN)
@@ -1832,10 +1831,15 @@ int main(int argc, char *argv[])
* 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. */
+ * Similarly, scripts spawned in subprocesses on Windows may have the same issue.
+ * However, Windows has lots of conversion rules and command line parsing rules that
+ * are too difficult and dangerous to reliably emulate. */
if((query_string = getenv("QUERY_STRING")) != NULL && strchr(query_string, '=') == NULL) {
+#ifdef PHP_WIN32
+ skip_getopt = cgi || fastcgi;
+#else
unsigned char *p;
- decoded_query_string = strdup(query_string);
+ char *decoded_query_string = strdup(query_string);
php_url_decode(decoded_query_string, strlen(decoded_query_string));
for (p = (unsigned char *)decoded_query_string; *p && *p <= ' '; p++) {
/* skip all leading spaces */
@@ -1844,22 +1848,8 @@ int main(int argc, char *argv[])
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);
+#endif
}
while (!skip_getopt && (c = php_getopt(argc, argv, OPTIONS, &php_optarg, &php_optind, 0, 2)) != -1) {
--
2.46.1

57
php-cve-2024-8927.patch Normal file
View File

@ -0,0 +1,57 @@
From c7308ba7cd0533501b40eba255602bb5e085550f Mon Sep 17 00:00:00 2001
From: Niels Dossche <7771979+nielsdos@users.noreply.github.com>
Date: Tue, 18 Jun 2024 21:28:26 +0200
Subject: [PATCH 06/11] Fix GHSA-94p6-54jq-9mwp
Apache only generates REDIRECT_STATUS, so explicitly check for that
if the server name is Apache, don't allow other variable names.
Furthermore, redirect.so and Netscape no longer exist, so
remove those entries as we can't check their server name anymore.
We now also check for the configuration override *first* such that it
always take precedence. This would allow for a mitigation path if
something like this happens in the future.
(cherry picked from commit 48808d98f4fc2a05193cdcc1aedd6c66816450f1)
(cherry picked from commit 8aa748ee0657cdee8d883ba50d04b68bc450f686)
---
sapi/cgi/cgi_main.c | 23 +++++++++++------------
1 file changed, 11 insertions(+), 12 deletions(-)
diff --git a/sapi/cgi/cgi_main.c b/sapi/cgi/cgi_main.c
index a2761aafd7b..ebce6302b93 100644
--- a/sapi/cgi/cgi_main.c
+++ b/sapi/cgi/cgi_main.c
@@ -1939,18 +1939,17 @@ int main(int argc, char *argv[])
/* check force_cgi after startup, so we have proper output */
if (cgi && CGIG(force_redirect)) {
- /* Apache will generate REDIRECT_STATUS,
- * Netscape and redirect.so will generate HTTP_REDIRECT_STATUS.
- * redirect.so and installation instructions available from
- * http://www.koehntopp.de/php.
- * -- kk@netuse.de
- */
- if (!getenv("REDIRECT_STATUS") &&
- !getenv ("HTTP_REDIRECT_STATUS") &&
- /* this is to allow a different env var to be configured
- * in case some server does something different than above */
- (!CGIG(redirect_status_env) || !getenv(CGIG(redirect_status_env)))
- ) {
+ /* This is to allow a different environment variable to be configured
+ * in case the we cannot auto-detect which environment variable to use.
+ * Checking this first to allow user overrides in case the environment
+ * variable can be set by an untrusted party. */
+ const char *redirect_status_env = CGIG(redirect_status_env);
+ if (!redirect_status_env) {
+ /* Apache will generate REDIRECT_STATUS. */
+ redirect_status_env = "REDIRECT_STATUS";
+ }
+
+ if (!getenv(redirect_status_env)) {
zend_try {
SG(sapi_headers).http_response_code = 400;
PUTS("<b>Security Alert!</b> The PHP CGI cannot be accessed directly.\n\n\
--
2.46.1

245
php-cve-2024-9026.patch Normal file
View File

@ -0,0 +1,245 @@
From 4a8b8fa2592bd8862adeacb5b2faacb30500b9f9 Mon Sep 17 00:00:00 2001
From: Jakub Zelenka <bukka@php.net>
Date: Thu, 12 Sep 2024 13:11:11 +0100
Subject: [PATCH 07/11] Fix GHSA-865w-9rf3-2wh5: FPM: Logs from childrens may
be altered
(cherry picked from commit 1f8e16172c7961045c2b0f34ba7613e3f21cdee8)
(cherry picked from commit 22f4d3504d7613ce78bb96aa53cbfe7d672fa036)
---
sapi/fpm/fpm/fpm_stdio.c | 2 +-
.../log-bwp-msg-flush-split-sep-pos-end.phpt | 47 +++++++++++++++++++
...log-bwp-msg-flush-split-sep-pos-start.phpt | 47 +++++++++++++++++++
3 files changed, 95 insertions(+), 1 deletion(-)
create mode 100644 sapi/fpm/tests/log-bwp-msg-flush-split-sep-pos-end.phpt
create mode 100644 sapi/fpm/tests/log-bwp-msg-flush-split-sep-pos-start.phpt
diff --git a/sapi/fpm/fpm/fpm_stdio.c b/sapi/fpm/fpm/fpm_stdio.c
index ddedfb48c7c..9d87273314a 100644
--- a/sapi/fpm/fpm/fpm_stdio.c
+++ b/sapi/fpm/fpm/fpm_stdio.c
@@ -177,7 +177,7 @@ stdio_read:
if ((sizeof(FPM_STDIO_CMD_FLUSH) - cmd_pos) <= in_buf &&
!memcmp(buf, &FPM_STDIO_CMD_FLUSH[cmd_pos], sizeof(FPM_STDIO_CMD_FLUSH) - cmd_pos)) {
zlog_stream_finish(log_stream);
- start = cmd_pos;
+ start = sizeof(FPM_STDIO_CMD_FLUSH) - cmd_pos;
} else {
zlog_stream_str(log_stream, &FPM_STDIO_CMD_FLUSH[0], cmd_pos);
}
diff --git a/sapi/fpm/tests/log-bwp-msg-flush-split-sep-pos-end.phpt b/sapi/fpm/tests/log-bwp-msg-flush-split-sep-pos-end.phpt
new file mode 100644
index 00000000000..52826320080
--- /dev/null
+++ b/sapi/fpm/tests/log-bwp-msg-flush-split-sep-pos-end.phpt
@@ -0,0 +1,47 @@
+--TEST--
+FPM: Buffered worker output plain log with msg with flush split position towards separator end
+--SKIPIF--
+<?php include "skipif.inc"; ?>
+--FILE--
+<?php
+
+require_once "tester.inc";
+
+$cfg = <<<EOT
+[global]
+error_log = {{FILE:LOG}}
+[unconfined]
+listen = {{ADDR}}
+pm = dynamic
+pm.max_children = 5
+pm.start_servers = 1
+pm.min_spare_servers = 1
+pm.max_spare_servers = 3
+catch_workers_output = yes
+decorate_workers_output = no
+EOT;
+
+$code = <<<EOT
+<?php
+file_put_contents('php://stderr', str_repeat('a', 1013) . "Quarkslab\0fscf\0Quarkslab");
+EOT;
+
+$tester = new FPM\Tester($cfg, $code);
+$tester->start();
+$tester->expectLogStartNotices();
+$tester->request()->expectEmptyBody();
+$tester->expectLogLine(str_repeat('a', 1013) . "Quarkslab", decorated: false);
+$tester->expectLogLine("Quarkslab", decorated: false);
+$tester->terminate();
+$tester->expectLogTerminatingNotices();
+$tester->close();
+
+?>
+Done
+--EXPECT--
+Done
+--CLEAN--
+<?php
+require_once "tester.inc";
+FPM\Tester::clean();
+?>
diff --git a/sapi/fpm/tests/log-bwp-msg-flush-split-sep-pos-start.phpt b/sapi/fpm/tests/log-bwp-msg-flush-split-sep-pos-start.phpt
new file mode 100644
index 00000000000..34905938553
--- /dev/null
+++ b/sapi/fpm/tests/log-bwp-msg-flush-split-sep-pos-start.phpt
@@ -0,0 +1,47 @@
+--TEST--
+FPM: Buffered worker output plain log with msg with flush split position towards separator start
+--SKIPIF--
+<?php include "skipif.inc"; ?>
+--FILE--
+<?php
+
+require_once "tester.inc";
+
+$cfg = <<<EOT
+[global]
+error_log = {{FILE:LOG}}
+[unconfined]
+listen = {{ADDR}}
+pm = dynamic
+pm.max_children = 5
+pm.start_servers = 1
+pm.min_spare_servers = 1
+pm.max_spare_servers = 3
+catch_workers_output = yes
+decorate_workers_output = no
+EOT;
+
+$code = <<<EOT
+<?php
+file_put_contents('php://stderr', str_repeat('a', 1009) . "Quarkslab\0fscf\0Quarkslab");
+EOT;
+
+$tester = new FPM\Tester($cfg, $code);
+$tester->start();
+$tester->expectLogStartNotices();
+$tester->request()->expectEmptyBody();
+$tester->expectLogLine(str_repeat('a', 1009) . "Quarkslab", decorated: false);
+$tester->expectLogLine("Quarkslab", decorated: false);
+$tester->terminate();
+$tester->expectLogTerminatingNotices();
+$tester->close();
+
+?>
+Done
+--EXPECT--
+Done
+--CLEAN--
+<?php
+require_once "tester.inc";
+FPM\Tester::clean();
+?>
--
2.46.1
From 1154fbd3ddfa418bf2492c5366adaefb47c47737 Mon Sep 17 00:00:00 2001
From: Remi Collet <remi@remirepo.net>
Date: Thu, 26 Sep 2024 11:50:54 +0200
Subject: [PATCH 09/11] NEWS for 8.1.30 backports
(cherry picked from commit af3fb385e7b328ab89db26ec712d89c7096f0743)
---
NEWS | 17 +++++++++++++++++
1 file changed, 17 insertions(+)
diff --git a/NEWS b/NEWS
index a96518695fb..62616d6312d 100644
--- a/NEWS
+++ b/NEWS
@@ -1,6 +1,23 @@
PHP NEWS
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
+Backported from 8.1.30
+
+- CGI:
+ . Fixed bug GHSA-p99j-rfp4-xqvq (Bypass of CVE-2024-4577, Parameter Injection
+ Vulnerability). (CVE-2024-8926) (nielsdos)
+ . Fixed bug GHSA-94p6-54jq-9mwp (cgi.force_redirect configuration is
+ bypassable due to the environment variable collision). (CVE-2024-8927)
+ (nielsdos)
+
+- FPM:
+ . Fixed bug GHSA-865w-9rf3-2wh5 (Logs from childrens may be altered).
+ (CVE-2024-9026) (Jakub Zelenka)
+
+- SAPI:
+ . Fixed bug GHSA-9pqp-7h25-4f32 (Erroneous parsing of multipart form data).
+ (CVE-2024-8925) (Arnaud)
+
Backported from 8.1.29
- CGI:
--
2.46.1
From bc574c256596abc4966e7f0e3e0913839092151e Mon Sep 17 00:00:00 2001
From: Remi Collet <remi@remirepo.net>
Date: Thu, 26 Sep 2024 15:48:11 +0200
Subject: [PATCH 10/11] adapt GHSA-865w-9rf3-2wh5 test for 7.x
---
sapi/fpm/tests/log-bwp-msg-flush-split-sep-pos-end.phpt | 4 ++--
sapi/fpm/tests/log-bwp-msg-flush-split-sep-pos-start.phpt | 4 ++--
sapi/fpm/tests/tester.inc | 4 ++--
3 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/sapi/fpm/tests/log-bwp-msg-flush-split-sep-pos-end.phpt b/sapi/fpm/tests/log-bwp-msg-flush-split-sep-pos-end.phpt
index 52826320080..bdd61782bfa 100644
--- a/sapi/fpm/tests/log-bwp-msg-flush-split-sep-pos-end.phpt
+++ b/sapi/fpm/tests/log-bwp-msg-flush-split-sep-pos-end.phpt
@@ -30,8 +30,8 @@ $tester = new FPM\Tester($cfg, $code);
$tester->start();
$tester->expectLogStartNotices();
$tester->request()->expectEmptyBody();
-$tester->expectLogLine(str_repeat('a', 1013) . "Quarkslab", decorated: false);
-$tester->expectLogLine("Quarkslab", decorated: false);
+$tester->expectLogLine(str_repeat('a', 1013) . "Quarkslab", true, false);
+$tester->expectLogLine("Quarkslab", true, false);
$tester->terminate();
$tester->expectLogTerminatingNotices();
$tester->close();
diff --git a/sapi/fpm/tests/log-bwp-msg-flush-split-sep-pos-start.phpt b/sapi/fpm/tests/log-bwp-msg-flush-split-sep-pos-start.phpt
index 34905938553..f3461e4a0c8 100644
--- a/sapi/fpm/tests/log-bwp-msg-flush-split-sep-pos-start.phpt
+++ b/sapi/fpm/tests/log-bwp-msg-flush-split-sep-pos-start.phpt
@@ -30,8 +30,8 @@ $tester = new FPM\Tester($cfg, $code);
$tester->start();
$tester->expectLogStartNotices();
$tester->request()->expectEmptyBody();
-$tester->expectLogLine(str_repeat('a', 1009) . "Quarkslab", decorated: false);
-$tester->expectLogLine("Quarkslab", decorated: false);
+$tester->expectLogLine(str_repeat('a', 1009) . "Quarkslab", true, false);
+$tester->expectLogLine("Quarkslab", true, false);
$tester->terminate();
$tester->expectLogTerminatingNotices();
$tester->close();
diff --git a/sapi/fpm/tests/tester.inc b/sapi/fpm/tests/tester.inc
index 7868afc4ac1..fe5f0c2fde7 100644
--- a/sapi/fpm/tests/tester.inc
+++ b/sapi/fpm/tests/tester.inc
@@ -1315,7 +1315,7 @@ class Tester
* @param string $message
* @return bool
*/
- public function expectLogLine(string $message, bool $is_stderr = true)
+ public function expectLogLine(string $message, bool $is_stderr = true, bool $decorated = true)
{
$messageLen = strlen($message);
$limit = $messageLen > 1024 ? $messageLen + 16 : 1024;
@@ -1325,7 +1325,7 @@ class Tester
$this->message("LOG LINE: " . ($logLines[0] ?? ''));
}
- return $this->logTool->checkWrappedMessage($logLines, false, true, $is_stderr);
+ return $this->logTool->checkWrappedMessage($logLines, false, $decorated, $is_stderr);
}
/**
--
2.46.1

View File

@ -60,7 +60,7 @@
Summary: PHP scripting language for creating dynamic web sites
Name: php
Version: %{upver}%{?rcver:~%{rcver}}
Release: 1%{?dist}
Release: 2%{?dist}
# All files licensed under PHP version 3.01, except
# Zend is licensed under Zend
# TSRM is licensed under BSD
@ -108,7 +108,20 @@ Patch47: php-5.6.3-phpinfo.patch
# Upstream fixes (100+)
# Security fixes (200+)
Patch200: php-CVE-2022-31631.patch
Patch200: php-cve-2022-31631.patch
Patch201: php-cve-2023-0567.patch
Patch202: php-cve-2023-0568.patch
Patch203: php-cve-2023-0662.patch
Patch204: php-cve-2023-3247.patch
Patch205: php-cve-2023-3823.patch
Patch206: php-cve-2023-3824.patch
Patch207: php-cve-2024-2756.patch
Patch208: php-cve-2024-3096.patch
Patch209: php-cve-2024-5458.patch
Patch210: php-cve-2024-8925.patch
Patch211: php-cve-2024-8926.patch
Patch212: php-cve-2024-8927.patch
Patch213: php-cve-2024-9026.patch
# Fixes for tests (300+)
# Factory is droped from system tzdata
@ -703,25 +716,38 @@ in pure PHP.
%setup -q -n php-%{upver}%{?rcver}
%patch1 -p1 -b .mpmcheck
%patch5 -p1 -b .includedir
%patch6 -p1 -b .embed
%patch8 -p1 -b .libdb
%patch -P1 -p1 -b .mpmcheck
%patch -P5 -p1 -b .includedir
%patch -P6 -p1 -b .embed
%patch -P8 -p1 -b .libdb
%patch42 -p1 -b .systzdata
%patch43 -p1 -b .headers
%patch -P42 -p1 -b .systzdata
%patch -P43 -p1 -b .headers
%if 0%{?fedora} >= 18 || 0%{?rhel} >= 7
%patch45 -p1 -b .ldap_r
%patch -P45 -p1 -b .ldap_r
%endif
%patch47 -p1 -b .phpinfo
%patch -P47 -p1 -b .phpinfo
# upstream patches
# security patches
%patch200 -p1 -b .cve31631
%patch -P200 -p1 -b .cve31631
%patch -P201 -p1 -b .cve0567
%patch -P202 -p1 -b .cve0568
%patch -P203 -p1 -b .cve0662
%patch -P204 -p1 -b .cve3247
%patch -P205 -p1 -b .cve3823
%patch -P206 -p1 -b .cve3824
%patch -P207 -p1 -b .cve2756
%patch -P208 -p1 -b .cve3096
%patch -P209 -p1 -b .cve5458
%patch -P210 -p1 -b .cve8925
%patch -P211 -p1 -b .cve8926
%patch -P212 -p1 -b .cve8927
%patch -P213 -p1 -b .cve9026
# Fixes for tests
%patch300 -p1 -b .datetests
%patch -P300 -p1 -b .datetests
# Prevent %%doc confusion over LICENSE files
@ -1508,6 +1534,35 @@ systemctl try-restart php-fpm.service >/dev/null 2>&1 || :
%changelog
* Wed Nov 13 2024 Remi Collet <rcollet@redhat.com> - 7.4.33-2
- fix low/moderate CVEs
RHEL-66589
- Fix cgi.force_redirect configuration is bypassable due to the environment variable collision
CVE-2024-8927
- Fix Logs from childrens may be altered
CVE-2024-9026
- Fix Erroneous parsing of multipart form data
CVE-2024-8925
- Fix filter bypass in filter_var FILTER_VALIDATE_URL
CVE-2024-5458
- Fix __Host-/__Secure- cookie bypass due to partial CVE-2022-31629 fix
CVE-2024-2756
- Fix password_verify can erroneously return true opening ATO risk
CVE-2024-3096
- Fix Security issue with external entity loading in XML without enabling it
CVE-2023-3823
- Fix Buffer mismanagement in phar_dir_read()
CVE-2023-3824
- Fix Missing error check and insufficient random bytes in HTTP Digest
authentication for SOAP
CVE-2023-3247
- fix #81744: Password_verify() always return true with some hash
CVE-2023-0567
- fix #81746: 1-byte array overrun in common path resolve code
CVE-2023-0568
- fix DOS vulnerability when parsing multipart request body
CVE-2023-0662
* Fri Jan 13 2023 Remi Collet <rcollet@redhat.com> - 7.4.33-1
- rebase to 7.4.33
- fix: due to an integer overflow PDO::quote() may return unquoted string