Detecting XML External Entities (XXE) Vulnerabilities

Detecting XML External Entities (XXE) Vulnerabilities

XML External Entity (XXE) is an attack that exploits a flaw in an application’s XML parser configuration to perform a number of malicious actions. These actions can include exposing the contents of protected files or causing exponential use of memory, resulting in a Denial-of-Service (DoS) attack.


Understanding XXE Attacks

XML, like JSON, makes up a significant part of the data transfer that powers the modern internet. As a system for encoding documents in both human and machine-readable ways, XML is common in older tech stacks and persists in API architectures like Simple Object Access Protocol (SOAP), even as modern web applications increasingly rely on JSON. The severity of this vulnerability led OWASP to name XXE as number four on their 2017 list of the Top 10 Web Vulnerabilities, a notable jump from its exclusion in the 2014 survey.

The Attack Mechanism

The nature of the XXE attack stems from XML’s concept of entities—a primitive data type that combines a string with a unique alias or reserved word.

  1. When an application’s XML parser encounters and attempts to expand the entity, the parser looks for and stores the contents of the URI specified by the entity in the final XML document.
  2. If the entity is pointing to a sensitive file on the web server (e.g., a system file), then that information is compromised.
  3. Because this vulnerability includes XML code being mistakenly parsed and executed after being submitted through an application input, XXE is classified as a form of code injection.

Risk Factors and Misconfigurations

XXE vulnerabilities are allowed by weakly or misconfigured XML parsers. A site is at risk if a parser accepts tainted data (untrusted user input) within the Document Type Declaration (DTD) and then processes that DTD and resolves external entities.

As a critical example, if you are using PHP, the language’s documentation specifically states that you need to set the libxml_disable_entity_loader variable to true to disable the ability to load external entities. (For reference, see: https://secure.php.net/manual/en/function.libxml-disable-entity-loader.php).

A Simple XXE Example for File Disclosure

XXE attacks can attempt Remote Code Execution (RCE) or, more commonly, disclose information from targeted files. Here is an example of a file disclosure attempt from OWASP:

]>&xxe;

Here, the external entity &xxe; is defined. Its SYSTEM keyword points to a URI with a file prefix and a system path (/etc/passwd), which attempts to access a sensitive file on the vulnerable server.

DoS Attack via “Billion Laughs”

XXE can also be used to conduct DoS attacks through an XML variant of a popular logic bomb tactic called a Billion Laughs attack (or exponential entity expansion).

A DoS attack that occurs via a logic bomb—a piece of code that, when executed, causes the host to max out its resource consumption—is different from a DoS attack caused by one or more outside agents (which is a DDoS attack). While a DoS attack is generally considered easier to mitigate (as there is only one source), a single-source logic bomb means an attacker only needs access to the vulnerable input, as opposed to a large swarm of machines (a botnet) generating traffic.

Example of a Billion Laughs XML Snippet:










]>
&lol9;

When the parser encounters the root element &lol9; and tries to expand it, it finds &lol8;, which needs to be expanded 10 times, and so on, cascading through the nested entity list. The result is that this small, less-than-1 KB snippet will create $10^9$ “lols”, totaling over 3 GB of memory usage, causing the server to freeze or crash.

Note: Billion Laughs attacks are not unique to XML (they can be performed in YAML or other file formats that support references), but they clearly illustrate the havoc an unguarded XXE vulnerability can wreak.


XML Injection Vectors

XML injection and related vulnerabilities are not always observable from the client-side code; the XML processing chain may be occurring server-side as the server formats your client-side input.

Following an OWASP example, a client-side form might submit data (e.g., via a GET request) like:

  • Username: james
  • Password: Thew45p!
  • E-mail: james.mowry@terran.gov

Before inserting the data into a database or datastore, the application might build an individual XML node from this input:

jamesThew45p!500james.mowry@terran.gov

An attacker can exploit this behavior to perform tag-based injection. This involves adding a valid XML tag within an input to spoof a valuable property. Assuming an tag with a value of 0 represents an admin user, an attacker could submit a password containing an injected tag:

  • Password: Thew45p!0james.mowry@terran.gov

When the application assembles this into the XML-like datastore, the redundant, injected closing tag might be filtered out or overwritten, resulting in a record that escalates James’s privileges:

jamesThew45p!0500james.mowry@terran.gov

The ability to perform XML tag injection provides a delivery mechanism for the XXE payload. If you have discovered a valid XML injection vector, you have the means to define and execute your XXE validation payload, showing how XML injection and XXE complement one another.


Testing for XXE – Verification and Discovery

The inputs available to you do not need to explicitly state that the application accepts XML for a service to be vulnerable to XXE. The XML parsing layer could be opaque (hidden) to you, stitching together data sent as a GET or POST request into an XML document on the server.

Finding XML Injection Points

  • API Services: Many API services support different data formats by default. Even if you make a GET request and receive JSON in return, you can test whether or not that API endpoint can format your request as XML by changing the Content-Type header in your request:
    • Content-Type: application/xml

Because services often have this built-in capacity to switch between different content types, the service owner might be unaware that it has the ability to format requests as XML, opening the door for an attack.

