This is the first of many posts in my “technique of the week” series, wherein I will be choosing a vulnerability and demonstrating it with an explanation as to how it works. For each weekly technique, I will attempt to choose a vulnerability that is uncommon, obscure, under-utilized, or lesser-known. The purpose of this weekly series is to highlight some attack vectors that may not be known to some new(er) hackers and/or bug hunters.

So, obviously, this first post is going to be covering Reflected File Download (hence the title) – even if you’re already aware of what RFD is and how it works, you hopefully may still learn something in this tutorial, and I’m also going to be using a vulnerability that existed in for a long time as a demo for my PoC. So, despite being obscure, this technique still exists on the most visited and most well-known website on the entire internet. Note that many people have found this bug in google, or slightly different variations of it – it’s out of scope for their bounty program and although they do occasionally patch them when they’re found, they tend to keep popping up there in slightly different ways, and it’s like google’s security team are playing whack-a-mole; except they’re playing it slowly, due to it not being in scope 😛 this vulnerability in question is my own variation of similar vulns found elsewhere on in the past.

This “technique of the week” series will be unique in two ways – not only is it the first in this series, but it’s also going to be the only one that I’ll actually be covering twice, with a part #1 and part #2. My reasoning for this is that I am introducing the generic concept here and showing some methods that work, while also demonstrating a vulnerability. The second part however, will address specific context-scenarios – because, not unlike XSS, there are different scenarios wherein different char(s) are required. Depending on the specific reflection context that has taken place, varying methods may be required to properly reflect your value via the JSONP Callback Response or wherever else it is reflecting. I’ll be covering all of that in part #2, whereas I’m just introducing the concepts here.

The Basics:

Reflected File Download, or RFD, is a client-sided attack that occurs through browser-based reflection. Think of it like a Reflective XSS in the sense that an attacker is essentially able to create a one-time URL to send to a victim, except, instead of injecting HTML/JavaScript into the page, it allows the attacker to serve a file by setting parameters which specify the file name and the file contents – this results in a malicious file being served from a trusted website (without the file actually being hosted on the website or uploaded to it).

RFD is a good example of a vulnerability that utilizes browser reflection but isn’t XSS. If you’re unable to inject HTML or JavaScript to trigger an XSS anywhere on your target due to sanitization or a WAF, then you could always test to see if you’re able perform RFD.

