Overriding Referrer-Policy and abusing endpoint-reuse to achieve XS leak, circumventing insecure CSP in the process


One of discoveries of this research was that it is possible to chain few functionalities and misconfigurations together in order to achieve Cross-Site (XS) leak with HTML injection, while strict Content-Security-Policy (CSP) is present.

Specifically, if a site uses forms to send user-supplied data, the usual CSP directive to prevent leakage of data via forms alone is form-action ’self’. However, even if this setting is present, we can still get around this directive by overriding a form’s attributes and abuse the Chrome Referrer-Policy handling bug. The technique in this blog can be abused if the default-src policy is not set. We hope that this blog will raise awareness and demonstrate the risks of not including this CSP policy in websites.

To understand this, let’s briefly touch on ‘Referer’ in web apps.

What is Referer


The ‘Referer’ (spelled "Referrer", but RFC has it misspelled) is an HTTP request header field that identifies the URL of the web page that linked to the requested resource. In other words, when you click a hyperlink, the browser typically sends the URL of the page you came from, to the server of the page you are visiting. This helps with analytics, logging, and security purposes.

What is Referrer-Policy


Referrer-Policy is HTTP response header that allows a website to control how much referrer information is sent along with requests. Different policies can be set to either send full URLs, stripped URLs, or no referrer information at all.

Some common values for the Referrer-Policy HTTP response header include:
  • no-referrer: No referrer information is sent
  • origin: Only the origin part of the URL is sent
  • strict-origin-when-cross-origin: Full URL is sent to same-origin requests and only the origin part is sent to cross-origin requests. (This will be the default value if the policy is not specified within the HTTP response header)

These policies help improve user privacy and security by restricting the amount of potentially sensitive information shared between websites.

The problem


The Referrer-Policy response header is often not specified by a server. Browsers will take care of this by providing a default fallback to strict-origin-when-cross-origin, which is regarded as a secure policy. However, browsers do not follow the HTTP specification correctly regarding the precedence of this policy.

Let's consider the scenario where the site utilizes forms with CSRF token to submit data. The attacker has the ability to inject arbitrary HTML in the input field.


Google’s CSP evaluator regards defined CSP policy as secure. Let’s break it.

Based on above, the following HTML injectable tags can specify their own referrerpolicy attribute: <meta>, <a>, <img>, <area>, <iframe>, <link>.

In our example, let’s say an attacker injects:

The Chrome browser will use the referrerpolicy defined from witin the tag attribute, as it has the highest precedence. This means that with only HTML injection, the attacker can leak the whole URL from the current page, no matter if the server previously defined its own policy via response header or if the header is missing.
As this is a bug in Chrome by itself, the application must have sensitive info in URL for this to be abused. If the entire URL can be leaked, I was wondering what if we can somehow forcefully transfer the CSRF token from the form to the URL and then leak it afterwards.
Turns out we can - by injecting one input field, which overrides form's formaction and formmethod attributes and points the submitted form data to the same endpoint, per inspiration by the Portswigger’s form hijacking blog post:


Once a user clicks on the injected button, the remaining parameters from within the form will be forwarded to the same-endpoint URL, including the CSRF token value. Great, we can forcefully include the CSRF token in the URL. Let’s see how this trick helps us.

Vulnerable parameter re-specification


As we injected one <input> of type submit, we can also inject an additional one, which will re-specify the vulnerable parameter. The injection of the second <input> tag will look similar to:

It will re-specify the ‘vulnerable’ GET parameter and set its value to arbitrary HTML. The final payload looks like the following:


Once a user clicks on the injected button, the CSRF token is moved to the URL. Furthermore, arbitrary HTML (image) gets rendered:


Re-specified vulnerable parameter will reflect the injected <img> tag with ‘src’ pointing to the attacker domain with overridden referrerpolicy, which in turn leaks the entire URL to the attacker – in the Referer header!

By using this technique, we utilized HTML injection within the same endpoint twice in order to leak the secret token cross-domain.

Summary of steps


  1. Injecting first <input> tag and abusing the ability to assign own formmethod and formaction properties to manipulate the form request method to GET, forcing remaining form parameters to the URL.
  2. Injecting second <input> tag. It will re-specify ‘vulnerable’ parameter after the malicious input button is pressed.
  3. Abusing the Referrer-policy override to leak CSRF token from the URL cross-domain. We used <img> tag, because there is no img-src and default-src CSP policies, however note that other tags could also be used.


Mitigations


This abuse can be prevented by including a default-src policy which, when missing the CSP evaluator does not evaluate as risky. Alternatively, explicitly specify all other *-src directives (there are quite a few of them). The shown technique works even if Content-Security-Policy: form-action is missing or defined as ‘self’, which mostly will be the case.
In order to stop this via form-action alone, it will need to be set to ‘none’, completely disabling form-usage within the site, which is not practical, especially having forms as intended functionality.
Hopefully, this blog demonstrates why sites should always include all ‘src’ directives in their CSP configuration.

Future specification improvements


Currently, Chrome is the only browser that follows the Referrer-Policy precedence, as detailed in specification and counter-intuitively this is the reason why it's vulnerable in this specific scenario. Unfortunately, Referrer-Policy defined from within the tag attribute of specific HTML tags will have the highest precedence. This allows attackers to overwrite the server-defined policy. From the security perspective this is not great, as discussed with Google engineers, the change in the specification will be required to define stricter Referrer-Policy precedence. Other browsers for the most part do not follow the specification therefore those are better protected against this attack. There are few edge cases, though. For example: Firefox will have referrerpolicy precedence only for the <a> tag and the same abuse can be achieved with two clicks.
With currently available mitigations, utilize default-src CSP directive and try not to transfer sensitive information via URL, though sometimes this is ultimately necessary. If so, make sure HTML injection vulnerability is not possible in that endpoint.
The issue with Referrer-Policy precedence has been raised to Google. It is not fixed yet, until the specification changes.
Issue regarding Google’s CSP evaluator edge case is also reported, unfortunately Google plans not to improve their CSP evaluator and include cross-site leak misconfigurations.

Links to these reported issues:
Google Issue Tracker
Github CSP Evaluator