Local File Read Access through XSS in Dynamically Generated Email Template PDF

Hello friends! I am sure you must have came across some interesting and strange vulnerabilities during your penetration tests. Here is one we came across recently.

While testing a web application at eSecurify labs we found an unusual vulnerability.
The web application had templates which could be edited as per requirements. The template editor however, allowed to run javascript code. We exploited it to fetch some sensitive files from the system.

Security test flow

To begin with, the template editor was tested to check if it allows to run javascript code. We used the payload <img src=x onerror=document.write('aaaa')>. This payload is very common to test whether javascript is getting executed.

On saving the changes to the template the code was executed and its output “aaaa” was reflected to the template. So far, so good.

Since the code was getting executed, it led us to wonder if this could be exploited to fetch some sensitive contents from the system and bring it on the web panel. If this was possible it would be quite a critical vulnerability since anything from the system could be read given we had the appropriate privileges for it.

We spent a lot of time to design a working payload which would do the task. Finally, after a complete day’s effort satisfying results were achieved with the final payload.
An AJAX XMLHttpRequest object was used to create a request and the path of “/etc/passwd” was provided to check if it would the reveal the contents of the passwd file from the system.

The following payload was used:
<script>x=new XMLHttpRequest;x.onload=function(){document.write(this.responseText)};x.open("GET","file:///etc/passwd");x.send();</script>

On putting the above payload on the template editor and then saving the template, the changes were saved successfully. However, no output was shown. But also no error was thrown either. This led us to the conclusion that code was getting executed. Now the job was to find where the output would be.

A noteworthy thing to mention is there was an option to download the template as pdf. This seemed interesting. Maybe it had the contents of the “/etc/passwd” file?! Hmmm.
When we downloaded the template and opened it, it indeed had the contents of the “/etc/passwd” file !!!

Next, the “/etc/hosts” file’s contents were fetched by using the same payload and providing path of hosts file like this.
<script>x=new XMLHttpRequest;x.onload=function(){document.write(this.responseText)};x.open("GET","file:///etc/hosts");x.send();</script>

This way any arbitrary file from the system could be read by exploiting LFI via Export injection.

The Problem

In the given scenario output encoding mechanism was used to prevent XSS. Thus, original script we inject remained as it is in the database and whenever it renders on web page, output encoding prevented it from executing. However, when the same content was requested by the server side PDF Engine, web application provided the actual unfiltered/unsanitized scripts as a result of which it executed in PDF.

Prevention Strategy

To prevent attacks like this, first and foremost XSS should be prevented which can be done by sanitization of input. But in this case, strict sanitization of input would prevent the users from designing their templates.

Still, tags like <script> and <iframe> could be blacklisted which can help mitigate the issue.

Final Words

If you come across any similar functionality where javascript is getting executed and pdf can be generated, be sure to test for this vulnerability. Adios and happy hacking!