From 882abe12cd8a8347a5dd75754a7e0ccb1926eea9 Mon Sep 17 00:00:00 2001 From: Lee Saferite Date: Wed, 17 Jun 2015 10:52:32 -0400 Subject: [PATCH] Applied patch SUPEE-3762 to address XML entity escaping vulnerability in the Zend framework --- app/etc/applied.patches.list | 6 ++ lib/Zend/Soap/Server.php | 25 +++--- lib/Zend/Xml/Exception.php | 36 ++++++++ lib/Zend/Xml/Security.php | 164 +++++++++++++++++++++++++++++++++++ 4 files changed, 220 insertions(+), 11 deletions(-) create mode 100644 lib/Zend/Xml/Exception.php create mode 100644 lib/Zend/Xml/Security.php diff --git a/app/etc/applied.patches.list b/app/etc/applied.patches.list index a35b092b9a8..b303b11b209 100644 --- a/app/etc/applied.patches.list +++ b/app/etc/applied.patches.list @@ -48,3 +48,9 @@ patching file lib/PEAR/PEAR/PEAR5.php patching file lib/Varien/Io/File.php +2014-08-12 23:00:46 UTC | SUPEE-3762 | EE_1.14.0.1 | v1 | 3608d69d73a826b371f9893b49ddcb347c9b3f37 | Thu Jun 12 11:08:43 2014 -0700 | v1.14.0.1..HEAD +patching file lib/Zend/Soap/Server.php +patching file lib/Zend/Xml/Exception.php +patching file lib/Zend/Xml/Security.php + + diff --git a/lib/Zend/Soap/Server.php b/lib/Zend/Soap/Server.php index 046cf2357fc..845883668f8 100644 --- a/lib/Zend/Soap/Server.php +++ b/lib/Zend/Soap/Server.php @@ -24,6 +24,12 @@ */ #require_once 'Zend/Server/Interface.php'; +/** @see Zend_Xml_Security */ +#require_once 'Zend/Xml/Security.php'; + +/** @see Zend_Xml_Exception */ +#require_once 'Zend/Xml/Exception.php'; + /** * Zend_Soap_Server * @@ -729,21 +735,18 @@ protected function _setRequest($request) $xml = $request; } - libxml_disable_entity_loader(true); $dom = new DOMDocument(); - if(strlen($xml) == 0 || !$dom->loadXML($xml)) { - #require_once 'Zend/Soap/Server/Exception.php'; - throw new Zend_Soap_Server_Exception('Invalid XML'); - } - foreach ($dom->childNodes as $child) { - if ($child->nodeType === XML_DOCUMENT_TYPE_NODE) { + try { + if(strlen($xml) == 0 || (!$dom = Zend_Xml_Security::scan($xml, $dom))) { #require_once 'Zend/Soap/Server/Exception.php'; - throw new Zend_Soap_Server_Exception( - 'Invalid XML: Detected use of illegal DOCTYPE' - ); + throw new Zend_Soap_Server_Exception('Invalid XML'); } + } catch (Zend_Xml_Exception $e) { + #require_once 'Zend/Soap/Server/Exception.php'; + throw new Zend_Soap_Server_Exception( + $e->getMessage() + ); } - libxml_disable_entity_loader(false); } $this->_request = $xml; return $this; diff --git a/lib/Zend/Xml/Exception.php b/lib/Zend/Xml/Exception.php new file mode 100644 index 00000000000..3418f35dee3 --- /dev/null +++ b/lib/Zend/Xml/Exception.php @@ -0,0 +1,36 @@ + 0) { + return true; + } + return false; + } + + /** + * Scan XML string for potential XXE and XEE attacks + * + * @param string $xml + * @param DomDocument $dom + * @throws Zend_Xml_Exception + * @return SimpleXMLElement|DomDocument|boolean + */ + public static function scan($xml, DOMDocument $dom = null) + { + // If running with PHP-FPM we perform an heuristic scan + // We cannot use libxml_disable_entity_loader because of this bug + // @see https://bugs.php.net/bug.php?id=64938 + if (self::isPhpFpm()) { + self::heuristicScan($xml); + } + + if (null === $dom) { + $simpleXml = true; + $dom = new DOMDocument(); + } + + if (!self::isPhpFpm()) { + $loadEntities = libxml_disable_entity_loader(true); + $useInternalXmlErrors = libxml_use_internal_errors(true); + } + + // Load XML with network access disabled (LIBXML_NONET) + // error disabled with @ for PHP-FPM scenario + set_error_handler(array('Zend_Xml_Security', 'loadXmlErrorHandler'), E_WARNING); + + $result = $dom->loadXml($xml, LIBXML_NONET); + restore_error_handler(); + + if (!$result) { + // Entity load to previous setting + if (!self::isPhpFpm()) { + libxml_disable_entity_loader($loadEntities); + libxml_use_internal_errors($useInternalXmlErrors); + } + return false; + } + + // Scan for potential XEE attacks using ENTITY, if not PHP-FPM + if (!self::isPhpFpm()) { + foreach ($dom->childNodes as $child) { + if ($child->nodeType === XML_DOCUMENT_TYPE_NODE) { + if ($child->entities->length > 0) { + #require_once 'Exception.php'; + throw new Zend_Xml_Exception(self::ENTITY_DETECT); + } + } + } + } + + // Entity load to previous setting + if (!self::isPhpFpm()) { + libxml_disable_entity_loader($loadEntities); + libxml_use_internal_errors($useInternalXmlErrors); + } + + if (isset($simpleXml)) { + $result = simplexml_import_dom($dom); + if (!$result instanceof SimpleXMLElement) { + return false; + } + return $result; + } + return $dom; + } + + /** + * Scan XML file for potential XXE/XEE attacks + * + * @param string $file + * @param DOMDocument $dom + * @throws Zend_Xml_Exception + * @return SimpleXMLElement|DomDocument + */ + public static function scanFile($file, DOMDocument $dom = null) + { + if (!file_exists($file)) { + #require_once 'Exception.php'; + throw new Zend_Xml_Exception( + "The file $file specified doesn't exist" + ); + } + return self::scan(file_get_contents($file), $dom); + } + + /** + * Return true if PHP is running with PHP-FPM + * + * @return boolean + */ + public static function isPhpFpm() + { + if (substr(php_sapi_name(), 0, 3) === 'fpm') { + return true; + } + return false; + } +}