Technical Requirements for Testing

To safely test for XXE against a dedicated lab environment:

  • Chrome (version 66.0.3359.139 is noted)
  • Burp Suite (for proxying and intercepting requests)
  • VirtualBox (a Virtual Machine client)
  • Vagrant (a deployment system that manages the developer environment deployment on top of VirtualBox)

These tools allow the bootstrapping of a deliberately vulnerable XXE app (such as the one from https://github.com/jbarone/xxelab). The command vagrant up can start the lab, typically running on an internal IP like http://192.168.33.10/.


XXE – An End-to-End Example

The following steps demonstrate how to take an XXE vulnerability from discovery to report submission using a test environment.

1. Interception and Discovery

  1. Navigate to the vulnerable application’s submission form.
  2. Enter test values, ensuring the Burp Suite proxy’s Intercept feature is turned on.
  3. Upon submission, view the intercepted raw HTTP request in Burp. The request reveals that the submission data is being formatted in XML: Edward Hawks5555555555example@roguemoon.com

2. Basic Entity Expansion Test (Non-Impactful PoC)

To non-impactfully test for the capacity for entity expansion, substitute a form value (like the email) with a test entity:

]>Edward Hawks5555555555&example;@roguemoon.com

If the app is designed to mimic data exfiltration through error messages (by printing the full email), a successful entity expansion will show “Success” within the returned error message, thus validating the presence of an XXE vulnerability. This is a harmless Proof of Concept (PoC) that points to the possible damage other XXE attacks could accomplish.

3. Causing Damage (DoS PoC)

To demonstrate impact, an attacker could replace the intercepted values with an XXE snippet designed to crash the server:

]>Edward Hawks5555555555&xxe;@roguemoon.com

When the server attempts to expand the entity &xxe; and access the contents of /dev/random (a special, pseudorandom number generator on Linux systems), it can cause the server thread to block indefinitely if there is insufficient entropy for the random number generation. This results in the server hanging and becoming unresponsive (a DoS attack). We have successfully crashed the server!

4. Gathering Report Information

DetailValue
CategoryXXE attack
Timestamps2018-07-28 16:27 (16:27) UTC (Example)
URLhttp://192.168.33.10/ (The application index)
PayloadThe XML snippet used for the basic entity expansion PoC.
MethodologyManually intercepting and modifying the HTTP POST request via Burp Proxy to replace the XML document with the payload.
Instructions to ReproduceNavigate to the form, enter dummy values, intercept the request with Burp, replace XML data with the payload, and forward the request.
Attack ScenarioA malicious agent could use XXE to disclose the contents of sensitive server files (e.g., /etc/password), cause a server crash (via /dev/random or Billion Laughs attack), or, in some cases, perform RCE.

5. Final Report Submission

The final report should be formatted clearly, providing the payload and clear instructions to reproduce the vulnerability.

CATEGORY: XXE attack
TIME: 2018-07-28 16:27 (16:27) UTC
URL: http://192.168.33.10/
PAYLOAD: 
]>Edward Hawks5555555555&example;@roguemoon.com
METHODOLOGY: The vulnerability was discovered by manually intercepting and editing the create account form to include the above entity replacement changes.
INSTRUCTIONS TO REPRODUCE:
1. Navigate to the create account form at http://192.168.33.10/.
2. Enter dummy values into the form and submit it.
3. Intercept the generated HTTP POST request using a tool like Burp Proxy. Edit the XML data to include the payload above.
4. Forward the POST request on to the server.
ATTACK SCENARIO:
In the case of this XXE attack, a malicious agent could submit entity expansion code to retrieve the contents of a sensitive file on the server, like the contents of /etc/password, or make a call to /dev/random and crash the server, or even use a different DoS method with the nested entity expansion strategy of a "Billion Laughs"-style attack (https://en.wikipedia.org/wiki/Billion_laughs_attack).

Summary

This article covered the fundamentals of XXE and the nature of XML parsing attacks. It reviewed the historical context of the Billion Laughs vulnerability, discussed the specific misconfiguration that makes many XML parsers vulnerable, and detailed the process of taking an XXE vulnerability from discovery to report submission.

Review Questions

1. What makes an XML parser susceptible to XXE? What is an example misconfiguration?

An XML parser is susceptible to XXE when it is configured to process and resolve external entities.

  • What are External Entities? They are a feature of XML that allows a document to reference data from an external source, using a URI. For example, .
  • The Misconfiguration: The vulnerability occurs when the XML parser has a setting (often enabled by default for legacy reasons) that allows these external entities to be defined and resolved.

Example Misconfiguration:
In Java’s DocumentBuilderFactory, the parser is vulnerable by default. To secure it, you must explicitly disable external entity processing:

// VULNERABLE Parser (Default)
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();

// SECURE Parser (Explicitly Disabled)
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
// Disable external entities
factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
// OR as an alternative:
factory.setFeature("http://xml.org/sax/features/external-general-entities", false);
factory.setFeature("http://xml.org/sax/features/external-parameter-entities", false);

The misconfiguration is failing to set these security features.

2. How do you use Burp to test for XXE?

Burp Suite is the perfect tool for testing XXE. Here’s the workflow:

  1. Intercept: Use Burp Proxy to intercept a request that contains XML data (e.g., a SOAP request, an API call, or even a form submission where the body is XML).
  2. Inject Payload: In the intercepted request, look for XML and inject a malicious external entity definition.
  3. Send to Repeater: Send the request to Burp Repeater for easy manipulation and re-sending.
  4. Craft and Send Payloads: Try different XXE payloads in the Repeater tab.

Basic In-band XXE Payload to try:



]>
&xxe;

