XML External Entities (XXE) attack failing - php

Please give me a hint why my code is NOT vulnerable to XXE.
code:
$text = $_POST['textarea'];
$doc= new DOMDocument();
$doc->loadXML($text);
echo $doc->textContent;
testcase 1:
<justsomexmltag>Hello world</justsomexmltag>
result 1:
Hello world
So far so good. However, when I'm trying to inject XML code to retrieve a local file's content:
<?xml version="1.0"?>
<!DOCTYPE log [
<!ENTITY ent SYSTEM "test.txt">
]>
<log><text>&ent;</text></log>
then nothing is printed. "test.txt" is on the same level in the file structure as the php file where I carry out the attack. I have tried
<!ENTITY ent SYSTEM file:///"test.txt">
as well as
<!ENTITY ent SYSTEM file:///full path to the file>
but to no avail.
test.txt:
This is just a test.
Have tried:
<test>This is just a test.</test>
no results.
Any hints?
reflecting #Paul Crovella, here's an edit:
CP-ing your code resulted in:
DOMDocument::loadXML(): I/O warning : failed to load external entity file:// full path to file name
DOMDocument::loadXML(): Failure to process entity ent in Entity
DOMDocument::loadXML(): Entity 'ent' not defined in Entity

By default libxml will not load external entities precisely to avoid this issue. To convince it to do so you'd need to set either substituteEntities or validateOnParse to true prior to loading. E.g.:
$xml = <<<'XML'
<?xml version="1.0"?>
<!DOCTYPE log [
<!ENTITY ent SYSTEM "test.txt">
]>
<log><text>&ent;</text></log>
XML;
$dom = new DOMDocument();
$dom->substituteEntities = true;
$dom->loadXML($xml);
echo $dom->textContent;
Outputs:
This is just a test.

Related

XML <!ENTITY e SYSTEM "/path/to/file"> does not work with PHP SimpleXMLElement

<?php
$str = <<<XML
<?xml version="1.0"?>
<!DOCTYPE doc [
<!ENTITY e SYSTEM "/tmp/exp">
]>
<tag>&e;</tag>
XML;
$xml = new SimpleXMLElement($str);
echo $xml;
?>
This should print out the contents of /tmp/exp but does not and I don't understand why, even when I run the script with sudo or change the /tmp/exp file permissions to 777.
Loading of external entities is disabled by default, because it can lead to various security vulnerabilities.
To safely enable it, you need to register a custom entity loader, which can check for expected entity paths and decide whether to load them. For instance, you might allow any file in a particular directory, but not elsewhere on disk - you probably don't need to allow a reference to system files like /etc/passwd. Or, you might map the path provided to a completely different location on your system.
You then also need to provide the LIBXML_NOENT option to tell the parser to expand the entities via your handler.
For instance:
libxml_set_external_entity_loader(function($public, $system, $context) {
if ($system === '/tmp/exp') {
return fopen('/tmp/exp', 'r');
}
else {
return null;
}
});
$xml = new SimpleXMLElement($str, LIBXML_NOENT);

DOMDocument::load(): Namespace default prefix was not found in Entity

