Closed Bug 1912763 Opened 1 year ago Closed 1 year ago

Cross-document script can run in <use> element, through event handlers, when loaded through a same-origin (but cross-document) SVG file

Categories

(Core :: SVG, defect)

defect

Tracking

()

RESOLVED INVALID

People

(Reporter: johanaxelcarlsson, Unassigned, NeedInfo)

References

Details

(Keywords: reporter-external, Whiteboard: [client-bounty-form])

Attachments

(4 files)

I don't know what to make of this, I tried to search in your issues but did not find anything there.

I found that Firefox implemented a block for iframes in content loaded through <use> tags here https://bugzilla.mozilla.org/show_bug.cgi?id=1754522 . It looks to be specifically to block this as an XSS vector.

However same origin <use> tags can still import JS event handlers. Making the above block useless. From what I have seen this works in Firefox and Webkit but not on Chrome.

SVG file on https://example.com:

<?xml version="1.0"?>
<svg id="x" xmlns="http://www.w3.org/2000/svg">
    <image href="1" onerror="alert(1)" />
</svg>

that is loaded like this:

<svg><use href="https://example.com/test.svg#x"/></svg>

will trigger an alert.

It might just be according to specs, but as the iframe fix broke other things in the name of protecting against XSS it looks strange that this still work.

POC:

  1. place the two files locally

  2. run a webserver

python3 -m http.server  8080
  1. Visit http://localhost:8080/poc.html in Firefox
Flags: sec-bounty?

I failed to upload the files. Here is a live example:
https://joaxcar.com/test/tt.html

Group: firefox-core-security → layout-core-security
Component: Security → SVG
Product: Firefox → Core
Attached file testcase 1