If the response contains the contents of /etc/passwd, you have a critical XXE vulnerability.

3. What are some impacts of an XXE vulnerability? What are some common attack scenarios?

XXE is incredibly powerful. Its impacts include:

  • File Disclosure: Read arbitrary files from the server’s filesystem (e.g., /etc/passwd, application code, configuration files with secrets).
  • Server-Side Request Forgery (SSRF): Force the server to make HTTP requests to internal services. This can be used to:
    • Scan the internal network.
    • Interact with internal APIs (e.g., AWS/Azure metadata endpoints to steal cloud credentials).
  • Denial-of-Service (DoS): Use entity expansion attacks (like Billion Laughs) to consume all server memory.
  • Remote Code Execution (RCE): In certain complex scenarios, especially with outdated libraries or specific PHP setups, XXE can lead to RCE.

Common Attack Scenarios:

  • The Attacker: Submits a malicious XML document as part of a profile update, a document upload, or an API call.
  • The Application: The vulnerable server-side parser processes the XML, resolves the external entity, and embeds the stolen data in its response.
  • The Outcome: The attacker views the application’s response and sees the contents of a sensitive file or the result of an internal network request.

4. What is /dev/random?

/dev/random is a special file on Linux and Unix-like systems that serves as a blocking pseudorandom number generator. It gathers environmental noise (from device drivers, etc.) to generate entropy (randomness).

  • Key Characteristic: It blocks—meaning it will pause and wait—if there is not enough entropy available to provide truly random data.
  • Relevance to XXE: It can be used in a time-based blind XXE attack. An attacker can reference it (file:///dev/random) in an entity. If the server tries to read from it and the system has low entropy, the request will hang. By measuring the response time, an attacker can infer that the XXE payload was processed, confirming the vulnerability even without direct output.

5. What’s a non-impactful way you can test for the presence of an XXE vulnerability?

The safest way is to use an external entity that points to a resource you control, rather than the server’s own filesystem. This avoids causing damage or overloading the server.

Non-Impactful Test:



]>
&xxe;
  • How it works: If the server is vulnerable, it will try to resolve the external entity by making an HTTP request to your Burp Collaborator server.
  • Why it’s safe: You are not reading the server’s files or causing a DoS. You are simply triggering an outbound HTTP call, which you can observe. Seeing an HTTP interaction in your Collaborator logs confirms the XXE vulnerability without any negative impact.

6. What’s the Billion Laughs attack?

The Billion Laughs attack (also known as an XML Entity Expansion attack) is a Denial-of-Service (DoS) attack that exploits the XML entity expansion feature.

  • How it works: It uses nested, recursive entity definitions to create an exponentially large XML document in memory.
  • Example:
    xml ]> &lol5;
    The single entity &lol5; expands to 10 &lol4; entities, each of which expands to 10 &lol3; entities, and so on. This results in 10^5 (100,000) “lol” strings, consuming gigabytes of memory and potentially crashing the parser.

7. How can some services (especially API endpoints) be vulnerable to XXE when they use JSON for data exchanges?

This is a common and dangerous misconception. Even if an API expects JSON, it might still process XML if it’s not properly validated.

Attack Vectors:

  1. Content-Type Manipulation:
    • The attacker intercepts a normal JSON request: Content-Type: application/json, Body: {"key": "value"}
    • They change the Content-Type header to application/xml and convert the body to a malicious XML payload.
    • If the backend processing logic trusts the Content-Type header and passes the data to an XML parser, XXE is possible.
  2. JSON Inside XML (Less Common): The attacker might find a field within a JSON structure that gets processed by an XML parser later in the application’s workflow. For example, if a JSON property contains an XML string that is used to generate a PDF or is parsed by a different microservice, that inner XML parser could be vulnerable.

The key takeaway is: Always test for XXE on any input endpoint, even those that primarily use JSON, by trying to send XML instead.

    Further Reading

    You can find out more about some of the topics discussed in this article at:

    • Billion Laughs Attack: https://en.wikipedia.org/wiki/Billion_laughs_attack
    • Hunting XXE For Fun and Profit: https://www.bugcrowd.com/advice-from-a-bug-hunter-xxe/

    Total
    1
    Shares

    Leave a Reply

    Previous Post
    Cross-Site Request Forgery (CSRF) and Insecure Session Authentication

    Cross-Site Request Forgery (CSRF) and Insecure Session Authentication

    Next Post
    Access Control and Security Through Obscurity

    Access Control and Security Through Obscurity

    Related Posts