CVE-2016-7480
Patch Available.
This Vulnerability has been fixed in the Never-Ending Support (NES) version offered by HeroDevs.
Overview
PHP is a general-purpose scripting language used mostly for web development. It runs behind web servers through server APIs such as Apache mod_php and PHP-FPM, and powers content management systems and frameworks such as WordPress, Drupal, Moodle and Laravel. Its SPL extension provides SplObjectStorage, a container that maps objects to data and can be serialized and unserialized.
An Improper Input Validation vulnerability (CVE-2016-7480) has been identified in the SplObjectStorage::unserialize() method of PHP's SPL extension, which allows an attacker who can supply serialized data to an application that unserializes it to make the storage keep a value that is not an object as one of its keys. PHP tracks this issue as bug #73257, together with the closely related bug #73258.
Per MITRE CWE-20: The product receives input or data, but it does not validate or incorrectly validates that the input has the properties that are required to process the data safely and correctly.
This issue affects PHP before 7.0.12, which includes the End-of-Life PHP 5.6 release line through its final release, 5.6.40.
Details
Module Info
- Product: PHP
- Affected packages:
php-src(ext/spl) - Affected versions: <7.0.12
- GitHub repository: https://github.com/php/php-src
- Published packages: https://www.php.net/downloads
- Package manager: N/A
- Fixed in:
- OSS PHP 7.0.12
- NES for PHP 5.6.1 (based on PHP 5.6.40)
Vulnerability Info
This Critical-severity vulnerability is found in the SPL extension of PHP before 7.0.12, including the End-of-Life PHP 5.6 release line, which never received the upstream fix. The CVE record, published by Check Point, carries no CVSS score; NVD scores it 9.8 under CVSS 3.1 (CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H). The record describes a missing check that a key is an object and an uninitialized memory access that could lead to code execution or a crash. HeroDevs' analysis found that the uninitialized variables belong to PHP 7 code; on PHP 5.6 the issue is the non-object key reported in bug #73258.
A serialized SplObjectStorage holds a list of entries, each an object followed by the information attached to it. SplObjectStorage::unserialize() reads them one at a time. In PHP 5.6.40 it unserializes the object, checks its type, and only then unserializes the information:
var_push_dtor(&var_hash, &pentry);
if(Z_TYPE_P(pentry) != IS_OBJECT) {
zval_ptr_dtor(&pentry);
goto outexcept;
}
ALLOC_INIT_ZVAL(pinf);
if (*p == ',') { /* new version has inf */
++p;
if (!php_var_unserialize(&pinf, &p, s + buf_len, &var_hash TSRMLS_CC)) {
zval_ptr_dtor(&pinf);
goto outexcept;
}
var_push_dtor(&var_hash, &pinf);
}The type check happens too early. The information that follows the key is attacker-controlled serialized data, and it can contain a reference back to the key. Through that reference, a __wakeup() method that runs while the information is unserialized can change the key after it passed the check. The storage then attaches an entry whose key is no longer an object. The reporter described this as a type confusion. Code that later relies on every key being an object may then work on a value of the wrong type.
Exploitation needs an application that passes attacker-controlled data to unserialize(), allows SplObjectStorage in that data, and has a class with a __wakeup() method available. This applies to any SAPI, and OPcache does not change it. Applications that never unserialize untrusted data are not exposed.
The upstream fix, in PHP 7.0.12, and its port to PHP 5.6 check the key only after its information has been unserialized, refuse a key that a reference points to, and make the storage keep its own copy of each key, so later changes through a reference cannot reach it.
Mitigation
The PHP project publishes security fixes only for its supported release lines. PHP 5.6 is End-of-Life and will not receive any public updates to address this issue.
Until an upgrade is possible, do not pass untrusted data to unserialize(); use a data format such as JSON for input that crosses a trust boundary.
Users of the affected components should apply one of the following mitigations:
- Upgrade to PHP 7.0.12, or later.
- Leverage a commercial support partner like HeroDevs (NES for PHP) for post-EOL security support.
Credits
- yannayl (Check Point) (finder)
- Stanislav Malyshev (remediation)