I am parsing some XML with PHP DOMDocument. This is my code:
$doc = new DOMDocument;
$doc->resolveExternals = true;
$doc->substituteEntities = true;
$doc->load('../poems_xml/'.$pid.'.xml');
$xsl = new DOMDocument;
$xsl->load('../xslt/title.xsl');
$proc = new XSLTProcessor;
$proc->importStylesheet($xsl);
$ptitle = $proc->transformToXML($doc);
I have an entity file declared at the beginning of my .xml:
<?xml version="1.0" encoding="utf-8"?>
<?oxygen RNGSchema="../dtd/dps.rng" type="xml"?>
<?xml-stylesheet href="../dtd/dps.css" type="text/css"?>
<!DOCTYPE TEI SYSTEM "../dtd/entities.ent">
[...]
And the entities file looks like this:
[...]
<!ENTITY d1_AytR_002 "<rs key='d1_AytR_002'>d1_AytR_002</rs>">
[...]
In my .xml I use these entities like so:
...&d1_AytR_002;...
Now, it all goes well in terms of parsing the file and transform it via the xslt and css files, except for the entities. They just get ignored. Turning on the php_error_log flag, I get this:
Notice: DOMDocument::load(): Namespace default prefix was not found in Entity, line: 1 in index.php on line 28
(line 28 of index.php is where the load('../poems_xml/'.$pid.'.xml') instruction is). Can someone shed some light on what I should check/add regarding my entities?
I'm using PHP 5.6.40.
A workaround (and possible permanent solution) is that of adding the namespace to each of the <!ENTITY>s, like so:
<!ENTITY d1_AytR_002 "<rs xmlns="http://www.tei-c.org/ns/1.0" key='d1_AytR_002'>d1_AytR_002</rs>">

styling xml-file by including xsl-stylesheet using php

thanks to this helpful community I've been enabled to make a xsl-stylesheet extracting some metainformation from xml-files on my site. Of course, I do not want to code the stylesheet directly in the xml-files, which shall be left untouched. Also, I do not want to preprocess the files in OxyGen and upload the metainfo-files.
So I simply tried this, in metainfo.php:
<?php echo '<?xml-stylesheet type="text/xsl" href="metainfo.xsl"?>'; include ('sample.xml') ?>
Still, loading metainfo.php will display the whole xml file. The source code looks fine, but when I copy it, save it as xml and open it in OxyGen, there is this little bugger '' in the code, which apperntly is called a BOM:
<?xml-stylesheet type="text/xsl" href="metainfo.xsl"?> <?xml-stylesheet type="text/xsl" href="metainfo.xsl"?>
Might this cause the trouble in the browser too? Or is it something else, more basic?
After some extra work, there's what I figured out as a solution myself:
<?php
$signatur = $_GET['signatur'];
# LOAD XML FILE
$XML = new DOMDocument();
$XML->load( 'xml/'.$signatur.'.xml' );
# START XSLT
$xslt = new XSLTProcessor();
# IMPORT STYLESHEET 1
$XSL = new DOMDocument();
$XSL->load( 'metainfo.xsl' );
$xslt->importStylesheet( $XSL );
#PRINT
print $xslt->transformToXML( $XML );
?>

Alternately getting the error (Extra content at the end of the document )

I am getting error while loading the xml file. I got many answers related to the topic but I really could not find why this error maybe coming in my file.
Warning: DOMDocument::load() [<a href='domdocument.load'>domdocument.load</a>]: Extra content at the end of the document
When I am running the file, it runs successfully, but when I reload it, it gives the above error instead of adding another node. But, next time when I reload it runs successfully again. This is happening alternatively. Please someone tell me why is this happening and how to solve the problem.
I am using this php code to edit the xml file:
<?php
$dom = new DomDocument("1.0", "UTF-8");
$dom->load('filename.xml');
$noteElem = $dom->createElement('note');
$toElem = $dom->createElement('to', 'Chikck');
$fromElem = $dom->createElement('from', 'ewrw');
$noteElem->appendChild($toElem);
$noteElem->appendChild($fromElem);
$dom->appendChild($noteElem);
$dom->formatOutput = TRUE;
//$xmlString = $dom->saveXML();
//echo $xmlString;
$dom->save('filename.xml');
?>
This is the xml file I am editing:
<?xml version="1.0" encoding="UTF-8"?>
<note>
<to>Chikck</to>
<from>ewrw</from>
</note>
The extra content error is caused by having two of the same node, in this case the note node, as a root element.
You could add a new root element notes for example, and then add more note elements within that.
Here's an example using the simplexml library (just because I use this one and I'm familiar with it)
New filename2.xml: (with added notes element as root)
<?xml version="1.0" encoding="UTF-8"?>
<notes>
<note>
<to>Chikck</to>
<from>ewrw</from>
</note>
</notes>
PHP script:
<?php
$xml = simplexml_load_file('filename2.xml');
$note = $xml->addChild('note');
$to = $note->addchild('to', 'Chikck');
$from = $note->addChild('from', 'ewrw');
$xml->asXML('filename2.xml');
?>
filename2.xml after running script:
<?xml version="1.0" encoding="UTF-8"?>
<notes>
<note>
<to>Chikck</to>
<from>ewrw</from>
</note>
<note>
<to>Chikck</to>
<from>ewrw</from>
</note>
</notes>

