Scanning the internal network using SimpleXML

XML is widely used throughout PHP applications in the representation arbitrary data structures such as with SOAP and REST web services. It supports the use of external entities allowing you to bring in information from external sources. This is useful when you want to create common references that are shared between XML documents – when you update the external source it becomes updated in all the XML documents. For example when we update the file copyright.xml it automatically gets dragged into the XML document below.

<?xml version="1.0" standalone="no" ?>
<!DOCTYPE copyright [
  <!ELEMENT copyright (#PCDATA)>
  <!ENTITY c SYSTEM "http://www.xmlwriter.net/copyright.xml">
]>
<copyright>&c;</copyright>

SimpleXML lets us bring any URL – for example http://www.example.com/blahblahblah.xml – into the document. The problem is that if the external entity isn’t  a valid XML endpoint it wont get parsed and PHP will throw an error like this.

Warning: simplexml_load_string(): php_network_getaddresses: getaddrinfo failed: Name or service not known in xml.php on line 5

Warning: simplexml_load_string(http://www.example.com/blahblahblah.xml): failed to open stream: php_network_getaddresses: getaddrinfo failed: Name or service not known in xml.php on line 5

Security issues arise because PHP places no restrictions on what URLs can be accessed; any URL that the parser can access can be included in the XML as a external entity. Even if allow_url_fopen is set to false it is still possible to include these files. Furthermore it is possible to specify ports to which the XML parser will connect, here we connect to localhost on port 22

<!DOCTYPE scan [<!ENTITY test SYSTEM "http://localhost:22">]>
<scan>&test;</scan>

As long as PHP error messages are enabled you get back the banner of the service running even if the port doesn’t support the HTTP protocol.

Warning: simplexml_load_string(http://localhost:22): failed to open stream: HTTP request failed! SSH-2.0-OpenSSH_5.5p1 Debian-4ubuntu5
 in testxml.php on line 10

If error messages are suppressed it is still possible to work out if a service is running on the port by comparing the time taken to connect to a known open port (such as localhost:80) to known closed port. Using this technique its possible to scan the networks attached to the XML parser endpoint for services that otherwise might be blocked to the outside world by a firewall.

XML for Local File Inclusion
Turns out not only can you include remote URL you can also include local files (a.k.a Local File Inclusion – LFI) by using the file:// prefix (thanks to Am for pointing this out) e.g.

<!DOCTYPE scan [<!ENTITY test SYSTEM "file:///etc/passwd">]>
<scan>&test;</scan>

If the result is echoed back to the end user it will contain a copy of /etc/passwd – The caveat is that the local file inclusion must be valid XML, this means that you cant include binary files – however, using PHP filters its possible to encode binary files as a Base64 encoded string e.g.

<!DOCTYPE scan [<!ENTITY test SYSTEM "php://filter/read=convert.base64-encode/resource=/etc/passwd">]>
<scan>&test;</scan>

This means that any PHP system parsing XML is potentially vulnerable to Local and Remote File Inclusion attacks.

Defending against XML External Entity attacks
There doesn’t appear to be a PHP configuration setting to disable the external entity feature however by issuing the command libxml_disable_entity_loader before the XML is parsed it is possible to turn external entities off at runtime and protect yourself from this threat e.g.

libxml_disable_entity_loader(true);
This entry was posted in Web Services and tagged , , , , , . Bookmark the permalink.

8 Responses to Scanning the internal network using SimpleXML

  1. am says:

    this is an interesting topic.
    are you sure the first 2 errors are because of invalid xml synthax? It looks 2 me that those 2 remote files are non-existent….one redirects, the other is a 404.

    Also, it should be tested if local files could be read.

    • Phil says:

      Good spot, I’ve updated the post. If the parser supports it then its a good idea to try LFI – I found file://etc/passwd works on most Java implementations but the file syntax doesn’t appear to work with ASP.NET / PHP.

  2. am says:

    i’ve managed to read files using php:)

  3. am says:

    ps: you should add an option to subscribe to comments via email. would be really useful!

  4. oxen says:

    Pardon my silly question but how can I test this. I’ve found my site is vulnerable to this type of attack but i’m unsure how to test for it? My background is OS level security so this web-stuff is a bit new to me (here’s to learning hey!).
    Cheers,

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>