php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #70055 Use-after-free when modifying ArrayObject during sorting
Submitted: 2015-07-12 17:19 UTC Modified: 2017-01-02 12:30 UTC
From: tehybel at gmail dot com Assigned: nikic (profile)
Status: Closed Package: SPL related
PHP Version: 5.6.11 OS: Any
Private report: No CVE-ID: None
 [2015-07-12 17:19 UTC] tehybel at gmail dot com
Description:
------------
It is possible to free an ArrayObject's internal array while it is being sorted, causing a use-after-free which can lead to arbitrary code execution.

An ArrayObject from the SPL extension can be sorted using a user-defined comparison function. It it not supposed to be possible to modify the ArrayObject while it is being sorted. Most of the extension code which modifies the ArrayObject first checks whether a sort is ongoing, but this was forgotten in the exchangeArray function. That is the root cause of this vulnerability.

I have included sample code which, when run, causes a segfault:

Program received signal SIGSEGV, Segmentation fault.
0x000000000082fbf5 in _zval_ptr_dtor (zval_ptr=0x5a5a5a5a5a5a5a5a, 
    __zend_filename=0xd0b170 "/tmp/php-5.6.11/Zend/zend_variables.c", __zend_lineno=0xbc)
    at /tmp/php-5.6.11/Zend/zend_execute_API.c:424
424		i_zval_ptr_dtor(*zval_ptr ZEND_FILE_LINE_RELAY_CC TSRMLS_CC);

As we can see, i_zval_ptr_dtor is being called on zval_ptr, which originates from freed memory. By heap spraying, an attacker could fully control zval_ptr. The attacker could then execute arbitrary code by crafting and pointing to a fake hash table and having its destructor called, gaining control of the program counter.

Since an attacker must control the custom comparison function, it's unlikely to be triggerable remotely. Still this vulnerabilty has real impact because gaining arbitrary code execution lets an attacker bypass security measures like open_basedir and disable_functions, which could have big impact in shared hosting situations.


Test script:
---------------
<?php

function cmp($b, $c){
    global $a;
    var_dump($a);
    $a->exchangeArray(array());
}

$a = new ArrayObject();
$a[1] = 1;
$a[2] = 2;
$a[3] = 3;

$a->uasort(cmp);

Expected result:
----------------
I expect to get an error message saying "Modification of ArrayObject during sorting is prohibited"

Actual result:
--------------
Running the attached script on Ubuntu 14.04.2 LTS, PHP 5.6.11 compiled with debugging enabled, gives the following stack trace:

#0  0x000000000082fbf5 in _zval_ptr_dtor (zval_ptr=0x5a5a5a5a5a5a5a5a, 
    __zend_filename=0xd0b170 "/tmp/php-5.6.11/Zend/zend_variables.c", __zend_lineno=0xbc)
    at /tmp/php-5.6.11/Zend/zend_execute_API.c:424
#1  0x000000000084369f in _zval_ptr_dtor_wrapper (zval_ptr=0x5a5a5a5a5a5a5a5a)
    at /tmp/php-5.6.11/Zend/zend_variables.c:188
#2  0x0000000000858d68 in zend_hash_destroy (ht=0x7ffff7fdef88)
    at /tmp/php-5.6.11/Zend/zend_hash.c:548
#3  0x000000000084323e in _zval_dtor_func (zvalue=0x7ffff7fdc348, 
    __zend_filename=0xd08e18 "/tmp/php-5.6.11/Zend/zend_execute.h", __zend_lineno=0x4f)
    at /tmp/php-5.6.11/Zend/zend_variables.c:45
#4  0x000000000082e8e0 in _zval_dtor (zvalue=0x7ffff7fdc348, 
    __zend_filename=0xd08e18 "/tmp/php-5.6.11/Zend/zend_execute.h", __zend_lineno=0x4f)
    at /tmp/php-5.6.11/Zend/zend_variables.h:35
#5  0x000000000082e9a7 in i_zval_ptr_dtor (zval_ptr=0x7ffff7fdc348, 
    __zend_filename=0xcd39f8 "/tmp/php-5.6.11/ext/spl/spl_array.c", __zend_lineno=0x98)
    at /tmp/php-5.6.11/Zend/zend_execute.h:79
#6  0x000000000082fc0a in _zval_ptr_dtor (zval_ptr=0x7ffff7fddf68, 
    __zend_filename=0xcd39f8 "/tmp/php-5.6.11/ext/spl/spl_array.c", __zend_lineno=0x98)
    at /tmp/php-5.6.11/Zend/zend_execute_API.c:424
#7  0x00000000006d2b01 in spl_array_object_free_storage (object=0x7ffff7fddf48)
    at /tmp/php-5.6.11/ext/spl/spl_array.c:152
#8  0x00000000008839b0 in zend_objects_store_del_ref_by_handle_ex (handle=0x1, 
    handlers=0x10313a0 <spl_handler_ArrayObject>)
    at /tmp/php-5.6.11/Zend/zend_objects_API.c:226
#9  0x00000000008836b5 in zend_objects_store_del_ref (zobject=0x7ffff7fdbe40)
    at /tmp/php-5.6.11/Zend/zend_objects_API.c:178
#10 0x000000000084328e in _zval_dtor_func (zvalue=0x7ffff7fdbe40, 
    __zend_filename=0xd08e18 "/tmp/php-5.6.11/Zend/zend_execute.h", __zend_lineno=0x4f)
    at /tmp/php-5.6.11/Zend/zend_variables.c:57
#11 0x000000000082e8e0 in _zval_dtor (zvalue=0x7ffff7fdbe40, 
    __zend_filename=0xd08e18 "/tmp/php-5.6.11/Zend/zend_execute.h", __zend_lineno=0x4f)
    at /tmp/php-5.6.11/Zend/zend_variables.h:35
#12 0x000000000082e9a7 in i_zval_ptr_dtor (zval_ptr=0x7ffff7fdbe40, 
    __zend_filename=0xd0b170 "/tmp/php-5.6.11/Zend/zend_variables.c", __zend_lineno=0xbc)
    at /tmp/php-5.6.11/Zend/zend_execute.h:79
#13 0x000000000082fc0a in _zval_ptr_dtor (zval_ptr=0x7ffff7fde5c0, 
    __zend_filename=0xd0b170 "/tmp/php-5.6.11/Zend/zend_variables.c", __zend_lineno=0xbc)
    at /tmp/php-5.6.11/Zend/zend_execute_API.c:424
#14 0x000000000084369f in _zval_ptr_dtor_wrapper (zval_ptr=0x7ffff7fde5c0)
    at /tmp/php-5.6.11/Zend/zend_variables.c:188
#15 0x0000000000857316 in i_zend_hash_bucket_delete (
    ht=0x1034448 <executor_globals+360>, p=0x7ffff7fde5a8)
    at /tmp/php-5.6.11/Zend/zend_hash.c:182
#16 0x00000000008573ed in zend_hash_bucket_delete (
    ht=0x1034448 <executor_globals+360>, p=0x7ffff7fde5a8)
    at /tmp/php-5.6.11/Zend/zend_hash.c:192
#17 0x0000000000859596 in zend_hash_reverse_apply (
    ht=0x1034448 <executor_globals+360>, 
    apply_func=0x82f283 <zval_call_destructor>)
    at /tmp/php-5.6.11/Zend/zend_hash.c:733
#18 0x000000000082f33b in shutdown_destructors ()
    at /tmp/php-5.6.11/Zend/zend_execute_API.c:214
#19 0x00000000008457e9 in zend_call_destructors ()
    at /tmp/php-5.6.11/Zend/zend.c:944
#20 0x00000000007ab6fb in php_request_shutdown (dummy=0x0)
    at /tmp/php-5.6.11/main/main.c:1824
#21 0x00000000008fa390 in do_cli (argc=0x2, argv=0x1037aa0)
    at /tmp/php-5.6.11/sapi/cli/php_cli.c:1177
#22 0x00000000008fac1a in main (argc=0x2, argv=0x1037aa0)
    at /tmp/php-5.6.11/sapi/cli/php_cli.c:1378



Patches

Add a Patch

Pull Requests

Add a Pull Request

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2015-07-13 07:08 UTC] stas@php.net
-Type: Security +Type: Bug
 [2015-07-16 01:18 UTC] cmb@php.net
-Status: Open +Status: Verified
 [2015-07-16 01:18 UTC] cmb@php.net
This issue has already been fixed in PHP 7. The relevant commit
seems to be dda7e84[1]. unexpected_array_mod_bug.phpt passes in
PHP 5.6, but not the test script given in this ticket. I don't
know how to backport the changes, though.

[1] <https://github.com/php/php-src/commit/dda7e84f10bffb0eca57754e682530988edab235>
 [2015-08-20 21:05 UTC] tehybel at gmail dot com
What's the status on this?
 [2017-01-02 12:30 UTC] nikic@php.net
-Status: Verified +Status: Closed -Assigned To: +Assigned To: nikic
 [2017-01-02 12:30 UTC] nikic@php.net
Closing as this has been fixed in PHP 7.0 and PHP 5.6 is going out of active support.
 
PHP Copyright © 2001-2024 The PHP Group
All rights reserved.
Last updated: Fri Mar 29 13:01:29 2024 UTC