Bug 1358395 (CVE-2016-5399)

Summary: CVE-2016-5399 php: Improper error handling in bzread()
Product: [Other] Security Response Reporter: Adam Mariš <amaris>
Component: vulnerabilityAssignee: Red Hat Product Security <security-response-team>
Status: CLOSED ERRATA QA Contact:
Severity: medium Docs Contact:
Priority: medium    
Version: unspecifiedCC: abhgupta, amorilia, anemec, carnil, dmcphers, erik-fedora, fedora, hhorak, jamartis, jaromir.capik, jchaloup, jialiu, jokerman, jorton, kseifried, lmeyer, mmccomas, rcollet, rjones, slawomir, tiwillia, webstack-team
Target Milestone: ---Keywords: Security
Target Release: ---   
Hardware: All   
OS: Linux   
Whiteboard:
Fixed In Version: Doc Type: If docs needed, set a value
Doc Text:
A flaw was found in the way certain error conditions were handled by bzread() function in PHP. An attacker could use this flaw to upload a specially crafted bz2 archive which, when parsed via the vulnerable function, could cause the application to crash or execute arbitrary code with the permissions of the user running the PHP application.
Story Points: ---
Clone Of: Environment:
Last Closed: 2016-11-15 15:10:00 UTC Type: ---
Regression: --- Mount Type: ---
Documentation: --- CRM:
Verified Versions: Category: ---
oVirt Team: --- RHEL 7.3 requirements from Atomic Host:
Cloudforms Team: --- Target Upstream Version:
Embargoed:
Bug Depends On: 1358401, 1358402, 1358403, 1358404, 1363588, 1525805, 1525806    
Bug Blocks: 1323912, 1352627    

Description Adam Mariš 2016-07-20 15:25:42 UTC
Wrong error handling in bzread() function that possibly leads to code execution was reported.

PHP bug:

https://bugs.php.net/bug.php?id=72613

Comment 1 Adam Mariš 2016-07-20 15:25:59 UTC
Acknowledgments:

Name: Hans Jerry Illikainen

Comment 2 Adam Mariš 2016-07-20 15:35:10 UTC
Created bzip2 tracking bugs for this issue:

Affects: fedora-all [bug 1358401]

Comment 3 Adam Mariš 2016-07-20 15:35:21 UTC
Created php tracking bugs for this issue:

Affects: fedora-all [bug 1358404]

Comment 4 Adam Mariš 2016-07-20 15:35:30 UTC
Created mingw-bzip2 tracking bugs for this issue:

Affects: fedora-all [bug 1358402]
Affects: epel-7 [bug 1358403]

Comment 5 Andrej Nemec 2016-07-21 08:17:39 UTC
References:

http://seclists.org/bugtraq/2016/Jul/96

Comment 6 Huzaifa S. Sidhpurwala 2016-08-02 10:05:16 UTC
I dont think this is a flaw with bzip2, the documentation (http://www.bzip.org/1.0.5/bzip2-manual-1.0.5.html#bzread) clearly states the following:

"BZ2_bzRead will supply len bytes, unless the logical stream end is detected or an error occurs. Because of this, it is possible to detect the stream end by observing when the number of bytes returned is less than the number requested. Nevertheless, this is regarded as inadvisable; you should instead check bzerror after every call and watch out for BZ_STREAM_END."

php_bz2iop_read does not do this, rather it exits only when the return from BZ2_bzRead < 1, which seems to be not what the bzip API states.

Comment 7 Huzaifa S. Sidhpurwala 2016-08-02 10:19:33 UTC
Upstream patch:

http://git.php.net/?p=php-src.git;a=commit;h=f3feddb5b45b5abd93abb1a95044b7e099d51c84

Also notice https://secure.php.net/manual/en/function.bzread.php (comment #2) gives a work around to this, ie check output of bzerrno() after every bzread invocation, and quit if its non-zero.

Comment 8 Remi Collet 2016-08-02 10:49:39 UTC
At first read it seems "return 0;" in case of error (read < 0) could be better, but this is handled in stream wrapper (justread && justread != (size_t)-1).

Unbuffered:
https://github.com/php/php-src/blob/master/ext/zlib/zlib_fopen_wrapper.c#L47

Buffered: 
https://github.com/php/php-src/blob/master/main/streams/streams.c#L580

Comment 9 Remi Collet 2016-08-02 11:00:39 UTC
Bad links, I mean for unbuffered: https://github.com/php/php-src/blob/master/main/streams/streams.c#L716

Comment 10 Huzaifa S. Sidhpurwala 2016-08-03 06:27:48 UTC
Exploitation Notes:
==================

When an attacker uploads a specially-crafted .bz2 archive to a php application, which attempts to extract files from the uploaded archive, it could result in arbitrary code execution.

This is because when the bz2 library reports a decompression error code, back to PHP, it does not stop the reading process, which can result in further calls to the decompression code of bz2, this may cause OOB writes which may under certain circumstances result in code execution.

However the above can easily be mitigated, by making sure that the PHP code calls bzerrno() after every call to bzread() something like:

$fh = bzopen('file.bz2','r');
while(!feof($fh)) {
  $buffer = bzread($fh);
  if($buffer === FALSE) die('Read problem');
  if(bzerrno($fh) !== 0) die('Compression Problem');
}
bzclose($fh);

Reference:
https://secure.php.net/manual/en/function.bzread.php

Comment 12 errata-xmlrpc 2016-11-03 21:05:59 UTC
This issue has been addressed in the following products:

  Red Hat Enterprise Linux 7

Via RHSA-2016:2598 https://rhn.redhat.com/errata/RHSA-2016-2598.html

Comment 14 errata-xmlrpc 2016-11-15 11:42:48 UTC
This issue has been addressed in the following products:

  Red Hat Software Collections for Red Hat Enterprise Linux 6
  Red Hat Software Collections for Red Hat Enterprise Linux 6.7 EUS
  Red Hat Software Collections for Red Hat Enterprise Linux 7
  Red Hat Software Collections for Red Hat Enterprise Linux 7.2 EUS
  Red Hat Software Collections for Red Hat Enterprise Linux 7.3 EUS

Via RHSA-2016:2750 https://rhn.redhat.com/errata/RHSA-2016-2750.html