复制XXE攻击是我正在努力做的,以防止它们,但是我似乎并不关心PHP与XML实体的工作方式.为了纪录我在Ubuntu 12.04上使用PHP 5.5.10,但是我已经在5.4和5.3上做了一些测试,而libxml2似乎是版本2.7.8(似乎不包括默认的解析实体).
在以下示例中,使用true或false调用libxml_disable_entity_loader()没有任何效果,或者我做错了.
$xml = <<<XML <?xml version="1.0"?> <!DOCTYPE root [ <!ENTITY c PUBLIC "bar" "/etc/passwd"> ]> <root> <test>Test</test> <sub>&c;</sub> </root> XML; libxml_disable_entity_loader(true); $dom = new DOMDocument(); $dom->loadXML($xml); // Prints Test. print $dom->textContent;
但是,我可以具体地传递一些参数来加载loadXML()以允许一些选项,当该实体是本地文件时,而不是当它是一个外部URL时.
$xml = <<<XML <?xml version="1.0"?> <!DOCTYPE root [ <!ENTITY c PUBLIC "bar" "/etc/passwd"> ]> <root> <test>Test</test> <sub>&c;</sub> </root> XML; $dom = new DOMDocument(); $dom->loadXML($xml,LIBXML_NOENT | LIBXML_DTDLOAD); // Prints Test. print $dom->textContent;
现在,如果我们将实体更改为其他的东西,如下面的例子,实体被解决了,但我无法使用参数或函数禁用它…发生什么事?
$xml = <<<XML <?xml version="1.0"?> <!DOCTYPE root [ <!ENTITY c "Blah blah"> ]> <root> <test>Test</test> <sub>&c;</sub> </root> XML; $dom = new DOMDocument(); $dom->loadXML($xml); // Prints Test. print $dom->textContent;
我可以找到的唯一方法是覆盖DOMDocument对象的属性.
> resolveExternals设置为1
> substituteEntities设置为1
所以总结一下,我真的很想明白我显然不明白.为什么这些参数和功能似乎没有效果? libxml2是否优先于PHP?
非常感谢!
参考文献:
> https://www.owasp.org/index.php/XML_External_Entity_%28XXE%29_Processing
> http://au2.php.net/libxml_disable_entity_loader
> http://au2.php.net/manual/en/libxml.constants.php
> http://www.vsecurity.com/download/papers/XMLDTDEntityAttacks.pdf
> http://www.mediawiki.org/wiki/XML_External_Entity_Processing
> How can I use PHP’s various XML libraries to get DOM-like functionality and avoid DoS vulnerabilities,like Billion Laughs or Quadratic Blowup?
你的第一个代码片段
libxml_disable_entity_loader根据您的系统默认情况下是否解析实体(我的)不执行或不执行任何操作.这由libxml的LIBXML_NOENT选项控制.
没有它,文档处理器可能甚至不会尝试翻译外部实体,因此libxml_disable_entity_loader没有任何真正的影响(如果libxml未在默认情况下加载实体,这在您的测试用例中似乎是这种情况).
将LIBXML_NOENT添加到loadXML(),如下所示:
$dom->loadXML($xml,LIBXML_NOENT);
你会很快得到:
PHP Warning: DOMDocument::loadXML(): I/O warning : Failed to load external entity "/etc/passwd" in ... PHP Warning: DOMDocument::loadXML(): Failure to process entity c in Entity,line: 7 in ... PHP Warning: DOMDocument::loadXML(): Entity 'c' not defined in Entity,line: 7 in ...
你的第二个代码片段
在这种情况下,您通过使用LIBXML_NOENT选项启用实体解析,这就是为什么它在/ etc / passwd之后.
该示例在我的机器上正常工作,即使是外部URL – 我将ENTITY更改为外部URL:
<!ENTITY c PUBLIC "bar" "https://stackoverflow.com/opensearch.xml">
然而,它甚至可以受到例如的影响. allow_url_fopen PHP INI设置 – 将其设置为false,PHP将不会加载远程文件.
你的第三个代码片段
您提供的XML实体不是外部的,而是内部的(参见例如here).
您的实体:
<!ENTITY c "Blah blah">
内部实体的定义如何:
<!ENTITY % name "entity_value">
因此,PHP或libxml没有任何理由阻止此类实体的解析.
结论
我已经很快地建立了一个PHP XXE tester script,它尝试了不同的设置,并显示XXE是否成功,在哪种情况下.
实际显示警告的唯一一行是“LIBXML_NOENT”.
如果任何其他线路加载警告,外部实体加载!您的设置默认允许加载外部实体.
使用SHOULD USE libxml_disable_entity_loader(),无论您的/您的提供商的机器默认设置如何,您都不能出错.如果你的应用程序被迁移,它可能会立即变得脆弱.
正确使用
正如MediaWiki在link you’ve posted所说.
Unfortunately,the way that libxml2 implements the disabling,the library is crippled when external entities are disabled,and functions that would otherwise be safe cause an exception in the entire parsing.
$oldValue = libxml_disable_entity_loader(true); // do whatever XML-processing related libxml_disable_entity_loader($oldValue);
注意:libxml_disable_entity_loader()还禁止直接加载外部xml文件(而不是通过实体):
<?PHP $remote_xml = "https://stackoverflow.com/opensearch.xml"; $dom = new DOMDocument(); if ($dom->load($remote_xml) !== FALSE) echo "loaded remote xml!\n"; else echo "Failed to load remote xml!\n"; libxml_disable_entity_loader(true); if ($dom->load($remote_xml) !== FALSE) echo "loaded remote xml after libxml_disable_entity_loader(true)!\n"; else echo "Failed to remote xml after libxml_disable_entity_loader(true)!\n";
在我的机器上
loaded remote xml! PHP Warning: DOMDocument::load(): I/O warning : Failed to load external entity "https://stackoverflow.com/opensearch.xml" in ... Failed to remote xml after libxml_disable_entity_loader(true)!
这可能与this PHP bug有关,但PHP真的很愚蠢:
libxml_disable_entity_loader(true); $dom->loadXML(file_get_contents($remote_xml));
工作很好