I post a question here as a last resort, I have browsed the web and went through many attempts but did not succeed.
Replicating a XXE attack is what I am trying to do, in order to prevent them, but I cannot seem to get my head around the way PHP works with XML entities. For the record I am using PHP 5.5.10 on Ubuntu 12.04, but I have done some tests on 5.4 and 5.3, and libxml2 seem to be of version 2.7.8 (which does not seem to include the default to not resolving entities).
In the following example, calling libxml_disable_entity_loader() with true or false has no effect, or I am doing something wrong.
$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;
But, I could specifically pass some arguments to loadXML() to allow some options, and that works when the entity is a local file, not when it is an external 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;
Now if we are changing the entity to something else, as in the following example, the entity is resolved but I could not disable it at all using the parameters or function... What is happening?!
$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;
The only way that I could find was to overwrite the properties of the DOMDocument object.
- resolveExternals set to 1
- substituteEntities set to 1
Then they are resolved, or not.
So to summarise, I would really like to understand what I am obviously not understanding . Why do those parameters and function seem to have no effect? Is libxml2 taking precedence over PHP?
Many thanks!
References:
- https://www.owasp.org/index.php/XML_External_Entity_%28XXE%29_Processing
- https://www.php.net/libxml_disable_entity_loader
- https://www.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?
Keeping it simple .. As it should be simple :-)
Your first code snippet
libxml_disable_entity_loader
does or does not do anything here based on whether your system resolves entities by default or not (mine does not). This is controlled byLIBXML_NOENT
option of libxml.Without it the document processor may not even try translating external entities and therefore
libxml_disable_entity_loader
has nothing to really influence (if libxml does not load entities by default which seems to be the case in your test-case).Add
LIBXML_NOENT
toloadXML()
like this:and you'll quickly get:
Your second code snippet
In this scenario you've enabled entity resolving by using the
LIBXML_NOENT
option, that's why it goes after/etc/passwd
.The example works just fine on my machine even for external URL - I changed the
ENTITY
to an external one like this:It can, however, be even influenced by eg.
allow_url_fopen
PHP INI setting - put it to false and PHP won't ever load a remote file.Your third code snippet
XML Entity that you've provided is not an external one but rather an internal one (see eg. here).
Your entity:
How internal entity is defined:
Therefore there is no reason for PHP or libxml to prevent resolving such entity.
Conclusion
I've quickly put up a PHP XXE tester script which tries out different settings and shows whether XXE is successful and in which case.
The only line that should actually show a warning is the "LIBXML_NOENT" one.
If any other line loads the
WARNING, external entity loaded!
your setup does allow loading external entities by default.You
can't go wrong by usingSHOULD USE libxml_disable_entity_loader() regardless of your/your provider's machine default settings. If your app ever gets migrated it might become vulnerable instantly.correct usage
As the MediaWiki states in link you've posted.
Note: libxml_disable_entity_loader() also prohibits loading external xml files directly (not through entities):
On my machine:
It might perhaps be related to this PHP bug but PHP is being really stupid about it as:
works just fine.