CVE-2021-21707
Patch Available.
This Vulnerability has been fixed in the Never-Ending Support (NES) version offered by HeroDevs.
Overview
PHP is a general-purpose scripting language used mostly for web development. It runs behind web servers through server APIs such as Apache mod_php and PHP-FPM, and powers content management systems and frameworks such as WordPress, Drupal, Moodle and Laravel. Its libxml-based extensions (dom, simplexml, xmlreader and others) parse XML documents and can load them directly from a file path or URL.
An Improper Input Validation vulnerability (CVE-2021-21707) has been identified in the file loading code shared by PHP's libxml-based XML extensions, which allows an attacker who can influence a file path that an application passes to functions such as simplexml_load_file() or DOMDocument::load() to make the function stop reading the path at an encoded NUL byte, so that a different file is loaded than the one the application checked. PHP tracks this issue as bug #79971.
Per MITRE CWE-158: The product receives input from an upstream component, but it does not neutralize or incorrectly neutralizes NUL characters or null bytes when they are sent to a downstream component.
This issue affects all versions of PHP before 7.3.33, PHP 7.4 before 7.4.26 and PHP 8.0 before 8.0.13, which includes the End-of-Life release lines PHP 5.6, 7.0, 7.1 and 7.2 through their final releases (5.6.40, 7.0.33, 7.1.33 and 7.2.34).
Details
Module Info
- Product: PHP
- Affected packages:
php-src(ext/libxml,ext/dom,ext/simplexml,ext/xmlreader) - Affected versions: <7.3.33, >=7.4.0 <7.4.26, >=8.0.0 <8.0.13
- GitHub repository: https://github.com/php/php-src
- Published packages: https://www.php.net/downloads
- Package manager: N/A
- Fixed in:
- OSS PHP 7.3.33, 7.4.26, 8.0.13
- NES for PHP 5.6.1 (based on PHP 5.6.40)
- NES for PHP 7.2.1 (based on PHP 7.2.34)
Vulnerability Info
This Medium-severity vulnerability is found in the XML extensions of all versions of PHP before 7.3.33, PHP 7.4 before 7.4.26 and PHP 8.0 before 8.0.13, including the End-of-Life PHP 5.6, 7.0, 7.1 and 7.2 release lines, which never received the upstream fix. The PHP project scores it 5.3 under CVSS 3.1 (CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N).
Functions such as simplexml_load_file(), DOMDocument::load(), DOMDocument::loadHTMLFile() and XMLReader::open() take a file name, but libxml2 treats it as a URI. PHP's stream opener for libxml, php_libxml_streams_IO_open_wrapper(), parses the value as a URI and, for local paths and file: URIs, URL-decodes it with xmlURIUnescapeString() before opening the result. A path that contains %00 is decoded into a string with a NUL byte in it, and everything after that byte is ignored when the file is opened. The reporter showed that inserting poc-1.xml%00 into a path makes simplexml_load_file() load poc-1.xml and ignore the rest of the path.
An application that builds a path from user input and validates it as a whole, for example by checking its extension or suffix with string functions, can therefore be tricked into loading another file it did not intend to read. The CVE record notes that this may lead to reading a different file than intended. HeroDevs' analysis puts the exposure in any SAPI with the libxml-based extensions, in code that loads an XML file whose path an attacker influences after validating it with string functions. For the PHP 5.6 line, the analysis covers NUL bytes in such paths in general and also lists SoapClient's WSDL loading among the affected paths. Applications that load XML only from fixed paths, or that read the file themselves and pass its contents to simplexml_load_string() or DOMDocument::loadXML(), are not affected.
The fix rejects any URI that contains a percent-encoded NUL byte before it is decoded, so the load fails with a warning instead of opening a truncated path:
if (strstr(filename, "%00")) {
php_error_docref(NULL, E_WARNING, "URI must not contain percent-encoded NUL bytes");
return NULL;
}
uri = xmlParseURI(filename);
if (uri && (uri->scheme == NULL ||
(xmlStrncmp(BAD_CAST uri->scheme, BAD_CAST "file", 4) == 0))) {
resolved_path = xmlURIUnescapeString(filename, 0, NULL);The same check is added to the output path used when libxml writes documents to a file.
Mitigation
The PHP project publishes security fixes only for its supported release lines. PHP 5.6, 7.0, 7.1 and 7.2 are End-of-Life and will not receive any public updates to address this issue.
Until an upgrade is possible, reject user-influenced paths that contain %00 or a NUL byte before passing them to the XML functions, or read the file with PHP's own file functions and parse the contents with simplexml_load_string() or DOMDocument::loadXML().
Users of the affected components should apply one of the following mitigations:
- Upgrade to PHP 7.3.33, 7.4.26, 8.0.13, or later.
- Leverage a commercial support partner like HeroDevs (NES for PHP) for post-EOL security support.
Credits
- rawataman6525 (finder)
- Christoph M. Becker (remediation)