XXE: The Complete Practical Guide to XML External Entity Injection
In-Band XXE File Read
How to read local files with in-band XXE: declaring a SYSTEM entity, placing it where the response echoes, and why some files break the parse.
Published
In-band XXE is the cleanest case: you declare an entity that points at a local file, put the reference where the app echoes it, and the file comes back in the response. It is the first thing to try on any XML endpoint, because when it works you read files directly with no listener and no second request.
Declaring an entity that points at a local file
Add a DOCTYPE with an external entity whose SYSTEM target is a file path:
<?xml version="1.0"?>
<!DOCTYPE foo [ <!ENTITY xxe SYSTEM "file:///etc/passwd"> ]>
<data>&xxe;</data>When the parser resolves &xxe;, it reads /etc/passwd and expands the reference into the file's contents. That is the entire primitive.
Placing the entity where the response echoes it
The file contents only reach you if the expanded entity ends up somewhere the application returns. So you place &xxe; in a field that is reflected:
- If the endpoint echoes a
name, atitle, or a search term, put the reference in that element. - If it returns a verbose error that includes part of your input, that error can be your read channel.
- Match the document shape the app expects, then swap the real value for
&xxe;:
<?xml version="1.0"?>
<!DOCTYPE foo [ <!ENTITY xxe SYSTEM "file:///etc/passwd"> ]>
<user><name>&xxe;</name><role>member</role></user>If the response comes back with the contents of /etc/passwd in the name position, you have confirmed in-band XXE.
Reading /etc/passwd and app secrets
/etc/passwd is the classic proof because it is world-readable and plain text. Once it returns, move to files that matter:
file:///etc/passwd proof of read
file:///etc/hostname quick, clean confirmation
file:///var/www/html/config.php app config and DB credentials
file:///proc/self/environ process environment (secrets, tokens)
file:///home/user/.ssh/id_rsa private keysRead the config and environment first, because that is where credentials and tokens tend to live.
Why some files break the parse
The catch with in-band reading is that the file's contents become part of the XML document after the entity expands. If the file contains characters that are special to XML, like <, >, or &, or if it is binary, the expansion produces a malformed document and the parser errors out instead of returning the data. Flat, text-only files read cleanly. Many of the files you actually want, source code and structured config, contain XML-special characters and will not come back in-band.
When the response is partial
Even when a file reads, you may get only part of it: the app might return a single line, truncate at the first special character, or reflect only a field it expected to be short. Read what you can, note where it cut off, and treat a partial or broken read as the signal to switch techniques.
When in-band reading fails, either because nothing is reflected or because the file breaks the parse, you escalate to blind XXE, which exfiltrates the contents out-of-band regardless of special characters. And when the XML comes from a file upload, see XXE via file upload. Practice reading a file on a free live target.
← Back to XXE: The Complete Practical Guide to XML External Entity Injection