澄清整个PHP版本中的XXE漏洞

时间:2014-06-09 09:52:12

标签: php xml security xxe

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

复制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吗?

非常感谢!

参考文献:

1 个答案:

答案 0 :(得分:8)

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

您的第一个代码段

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

没有它,文档处理器甚至可能不会尝试翻译外部实体,因此libxml_disable_entity_loader没有任何影响(如果libxml默认情况下不加载实体,这在您的测试用例中似乎就是这种情况)。 / p>

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之后的原因。

即使对于外部网址,该示例在我的计算机上运行正常 - 我将ENTITY更改为外部网址:

<!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是否成功以及在哪种情况下。

唯一应该显示警告的行是&#34; LIBXML_NOENT&#34;之一。

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

使用不管您/您的提供商的计算机默认设置是否应该使用libxml_disable_entity_loader(),都可能出错。如果您的应用迁移了,它可能会立即变得脆弱。

正确用法

正如MediaWiki在link you've posted中所述。

  

不幸的是,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)!

它可能与this PHP bug有关,但PHP对它真的很愚蠢:

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

工作正常。