Last Comment Bug 750096 - (CVE-2012-1957) JavaScript execution via special HTML in RSS view; XSS when pasting malicious content into contenteditable
(CVE-2012-1957)
: JavaScript execution via special HTML in RSS view; XSS when pasting malicious...
Status: VERIFIED FIXED
[advisory-tracking+]
: sec-high
Product: Core
Classification: Components
Component: Security (show other bugs)
: 15 Branch
: All All
: -- major (vote)
: mozilla16
Assigned To: Henri Sivonen (:hsivonen)
:
Mentors:
Depends on:
Blocks:
  Show dependency treegraph
 
Reported: 2012-04-29 10:08 PDT by Mario Heiderich
Modified: 2014-06-30 12:06 PDT (History)
14 users (show)
rforbes: sec‑bounty+
ryanvm: in‑testsuite+
See Also:
Crash Signature:
(edit)
QA Whiteboard:
Iteration: ---
Points: ---
Has Regression Range: ---
Has STR: ---
wontfix
+
verified
+
verified
+
verified
14+
verified
unaffected


Attachments
test.rss (1.60 KB, application/rss+xml)
2012-04-29 10:08 PDT, Mario Heiderich
no flags Details
Simple screenshot of the effect (54.25 KB, image/png)
2012-04-29 10:14 PDT, Mario Heiderich
no flags Details
testcase, <embed> in <description> (2.21 KB, application/rss+xml)
2012-04-29 20:31 PDT, Daniel Veditz [:dveditz]
no flags Details
Potential fix (5.59 KB, patch)
2012-06-11 03:36 PDT, Henri Sivonen (:hsivonen)
bzbarsky: review+
lukasblakk+bugs: approval‑mozilla‑aurora+
lukasblakk+bugs: approval‑mozilla‑beta+
Details | Diff | Review
Mochitest, to be landed later (2.53 KB, patch)
2012-06-11 05:01 PDT, Henri Sivonen (:hsivonen)
bzbarsky: review+
Details | Diff | Review
Mochitest, to be landed later, v2 (2.63 KB, patch)
2012-06-13 23:47 PDT, Henri Sivonen (:hsivonen)
bzbarsky: review+
Details | Diff | Review
Backport to ESR (5.59 KB, patch)
2012-06-14 04:00 PDT, Henri Sivonen (:hsivonen)
bzbarsky: review+
lukasblakk+bugs: approval‑mozilla‑esr10+
Details | Diff | Review

Description Mario Heiderich 2012-04-29 10:08:57 PDT
Created attachment 619421 [details]
test.rss

User Agent: Mozilla/5.0 (X11; Ubuntu; Linux i686; rv:12.0) Gecko/20100101 Firefox/12.0
Build ID: 20120423123513

Steps to reproduce:

It is possible to execute JavaScript on the Feed-View - indicating that there might be a security issue leading to RCE. The bug was found as a side-effect while crafting a RSS version of the H5SC.

A PoC file is attached - please see below. The relevant part in the PoC file is this small snippet:

