From:  pear-qa@lists.php.net ("research@karmainsecurity.com")
Date:  02 Feb 2017 22:09:49 Hong Kong Time
Newsgroup:  news.php.net/php.pear.bugs
Subject:  

[PEAR-BUG] Bug #21165 [Com]: PHP Object Injection Vulnerability through PHP Serializer

NNTP-Posting-Host:  null

Edit report at https://pear.php.net/bugs/bug.php?id=21165&edit=1

 ID:               21165
 Comment by:       research@karmainsecurity.com
 Reported By:      research at karmainsecurity dot com
 Summary:          PHP Object Injection Vulnerability through PHP
                    Serializer
 Status:           Critical
 Type:             Bug
 Package:          HTML_AJAX
 Package Version:  0.5.7
 PHP Version:      5.6.29
 Roadmap Versions: 
 New Comment:

FYI: the Common Vulnerabilities and Exposures project (cve.mitre.org)
has assigned the name CVE-2017-5677 to this vulnerability.


Previous Comments:
------------------------------------------------------------------------

[2017-01-25 19:03:40] egix

-Status: Open
+Status: Critical
I'm going to change the status to "Critical" because I think this poses
a serious security risk for every 
application which uses the 
HTML_AJAX PEAR package.

Last week the details of a type-confusion vulnerability in the GMP
deserialization has been published 
on the Full Disclosure mailing 
list [1], and this can be exploited through the vulnerability I reported
here, allowing a potential attacker 
to manipulate properties of 
already created objects within an application which uses HTML_AJAX to
implement an AJAX server 
(which may result in catastrophic 
consequences).

In addition to this recent PHP bug, a talk published on December 2014
[2] shows how it's possible to 
exploit unserialize() 
vulnerabilities in PEAR applications leveraging a magic method from the
DB_common class, which 
allows to call arbitrary callbacks 
(in other words, Remote Code Execution).

References:
[1] http://seclists.org/fulldisclosure/2017/Jan/55
[2] https://hack4.org/2014/talks/exploit-alchemy/Exploit%20Alchemy.pdf

------------------------------------------------------------------------

[2017-01-18 21:39:35] egix

Description:
------------
When handling AJAX requests a "serializer" will be selected
automatically 
relying on the Content-Type HTTP header 
(AJAX.php, lines 427-436):

        // auto-detect serializer to use from content-type
        $type = $this->unserializer;
        $key  = array_search($this->_getClientPayloadContentType(),
            $this->contentTypeMap);
        if ($key) {
            $type = $key;
        }
        $unserializer = $this->_getSerializer($type);

        $args = $unserializer->unserialize($this->_getClientPayload(),
$this-
>_allowedClasses);

When the header is set to "application/php-serialized", the 
HTML_AJAX_Serializer_PHP class will be used to deserialize user 
input coming from the AJAX request. Such a class will use the
unserialize() 
PHP function with user-controlled input unless a 
class is found within the serialized string which is not in the provided
array of 
allowed class names:

    function unserialize($input, $allowedClasses) 
    {

        [...]

        $classes = $this->_getSerializedClassNames($input);
        if ($classes === false) {
            trigger_error('Invalidly serialized string');
            return false;
        }
        $diff = array_diff($classes, $allowedClasses);
        if (!empty($diff)) {
            trigger_error('Class(es) not allowed to be serialized');
            return false;
        }
        return unserialize($input);
    }

By default the $allowedClasses array is empty, meaning that no classes
are 
allowed to be unserialized. However, due to a faulty 
security check in the "_getSerializedClassNames()" method it might be 
possible to bypass such a restriction by replacing "O:X" 
with "O:+X" from within the serialized string, where X is the length of
the 
class-name:

    function _getSerializedClassNames($string) {

        [...]
        
        // Pull out the class names
        preg_match_all('/O:[0-9]+:"(.*)"/U', $string, $matches); <===
faulty regex 
leading to a security bypass
        
        // Make sure names are unique (same object serialized twice)
        return array_unique($matches[1]);
    }

This can be exploited to inject arbitrary PHP objects into the
application 
scope, allowing an attacker to perform some attacks by 
using a "POP chain" or exploit memory corruption vulnerabilities within
the 
internal implementation of the unserialize() function, 
which has a long history of security bugs (see e.g. CVE-2016-5771).

More info: https://www.owasp.org/index.php/PHP_Object_Injection

NOTE: I discovered this vulnerability in a PHP application which uses
the 
HTML_AJAX Pear package for its AJAX server. They 
asked to get a CVE created for this issue, so that other developers
using the 
package will be notified about the potential security 
risk. I'm not sure if MITRE will assign a CVE identifier for
vulnerabilities in 
Pear packages, since they product list mention PHP 
but not Pear 
(https://cve.mitre.org/cve/data_sources_product_coverage.html). Could
you 
please try to request a CVE for this 
vulnerability?

Test script:
---------------
HTTP request to reproduce the issue:

POST /HTML_AJAX-0.5.7/examples/server.php?c=test&m=echo_string HTTP/1.1
Host: localhost
X-Content-Type: application/php-serialized
Content-Length: 20
Connection: close

O:+8:"stdClass":0:{}

Expected result:
----------------
{"errNo":1024,"errStr":"Class(es) not allowed to be 
serialized","errFile":"\/HTML_AJAX-
0.5.7\/HTML\/AJAX\/Serializer\/PHP.php","errLine":51}

Actual result:
--------------
{"errNo":4096,"errStr":"Object of class stdClass could not be converted
to 
string","errFile":"\/HTML_AJAX-
0.5.7\/examples\/support\/test.class.php","errLine":20}

------------------------------------------------------------------------


-- 
Edit this bug report at https://pear.php.net/bugs/bug.php?id=21165&edit=1