Anytime I see a file upload form during an application test, my attention is piqued. In a best-case scenario, I can upload a reverse shell in a scripting language available on the webserver. If the application is running in PHP or ASP for example, it becomes quite easy. If I can’t get a backdoor uploaded, I will attempt to try to upload an HTML page to get my own client-side javascript uploaded for XSS attacks.

Click for full size image

While testing a web application, I found that an authenticated user can upload a file for a claim. The upload button is designed to allow the upload of Microsoft Word .docx files.

Click for full size image

After a user uploads a file, it can be downloaded. A comparison between the uploaded .docx file and the downloaded.docx file showed that they were different, which would imply that some processing is done on the file before being hosted for download.

Click for full size image

While the file being uploaded must be a valid .docx, it is possible to modify the file extension. I changed the file extension to .html.

Click for full size image

When the file with an .html extension is retrieved from the server, the Content-Type header is set to text/html. This causes the browser to attempt to render the file as HTML.

I then attempted to smuggle an XSS payload within a valid .docx file. Because the file type is compressed, I needed to identify an area within the file body that would not be modified during compression or by post-processing from the application. It was found that certain file paths within the file structure were not modified during the upload process. I modified the filename of Settings.xml within a .docx file to pad it with characters.

After compressing the folder structure into a .docx, I utilized a hex editor to overwrite some bytes within the filename to insert JavaScript code.

Click for full size image

The server accepted the upload of the modified .docx file. The file extension was modified to .html during the HTTP POST.

Click for full size image

When requesting the file, it was served as an HTML file with the XSS payload intact.

Click for full size image

When rendered in the browser, the JavaScript executed. Anyone who clicked a link to that file would have JavaScript execute in their browser under the context of the domain that the application was hosted on.

Click for full size image

To obfuscate this attack, an attacker could include the URI of the upload within a small or invisible iframe to keep the victim from noticing the payload being executed. For this example, a visible payload is displayed.

Click for full size image

So, what’s a developer to do? To prevent this from happening, the developer has a couple different options:

They can validate on the server-side that the file extension is a .doc or .docx before allowing the upload. This was only being enforced client-side in HTML.

Do not let the user control the Content-Type. Return the same Content-Type regardless of what is specified in the content type header or the specified extension.

Control what happens when the file is downloaded. Add the response header: “Content-Disposition: attachment” to prevent the file from being displayed inline in the browser.

Filter out any uploads containing HTML tags. Because a .docx is a compressed file, even if the document contained HTML tags in the document body they should not be present in the raw data. The presence of an HTML tag within the body is a sign that it has been tampered with.

This is not an exhaustive list, but these recommendations are most relevant to the application. A layered approach is often best, and none of these solutions are mutually exclusive.