So, before I explain the vulnerability in a technical manner, I will first describe how a typical attack scenario could potentially play out:

  • Attacker finds an RFD vulnerability in a popular website (
  • Attacker scrapes or somehow obtains list of emails of users who use the website in question
  • Attacker sends out spoofed emails, from telling users to update to a new version, because of a (non-existent) security issue.
  • Within the email, the attacker provides a URL which is with some URL params (these can be urlencoded for some mild obfuscation)
  • The attackers payload uses one param to specify the filename (for the sake of this example, it could be named new-client-update.bat and there would be another param which specifies the file contents
  • Victim visits website and sees an “update” being downloaded from the trusted website, resulting in them running it

For example, when the attacker visits the URL they could be displayed a prompt like this:

  • Since the victim sees that the download is coming from the “trusted” source as they expected, this results in the attacker gaining code execution on the victims machine
  • via this code execution, an attacker is able to do anything from install a RAT to locking all the victim’s files with ransomware, to simply destroying the contents of their PC.

Now that I’ve given a general overview of the concept and how a possible attack scenario could work, I’ll now go on to explain how RFD works in a technical manner, and how to properly test for it.

Testing and Exploitation:

Generally, a good place to look for RFD is areas of websites that have JSON functionality, you can reflect the values through JSONP Injection or other techniques – in the past I’ve found occurrences of this vulnerability while performing API testing on private bug bounties, and have resulted in triggering PoC’s for RFD via JSON API’s.

Another good indicator is if URL mapping is permitted (for example ; and /)

The good thing about JSON is it’s ridiculously commonly used across the web these days, meaning despite being a lesser-known vulnerability, you’ll actually be fairly surprised as to how commonly you’ll be able to find it.

There are three necessary prerequisites for RFD to take place, and, the clue is all in the name:

  • Your user input needs to be getting reflected in the response. This reflected input would be the code that you wanted to run in your file, such as code for a malicious bash shell script, or batch, or powershell script.
  • You need to be able to control the filename via your inputs (this condition generally occurs if the Content-Disposition header isn’t setting the filename in the response – if this is the case then the browser will instead set the filename based upon the URL*)
  • The response needs to be getting served to the browser and getting downloaded

* extra info for condition #2: a site could serve a download like so: and via the content-disposition header setting the extension, the file that will actually be downloaded is file.exe despite the URL only showing /file – so, if their isn’t a content disposition header set then you could possibly request and in absence of the header, it would set the extension as .bat (note that extra URL tricks are often required, which I will explain shortly)

If these three conditions are simultaneously present, then RFD should be possible. In order to set the filename in the response, a few URL tricks are often required. It’s not always as simple as what I just described above (although it’s rarely too much more complex).

Depending on the browser in use, an additional character or two may be required for the filename to be included in the response in absence of the content-disposition header. In my upcoming book, I’ve published an extensive cross-browser analysis of which character(s) are required for which browser(s), but, in this case I’ll just be sharing two characters which are publicly known. These characters are the semi-colon, and the forward-slash character. So, for example, if you were unable to get it to set your extension of choice in the response via something like the following (note that these vary between browsers – this is very very uncommon for this to ever work and I’m not even sure if this particular case of it working is even via the same cause, so I’d suggest skipping this and moving straight onto including your own file in the URL after their filename, by seperating it using one of the chars described above):

How to control the filename in the response:

Note that these methods I am about to explain in this section aren’t full payloads for an RFD attack (that’ll come next), these are just different methods you can use to control the filename in the response.

you can use a semi-colon, like so, and set your own filename with your own extension:;evil.bat

sometimes a semi-colon on both sides will work:;evil.bat;

the forward slash character can also be used:

The forward slash and semi-colon can sometimes be used in conjuction with eachother, like so:;/evil.bat

same as above, except with an additional semi-colon at the end:;/evil.bat;

if a dot is filtered in the URL path after the TLD (very uncommon, but I really have seen it) so you can only do /file and not /file.ext then there are a few methods that can potentially act as workarounds for this (these are edge-cases):



Taking advantage of browser-based input normalization (this will be ‘normalized’ back to evil.bat by certain browsers:;evil。bat

urlencoded normalized inputs:;evil%E3%80%82bat

An example of a full payload & example of a reflection context that would result in download condition:

So, now that I’ve explained how an attacker could craft a URL taking advantage of no content-disposition header, in order to include their own filename / file extension in the response, I’ll now cover what a full payload would look like (which is just an additional param containing the value being reflected into file — often occurring through JSONP callback functionality), but first, I’ll show a reflection context that would cause the third condition (the response being downloaded) to take place. Consider the following:

Original intended URL:

URL Crafted by attacker:;/evil.bat;?q=evil_command

sometimes, a little more URL manipulation is required for the command. This can be done via the backslash and quotation mark, like so:;/evil.bat;?q=\"evil_command

sometimes urlencoding will be required for the quote:;/evil.bat;?q=\%22evil_command

sometimes, you’ll need to manipulate the URL further via the vertical bar character (as an OR operator):;/evil.bat\%22||evil_command||

For the sake of our example however, we will be sticking with a simple injection via the ?q= param (reflecting into source), so, the attacker crafts the URL;/evil.bat;?q=evil_command and when the URL is accessed, the filename and command are reflected into the HTML page source in a context like this:

<a download href=";/evil.bat;?q=evil_command">

an example of what reflection in the response via JSON would look like:

HTTP/1.1 200 OK
Content-Type: application/json;
Content-Disposition: attachment
Content-Length: 12…
{"results":["q", "rfd\"||calc||","eleet"]}

It should also be noted that “stored” reflective file download is a thing – as in, there are contexts where you could have your payload stored somewhere, and then take advantage of the missing content-disposition header to set your own extension on the page where it’s stored.

For example, let’s assume there was a website and there was a page on there named which is supposed to download a textfile displaying the title of the most recently uploaded video, well, an attacker could upload a video with something like lol\%22||calc|| as the title, then they could make a request to and it will serve the textfile as a batch file, since its relying on the URL itself due to lack of content-disposition header as already well-established. Since lol\%22||calc|| is reflected to the source in a stored manner, it would be ran as a command when /most-recent-upload-title.bat is downloaded and ran.

protip: if you’re trying to get RFD via a stored reflection and its reflecting as plaintext rather than JSON, you can attempt either manipulating the content-type value to application/json; or alternatively sometimes you can send a param via POST/GET to change the content type. For example you could append ?format=json to the the URL to change the content-type, like demonstrated in this report

Real-world example:

The following is an example of a reflected file download in wherein the original file (that google is intended to be downloaded) is named ‘info’ but due to the previously described content-disposition paired with google’s URL mapping, the “info” filename can be manipulated, as well as a command being injected through JSON callback. Here is an example URL:;Chrome%20Installer.bat?q=blah&callback=some-command

the ?q= param isn’t even necessary, but an arbitrary plaintext value can be added there in conjunction with urlencoding the callback value and the filename too, if necessary. This can add an albeit small layer of obfuscation to your payload:;%63%68%72%6f%6d%65%20%69%6e%73%74%61%6c%6c%65%72%2e%62%61%74&q=Google%20Chrome%20Installer&callback=%65%76%69%6c%2d%63%6f%6d%6d%61%6e%64

Below is a video PoC of the vulnerability in action (now patched):

Final Notes:

Now that I’ve explained the general concept of RFD as well as demonstrating the char(s) required to take advantage of URL mapping, and the char(s) required for different JSON contexts, that’s all for this post. I will do a follow up post including some research from my book which will show all of the different possible reflection contexts, and all of the different possible browser contexts, and more importantly, it will show the exact characters required for each of those specific contexts.

I will also be covering more areas where you’ll be able to potentially find RFD within my follow-up post, as well as explaining how to craft more advanced payloads, while demonstrating some (semi) weaponized payloads so that you can show impact with your PoC during a report.

That’s all for now.

./mlt –out