summaryrefslogtreecommitdiffstats
path: root/php-bug81726.patch
blob: 26c95a1601cccc2012215c3abce8453cf473e8f4 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
From 8fc7239a03a099ad20c9de4ea32489099b942ea7 Mon Sep 17 00:00:00 2001
From: "Christoph M. Becker" <cmbecker69@gmx.de>
Date: Mon, 25 Jul 2022 15:58:59 +0200
Subject: [PATCH 2/2] 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] <https://honno.dev/gzip-quine/>

(cherry picked from commit 404e8bdb68350931176a5bdc86fc417b34fb583d)
(cherry picked from commit 96fda78bcddd1d793cf2d0ee463dbb49621b577f)
---
 NEWS                         |   2 ++
 ext/phar/phar.c              |  16 +++++++++++-----
 ext/phar/tests/bug81726.gz   | Bin 0 -> 204 bytes
 ext/phar/tests/bug81726.phpt |  14 ++++++++++++++
 4 files changed, 27 insertions(+), 5 deletions(-)
 create mode 100644 ext/phar/tests/bug81726.gz
 create mode 100644 ext/phar/tests/bug81726.phpt

diff --git a/NEWS b/NEWS
index e1362dbdd1..4daea9bf2a 100644
--- a/NEWS
+++ b/NEWS
@@ -4,6 +4,8 @@ PHP                                                                        NEWS
 Backported from 7.4.31
 
 - Core:
+  . Fixed bug #81726: phar wrapper: DOS when using quine gzip file.
+    (CVE-2022-31628). (cmb)
   . Fixed bug #81727: Don't mangle HTTP variable names that clash with ones
     that have a specific semantic meaning. (CVE-2022-31629). (Derick)
 
diff --git a/ext/phar/phar.c b/ext/phar/phar.c
index e117ab0dc0..f62d18c094 100644
--- a/ext/phar/phar.c
+++ b/ext/phar/phar.c
@@ -1574,7 +1574,8 @@ static int phar_open_from_fp(php_stream* fp, char *fname, int fname_len, char *a
 	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);
@@ -1602,8 +1603,7 @@ static int phar_open_from_fp(php_stream* fp, char *fname, int fname_len, char *a
 			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;
@@ -1663,7 +1663,10 @@ static int phar_open_from_fp(php_stream* fp, char *fname, int fname_len, char *a
 				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;
@@ -1701,7 +1704,10 @@ static int phar_open_from_fp(php_stream* fp, char *fname, int fname_len, char *a
 				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;
 			}