21

我在这里发布一个问题作为最后的手段,我浏览了网络并进行了多次尝试但没有成功。

复制 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
  • 替代实体设置为 1

然后他们解决了,或者没有。

总而言之,我真的很想了解我显然不了解的内容。为什么那些参数和函数似乎没有效果?libxml2 是否优先于 PHP?

非常感谢!

参考:

4

1 回答 1

13

保持简单..因为它应该很简单:-)

你的第一个代码片段

libxml_disable_entity_loader根据您的系统是否默认解析实体(我的没有),在这里做或不做任何事情。这由LIBXML_NOENTlibxml 的选项控制。

没有它,文档处理器甚至可能不会尝试翻译外部实体,因此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,该示例在我的机器上也能正常工作 - 我将其更改为外部 URL,ENTITY如下所示:

<!ENTITY c PUBLIC "bar" "https://stackoverflow.com/opensearch.xml">

但是,它甚至可能受到例如的影响。allow_url_fopenPHP INI 设置 - 将其设置为 false,PHP 将永远不会加载远程文件。

您的第三个代码段

您提供的 XML 实体不是外部实体,而是内部实体(参见例如此处)。

您的实体:

<!ENTITY c "Blah blah">

如何定义内部实体:

<!ENTITY % name "entity_value">

因此 PHP 或 libxml 没有理由阻止解析此类实体。

结论

我很快建立了一个PHP XXE 测试脚本,它尝试不同的设置并显示 XXE 是否成功以及在哪种情况下成功。

实际上应该显示警告的唯一行是“LIBXML_NOENT”行。

如果任何其他行加载,WARNING, external entity loaded!则默认情况下您的设置确实允许加载外部实体。

无论您/您的提供商的机器默认设置如何,使用应该使用 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 错误有关,但 PHP 对此非常愚蠢,因为:

libxml_disable_entity_loader(true);
$dom->loadXML(file_get_contents($remote_xml));

工作得很好。

于 2014-08-20T01:19:00.727 回答