CVE-2016-5773 (published 2016-08-07T10:59Z)

php_zip.c in the zip extension in PHP before 5.5.37, 5.6.x before 5.6.23, and 7.x before 7.0.8 improperly interacts with the unserialize implementation and garbage collection, which allows remote attackers to execute arbitrary code or cause a denial of service (use-after-free and application crash) via crafted serialized data containing a ZipArchive object.

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: Stanislav Malyshev
author_date: 2016-06-20 21:35:22-07:00
msg: Fix bug #72434: ZipArchive class Use After Free Vulnerability in PHP's GC algorithm and unserialize
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: bug72434.phpt
change_type: ModificationType.ADD
diff: @@ -0,0 +1,33 @@
+--TEST--
+Bug #72434: ZipArchive class Use After Free Vulnerability in PHP's GC algorithm and unserialize
+--SKIPIF--
+<?php
+if(!class_exists('zip')) die('ZipArchive');
+?>
+--FILE--
+<?php
+// The following array will be serialized and this representation will be freed later on.
+$free_me = array(new StdClass());
+// Create our payload and unserialize it.
+$serialized_payload = 'a:3:{i:1;N;i:2;O:10:"ZipArchive":1:{s:8:"filename";'.serialize($free_me).'}i:1;R:4;}';
+$unserialized_payload = unserialize($serialized_payload);
+gc_collect_cycles();
+// The reference counter for $free_me is at -1 for PHP 7 right now.
+// Increment the reference counter by 1 -> rc is 0
+$a = $unserialized_payload[1];
+// Increment the reference counter by 1 again -> rc is 1
+$b = $a;
+// Trigger free of $free_me (referenced by $m[1]).
+unset($b);
+$fill_freed_space_1 = "filler_zval_1";
+$fill_freed_space_2 = "filler_zval_2";
+$fill_freed_space_3 = "filler_zval_3";
+$fill_freed_space_4 = "filler_zval_4";
+debug_zval_dump($unserialized_payload[1]);
+?>
+--EXPECTF--
+array(1) refcount(1){
+  [0]=>
+  object(stdClass)#%d (0) refcount(3){
+  }
+}

programming_language: PHP
filename: php_zip.c
change_type: ModificationType.MODIFY
diff: @@ -1015,6 +1015,14 @@ static int php_zip_has_property(zval *object, zval *member, int type, const zend
 }
 /* }}} */
 
+static HashTable *php_zip_get_gc(zval *object, zval ***gc_data, int *gc_data_count TSRMLS_DC) /* {{{ */
+{
+	*gc_data = NULL;
+	*gc_data_count = 0;
+	return zend_std_get_properties(object TSRMLS_CC);
+}
+/* }}} */
+
 static HashTable *php_zip_get_properties(zval *object TSRMLS_DC)/* {{{ */
 {
 	ze_zip_object *obj;
@@ -2777,6 +2785,7 @@ static PHP_MINIT_FUNCTION(zip)
 	zip_object_handlers.clone_obj		= NULL;
 	zip_object_handlers.get_property_ptr_ptr = php_zip_get_property_ptr_ptr;
 
+	zip_object_handlers.get_gc          = php_zip_get_gc;
 	zip_object_handlers.get_properties = php_zip_get_properties;
 	zip_object_handlers.read_property	= php_zip_read_property;
 	zip_object_handlers.has_property	= php_zip_has_property;

programming_language: C