Skip to content

UAF in user filter when adding existing filter name due to incorrect error handling #17037

Closed
@chongwick

Description

@chongwick

Description

The following code:

<?php
function php_int_max_crash() {
    $stream_filter = stream_get_filters();
    $filter ='string.toupper';
    $params = array('filter'=>$filter);
    stream_filter_register($filter, 'filter_string_toupper');
    $fp = fopen('php://output', 'w');
    stream_filter_register($filter, 'filter_string_toupper', $params);
    stream_filter_push($fp, $filter);
    fwrite($fp, PHP_INT_MAX);
    fclose($fp);
}

class CrashArray {
    public function __construct() {
        return unpack("C", pack("C", PHP_INT_MAX));
    }

    public function buffer() {
        return PHP_INT_MAX;
    }
}

function evil_callback() {
    gc_collect_cycles();
    return PHP_INT_MAX; // 9223372036854775807
}

$evil_object = (object) array('valueOf' => 'evil_callback');

$crash_array = new CrashArray();
$crash_array->__construct();

echo $crash_array->buffer() === PHP_INT_MAX; // true

try {
    foreach ($crash_array as $__v_0) {
    }
} catch (Exception $e) {
    // do nothing
}

php_int_max_crash();

function time_travel() {
    $now = localtime();
    echo "Current time: ". $now['tm_year']. " year, ". ($now['tm_mon'] + 1). " month, ". $now['tm_mday']. " day, ". $now['tm_hour']. ":". $now['tm_min']. ":". $now['tm_sec']. "\n";

    // Time travel to the beginning of time
    $beginning_of_time = array('tm_year' => PHP_INT_MIN, 'tm_mon' => 0, 'tm_mday' => 1, 'tm_hour' => 0, 'tm_min' => 0, 'tm_sec' => 0);
    setlocale(LC_TIME, 'C');
    $beginning_of_time_string = strftime("%Y %B %d %H:%M:%S", mktime($beginning_of_time['tm_hour'], $beginning_of_time['tm_min'], $beginning_of_time['tm_sec'], $beginning_of_time['tm_mon'] + 1, $beginning_of_time['tm_mday'], $beginning_of_time['tm_year']));
    echo "Beginning of time: ". $beginning_of_time_string. "\n";
}

time_travel();

?>

Resulted in this output:

==2134144==ERROR: AddressSanitizer: heap-use-after-free on address 0x602000004d98 at pc 0x564b64e07b85 bp 0x7ffdb8c82540 sp 0x7ffdb8c82530
READ of size 8 at 0x602000004d98 thread T0
    #0 0x564b64e07b84 in filter_item_dtor /home/dan/php-src/ext/standard/user_filters.c:336
    #1 0x564b652705ba in zend_hash_destroy /home/dan/php-src/Zend/zend_hash.c:1763
    #2 0x564b64e0ac22 in zm_deactivate_user_filters /home/dan/php-src/ext/standard/user_filters.c:98
    #3 0x564b64ce0c7e in zm_deactivate_basic /home/dan/php-src/ext/standard/basic_functions.c:492
    #4 0x564b64ff0f11 in zend_deactivate_modules /home/dan/php-src/Zend/zend_API.c:3399
    #5 0x564b64e7ed70 in php_request_shutdown /home/dan/php-src/main/main.c:1927
    #6 0x564b6535c159 in do_cli /home/dan/php-src/sapi/cli/php_cli.c:1106
    #7 0x564b646c83c2 in main /home/dan/php-src/sapi/cli/php_cli.c:1310
    #8 0x1490625c8d8f  (/lib/x86_64-linux-gnu/libc.so.6+0x29d8f)
    #9 0x1490625c8e3f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x29e3f)
    #10 0x564b646c9634 in _start (/home/w023dtc/php_engines/san_php+0x4c8634)

