XXE Injection
An attacker reads the server's password file through a purchase-order XML upload whose parser resolves external entities, then you ship the parser-level fix.
What Is XXE Injection?
XXE turns an XML parser into a file-read and SSRF primitive. The attacker declares a SYSTEM entity in a DOCTYPE, references it from a tag the handler echoes back, and the parser inlines the file contents (or the response from an internal URL) before the application ever sees the document. This exercise puts you on both sides. As Bob, an attacker working Brackwell Logistics' supplier intake, you find a purchase-order XML upload that echoes the parsed vendor field back. You open an ordinary exported order in the code editor and weaponize it, adding <!ENTITY xxe SYSTEM "file:///etc/passwd"> and pointing <vendor> at &xxe; , then upload the crafted file through the same feature; the server's password file comes back in the vendor string, and the app's own secrets file is reachable the same way. As Alice, the application security engineer who owns the intake, you open the handler, see libxmljs.parseXml(..., { noent: true }) , and ship the fix: reject DOCTYPE and entity declarations up front and parse with external-entity and DTD resolution turned off, so a SYSTEM reference can never be followed. You then re-upload the exact crafted file and watch it return XXE_BLOCKED. The exercise closes with quiz questions on what XXE actually is, what noent:true does, why an input deny-list alone is not enough, and what the attacker sees on retry.
What You'll Learn in XXE Injection
- Recognize XXE as a parser-configuration bug, not an input-shape bug
- Read an XML payload and identify the SYSTEM-entity declaration that makes it dangerous
- Understand that XXE generalizes from local-file read to internal-URL fetch (SSRF)
- Apply the parser-level fix (disable external-entity resolution) and layer defence-in-depth controls on top
- Audit every XML parse call site for the same flag once one is found
XXE Injection — Training Steps
-
Find the intake feature
Brackwell Logistics lets any registered supplier submit a purchase order by uploading it as an XML file. The form takes the document, parses it on Brackwell's servers, and reads the vendor and total back. Bob opens the feature to see how it behaves before he touches it.
-
Where the parsing happens
One line on that page is the whole reason Bob is interested.
-
Upload a normal order
First, watch the feature behave normally. Bob attaches an ordinary purchase order and submits it. The browser's Network panel records the request and the server's reply, exactly what a developer sees in DevTools.
-
What the parser echoed
The order parsed cleanly. One detail in the response tells Bob how to attack it.
-
Start from a normal file
Bob opens the ordinary purchase order in his editor. Right now it is exactly what it looks like: a vendor and a total, plain data. XML lets a document declare an external entity, a named placeholder whose value the parser fetches from somewhere else. Bob is about to add one.
-
Weaponize the file
Bob adds a DOCTYPE that declares an external entity pointing at a file on disk, then points the vendor field at that entity. The file still parses as a purchase order. He starts with the file every Linux box has, to prove the parser follows the reference at all.
-
The entity
Two lines are all it took.
-
Upload the crafted file
Bob submits his crafted file through the same intake feature. To the browser it is just another purchase-order upload.
-
The file comes back
The response is not a vendor name. It is the server's password file, read off disk and handed straight back through the upload's normal output.
-
Knowledge check
You just watched a purchase-order upload read the server's files. Lock in why.