From 9a37b5e6a1dc629be1466ad11a221bb9386a85ee Mon Sep 17 00:00:00 2001 From: slava86 Date: Mon, 22 May 2023 20:43:19 +0300 Subject: [PATCH] security fixed --- ...rapper-DOS-when-using-quine-gzip-fil.patch | 72 ++++++++++++ ...ssion-introduced-by-fixing-bug-81726.patch | 57 +++++++++ ...mangle-HTTP-variable-names-that-clas.patch | 62 ++++++++++ ...ffer-overflow-in-hash_update-on-long.patch | 109 ++++++++++++++++++ php7.spec | 14 ++- 5 files changed, 313 insertions(+), 1 deletion(-) create mode 100644 0059-Fix-81726-phar-wrapper-DOS-when-using-quine-gzip-fil.patch create mode 100644 0060-Fix-regression-introduced-by-fixing-bug-81726.patch create mode 100644 0061-Fix-81727-Don-t-mangle-HTTP-variable-names-that-clas.patch create mode 100644 0062-Fix-bug-81738-buffer-overflow-in-hash_update-on-long.patch diff --git a/0059-Fix-81726-phar-wrapper-DOS-when-using-quine-gzip-fil.patch b/0059-Fix-81726-phar-wrapper-DOS-when-using-quine-gzip-fil.patch new file mode 100644 index 0000000..6a8320a --- /dev/null +++ b/0059-Fix-81726-phar-wrapper-DOS-when-using-quine-gzip-fil.patch @@ -0,0 +1,72 @@ +From 404e8bdb68350931176a5bdc86fc417b34fb583d Mon Sep 17 00:00:00 2001 +From: "Christoph M. Becker" +Date: Mon, 25 Jul 2022 15:58:59 +0200 +Subject: [PATCH] Fix #81726: phar wrapper: DOS when using quine gzip file + +The phar wrapper needs to uncompress the file; the uncompressed file +might be compressed, so the wrapper implementation loops. This raises +potential DOS issues regarding too deep or even infinite recursion (the +latter are called compressed file quines[1]). We avoid that by +introducing a recursion limit; we choose the somewhat arbitrary limit +`3`. + +This issue has been reported by real_as3617 and gPayl0ad. + +[1] +--- + NEWS | 1 + + ext/phar/phar.c | 16 +++++++++++----- + ext/phar/tests/bug81726.gz | Bin 0 -> 204 bytes + ext/phar/tests/bug81726.phpt | 14 ++++++++++++++ + 4 files changed, 26 insertions(+), 5 deletions(-) + create mode 100644 ext/phar/tests/bug81726.gz + create mode 100644 ext/phar/tests/bug81726.phpt + +diff --git a/ext/phar/phar.c b/ext/phar/phar.c +index 7cb1b06363..4a761ef799 100644 +--- a/ext/phar/phar.c ++++ b/ext/phar/phar.c +@@ -1584,7 +1584,8 @@ static int phar_open_from_fp(php_stream* fp, char *fname, size_t fname_len, char + const char zip_magic[] = "PK\x03\x04"; + const char gz_magic[] = "\x1f\x8b\x08"; + const char bz_magic[] = "BZh"; +- char *pos, test = '\0'; ++ char *pos; ++ int recursion_count = 3; // arbitrary limit to avoid too deep or even infinite recursion + const int window_size = 1024; + char buffer[1024 + sizeof(token)]; /* a 1024 byte window + the size of the halt_compiler token (moving window) */ + const zend_long readsize = sizeof(buffer) - sizeof(token); +@@ -1612,8 +1613,7 @@ static int phar_open_from_fp(php_stream* fp, char *fname, size_t fname_len, char + MAPPHAR_ALLOC_FAIL("internal corruption of phar \"%s\" (truncated entry)") + } + +- if (!test) { +- test = '\1'; ++ if (recursion_count) { + pos = buffer+tokenlen; + if (!memcmp(pos, gz_magic, 3)) { + char err = 0; +@@ -1673,7 +1673,10 @@ static int phar_open_from_fp(php_stream* fp, char *fname, size_t fname_len, char + compression = PHAR_FILE_COMPRESSED_GZ; + + /* now, start over */ +- test = '\0'; ++ if (!--recursion_count) { ++ MAPPHAR_ALLOC_FAIL("unable to decompress gzipped phar archive \"%s\""); ++ break; ++ } + continue; + } else if (!memcmp(pos, bz_magic, 3)) { + php_stream_filter *filter; +@@ -1711,7 +1714,10 @@ static int phar_open_from_fp(php_stream* fp, char *fname, size_t fname_len, char + compression = PHAR_FILE_COMPRESSED_BZ2; + + /* now, start over */ +- test = '\0'; ++ if (!--recursion_count) { ++ MAPPHAR_ALLOC_FAIL("unable to decompress bzipped phar archive \"%s\""); ++ break; ++ } + continue; + } + diff --git a/0060-Fix-regression-introduced-by-fixing-bug-81726.patch b/0060-Fix-regression-introduced-by-fixing-bug-81726.patch new file mode 100644 index 0000000..1bf39c1 --- /dev/null +++ b/0060-Fix-regression-introduced-by-fixing-bug-81726.patch @@ -0,0 +1,57 @@ +From 432bf196d59bcb661fcf9cb7029cea9b43f490af Mon Sep 17 00:00:00 2001 +From: "Christoph M. Becker" +Date: Tue, 27 Sep 2022 17:43:40 +0200 +Subject: [PATCH] Fix regression introduced by fixing bug 81726 + +When a tar phar is created, `phar_open_from_fp()` is also called, but +since the file has just been created, none of the format checks can +succeed, so we continue to loop, but must not check again for the +format. Therefore, we bring back the old `test` variable. + +Closes GH-9620. +--- + ext/phar/phar.c | 7 +++++-- + 1 file changed, 5 insertions(+), 2 deletions(-) + +diff --git a/ext/phar/phar.c b/ext/phar/phar.c +index 4a761ef799..ecab9162fa 100644 +--- a/ext/phar/phar.c ++++ b/ext/phar/phar.c +@@ -1584,7 +1584,7 @@ static int phar_open_from_fp(php_stream* fp, char *fname, size_t fname_len, char + const char zip_magic[] = "PK\x03\x04"; + const char gz_magic[] = "\x1f\x8b\x08"; + const char bz_magic[] = "BZh"; +- char *pos; ++ char *pos, test = '\0'; + int recursion_count = 3; // arbitrary limit to avoid too deep or even infinite recursion + const int window_size = 1024; + char buffer[1024 + sizeof(token)]; /* a 1024 byte window + the size of the halt_compiler token (moving window) */ +@@ -1613,7 +1613,8 @@ static int phar_open_from_fp(php_stream* fp, char *fname, size_t fname_len, char + MAPPHAR_ALLOC_FAIL("internal corruption of phar \"%s\" (truncated entry)") + } + +- if (recursion_count) { ++ if (!test && recursion_count) { ++ test = '\1'; + pos = buffer+tokenlen; + if (!memcmp(pos, gz_magic, 3)) { + char err = 0; +@@ -1673,6 +1674,7 @@ static int phar_open_from_fp(php_stream* fp, char *fname, size_t fname_len, char + compression = PHAR_FILE_COMPRESSED_GZ; + + /* now, start over */ ++ test = '\0'; + if (!--recursion_count) { + MAPPHAR_ALLOC_FAIL("unable to decompress gzipped phar archive \"%s\""); + break; +@@ -1714,6 +1716,7 @@ static int phar_open_from_fp(php_stream* fp, char *fname, size_t fname_len, char + compression = PHAR_FILE_COMPRESSED_BZ2; + + /* now, start over */ ++ test = '\0'; + if (!--recursion_count) { + MAPPHAR_ALLOC_FAIL("unable to decompress bzipped phar archive \"%s\""); + break; +-- +2.30.2 + diff --git a/0061-Fix-81727-Don-t-mangle-HTTP-variable-names-that-clas.patch b/0061-Fix-81727-Don-t-mangle-HTTP-variable-names-that-clas.patch new file mode 100644 index 0000000..3507e64 --- /dev/null +++ b/0061-Fix-81727-Don-t-mangle-HTTP-variable-names-that-clas.patch @@ -0,0 +1,62 @@ +From 0611be4e82887cee0de6c4cbae320d34eec946ca Mon Sep 17 00:00:00 2001 +From: Derick Rethans +Date: Fri, 9 Sep 2022 16:54:03 +0100 +Subject: [PATCH] Fix #81727: Don't mangle HTTP variable names that clash with + ones that have a specific semantic meaning. + +--- + NEWS | 6 ++++++ + ext/standard/tests/bug81727.phpt | 15 +++++++++++++++ + main/php_variables.c | 14 ++++++++++++++ + 3 files changed, 35 insertions(+) + create mode 100644 ext/standard/tests/bug81727.phpt + +diff --git a/ext/standard/tests/bug81727.phpt b/ext/standard/tests/bug81727.phpt +new file mode 100644 +index 0000000000..71a9cb46c8 +--- /dev/null ++++ b/ext/standard/tests/bug81727.phpt +@@ -0,0 +1,15 @@ ++--TEST-- ++Bug #81727: $_COOKIE name starting with ..Host/..Secure should be discarded ++--COOKIE-- ++..Host-test=ignore; __Host-test=correct; . Secure-test=ignore; . Elephpant=Awesome; ++--FILE-- ++ ++--EXPECT-- ++array(2) { ++ ["__Host-test"]=> ++ string(7) "correct" ++ ["__Elephpant"]=> ++ string(7) "Awesome" ++} +diff --git a/main/php_variables.c b/main/php_variables.c +index cbdc7cf171..18f6b65a6c 100644 +--- a/main/php_variables.c ++++ b/main/php_variables.c +@@ -115,6 +115,20 @@ 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); +-- +2.30.2 + diff --git a/0062-Fix-bug-81738-buffer-overflow-in-hash_update-on-long.patch b/0062-Fix-bug-81738-buffer-overflow-in-hash_update-on-long.patch new file mode 100644 index 0000000..e68e519 --- /dev/null +++ b/0062-Fix-bug-81738-buffer-overflow-in-hash_update-on-long.patch @@ -0,0 +1,109 @@ +From 248f647724e385bfb8d83aa5b5a5ca3c4ee2c7fd Mon Sep 17 00:00:00 2001 +From: Stanislav Malyshev +Date: Thu, 20 Oct 2022 23:57:35 -0600 +Subject: [PATCH] Fix bug #81738 (buffer overflow in hash_update() on long + parameter) + +--- + NEWS | 4 ++++ + ext/hash/sha3/generic32lc/KeccakSponge.inc | 14 ++++++++------ + ext/hash/sha3/generic64lc/KeccakSponge.inc | 14 ++++++++------ + main/php_version.h | 10 +++++----- + 4 files changed, 25 insertions(+), 17 deletions(-) + +diff --git a/ext/hash/sha3/generic32lc/KeccakSponge.inc b/ext/hash/sha3/generic32lc/KeccakSponge.inc +index 42a15aac6d..f8c42ff788 100644 +--- a/ext/hash/sha3/generic32lc/KeccakSponge.inc ++++ b/ext/hash/sha3/generic32lc/KeccakSponge.inc +@@ -160,7 +160,7 @@ int SpongeAbsorb(SpongeInstance *instance, const unsigned char *data, size_t dat + i = 0; + curData = data; + while(i < dataByteLen) { +- if ((instance->byteIOIndex == 0) && (dataByteLen >= (i + rateInBytes))) { ++ if ((instance->byteIOIndex == 0) && (dataByteLen-i >= rateInBytes)) { + #ifdef SnP_FastLoop_Absorb + /* processing full blocks first */ + if ((rateInBytes % (SnP_width/200)) == 0) { +@@ -186,9 +186,10 @@ int SpongeAbsorb(SpongeInstance *instance, const unsigned char *data, size_t dat + } + else { + /* normal lane: using the message queue */ +- partialBlock = (unsigned int)(dataByteLen - i); +- if (partialBlock+instance->byteIOIndex > rateInBytes) ++ if (dataByteLen-i > rateInBytes-instance->byteIOIndex) + partialBlock = rateInBytes-instance->byteIOIndex; ++ else ++ partialBlock = (unsigned int)(dataByteLen - i); + #ifdef KeccakReference + displayBytes(1, "Block to be absorbed (part)", curData, partialBlock); + #endif +@@ -263,7 +264,7 @@ int SpongeSqueeze(SpongeInstance *instance, unsigned char *data, size_t dataByte + i = 0; + curData = data; + while(i < dataByteLen) { +- if ((instance->byteIOIndex == rateInBytes) && (dataByteLen >= (i + rateInBytes))) { ++ if ((instance->byteIOIndex == rateInBytes) && (dataByteLen-i >= rateInBytes)) { + for(j=dataByteLen-i; j>=rateInBytes; j-=rateInBytes) { + SnP_Permute(instance->state); + SnP_ExtractBytes(instance->state, curData, 0, rateInBytes); +@@ -280,9 +281,10 @@ int SpongeSqueeze(SpongeInstance *instance, unsigned char *data, size_t dataByte + SnP_Permute(instance->state); + instance->byteIOIndex = 0; + } +- partialBlock = (unsigned int)(dataByteLen - i); +- if (partialBlock+instance->byteIOIndex > rateInBytes) ++ if (dataByteLen-i > rateInBytes-instance->byteIOIndex) + partialBlock = rateInBytes-instance->byteIOIndex; ++ else ++ partialBlock = (unsigned int)(dataByteLen - i); + i += partialBlock; + + SnP_ExtractBytes(instance->state, curData, instance->byteIOIndex, partialBlock); +diff --git a/ext/hash/sha3/generic64lc/KeccakSponge.inc b/ext/hash/sha3/generic64lc/KeccakSponge.inc +index 42a15aac6d..f8c42ff788 100644 +--- a/ext/hash/sha3/generic64lc/KeccakSponge.inc ++++ b/ext/hash/sha3/generic64lc/KeccakSponge.inc +@@ -160,7 +160,7 @@ int SpongeAbsorb(SpongeInstance *instance, const unsigned char *data, size_t dat + i = 0; + curData = data; + while(i < dataByteLen) { +- if ((instance->byteIOIndex == 0) && (dataByteLen >= (i + rateInBytes))) { ++ if ((instance->byteIOIndex == 0) && (dataByteLen-i >= rateInBytes)) { + #ifdef SnP_FastLoop_Absorb + /* processing full blocks first */ + if ((rateInBytes % (SnP_width/200)) == 0) { +@@ -186,9 +186,10 @@ int SpongeAbsorb(SpongeInstance *instance, const unsigned char *data, size_t dat + } + else { + /* normal lane: using the message queue */ +- partialBlock = (unsigned int)(dataByteLen - i); +- if (partialBlock+instance->byteIOIndex > rateInBytes) ++ if (dataByteLen-i > rateInBytes-instance->byteIOIndex) + partialBlock = rateInBytes-instance->byteIOIndex; ++ else ++ partialBlock = (unsigned int)(dataByteLen - i); + #ifdef KeccakReference + displayBytes(1, "Block to be absorbed (part)", curData, partialBlock); + #endif +@@ -263,7 +264,7 @@ int SpongeSqueeze(SpongeInstance *instance, unsigned char *data, size_t dataByte + i = 0; + curData = data; + while(i < dataByteLen) { +- if ((instance->byteIOIndex == rateInBytes) && (dataByteLen >= (i + rateInBytes))) { ++ if ((instance->byteIOIndex == rateInBytes) && (dataByteLen-i >= rateInBytes)) { + for(j=dataByteLen-i; j>=rateInBytes; j-=rateInBytes) { + SnP_Permute(instance->state); + SnP_ExtractBytes(instance->state, curData, 0, rateInBytes); +@@ -280,9 +281,10 @@ int SpongeSqueeze(SpongeInstance *instance, unsigned char *data, size_t dataByte + SnP_Permute(instance->state); + instance->byteIOIndex = 0; + } +- partialBlock = (unsigned int)(dataByteLen - i); +- if (partialBlock+instance->byteIOIndex > rateInBytes) ++ if (dataByteLen-i > rateInBytes-instance->byteIOIndex) + partialBlock = rateInBytes-instance->byteIOIndex; ++ else ++ partialBlock = (unsigned int)(dataByteLen - i); + i += partialBlock; + + SnP_ExtractBytes(instance->state, curData, instance->byteIOIndex, partialBlock); diff --git a/php7.spec b/php7.spec index 570d698..b91e0bd 100644 --- a/php7.spec +++ b/php7.spec @@ -27,7 +27,7 @@ Summary: The PHP7 scripting language Name: php Version: 7.4.30 -Release: 2 +Release: 3 Source0: http://ch1.php.net/distributions/php-%{version}.tar.gz Source1: macros.php Group: Development/PHP @@ -81,6 +81,13 @@ Patch114: php-no_pam_in_c-client.diff # Fix bugs Patch121: php-bug43221.diff Patch122: php-not-use-libgd-const.patch +# CVE-2022-31628 +Patch123: 0059-Fix-81726-phar-wrapper-DOS-when-using-quine-gzip-fil.patch +Patch124: 0060-Fix-regression-introduced-by-fixing-bug-81726.patch +# CVE-2022-31629 +Patch125: 0061-Fix-81727-Don-t-mangle-HTTP-variable-names-that-clas.patch +# CVE-2022-37454 +Patch126: 0062-Fix-bug-81738-buffer-overflow-in-hash_update-on-long.patch BuildRequires: autoconf BuildRequires: automake @@ -1272,6 +1279,11 @@ fi %patch121 -p0 -b .bug43221.droplet %patch122 -p1 +%patch123 -p1 +%patch124 -p1 +%patch125 -p1 +%patch126 -p1 + cp %{SOURCE2} maxlifetime cp %{SOURCE3} php.crond cp %{SOURCE4} php-fpm.service