0x602000004d98 is located 8 bytes inside of 16-byte region [0x602000004d90,0x602000004da0)
freed by thread T0 here:
    #0 0x149062c92537 in __interceptor_free ../../../../src/libsanitizer/asan/asan_malloc_linux.cpp:127
    #1 0x564b64e0be41 in zif_stream_filter_register /home/dan/php-src/ext/standard/user_filters.c:529
    #2 0x564b65223ee0 in ZEND_DO_ICALL_SPEC_RETVAL_UNUSED_HANDLER /home/dan/php-src/Zend/zend_vm_execute.h:1287
    #3 0x564b65223ee0 in execute_ex /home/dan/php-src/Zend/zend_vm_execute.h:58774
    #4 0x564b6523e490 in zend_execute /home/dan/php-src/Zend/zend_vm_execute.h:64206
    #5 0x564b65358645 in zend_execute_script /home/dan/php-src/Zend/zend.c:1934
    #6 0x564b64e814e6 in php_execute_script_ex /home/dan/php-src/main/main.c:2574
    #7 0x564b6535ce36 in do_cli /home/dan/php-src/sapi/cli/php_cli.c:935
    #8 0x564b646c83c2 in main /home/dan/php-src/sapi/cli/php_cli.c:1310
    #9 0x1490625c8d8f  (/lib/x86_64-linux-gnu/libc.so.6+0x29d8f)

previously allocated by thread T0 here:
    #0 0x149062c92887 in __interceptor_malloc ../../../../src/libsanitizer/asan/asan_malloc_linux.cpp:145
    #1 0x564b64fcb644 in __zend_malloc /home/dan/php-src/Zend/zend_alloc.c:3280
    #2 0x564b64fda98b in _ecalloc /home/dan/php-src/Zend/zend_alloc.c:2804
    #3 0x564b64e0bbf1 in zif_stream_filter_register /home/dan/php-src/ext/standard/user_filters.c:521
    #4 0x564b65223ee0 in ZEND_DO_ICALL_SPEC_RETVAL_UNUSED_HANDLER /home/dan/php-src/Zend/zend_vm_execute.h:1287
    #5 0x564b65223ee0 in execute_ex /home/dan/php-src/Zend/zend_vm_execute.h:58774
    #6 0x564b6523e490 in zend_execute /home/dan/php-src/Zend/zend_vm_execute.h:64206
    #7 0x564b65358645 in zend_execute_script /home/dan/php-src/Zend/zend.c:1934
    #8 0x564b64e814e6 in php_execute_script_ex /home/dan/php-src/main/main.c:2574
    #9 0x564b6535ce36 in do_cli /home/dan/php-src/sapi/cli/php_cli.c:935
    #10 0x564b646c83c2 in main /home/dan/php-src/sapi/cli/php_cli.c:1310
    #11 0x1490625c8d8f  (/lib/x86_64-linux-gnu/libc.so.6+0x29d8f)

SUMMARY: AddressSanitizer: heap-use-after-free /home/dan/php-src/ext/standard/user_filters.c:336 in filter_item_dtor
Shadow bytes around the buggy address:
  0x0c047fff8960: fa fa fd fd fa fa fd fa fa fa fd fd fa fa fd fa
  0x0c047fff8970: fa fa fd fd fa fa 00 06 fa fa fd fd fa fa fd fa
  0x0c047fff8980: fa fa fd fa fa fa fd fa fa fa fd fa fa fa fd fd
  0x0c047fff8990: fa fa fd fd fa fa fd fd fa fa 00 00 fa fa fd fa
  0x0c047fff89a0: fa fa fd fa fa fa fd fa fa fa fd fa fa fa 01 fa
=>0x0c047fff89b0: fa fa fd[fd]fa fa fd fd fa fa fa fa fa fa fa fa
  0x0c047fff89c0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c047fff89d0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c047fff89e0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c047fff89f0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c047fff8a00: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
Shadow byte legend (one shadow byte represents 8 application bytes):
  Addressable:           00
  Partially addressable: 01 02 03 04 05 06 07 
  Heap left redzone:       fa
  Freed heap region:       fd
  Stack left redzone:      f1
  Stack mid redzone:       f2
  Stack right redzone:     f3
  Stack after return:      f5
  Stack use after scope:   f8
  Global redzone:          f9
  Global init order:       f6
  Poisoned by user:        f7
  Container overflow:      fc
  Array cookie:            ac
  Intra object redzone:    bb
  ASan internal:           fe
  Left alloca redzone:     ca
  Right alloca redzone:    cb
  Shadow gap:              cc
==2134144==ABORTING

But I expected this output instead:

PHP Version

8.4.1

Operating System

No response

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions