Skip to content

Commit 404e8bd

Browse files
cmb69derickr
authored andcommittedSep 9, 2022
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/>
1 parent 0611be4 commit 404e8bd

File tree

4 files changed

+26
-5
lines changed

4 files changed

+26
-5
lines changed
 

‎NEWS

+1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ PHP NEWS
33
29 Sep 2022, PHP 7.4.31
44

55
- Core:
6+
. Fixed bug #81726: phar wrapper: DOS when using quine gzip file. (cmb)
67
. Fixed bug #81727: Don't mangle HTTP variable names that clash with ones
78
that have a specific semantic meaning. (Derick)
89

‎ext/phar/phar.c

+11-5
Original file line numberDiff line numberDiff line change
@@ -1584,7 +1584,8 @@ static int phar_open_from_fp(php_stream* fp, char *fname, size_t fname_len, char
15841584
const char zip_magic[] = "PK\x03\x04";
15851585
const char gz_magic[] = "\x1f\x8b\x08";
15861586
const char bz_magic[] = "BZh";
1587-
char *pos, test = '\0';
1587+
char *pos;
1588+
int recursion_count = 3; // arbitrary limit to avoid too deep or even infinite recursion
15881589
const int window_size = 1024;
15891590
char buffer[1024 + sizeof(token)]; /* a 1024 byte window + the size of the halt_compiler token (moving window) */
15901591
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
16121613
MAPPHAR_ALLOC_FAIL("internal corruption of phar \"%s\" (truncated entry)")
16131614
}
16141615

1615-
if (!test) {
1616-
test = '\1';
1616+
if (recursion_count) {
16171617
pos = buffer+tokenlen;
16181618
if (!memcmp(pos, gz_magic, 3)) {
16191619
char err = 0;
@@ -1673,7 +1673,10 @@ static int phar_open_from_fp(php_stream* fp, char *fname, size_t fname_len, char
16731673
compression = PHAR_FILE_COMPRESSED_GZ;
16741674

16751675
/* now, start over */
1676-
test = '\0';
1676+
if (!--recursion_count) {
1677+
MAPPHAR_ALLOC_FAIL("unable to decompress gzipped phar archive \"%s\"");
1678+
break;
1679+
}
16771680
continue;
16781681
} else if (!memcmp(pos, bz_magic, 3)) {
16791682
php_stream_filter *filter;
@@ -1711,7 +1714,10 @@ static int phar_open_from_fp(php_stream* fp, char *fname, size_t fname_len, char
17111714
compression = PHAR_FILE_COMPRESSED_BZ2;
17121715

17131716
/* now, start over */
1714-
test = '\0';
1717+
if (!--recursion_count) {
1718+
MAPPHAR_ALLOC_FAIL("unable to decompress bzipped phar archive \"%s\"");
1719+
break;
1720+
}
17151721
continue;
17161722
}
17171723

‎ext/phar/tests/bug81726.gz

204 Bytes
Binary file not shown.

‎ext/phar/tests/bug81726.phpt

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
--TEST--
2+
Bug #81726 (phar wrapper: DOS when using quine gzip file)
3+
--SKIPIF--
4+
<?php
5+
if (!extension_loaded("phar")) die("skip phar extension not available");
6+
if (!extension_loaded("zlib")) die("skip zlib extension not available");
7+
?>
8+
--FILE--
9+
<?php
10+
var_dump(fopen("phar://" . __DIR__ . "/bug81726.gz", "r"));
11+
?>
12+
--EXPECTF--
13+
Warning: fopen(phar://%s): failed to open stream: unable to decompress gzipped phar archive "%s" in %s on line %d
14+
bool(false)

0 commit comments

Comments
 (0)