Forcing Firefox to Execute XSS Payloads during 302 Redirects

Submitted by quentin on Wed, 09/30/2020 - 14:49

Initial Discovery

During a recent engagement I identified an open redirect where a GET parameter would be reflected as-is in the HTTP response Location header without any kind of sanitization. Something similar to this:


Trying multiple kinds of injections, I discovered that newlines and carriage returns characters could be inserted, leading to header injection:


Even more interesting, we can inject arbitrary content in the HTTP response body by inserting two newline characters, leading to reflected cross-site scripting:


However, modern browsers (Google Chrome, Internet Explorer, Firefox) do not interpret the HTTP response body if the HTTP response status code is a 302, so our cross-site scripting payload is useless. Time to find a bypass !

Prior Work

By searching for prior bypasses, I stumbled upon this blog post where Fortinet describes how they bypassed the execution block by setting the Location header to a URI starting with 'mailto://'. Bugcrowd forums also provides some insight into bypasses that may have worked in the past. And this excellent HackerOne report on XSS affecting Twitter, where they used a Location header starting with '//x:1/' definitely sent me in the right direction.

Let's Fuzz

Given that none of the already documented bypasses worked, I decided to write a dumb fuzzer that would generate a list of URLs and open them with xdg-open. To do so, I downloaded the IANA URI schemes list and generated a list of URLs following this format: http://acme.corp/?redir=[URI_SCHEME]://[XSS_PAYLOAD]. Google Chrome and Firefox were tested in this way, Internet Explorer was also tested but with a PowerShell script rather than simply calling xdg-open.

I then spent quite some time closing browser tabs, hoping to be greeted with an alert box :)

A Valid Candidate

Two candidates out of the full IANA URI scheme list worked, and only on Firefox:

  • ws:// (WebSocket)
  • wss:// (Secure WebSocket).

It simply looks like this:


Opening the link in the latest version of Firefox (version 81 at the time of writing) and we see we are executing JavaScript under the right domain, without being redirected:



If you want to test this at home, you can download the 302_server script. It will launch a Python3 HTTP server on port 8000, mimicking the behavior I just described.

Update - October 1st 2020

Sergey Bobrov just pointed out that using an empty Location header will work to force Google Chrome to execute the payload. Nice find !

Update - October 2nd 2020

Maxim Rupp just pointed out that using an resource:// URI in the Location header will work to force Firefox 81 to execute the payload. Nice find !


+32 (0) 2 215 53 58

Gremwell BVBA
Sint-Katherinastraat 24
1742 Ternat
VAT: BE 0821.897.133.