(Hmm, the testcase doesn't repro the issue in the BMO-hosted version, in part due to how BMO attachments auto-redirect I think.)

I'll attach a zipped up version of the testcase that I confirmed does repro the bug if served from a local http server, so that this can be investigated without relying on the reporter's web server.

Attachment #9418837 - Attachment description: testcase 1 (from reporter, adapted to use bmo-hosted test.svg) → testcase 1 (which unfortunately doesn't work; adapted to use bmo-hosted test.svg which trips some BMO special case)

STR with this zipped up testcase:

  1. Extract locally
  2. in a terminal, change to the http-server-contents directory (in this zip file)
  3. Run e.g. python3 -m http.server to serve that directory in a local http server
  4. Visit http://localhost:8000/tt.html (assuming your http server is using port 8000)

EXPECTED RESULTS:
No alerts appear.

ACTUAL RESULTS:
An alert appears saying 1.

Firefox/Epiphany (WebKit) give ACTUAL RESULTS.
Chrome gives EXPECTED RESULTS.

This is probably not super-concerning from a security perspective -- the referenced bug (bug 1754522) ended up being viewed more as a compat issue than as a sec issue, per bug 1754522 comment 19 and surrounding comments (e.g. bug 1754522 comment 23 where emilio and the reporter seem to note that Firefox at-that-time was technically matching the spec.

Here, as with bug 1754522, I think our behavior (which in this case matches at least one other browser) is also correct per-spec....

Script elements are blocked due to this note in the spec:
Within a use-element shadow tree, ‘script’ elements are inert (do not execute).
https://svgwg.org/svg2-draft/struct.html#UseElement
...and that's why we blocked iframe elements as well in bug 1754522. That wouldn't automatically imply that event handlers like onerror, onclick, etc. should be blocked, though. (Though I do see the logic in expecting that they might be blocked.)

In fact, the spec has a whole section on how event handlers should work in <use> shadow trees (implying that they should work):
https://svgwg.org/svg2-draft/struct.html#UseEventHandling

So at first glance, I think that spec section requires onerror (and onclick, etc) to work in a use shadow-tree, in the way that it currently does in WebKit/Gecko, unless I'm missing something...

Yeah, also I don't think we limit <iframe> for same-origin use cases? That's what svg.use-element.graphics-element-restrictions controls right now.

I think this probably shouldn't be hidden (and probably shouldn't be considered XSS since it's same-origin only? Or am I missing something?) If it works cross-origin then it could have some impact I guess (though still unclear how much as per the other bug)

See Also: → CVE-2022-28284

(In reply to Emilio Cobos Álvarez (:emilio) from comment #7)

Yeah, also I don't think we limit <iframe> for same-origin use cases? That's what svg.use-element.graphics-element-restrictions controls right now.

That pref seems to relate to same-vs-cross document use cases, not same-vs-cross origin. (In particular: we do limit <iframe> for same-origin-but-cross-document use-cases, depending on the value of that pref; see B vs. B' in my next comment.)

But setting that aside: on the broader point, I agree with you: this shouldn't be considered XSS since these scripts only run when everything is same-origin. I tested with a cross-origin SVG file (two different http servers running on different ports) and got an error like this in my error console:
Security Error: Content at http://localhost:8000/test-separatedoc-xor.html may not load data from http://localhost:8001/resource.svg.
...and the <use>-clone failed entirely (nothing from it renders or fires event handlers), as expected.

Here's a testcase that I used locally to prod at this a bit more. In this testcase, everything is same-document.

For content inside the <use>-cloned subtree, the presence of redundant-looking log message is an indication that the <use>-cloned version was able to run scripts.

In this same-document example:
A. <script> elements: Gecko, WebKit, Chromium agree they do not get run in the <use> clone
B. <iframe> elements: Gecko honors/runs them in the <use> clone; WebKit/Chromium do not. (Gecko with svg.use-element.graphics-element-restrictions = 2 behaves like WebKit/Chromium, matching the documentation for the pref)
C. event handlers (onload, onclick, onerror): Gecko, WebKit, Chromium agree that they do get run in the <use> clone.

If I change this testcase to move the <use>-targeted resource to a separate (but same-origin) SVG document (note that this means the first copy of each category of log message no longer runs because the svg resource doc itself isn't part of the main document), then I get the following results:
A'. <script> elements: as above, they don't get run in any browser.
B'. <iframe> elements: still not honored in WebKit/Chromium; also no longer honored in Gecko, unless I relax svg.use-element.graphics-element-restrictions = 0 per that pref's documentation.
C'. event handlers: Chromium now decides that they do not get run. Gecko/WebKit still run them.

So C vs. C' is the relevant difference here. Chromium makes a distinction between same-doc vs. cross-doc for event handlers, which they do not make for other categories of scripts (script and iframe). I don't know why they do that.

Seems like a Chrome bug. I also don't see a Firefox bug here.

Un-hiding per recent comments.

Group: layout-core-security

Comment on attachment 9418837 [details]
testcase 1

(now that this is un-hidden, testcase 1 actually works to demonstrate Firefox's behavior, since BMO's extra magic token args on secure bug attachments are no longer a factor.)

Attachment #9418837 - Attachment description: testcase 1 (which unfortunately doesn't work; adapted to use bmo-hosted test.svg which trips some BMO special case) → testcase 1
Summary: XSS in <use> element through event handlers when loaded through a same-origin SVG file → Cross-document script can run in <use> element, through event handlers, when loaded through a same-origin (but cross-document) SVG file

Thanks for looking into this and the deep dive into how this should/does work!

I agree that this is probably the most spec-compliant implementation.

I would say (without knowing the details) that Chrome blocks these event handlers to prevent this situation:

  1. A site allows for file uploads and protects itself from XSS by adding a content-disposition: attachment to the SVG files.
  2. The same site allows for some restricted user-controlled HTML but uses some form of sanitization for XSS. This sanitization does not include use tags (see the Salesforce example in the issue with iframes)

This scenario leads to XSS on Firefox and Safari but not in Chrome. But I believe you are right that Chrome is breaking the specs here, in the name of security.

Thanks again, I learned a lot!
/Johan

Thanks. Closing as INVALID for now, since I don't think we intend to change behavior here.

(We could in theory extend the svg.use-element.graphics-element-restrictions:1 configuration to match Chrome on this, but it's not clear that that's adding much in the way of robust protections, or that Chrome's behavior is specced or intentional).

(In reply to johanaxelcarlsson from comment #13)

I would say (without knowing the details) that Chrome blocks these event handlers to prevent this situation:

That's possible, though it could also just be an inadvertent bug on Chrome's end. :) For what it's worth, I tested some old Chrome versions and found that:

  • Chrome 71 (and earlier) match Firefox/Safari on testcase 1, showing the alert(1)
  • Chrome 72 (and newer) match current Chrome on testcase 1, and do not show any alert.

I don't see anything in the Chrome 72 release notes that would correspond to this change (though of course they're incomplete, but they tend to highlight security-relevant changes and known-breaking-changes). So that lends a little bit of credibility to Chrome's behavior here being inadvertent rather than an intentional defense mechanism (particularly given that it's inconsistent per last sentence in comment 9)

This scenario leads to XSS on Firefox and Safari but not in Chrome. But I believe you are right that Chrome is breaking the specs here, in the name of security.

Again, "XSS" isn't quite the right word here, since nothing happens at all here unless the content is all hosted same-origin. The setup you're describing has multiple bits of untrusted user-provided content, hosted on an origin that's got data that's meant to be protected from those untrusted users; that hypothetical setup is arguably the problem in that scenario, rather than SVG <use> restrictions being insufficient.

(Having said that: the web is messy, and I'm sure there are folks doing what you're describing.)

Status: UNCONFIRMED → RESOLVED
Closed: 1 year ago
Resolution: --- → INVALID

Johan, it would be really interesting if you could find what triggered this change in Chrome. I tried going through the release notes, their whole changelog (terrible slow big HTML file. I also looked at the json format), but I could not find anything relevant. Maybe you'll find some supporting evidence why this should be fixed?

I am also CCing Leo Balter, as I presume he might have some additional insights into site-specific behavior.

Flags: needinfo?(johanaxelcarlsson)
Flags: sec-bounty? → sec-bounty-
You need to log in before you can comment on or make changes to this bug.

Attachment

General

Created:
Updated:
Size: