Server-side attacks and the file system
Log in Join

Lesson 6 · Reading · 35 min

XXE and web services

XML is everywhere the web talks to itself: SOAP APIs, document formats like DOCX and SVG, sitemaps, SAML, and countless import features. The XML standard has a feature, external entities, that, if the parser leaves it on, lets a document you supply tell the parser to go read files or make requests. That is XXE, and it is your file-read and SSRF through the data layer. This lesson also covers testing web services generally, an exam objective in its own right.

XXE: a declared external entity makes the XML parser read a file

What an XML external entity is

XML lets a document define entities: shorthand that expands to some value when parsed. An external entity expands to the contents of a resource named by a URI, including a local file:

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

When a vulnerable parser reads this, it fetches file:///etc/passwd and inlines its contents wherever &x; appears. If the application then echoes that value back, in a response, an error, a rendered document, you have read a server file through the XML parser. Point the entity at http:// instead and the parser makes a request: XXE becomes SSRF, including the cloud-metadata jackpot from the last lesson.

Where XXE hides

Anywhere the app parses XML you can influence:

  • XML/SOAP API endpoints: send XXE in the request body.
  • File uploads that are secretly XML: .svg images, .docx/.xlsx/.pptx (zipped XML), .gpx tracks, RSS/Atom imports. An "upload your avatar" that accepts SVG is a common XXE door.
  • Any "import" feature taking XML.
  • SAML / SSO assertions.

The tell is: the app accepts structured data and parses it server-side. Try adding a DOCTYPE with an entity and see whether it expands.

Blind and out-of-band XXE

Often the app parses your XML but does not echo the entity back. Then, as with the other blind bugs, you exfiltrate out-of-band: define entities that make the parser fetch a URL on your server, carrying the file contents in the request. This uses an external DTD you host and a parameter-entity trick to smuggle the file into a URL your listener records. It is fiddly, but it means "the response shows nothing" is not "the parser is safe", an out-of-band callback confirms and exfiltrates. Watch your listener for the parser reaching out.

The billion-laughs aside

XML entities can also be defined to reference each other, expanding exponentially and exhausting memory, a denial-of-service called "billion laughs". Know it exists so you recognise it, but do not fire it at a real target: crashing a service is destructive and usually explicitly out of scope. It is a footnote, not a technique to deploy.

Testing web services more broadly

XXE is one web-service bug; the exam objective ("enumerate and exploit web services") is wider. When you meet an API, REST/JSON, GraphQL, SOAP, bring the whole toolkit:

  • Enumerate it. Find the documentation the recon path taught you to look for: swagger.json, /openapi.json, WSDL files for SOAP, GraphQL introspection. These list every operation and parameter for you.
  • Reuse your bug classes. APIs are just another surface for everything you already know: BOLA/IDOR on object ids (the access path), injection in parameters, mass assignment, missing function-level authorization on API-only endpoints, and weak or missing rate limits.
  • Watch for looser auth. APIs, especially older or mobile-facing ones, often trust callers more than the website does, skipping checks the web UI enforces. An action blocked in the browser may be wide open on the API.
  • Content-type tricks. An endpoint may accept XML and JSON; the XML path may be the vulnerable one (XXE) even when JSON is clean.

The honesty gate

The finding is reading a file or reaching an internal resource through the parser. Prove it with a harmless target, /etc/passwd, or an out-of-band callback that shows the parser fetched your URL, and stop. Never fire a billion-laughs DoS at a real system, and do not exfiltrate every secret once you have file read; one proof file establishes it. On this Academy's labs, extract freely.

Your labs

InvoicePilot XML import file read and InvoicePilot import (XXE) are import features that parse your XML, define an external entity and read a server file. FlightLog GPX out-of-band XXE is the blind case: no data comes back on the page, so you confirm and exfiltrate via an out-of-band callback. Do the InvoicePilot labs first for direct file read, then FlightLog for the out-of-band technique that works when the response shows nothing.

Where this goes next

XXE abused a data parser. The next lesson abuses a template engine: server-side template injection, where input meant to fill a template is instead evaluated as an expression, and {{7*7}} becoming 49 is the first step toward running code.

This lesson has 4 labs to try what you just read.