<description><![CDATA[<embed
src="javascript:top.SubscribeHandler.uninit=function(){alert('hello
chrome://')}">]]></description>

Firefox doesn't seem to filter <embed> elements inside the RSS <description> properly - so we can exec JavaScript. This allows us to hook into the pre-existing script tags. Note the alert in the PoC file is not a "classic website alert" but indicates privileged script exec. 

The bug was found on Ubuntu 11.10:
Mozilla/5.0 (X11; Ubuntu; Linux i686; rv:11.0) Gecko/20100101 Firefox/11.0

I also tested in Win7 with Nightly (15.0.a1 - today's build) - same effect, JavaScript execution in Feed-View.
Comment 1 Mario Heiderich 2012-04-29 10:14:56 PDT
Created attachment 619422 [details]
Simple screenshot of the effect
Comment 2 Mario Heiderich 2012-04-29 10:17:20 PDT
One note: The PoC might need to be refreshed once to work, I overloaded the "uninit" method - thus the alert will not happen unless you refresh the Feed-View once. 

Also, note that the headers might need to be set properly to have it work - I am using application/rss+xml.
Comment 3 Boris Zbarsky [:bz] (Out June 25-July 6) 2012-04-29 19:45:19 PDT
Henri, do you know what we use to sanitize this stuff?
Comment 4 Daniel Veditz [:dveditz] 2012-04-29 20:26:13 PDT
Confirmed that <embed> leads to code execution, but so far am not seeing a privilege escalation. You do have access to the SubscribeHandler._feedWriter element so there may be some way.

Firefox 3.6.x is fine so this may be related to the new HTML5 parser or the replacement of the old ParanoidSinkFragment. If so some add-ons might be in trouble because they may not be as diligent as the feed handler at running everything in a non-privileged sandbox.
Comment 5 Daniel Veditz [:dveditz] 2012-04-29 20:31:02 PDT
Created attachment 619475 [details]
testcase, <embed> in <description>

This testcase demonstrates that the testcase doesn't run with privilege, nor does the later uninit() call. For an attacker triggering that is easy -- just cause the page/frame to navigate.
Comment 7 Henri Sivonen (:hsivonen) 2012-04-30 04:13:43 PDT
(In reply to Boris Zbarsky (:bz) from comment #3)
> Henri, do you know what we use to sanitize this stuff?

We're supposed to use nsTreeSanitizer, but it looks like it's not being used, since embed is not on its white list and it always sanitizes the src attribute for javascript: URLs.
Comment 8 David Bolter [:davidb] 2012-05-03 12:36:29 PDT
Henri would you be a good person to take this bug?
Comment 9 Henri Sivonen (:hsivonen) 2012-05-04 06:20:04 PDT
(In reply to David Bolter [:davidb] from comment #8)
> Henri would you be a good person to take this bug?

Normally I would, but not right now.  Sorry.  I'm semi-absent due to health issues.

Preliminary debugging suggests that the sanitizer is removing the embed element as it should, but since the element has been inserted into the document fragment once and didn't get its src removed, nsHTMLSharedObjectElement::StartObjectLoad stills end up running off a runnable using the src.

Are detached embed elements expected to start loads like detached img elements or is it a bug of that the embed element still attempts to start the load?
Comment 10 Boris Zbarsky [:bz] (Out June 25-July 6) 2012-05-04 09:06:00 PDT
> Are detached embed elements expected to start loads

This might have been a recent change from Josh's work to move plug-ins to content.  Josh?
Comment 11 [On PTO until 6/29] 2012-05-07 15:44:36 PDT
Mario, if you can find a proof of concept that shows an escalation of privilege, this would qualify for a bug bounty. As it is, this does not look to be exploitable.
Comment 12 Henri Sivonen (:hsivonen) 2012-05-15 04:46:08 PDT
(In reply to Boris Zbarsky (:bz) from comment #10)
> > Are detached embed elements expected to start loads
> 
> This might have been a recent change from Josh's work to move plug-ins to
> content.  Josh?

Josh, ping?

So I've been thinking what we might do about this. I've come up with three possible solutions.

1) add a method to all elements for making the element harmless. Make the sanitizer call this method on each element that gets removed.

2) remove all attributes from all elements that the sanitizer removes.

3) move attribute sanitation to the tree op executor.

Option #1 would require changes to the classes that implement elements but would probably perform slightly better than option #2. Option #2 would be the least invasive option. Option #3 would be the safest option but would also be the ugliest in terms of encapsulation and code organization.

bz, any advice?
Comment 13 Mario Heiderich 2012-05-15 04:50:49 PDT
I am currently blocked by other things and won't find time to look for a PoC. I am perfectly fine with simply having the issue be fixed ;)
Comment 14 [On PTO until 6/29] 2012-05-16 10:42:38 PDT
Critsmash triage is rating this as sec-low unless escalation of privilege can be found for this.
Comment 15 Boris Zbarsky [:bz] (Out June 25-July 6) 2012-05-22 21:03:21 PDT
(In reply to Henri Sivonen (:hsivonen) from comment #12)
Option #1 seems like the thing most likely to continue working correctly as new features are added to elements, of the three options.  Unless option 2 is talking about a whitelist?
Comment 16 Henri Sivonen (:hsivonen) 2012-05-29 05:22:31 PDT
(In reply to Boris Zbarsky (:bz) from comment #15)
> (In reply to Henri Sivonen (:hsivonen) from comment #12)
> Option #1 seems like the thing most likely to continue working correctly as
> new features are added to elements, of the three options.  Unless option 2
> is talking about a whitelist?

No, option 2 is not about a whitelist. Option 2 is about removing all attributes from all elements that the sanitizer ends up removing from the tree for any reason. The possible reasons for removing an element are: the sanitizer decides to flatten the element itself (the element is removed but its children are retained), the sanitizer decides to prune the element itself (the element and the whole subtree rooted at the element are removed) and the sanitizer decided to prune an ancestor of the element.

I was actually starting to prefer option #2.

Option #3 would be insufficient as long as XML does not go through the tree  op executor.
Comment 17 Boris Zbarsky [:bz] (Out June 25-July 6) 2012-05-29 18:43:25 PDT
Ah, ok.  Option #2 sounds fine, then.
Comment 18 Henri Sivonen (:hsivonen) 2012-06-01 03:02:01 PDT
Simply implementing option #2 isn't enough. The embed handling runnable runs off the script runner queue instead of the event loop, so the sanitizer runs to late. To do with this problem, I need to put an update batch around both the parse and the sanitation. This leads to another problem: The code in the sanitizer currently assumes it's not wrapped in an update batch and calls methods that try to notify. It seems that it's necessary to find non-notifying alternatives for such tree mutations. As far as I can tell, the non-notifying alternatives use the old child  indices, which is somewhat inconvenient. Also, the parser occasionally intentionally breaks out of the update batch it itself has established. It's not clear to me yet if wrapping the parse in an extra update batch is going to be a problem in the cases where the sanitizer is used (i.e. where the parser isn't appending to a tree that's being rendered at the same time).

bz, are there non-notifying tree mutation methods that don't use the old child indices?
Comment 19 Boris Zbarsky [:bz] (Out June 25-July 6) 2012-06-01 08:50:17 PDT
> bz, are there non-notifying tree mutation methods that don't use the old child indices?

Not at the moment; we're trying to get rid of the non-notifying stuff altogether, iirc...
Comment 20 Henri Sivonen (:hsivonen) 2012-06-04 02:33:19 PDT
(In reply to Boris Zbarsky (:bz) from comment #19)
> > bz, are there non-notifying tree mutation methods that don't use the old child indices?
> 
> Not at the moment; we're trying to get rid of the non-notifying stuff
> altogether, iirc...

So the crux of the matter isn't notifying per se but inhibiting script runners from running from the start of the parse to the end of the sanitization. It just seems that establishing an update batch is the most appropriate way to inhibit  script runners from running and that mutations that do notify also attempt to fire mutation events and assert if script runners are being inhibited.

What would be the most appropriate way to inhibit script runners from running from the start of the parse to the end of the sanitization and to turn off attempts to fire mutation events during the modifications the sanitizer performs?
Comment 21 Boris Zbarsky [:bz] (Out June 25-July 6) 2012-06-04 08:22:13 PDT
I don't actually know.  We might need to add some API for this.  :(  Jonas?
Comment 22 Jonas Sicking (:sicking) PTO Until July 5th 2012-06-04 12:56:10 PDT
I'd say we don't have a good way of preventing any and all scripts for an "extended" period, if "extended" means across multiple asynchronous calls. Script blockers are always stack-based, and I think they need to stay that way since they are a global mechanism and doesn't just block one document.

So if you need something across multiple async calls, we need something else which is per-document.
Comment 23 Daniel Veditz [:dveditz] 2012-06-04 15:16:00 PDT
fwiw moz_bug_r_a4 could not find a way to escalate privilege here, but it's still not good having script running amok in our feed UI.
Comment 24 Henri Sivonen (:hsivonen) 2012-06-06 02:23:13 PDT
(In reply to Jonas Sicking (:sicking) from comment #22)
> I'd say we don't have a good way of preventing any and all scripts for an
> "extended" period, if "extended" means across multiple asynchronous calls.

That's not what it means. I'm looking for a stack-based solution that's in effect for the duration of a synchronous parse and for the duration of a synchronous sanitization both invoked from a parent method on the stack.

Existing update batches successfully deal with script runners. The problem is that in order to remove a node during an update batch without triggering an assertion in the code that tries to fire a mutation event is to use the deprecated index-based methods.

So I'm looking for a solution that has the following three properties:
 * Is not deprecated.
 * Prevents script runners from running.
 * Does not try to fire mutation events while script runners are prevented from running.

If there is no such solution, I guess I should proceed with the deprecated APIs, but it's sad to add more uses of APIs that were trying to get rid of.

(In reply to Daniel Veditz [:dveditz] from comment #23)
> fwiw moz_bug_r_a4 could not find a way to escalate privilege here, but it's
> still not good having script running amok in our feed UI.

I expect there might be privilege escalation in the clipboard case.
Comment 25 Henri Sivonen (:hsivonen) 2012-06-06 02:37:20 PDT
One thing worth exploring would be parsing into a document that's set as loadedAsData and then changing the owner of the document fragment before returning it. However, I would still like to have some sort of script runner prevention thing going on that encompasses both the parsing and the sanitization.
Comment 26 Jonas Sicking (:sicking) PTO Until July 5th 2012-06-06 17:43:59 PDT
Ah, cool, that is much simpler!

The assertion only means that we've suppressed the event, it doesn't actually mean that the event was fired when it was dangerous to do so.

So technically using a scriptblocker/updatebatch fulfills all the requirements you set up. But I think you additionally don't want to assert.

You could simply do that by putting a nsAutoScriptBlockerSuppressNodeRemoved around the code you want to protect.

However, keep in mind that scriptblockers doesn't actually completely block scriptrunners, they just make them execute later. So wouldn't it still be a problem that script would run for any injected <script> elements, it would just run later?
Comment 27 Henri Sivonen (:hsivonen) 2012-06-11 03:36:42 PDT
Created attachment 631845 [details] [diff] [review]
Potential fix

(In reply to Jonas Sicking (:sicking) Vacation June 11-22 from comment #26)
> You could simply do that by putting a nsAutoScriptBlockerSuppressNodeRemoved
> around the code you want to protect.

Awesome! This is exactly what I was looking for.

> However, keep in mind that scriptblockers doesn't actually completely block
> scriptrunners, they just make them execute later. So wouldn't it still be a
> problem that script would run for any injected <script> elements, it would
> just run later?

Script elements injected during fragment parsing are marked as already started, so they are prevented from executing even before we get to the sanitizer.

I believe the patch I'm attaching is a sufficient fix. However, to add more defense in depth, we could make the fragment parsing use a separate document that's marked as "loaded as data" and then transfer the ownership of the document fragment to another document after the sanitization. Should I do that?
Comment 28 Henri Sivonen (:hsivonen) 2012-06-11 04:17:57 PDT
When trying to write a test case for this, I realized that <img src> and <video src> don't evaluate javascript: URLs with window as the |this| object but <embed src> does. Why do we evaluate <embed src> with window as |this|?
Comment 29 Henri Sivonen (:hsivonen) 2012-06-11 05:01:59 PDT
Created attachment 631859 [details] [diff] [review]
Mochitest, to be landed later
Comment 30 Boris Zbarsky [:bz] (Out June 25-July 6) 2012-06-11 08:16:11 PDT
> Why do we evaluate <embed src> with window as |this|?

Also <object data>.  See bug 300263 for why, though apparently no one else supports that so we should consider removing it...
Comment 31 Henri Sivonen (:hsivonen) 2012-06-13 07:07:23 PDT
Comment on attachment 631845 [details] [diff] [review]
Potential fix

Requesting review for the solution, because it seems to work and I think we need to land something.

However, I'm not quite happy with this fix. The other cases in nsParserUtils that use the parser to create a DOM that supposed to be inert, a document marked as "loaded as data" is used. To use the same mechanism here, it would be necessary to transfer the ownership of the document fragment to the document of the context node after performing the parsing with the document that's not the final destination document. However, it appears that we don't have reusable code for forcing node adoption across different-origin documents and ideally the dummy document would use a null principal, so I gave up trying to do that.
Comment 32 Boris Zbarsky [:bz] (Out June 25-July 6) 2012-06-13 09:22:54 PDT
Comment on attachment 631845 [details] [diff] [review]
Potential fix

r=me
Comment 33 Boris Zbarsky [:bz] (Out June 25-July 6) 2012-06-13 09:24:10 PDT
Comment on attachment 631859 [details] [diff] [review]
Mochitest, to be landed later

r=me, but do you want to test iframe too?

I assume the test does fail without your patch?
Comment 34 Henri Sivonen (:hsivonen) 2012-06-13 23:47:50 PDT
Created attachment 633048 [details] [diff] [review]
Mochitest, to be landed later, v2

(In reply to Boris Zbarsky (:bz) from comment #32)
> Comment on attachment 631845 [details] [diff] [review]
> Potential fix
> 
> r=me

Thank you. Landed.
https://hg.mozilla.org/integration/mozilla-inbound/rev/c233e8733ba3

(In reply to Boris Zbarsky (:bz) from comment #33)
> Comment on attachment 631859 [details] [diff] [review]
> Mochitest, to be landed later
> 
> r=me, but do you want to test iframe too?

Attaching a revised version with iframe.

> I assume the test does fail without your patch?

Yes, it does.
Comment 35 Henri Sivonen (:hsivonen) 2012-06-14 04:00:26 PDT
Created attachment 633095 [details] [diff] [review]
Backport to ESR

I think we should make the security rating more severe here to make a fix eligible for ESR inclusion. In the builds without the fix for this bug, if the user copies a run of text on one Web site and the run of text has an indivisible embed element with the JavaScript URL as its src, pasting into contenteditable on a different-origin Web site runs the script in the context of that site.

I verified that both trunk and ESR suffer from this problem. I'm attaching a backport of the fix for ESR.
Comment 36 Mario Heiderich 2012-06-14 06:23:34 PDT
Confirmed that on current FF versions. I ran the same tests against the copy&paste scenario that I ran against the RSS view; <embed> is the only one executing JavaScript.

For reference sake: I used the string you can get on html5sec.org when executing: Firebug -> vectors() (all vectors - isolated and concatenated)
Comment 37 Boris Zbarsky [:bz] (Out June 25-July 6) 2012-06-14 07:02:23 PDT
Comment on attachment 633048 [details] [diff] [review]
Mochitest, to be landed later, v2

r=me
Comment 38 Boris Zbarsky [:bz] (Out June 25-July 6) 2012-06-14 07:15:11 PDT
Comment on attachment 633095 [details] [diff] [review]
Backport to ESR

r=me
Comment 39 Ed Morley [:emorley] 2012-06-15 06:12:50 PDT
https://hg.mozilla.org/mozilla-central/rev/c233e8733ba3
Comment 40 [On PTO until 6/29] 2012-06-18 07:23:27 PDT
Verified fixed in the 6/18 build of Mozilla Central using attached POCs.
Comment 41 Henri Sivonen (:hsivonen) 2012-06-18 07:51:34 PDT
Comment on attachment 631845 [details] [diff] [review]
Potential fix

[Approval Request Comment]
Bug caused by (feature/regressing bug #):
Bug 482909.

User impact if declined:
Unauthorized cross-site execution in the context of site that has a contenteditable-based editable area if the user can be convinced into copying some text from a malicious site and pasting it into the editable area. Unauthorized script access between distinct syndication items in a feed. Potentially similar vulnerabilities in Firefox extensions that use the same API as the feed reading features of Firefox.

Testing completed (on m-c, etc.): 
Has baked on mozilla-central over the weekend.

Risk to taking this patch (and alternatives if risky):
No true risk in sight. The first risk that I can think of is that some element implementation might misbehave if it's associated script runner gets deferred for longer than the element implementation expects. However, that risk since low, since the sanitizer is trying to eliminate this sort of things that would usually run off a script runner.
 
String or UUID changes made by this patch: 
None.
Comment 42 Henri Sivonen (:hsivonen) 2012-06-18 07:54:37 PDT
Comment on attachment 633095 [details] [diff] [review]
Backport to ESR

[Approval Request Comment]
If this is not a sec:{high,crit} bug, please state case for ESR consideration:
I believe the user impact described in comment 41 merits more serious treatment than the current security rating of this bug (assigned prior to comments about the clipboard-based XSS) suggests.

User impact if declined:
See comment 41.

Fix Landed on Version:
Firefox 16

Risk to taking this patch (and alternatives if risky): 
See comment 41.

String or UUID changes made by this patch:
None.
Comment 43 Lukas Blakk [:lsblakk] use ?needinfo 2012-06-18 16:00:36 PDT
Comment on attachment 631845 [details] [diff] [review]
Potential fix

[Triage Comment]
Approving for Beta/Aurora - will discuss ESR in security triage later this week.
Comment 46 Daniel Veditz [:dveditz] 2012-06-21 17:45:53 PDT
Raising security severity because this affects more than originally thought. The copy/paste case might be more "moderate" than "high" given the required social engineering (but a click-dragging attack might work well), but there's also worry an unknown number of addons might rely on this same sanitization API.
Comment 47 Henri Sivonen (:hsivonen) 2012-06-22 00:42:37 PDT
Comment on attachment 633095 [details] [diff] [review]
Backport to ESR

[Approval Request Comment]
If this is not a sec:{high,crit} bug, please state case for ESR consideration:
See comment 42 and comment 46.

User impact if declined: 
See comment 41.

Fix Landed on Version:
Firefox 16; also branch-landed on 15 and 14.

Risk to taking this patch (and alternatives if risky): 
See comment 41.

String or UUID changes made by this patch: 
None.

I thought I already requested approval for ESR, but a bug history says otherwise. Anyway, I'm requesting approval for ESR now in the light of comment 46.

I'll be on vacation returning on July 11, so if the ESR backport patch gets approval before that, please treat it as checkin-needed (just bug number, review and approval in the commit comment).
Comment 48 Lukas Blakk [:lsblakk] use ?needinfo 2012-06-22 14:55:53 PDT
Comment on attachment 633095 [details] [diff] [review]
Backport to ESR

Thanks Henri - will set check-in needed.
Comment 49 [On PTO until 6/29] 2012-07-09 17:24:27 PDT
This was marked for ESR checkin over two weeks ago. Can someone please check the patch in or will Henri be doing it after 7/11?
Comment 50 Henri Sivonen (:hsivonen) 2012-07-11 04:40:58 PDT
Landed on esr:
https://hg.mozilla.org/releases/mozilla-esr10/rev/bdf10a513d20

Thanks for the approval.
Comment 51 Henri Sivonen (:hsivonen) 2012-07-16 00:24:19 PDT
Editing summary as a reminder to cover the copy&paste aspect in the advisory.
Comment 52 Daniel Veditz [:dveditz] 2012-09-23 10:57:29 PDT
Henri: don't forget to check these tests in.
Comment 53 Henri Sivonen (:hsivonen) 2012-09-25 00:35:55 PDT
(In reply to Daniel Veditz [:dveditz] from comment #52)
> Henri: don't forget to check these tests in.

Landed https://hg.mozilla.org/integration/mozilla-inbound/rev/3a8ee10cf6ec
Comment 54 Ryan VanderMeulen [:RyanVM] 2012-09-25 14:40:26 PDT
https://hg.mozilla.org/mozilla-central/rev/3a8ee10cf6ec
Comment 55 Raymond Forbes[:rforbes] 2013-07-19 18:08:15 PDT
rforbes-bugspam-for-setting-that-bounty-flag-20130719

Note You need to log in before you can comment on or make changes to this bug.