How do I include an external XML file in PHP?

I need a way to include an external XML file in PHP which does not use simplexml tags. Furthermore, I'd also need it to integrate with other imported XMLs, hence removing file headers as <?XML version...>
Basically have a PHP class which includes methods to dynamically create XML elements based on user-input. For example, I could create a node called "test", set "id=1" as attribute and add child nodes to it. What I basically need, is a way to extract further XML content from other files and have my PHP script recognize it, hence being able to call methods on this imported code. I tried using php's fopen() function but, although it would print the imported XML to the screen, it would not validate and signal an error as soon as the imported code began. I cannot use simpleXML extension for two main reasons. Firstly, the entire class is written using Pre-PHP5 XML handling, and I cannot re-write the whole thing from scratch as it is part of a team-project, secondly, such class features methods which could not be replicated with simpleXML extension.
This is the XML I generate: <?xml version="1.0"?> <ga><dsa>hea</dsa><sda>eh</sda></ga> <gg><ds>he</ds><sd>eh</sd></gg> And it returns: Illegal Content, Line 3 Column 1, highliting the "<" of the "gg" tag... (Which, by the way, is the part imported from the external file.)
This is a snippet of the code used to print imported XML:
$file = simplexml_load_file($url);
foreach($file as $key => $value) {
echo "<" . $key . ">" . $value . "</" . $key . ">\n";
}
How can this be done?
Additional note: Yes, the server suppors PHP 5 (5.2.6), but the code was written in pre-php5.
Judging from your comments I'd say you get an error because a valid XML document needs a root element. You XML has two: <ga> and <gg>, which means the XML is invalid and cannot be parsed.
You should fix your XML by adding a root element. Then the parsing errors will go away.
Another option would be to load the snippet as a document fragment with DOM:
$brokenXML = <<< XML
<?xml version="1.0"?>
<ga><dsa>hea</dsa><sda>eh</sda></ga>
<gg><ds>he</ds><sd>eh</sd></gg>
XML;
$dom = new DOMDocument;
$fragment = $dom->createDocumentFragment();
$fragment->appendXML(trim(str_replace('<?xml version="1.0"?>', '', $brokenXML)));
echo $dom->saveXml($fragment);
Output:
<ga><dsa>hea</dsa><sda>eh</sda></ga>
<gg><ds>he</ds><sd>eh</sd></gg>
But note that this is still not a complete XML document because it misses a root element.
If you want to import a DOM Tree into another, you can use DOMDocument::importNode. To use that with the fragment above, you would do
$dom2 = new DOMDocument('1.0', 'utf-8');
$dom2->appendChild($dom2->createElement('foo'))
->appendChild($dom2->importNode($fragment, true));
echo $dom2->saveXml();
That would result in
<?xml version="1.0" encoding="utf-8"?>
<foo><ga><dsa>hea</dsa><sda>eh</sda></ga>
<gg><ds>he</ds><sd>eh</sd></gg></foo>
If you have an existing document you want to import to, you would simply do
$dom2 = new DOMDocument;
$dom2->load('existingFile.xml');
$dom2->documentElement->appendChild($dom2->importNode($fragment, true));
This would append the fragment as the last child of the root node. If you want to have it somewhere else on the DOM tree, you would have to traverse the DOM tree with Xpath or getElementsByTagName or getElementsById or the childNodes property on the various nodes and then append to that node instead.

Categories