我在这里发布一个问题作为最后的手段,我浏览了网页,经历了许多尝试,但没有成功。
复制XXE攻击是我试图做的,以防止它们,但我似乎无法理解PHP处理XML实体的方式。郑重声明,我在Ubuntu 12.04上使用PHP5.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对象的属性。
然后他们解决了,或者没有。
所以总结一下,我真的很想理解我显然不理解的东西。为什么那些参数和函数似乎没有影响?libxml2优先于PHP吗?
非常感谢!
参考资料:
保持简单…因为它应该很简单:-)
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
更改为外部,如下所示:
<!ENTITY c PUBLIC "bar" "https://stackoverflow.com/opensearch.xml">
但是,它甚至可能受到例如allow_url_fopen
PHPINI设置的影响-将其设置为false,PHP将永远不会加载远程文件。
XML您提供的实体不是外部实体,而是内部实体(参见此处)。
您的实体:
<!ENTITY c "Blah blah">
如何定义内部实体:
<!ENTITY % name "entity_value">
因此,没有理由PHP或libxml来阻止解析此类实体。
我很快发布了一个PHPXXE测试脚本,它尝试不同的设置,并显示XXE是否成功以及在何种情况下。
唯一应该真正显示警告的线是“LIBXML_NOENT”线。
如果任何其他行加载警告,加载外部实体!
您的设置默认允许加载外部实体。
你呀 <罢工> 使用不会出错 无论您/您的提供商的机器默认设置如何,都应该使用libxml_disable_entity_loader()。如果您的应用程序被迁移,它可能会立即变得容易受到攻击。
正如您发布的链接中的MediaWiki所述。
不幸的是,libxml2实现禁用的方式,当外部实体被禁用时,库会被瘫痪,否则安全的函数会在整个解析中导致异常。
$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)!
这可能与PHP有关bug但PHP真的很愚蠢,因为:
libxml_disable_entity_loader(true);
$dom->loadXML(file_get_contents($remote_xml));
工作得很好。