php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Sec Bug #71020 Use after free in Collator::sortWithSortKeys
Submitted: 2015-12-03 22:09 UTC Modified: 2015-12-22 17:13 UTC
From: emmanuel dot law at gmail dot com Assigned: laruence (profile)
Status: Closed Package: intl (PECL)
PHP Version: 7.0.0 OS: *
Private report: No CVE-ID: 2015-8616
 [2015-12-03 22:09 UTC] emmanuel dot law at gmail dot com
Description:
------------
This is a vulnerability is in the function Collator::sortWithSortKeys. The vulnerable code is in ext/intl/collator/collator_sort.c

1) Given an array, each element (hashData) is being referenced by a pointer in sortKeyIndxBuf[sortKeyCount].zstr (line 493):

414: hash = Z_ARRVAL_P( array );
...
425: ZEND_HASH_FOREACH_VAL(hash, hashData) {
....
493: sortKeyIndxBuf[sortKeyCount].zstr = hashData;

2) The array is then destroy and it's hashData elements freed (ref count -1):
508: zval_ptr_dtor( array );


3) Note that at this point sortKeyIndxBuf[sortKeyCount].zstr still contain pointers to the hashData elements which has been freed. In essence, sortKeyIndxBuf[sortKeyCount].zstr  are dangling pointers.

4) A new array is reinitialized,
510: array_init(array);

5) The dangling pointers are then added as elements into the new array:
515: zend_hash_next_index_insert( Z_ARRVAL_P(array), sortKeyIndxBuf[j].zstr);



6) I've included a POC that triggers this  vulnerability, resulting in a null pointer dereference. The POC can be obtained via: https://www.dropbox.com/s/5y9azo01hvflzug/collate-UAF-Poc.php?dl=0

		-an array of 0xbb elements is being passed into the function
		-sortKeyIndxBuf[0].zstr to sortKeyIndxBuf[0xba].zstr points to the elements in the array
		- Array is destroyed via zval_ptr_dtor( array );
		- sortKeyIndxBuf[0....0xba].zstr are now dangling pointers
		- New array initialized (Hashtable with initial element size of 8)
		- As the dangling pointers are added to array, the size of the Hashtable grows.
		- As the Hashtable grows, it's allocated more memory via zend_hash_do_resize()
		- It will then be allocated memory that co-incides with an address pointed to by the dangling pointer sortKeyIndxBuf[j].zstr. Thus sortKeyIndxBuf[j].zstr now no longer points to a valid zval.
		- When the code below dereferences this address, because it is pointing to an invalid zval, it will access dereference whatever is the value within this "corrupted zval". In this case it's a null pointer dereference. 
		  514:Z_TRY_ADDREF_P( sortKeyIndxBuf[j].zstr );



Stopped reason: SIGSEGV
0x000000000069f9cd in zval_addref_p (pz=0x7fffed28f840) at /home/elaw/php-7.0.0RC8/Zend/zend_types.h:822
822             return ++GC_REFCOUNT(Z_COUNTED_P(pz));
gdb-peda$ bt
#0  0x000000000069f9cd in zval_addref_p (pz=0x7fffed28f840) at /home/elaw/php-7.0.0RC8/Zend/zend_types.h:822
#1  zif_collator_sort_with_sort_keys (execute_data=0x7fffed213120, return_value=0x7fffed213110) at /home/elaw/php-7.0.0RC8/ext/intl/collator/collator_sort.c:514
#2  0x0000000000ae496c in ZEND_DO_FCALL_SPEC_HANDLER () at /home/elaw/php-7.0.0RC8/Zend/zend_vm_execute.h:842
#3  0x0000000000ae0a37 in execute_ex (ex=0x7fffed213030) at /home/elaw/php-7.0.0RC8/Zend/zend_vm_execute.h:414
#4  0x0000000000ae135e in zend_execute (op_array=0x7fffed27f000, return_value=0x0) at /home/elaw/php-7.0.0RC8/Zend/zend_vm_execute.h:458
#5  0x0000000000a58578 in zend_execute_scripts (type=0x8, retval=0x0, file_count=0x3) at /home/elaw/php-7.0.0RC8/Zend/zend.c:1428
#6  0x00000000009b143d in php_execute_script (primary_file=0x7fffffffdee0) at /home/elaw/php-7.0.0RC8/main/main.c:2471
#7  0x0000000000bb41e3 in do_cli (argc=0x2, argv=0x14cc4d0) at /home/elaw/php-7.0.0RC8/sapi/cli/php_cli.c:974
#8  0x0000000000bb53b5 in main (argc=0x2, argv=0x14cc4d0) at /home/elaw/php-7.0.0RC8/sapi/cli/php_cli.c:1345
#9  0x00007ffff0c5ab45 in __libc_start_main (main=0xbb4d24 <main>, argc=0x2, argv=0x7fffffffe328, init=<optimized out>, fini=<optimized out>, rtld_fini=<optimized out>,
    stack_end=0x7fffffffe318) at libc-start.c:287
