Skip to content
DDevToolery

9 July 2025 · 5 min read

The XML feature that reads your files

External entities let a document pull in content from elsewhere. That includes /etc/passwd.

XML has a document type definition, and a DTD can declare entities — named shorthands expanded when the document is parsed. Entities may be defined inline, or they may point at a URL or a file path. The parser fetches them.

The attack

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

A parser with external entity resolution enabled reads the file and substitutes its contents. If the application echoes the parsed value back — in an error message, a preview, a generated document — the file leaves the server.

The same mechanism reaches internal network addresses. An entity pointing at an internal admin endpoint or a cloud metadata service turns an XML upload into server-side request forgery, from inside your perimeter.

This is not a parser bug. It is a specified feature of XML being used exactly as designed, by someone who was not supposed to control the document.

The denial of service variant

Entities can reference other entities. Ten levels of an entity that expands to ten copies of the next produces a gigabyte of memory from a few hundred bytes of input — the billion laughs attack. No file access required, and it exhausts the process.

Why it persists

Many XML libraries enabled external entities by default for a long time, because the specification says to. Applications that never think about XML — a SOAP endpoint, a SAML login flow, an office document upload, an SVG avatar — inherit the behaviour without anyone choosing it.

Fixing it

  • Disable DTD processing entirely. Almost no application needs it, and turning it off removes both attacks at once.
  • If DTDs are required, disable external entity resolution specifically.
  • Set an expansion limit, which most modern parsers now default to.
  • Treat SVG uploads as XML, because they are, and as HTML, because they can carry scripts.

In the browser

The parser DevToolery uses performs no DTD processing and resolves no external entities at all — the capability is absent rather than switched off. That is why the XML tools state plainly that a document cannot make your browser fetch anything. It is a property of the parser, not a promise about our intentions.

Tools mentioned