CVE-2016-9936 (published 2017-01-04T20:59Z)

The unserialize implementation in ext/standard/var.c in PHP 7.x before 7.0.14 allows remote attackers to cause a denial of service (use-after-free) or possibly have unspecified other impact via crafted serialized data.  NOTE: this vulnerability exists because of an incomplete fix for CVE-2015-6834.

Severity: HIGH
CVSS v2 base score: 7.5 (AV:N/AC:L/Au:N/C:P/I:P/A:P)
CVSS v3 base score: 9.8, CRITICAL (CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H)

Weakness information:
cwe_id: CWE-416
cwe_name: Use After Free
description: The product reuses or references memory after it has been freed. At some point afterward, the memory may be allocated again and saved in another pointer, while the original pointer references a location somewhere within the new allocation. Any operations using the original pointer are no longer valid because the memory "belongs" to the code that operates on the new pointer.
extended_description: 
             
             
             
			 
             
             
         
         

Fix information:
repo_url: https://github.com/php/php-src
commit_details: repo_url: https://github.com/php/php-src
author: Nikita Popov
author_date: 2016-10-23 21:37:36+02:00
msg: Complete the fix of bug #70172 for PHP 7
repository: repo_url: https://github.com/php/php-src
repo_name: php/php-src
description: The PHP Interpreter
repo_language: C
owner: php
file_changes: filename: bug70172_2.phpt
change_type: ModificationType.MODIFY
diff: @@ -1,7 +1,5 @@
 --TEST--
 Bug #70172 - Use After Free Vulnerability in unserialize()
---XFAIL--
-Unfinished merge, needs fix.
 --FILE--
 <?php
 class obj implements Serializable {
@@ -61,10 +59,10 @@ function ptr2str($ptr)
     [0]=>
     array(1) {
       [0]=>
-      &object(obj2)#%d (1) {
+      object(obj2)#%d (1) {
         ["ryat"]=>
         int(1)
       }
     }
   }
-}
\ No newline at end of file
+}

programming_language: PHP
filename: var.c
change_type: ModificationType.MODIFY
diff: @@ -1036,6 +1036,7 @@ PHP_FUNCTION(unserialize)
 	const unsigned char *p;
 	php_unserialize_data_t var_hash;
 	zval *options = NULL, *classes = NULL;
+	zval *retval;
 	HashTable *class_hash = NULL;
 
 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|a", &buf, &buf_len, &options) == FAILURE) {
@@ -1067,22 +1068,21 @@ PHP_FUNCTION(unserialize)
 		}
 	}
 
-	if (!php_var_unserialize_ex(return_value, &p, p + buf_len, &var_hash, class_hash)) {
+	retval = var_tmp_var(&var_hash);
+	if (!php_var_unserialize_ex(retval, &p, p + buf_len, &var_hash, class_hash)) {
 		PHP_VAR_UNSERIALIZE_DESTROY(var_hash);
 		if (class_hash) {
 			zend_hash_destroy(class_hash);
 			FREE_HASHTABLE(class_hash);
 		}
-		zval_ptr_dtor(return_value);
 		if (!EG(exception)) {
 			php_error_docref(NULL, E_NOTICE, "Error at offset " ZEND_LONG_FMT " of %zd bytes",
 				(zend_long)((char*)p - buf), buf_len);
 		}
 		RETURN_FALSE;
 	}
-	/* We should keep an reference to return_value to prevent it from being dtor
-	   in case nesting calls to unserialize */
-	var_push_dtor(&var_hash, return_value);
+
+	ZVAL_COPY(return_value, retval);
 
 	PHP_VAR_UNSERIALIZE_DESTROY(var_hash);
 	if (class_hash) {

programming_language: C