Skip to content
This repository was archived by the owner on May 16, 2018. It is now read-only.

Commit 1b5e861

Browse files
author
matthew
committed
Fixes for potential XXE/XEE vulnerabilities
- Patch provided by Padriac Brady, reviewed by Ralph Schindler and Matthew Weier O'Phinney - Merged from r25031 git-svn-id: http://framework.zend.com/svn/framework/standard/branches/release-1.12@25033 44c647ce-9c0f-0410-b52a-842ac1e357ba
1 parent 1693f72 commit 1b5e861

File tree

17 files changed

+847
-8
lines changed

17 files changed

+847
-8
lines changed

library/Zend/Dom/Query.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,7 @@ public function queryXpath($xpathQuery, $query = null)
245245

246246
$encoding = $this->getEncoding();
247247
libxml_use_internal_errors(true);
248+
libxml_disable_entity_loader(true);
248249
if (null === $encoding) {
249250
$domDoc = new DOMDocument('1.0');
250251
} else {
@@ -254,6 +255,14 @@ public function queryXpath($xpathQuery, $query = null)
254255
switch ($type) {
255256
case self::DOC_XML:
256257
$success = $domDoc->loadXML($document);
258+
foreach ($domDoc->childNodes as $child) {
259+
if ($child->nodeType === XML_DOCUMENT_TYPE_NODE) {
260+
require_once 'Zend/Dom/Exception.php';
261+
throw new Zend_Dom_Exception(
262+
'Invalid XML: Detected use of illegal DOCTYPE'
263+
);
264+
}
265+
}
257266
break;
258267
case self::DOC_HTML:
259268
case self::DOC_XHTML:
@@ -266,6 +275,7 @@ public function queryXpath($xpathQuery, $query = null)
266275
$this->_documentErrors = $errors;
267276
libxml_clear_errors();
268277
}
278+
libxml_disable_entity_loader(false);
269279
libxml_use_internal_errors(false);
270280

271281
if (!$success) {

library/Zend/Feed/Reader.php

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -333,10 +333,19 @@ public static function importFeed(Zend_Feed_Abstract $feed)
333333
*/
334334
public static function importString($string)
335335
{
336-
337336
$libxml_errflag = libxml_use_internal_errors(true);
337+
$oldValue = libxml_disable_entity_loader(true);
338338
$dom = new DOMDocument;
339339
$status = $dom->loadXML($string);
340+
foreach ($dom->childNodes as $child) {
341+
if ($child->nodeType === XML_DOCUMENT_TYPE_NODE) {
342+
require_once 'Zend/Feed/Exception.php';
343+
throw new Zend_Feed_Exception(
344+
'Invalid XML: Detected use of illegal DOCTYPE'
345+
);
346+
}
347+
}
348+
libxml_disable_entity_loader($oldValue);
340349
libxml_use_internal_errors($libxml_errflag);
341350

342351
if (!$status) {
@@ -407,8 +416,10 @@ public static function findFeedLinks($uri)
407416
}
408417
$responseHtml = $response->getBody();
409418
$libxml_errflag = libxml_use_internal_errors(true);
419+
$oldValue = libxml_disable_entity_loader(true);
410420
$dom = new DOMDocument;
411421
$status = $dom->loadHTML($responseHtml);
422+
libxml_disable_entity_loader($oldValue);
412423
libxml_use_internal_errors($libxml_errflag);
413424
if (!$status) {
414425
// Build error message
@@ -442,8 +453,18 @@ public static function detectType($feed, $specOnly = false)
442453
$dom = $feed;
443454
} elseif(is_string($feed) && !empty($feed)) {
444455
@ini_set('track_errors', 1);
456+
$oldValue = libxml_disable_entity_loader(true);
445457
$dom = new DOMDocument;
446458
$status = @$dom->loadXML($feed);
459+
foreach ($dom->childNodes as $child) {
460+
if ($child->nodeType === XML_DOCUMENT_TYPE_NODE) {
461+
require_once 'Zend/Feed/Exception.php';
462+
throw new Zend_Feed_Exception(
463+
'Invalid XML: Detected use of illegal DOCTYPE'
464+
);
465+
}
466+
}
467+
libxml_disable_entity_loader($oldValue);
447468
@ini_restore('track_errors');
448469
if (!$status) {
449470
if (!isset($php_errormsg)) {

library/Zend/Serializer/Adapter/Wddx.php

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,19 @@ public function unserialize($wddx, array $opts = array())
100100
// check if the returned NULL is valid
101101
// or based on an invalid wddx string
102102
try {
103-
$simpleXml = new SimpleXMLElement($wddx);
103+
$oldLibxmlDisableEntityLoader = libxml_disable_entity_loader(true);
104+
$dom = new DOMDocument;
105+
$dom->loadXML($wddx);
106+
foreach ($dom->childNodes as $child) {
107+
if ($child->nodeType === XML_DOCUMENT_TYPE_NODE) {
108+
require_once 'Zend/Serializer/Exception.php';
109+
throw new Zend_Serializer_Exception(
110+
'Invalid XML: Detected use of illegal DOCTYPE'
111+
);
112+
}
113+
}
114+
$simpleXml = simplexml_import_dom($dom);
115+
libxml_disable_entity_loader($oldLibxmlDisableEntityLoader);
104116
if (isset($simpleXml->data[0]->null[0])) {
105117
return null; // valid null
106118
}

library/Zend/Soap/Client/Local.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,13 @@ public function _doRequest(Zend_Soap_Client_Common $client, $request, $location,
8484
ob_start();
8585
$this->_server->handle($request);
8686
$response = ob_get_clean();
87+
88+
if ($response === null || $response === '') {
89+
$serverResponse = $this->server->getResponse();
90+
if ($serverResponse !== null) {
91+
$response = $serverResponse;
92+
}
93+
}
8794

8895
return $response;
8996
}

library/Zend/Soap/Server.php

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -729,11 +729,21 @@ protected function _setRequest($request)
729729
$xml = $request;
730730
}
731731

732+
libxml_disable_entity_loader(true);
732733
$dom = new DOMDocument();
733734
if(strlen($xml) == 0 || !$dom->loadXML($xml)) {
734735
require_once 'Zend/Soap/Server/Exception.php';
735736
throw new Zend_Soap_Server_Exception('Invalid XML');
736737
}
738+
foreach ($dom->childNodes as $child) {
739+
if ($child->nodeType === XML_DOCUMENT_TYPE_NODE) {
740+
require_once 'Zend/Soap/Server/Exception.php';
741+
throw new Zend_Soap_Server_Exception(
742+
'Invalid XML: Detected use of illegal DOCTYPE'
743+
);
744+
}
745+
}
746+
libxml_disable_entity_loader(false);
737747
}
738748
$this->_request = $xml;
739749
return $this;
@@ -866,16 +876,16 @@ public function handle($request = null)
866876

867877
$soap = $this->_getSoap();
868878

879+
$fault = false;
869880
ob_start();
870-
if($setRequestException instanceof Exception) {
871-
// Send SOAP fault message if we've catched exception
872-
$soap->fault("Sender", $setRequestException->getMessage());
881+
if ($setRequestException instanceof Exception) {
882+
// Create SOAP fault message if we've caught a request exception
883+
$fault = $this->fault($setRequestException->getMessage(), 'Sender');
873884
} else {
874885
try {
875886
$soap->handle($this->_request);
876887
} catch (Exception $e) {
877888
$fault = $this->fault($e);
878-
$soap->fault($fault->faultcode, $fault->faultstring);
879889
}
880890
}
881891
$this->_response = ob_get_clean();
@@ -884,6 +894,11 @@ public function handle($request = null)
884894
restore_error_handler();
885895
ini_set('display_errors', $displayErrorsOriginalState);
886896

897+
// Send a fault, if we have one
898+
if ($fault) {
899+
$this->_response = $fault;
900+
}
901+
887902
if (!$this->_returnResponse) {
888903
echo $this->_response;
889904
return;

library/Zend/Soap/Wsdl.php

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,13 +96,23 @@ public function __construct($name, $uri, $strategy = true)
9696
xmlns:xsd='http://www.w3.org/2001/XMLSchema'
9797
xmlns:soap-enc='http://schemas.xmlsoap.org/soap/encoding/'
9898
xmlns:wsdl='http://schemas.xmlsoap.org/wsdl/'></definitions>";
99+
libxml_disable_entity_loader(true);
99100
$this->_dom = new DOMDocument();
100101
if (!$this->_dom->loadXML($wsdl)) {
101102
require_once 'Zend/Server/Exception.php';
102103
throw new Zend_Server_Exception('Unable to create DomDocument');
103104
} else {
105+
foreach ($this->_dom->childNodes as $child) {
106+
if ($child->nodeType === XML_DOCUMENT_TYPE_NODE) {
107+
require_once 'Zend/Server/Exception.php';
108+
throw new Zend_Server_Exception(
109+
'Invalid XML: Detected use of illegal DOCTYPE'
110+
);
111+
}
112+
}
104113
$this->_wsdl = $this->_dom->documentElement;
105114
}
115+
libxml_disable_entity_loader(false);
106116

107117
$this->setComplexTypeStrategy($strategy);
108118
}
@@ -125,8 +135,10 @@ public function setUri($uri)
125135
// @todo: This is the worst hack ever, but its needed due to design and non BC issues of WSDL generation
126136
$xml = $this->_dom->saveXML();
127137
$xml = str_replace($oldUri, $uri, $xml);
138+
libxml_disable_entity_loader(true);
128139
$this->_dom = new DOMDocument();
129140
$this->_dom->loadXML($xml);
141+
libxml_disable_entity_loader(false);
130142
}
131143

132144
return $this;

library/Zend/XmlRpc/Request.php

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -306,7 +306,17 @@ public function loadXml($request)
306306
// @see ZF-12293 - disable external entities for security purposes
307307
$loadEntities = libxml_disable_entity_loader(true);
308308
try {
309-
$xml = new SimpleXMLElement($request);
309+
$dom = new DOMDocument;
310+
$dom->loadXML($request);
311+
foreach ($dom->childNodes as $child) {
312+
if ($child->nodeType === XML_DOCUMENT_TYPE_NODE) {
313+
require_once 'Zend/XmlRpc/Exception.php';
314+
throw new Zend_XmlRpc_Exception(
315+
'Invalid XML: Detected use of illegal DOCTYPE'
316+
);
317+
}
318+
}
319+
$xml = simplexml_import_dom($dom);
310320
libxml_disable_entity_loader($loadEntities);
311321
} catch (Exception $e) {
312322
// Not valid XML

library/Zend/XmlRpc/Response.php

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,18 @@ public function loadXml($response)
180180
$loadEntities = libxml_disable_entity_loader(true);
181181
$useInternalXmlErrors = libxml_use_internal_errors(true);
182182
try {
183+
$dom = new DOMDocument;
184+
$dom->loadXML($response);
185+
foreach ($dom->childNodes as $child) {
186+
if ($child->nodeType === XML_DOCUMENT_TYPE_NODE) {
187+
require_once 'Zend/XmlRpc/Exception.php';
188+
throw new Zend_XmlRpc_Exception(
189+
'Invalid XML: Detected use of illegal DOCTYPE'
190+
);
191+
}
192+
}
193+
// TODO: Locate why this passes tests but a simplexml import doesn't
194+
// $xml = simplexml_import_dom($dom);
183195
$xml = new SimpleXMLElement($response);
184196
libxml_disable_entity_loader($loadEntities);
185197
libxml_use_internal_errors($useInternalXmlErrors);

tests/Zend/Dom/QueryTest.php

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -348,6 +348,20 @@ public function testXhtmlDocumentWithXmlAndDoctypeDeclaration()
348348
$this->query->setDocument($xhtmlWithXmlDecl, 'utf-8');
349349
$this->assertEquals(1, $this->query->query('//p')->count());
350350
}
351+
352+
public function testLoadingXmlContainingDoctypeShouldFailToPreventXxeAndXeeAttacks()
353+
{
354+
$xml = <<<XML
355+
<?xml version="1.0"?>
356+
<!DOCTYPE results [<!ENTITY harmless "completely harmless">]>
357+
<results>
358+
<result>This result is &harmless;</result>
359+
</results>
360+
XML;
361+
$this->query->setDocumentXml($xml);
362+
$this->setExpectedException("Zend_Dom_Exception");
363+
$this->query->queryXpath('/');
364+
}
351365
}
352366

353367
// Call Zend_Dom_QueryTest::main() if this source file is executed directly.
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<!DOCTYPE results [ <!ENTITY discloseInfo SYSTEM "XXE_URI"> ]>
3+
<feed xmlns="http://www.w3.org/2005/Atom">
4+
<title type="text">info:&discloseInfo;</title>
5+
</feed>
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
xxe-information-disclosed

tests/Zend/Feed/ReaderTest.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -336,6 +336,14 @@ public function testImportingUriWithEmptyResponseBodyTriggersException()
336336
$result = Zend_Feed_Reader::import('http://www.example.com');
337337
}
338338

339+
public function testXxePreventionOnFeedParsing()
340+
{
341+
$string = file_get_contents($this->_feedSamplePath.'/Reader/xxe-atom10.xml');
342+
$string = str_replace('XXE_URI', $this->_feedSamplePath.'/Reader/xxe-info.txt', $string);
343+
$this->setExpectedException('Zend_Feed_Exception');
344+
$feed = Zend_Feed_Reader::importString($string);
345+
}
346+
339347
protected function _getTempDirectory()
340348
{
341349
$tmpdir = array();

tests/Zend/Serializer/Adapter/WddxTest.php

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,28 @@ public function testSerializeStringUtf8() {
221221
$this->assertEquals($expected, $data);
222222
}
223223

224+
public function testUnserializeInvalidXml()
225+
{
226+
if (!class_exists('SimpleXMLElement', false)) {
227+
$this->markTestSkipped('Skipped by missing ext/simplexml');
228+
}
229+
230+
$value = 'not a serialized string';
231+
$this->setExpectedException(
232+
'Zend_Serializer_Exception',
233+
'DOMDocument::loadXML(): Start tag expected'
234+
);
235+
$this->_adapter->unserialize($value);
236+
}
237+
238+
public function testShouldThrowExceptionIfXmlToUnserializeFromContainsADoctype()
239+
{
240+
$value = '<!DOCTYPE>'
241+
. '<wddxPacket version=\'1.0\'><header/>'
242+
. '<data><string>test</string></data></wddxPacket>';
243+
$this->setExpectedException("Zend_Serializer_Exception");
244+
$data = $this->_adapter->unserialize($value);
245+
}
224246
}
225247

226248

tests/Zend/Soap/ServerTest.php

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@
2727

2828
require_once "Zend/Config.php";
2929

30+
require_once dirname(__FILE__) . '/TestAsset/commontypes.php';
31+
3032
/**
3133
* Zend_Soap_Server
3234
*
@@ -542,6 +544,9 @@ public function testGetPersistence()
542544
$this->assertEquals(SOAP_PERSISTENCE_SESSION, $server->getPersistence());
543545
}
544546

547+
/**
548+
* @runInSeparateProcess
549+
*/
545550
public function testGetLastRequest()
546551
{
547552
if (headers_sent()) {
@@ -575,6 +580,9 @@ public function testGetLastRequest()
575580
$this->assertEquals($request, $server->getLastRequest());
576581
}
577582

583+
/**
584+
* @runInSeparateProcess
585+
*/
578586
public function testWsiCompliant()
579587
{
580588
$server = new Zend_Soap_Server(null, array('wsi_compliant' => true));
@@ -877,11 +885,13 @@ public function testLoadFunctionsIsNotImplemented()
877885
}
878886
}
879887

888+
/**
889+
* @runInSeparateProcess
890+
*/
880891
public function testErrorHandlingOfSoapServerChangesToThrowingSoapFaultWhenInHandleMode()
881892
{
882893
if (headers_sent()) {
883894
$this->markTestSkipped('Cannot run ' . __METHOD__ . '() when headers have already been sent; enable output buffering to run this test');
884-
return;
885895
}
886896

887897
$server = new Zend_Soap_Server();
@@ -967,6 +977,35 @@ public function testHandleUsesProperRequestParameter()
967977
$r = $server->handle(new DomDocument('1.0', 'UTF-8'));
968978
$this->assertTrue(is_string($server->mockSoapServer->handle[0]));
969979
}
980+
981+
/**
982+
* @runInSeparateProcess
983+
*/
984+
public function testShouldThrowExceptionIfHandledRequestContainsDoctype()
985+
{
986+
$server = new Zend_Soap_Server();
987+
$server->setOptions(array('location'=>'test://', 'uri'=>'http://framework.zend.com'));
988+
$server->setReturnResponse(true);
989+
990+
$server->setClass('Zend_Soap_TestAsset_ServerTestClass');
991+
992+
$request =
993+
'<?xml version="1.0" encoding="UTF-8"?>' . "\n" . '<!DOCTYPE foo>' . "\n"
994+
. '<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" '
995+
. 'xmlns:ns1="http://framework.zend.com" '
996+
. 'xmlns:xsd="http://www.w3.org/2001/XMLSchema" '
997+
. 'xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" '
998+
. 'xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" '
999+
. 'SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">'
1000+
. '<SOAP-ENV:Body>'
1001+
. '<ns1:testFunc2>'
1002+
. '<param0 xsi:type="xsd:string">World</param0>'
1003+
. '</ns1:testFunc2>'
1004+
. '</SOAP-ENV:Body>'
1005+
. '</SOAP-ENV:Envelope>' . "\n";
1006+
$response = $server->handle($request);
1007+
$this->assertContains('Invalid XML', $response->getMessage());
1008+
}
9701009
}
9711010

9721011

0 commit comments

Comments
 (0)