#10 0x0000000000445719 in _start ()


This bug seems to have been introduced in this commit: Bug introducted in https://github.com/php/php-src/commit/4fbaddb4f8b041769bea7efdd12313641387bd14
Only php 7 is affected.


Test script:
---------------
./configure --enable-intl; make 
php collate-UAF-Poc.php


Patches

Pull Requests

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2015-12-03 22:11 UTC] emmanuel dot law at gmail dot com
-Summary: Use after free in Collator::sortWithSortKeys5 +Summary: Use after free in Collator::sortWithSortKeys
 [2015-12-03 22:11 UTC] emmanuel dot law at gmail dot com
Type in summary
 [2015-12-07 01:25 UTC] stas@php.net
-Assigned To: +Assigned To: laruence
 [2015-12-07 08:54 UTC] stas@php.net
Proposed patch:

diff --git a/ext/intl/collator/collator_sort.c b/ext/intl/collator/collator_sort.c
index deb2f7b..8da83a9 100644
--- a/ext/intl/collator/collator_sort.c
+++ b/ext/intl/collator/collator_sort.c
@@ -36,7 +36,7 @@ typedef zend_long ptrdiff_t;
  */
 typedef struct _collator_sort_key_index {
        char* key;       /* pointer to sort key */
-       zval* zstr;     /* pointer to original string(hash-item) */
+       zval  zstr;     /* pointer to original string(hash-item) */
 } collator_sort_key_index_t;
 
 ZEND_EXTERN_MODULE_GLOBALS( intl )
@@ -490,7 +490,8 @@ PHP_FUNCTION( collator_sort_with_sort_keys )
 
                sortKeyIndxBuf[sortKeyCount].key = (char*)sortKeyBufOffset;    /* remember just offset, cause address */
                                                                               /* of 'sortKeyBuf' may be changed due to realloc. */
-               sortKeyIndxBuf[sortKeyCount].zstr = hashData;
+               Z_TRY_ADDREF_P(hashData);
+               sortKeyIndxBuf[sortKeyCount].zstr = *hashData;
 
                sortKeyBufOffset += sortKeyLen;
                ++sortKeyCount;
@@ -511,8 +512,7 @@ PHP_FUNCTION( collator_sort_with_sort_keys )
 
        for( j = 0; j < sortKeyCount; j++ )
        {
-               Z_TRY_ADDREF_P( sortKeyIndxBuf[j].zstr );
-               zend_hash_next_index_insert( Z_ARRVAL_P(array), sortKeyIndxBuf[j].zstr);
+               zend_hash_next_index_insert( Z_ARRVAL_P(array), &sortKeyIndxBuf[j].zstr);
        }
 
        if( utf16_buf )


Please see if this fixes the issue for you.
 [2015-12-07 11:01 UTC] emmanuel dot law at gmail dot com
Yes that seems to solve it.
 [2015-12-07 15:54 UTC] laruence@php.net
it's weird, this has already been fixed in https://github.com/php/php-src/commit/e48988311d2e726eeeb25ebbbde42146c0f53b48 not sure why it doesn't close this automatically?
 [2015-12-07 16:01 UTC] kaplan@php.net
Making public as the fix is already committed.
 [2015-12-07 17:58 UTC] stas@php.net
Putting this in public before the release probably wasn't the best idea, as this could be exploitable if the sortable data is user-controlled.
 [2015-12-07 19:04 UTC] ab@php.net
Yeah, we should have kept this till short before the release, as usually done for security patches.

Thanks.
 [2015-12-07 22:08 UTC] kaplan@php.net
-Private report: No +Private report: Yes
 [2015-12-07 22:08 UTC] kaplan@php.net
My apologies for the early publication.
 [2015-12-08 07:19 UTC] stas@php.net
-Status: Assigned +Status: Closed
 [2015-12-08 07:19 UTC] stas@php.net
I think since the fix has been committed we can close it?
 [2015-12-22 17:13 UTC] kaplan@php.net
-CVE-ID: +CVE-ID: 2015-8616
 [2016-07-20 11:34 UTC] davey@php.net
Automatic comment on behalf of laruence@gmail.com
Revision: http://git.php.net/?p=php-src.git;a=commit;h=e48988311d2e726eeeb25ebbbde42146c0f53b48
Log: Fixed bug #71020 (Use after free in Collator::sortWithSortKeys)
 
PHP Copyright © 2001-2024 The PHP Group
All rights reserved.
Last updated: Thu Nov 21 08:01:29 2024 UTC