Closed Bug 1248315 Opened 8 years ago Closed 8 years ago

Security: Timing attack on SVG feComposite filter circumvents same-origin policy

Categories

(Core :: SVG, defect)

All
Windows 7
defect
Not set
normal

Tracking

()

RESOLVED INVALID

People

(Reporter: chromium.khalil, Unassigned)

Details

Attachments

(6 files)

Using the SVG feComposite filter may produce intermediate pixel color values outside of the normal 0-255 byte range, hence the intermediate values get clamped between the right values. However, the time required to perform clamping is not independent of the intermediate color value, because the number of comparison statements traversed is different. This allows an attacker to differentiate between values smaller than zero, and values in the range of 0-255. Because the filter can be applied to sources from other origins, such as iframes, this allows an attacker to read the contents of sources with a different origin.

An exploit is attached: exploit.html, it reads a character from example.com.

The exploit performs the computations of feComposite so many times that the difference becomes measurable by JavaScript, and thus an original image can be reconstructed. The timing is of course very noisy so the provided exploit doesn't reconstruct the image perfectly, however the exploit could of course be improved to produce a better image. My results show approximately 75% correct pixels which is significantly better than a random function of 50%.

Despite the exploit showing a very slow reconstruction of a single character, this is only a proof of concept. An actual exploit would be better because it could look at certain defining pixels instead of entire characters (or combinations of pixels acquired by applying filters). This way every pixel reconstructed leaks exactly 1 bit of information.

I performed the exploit a couple of times and the results and analysis are attached in the attached image results.png
Attached image results.png
Do you know what webkit/blink do in this case?
Group: firefox-core-security → core-security
Component: General → SVG
Flags: needinfo?(chromium.khalil)
Product: Firefox → Core
Oops sorry I uploaded the wrong testcase.
Group: core-security → layout-core-security
This sounds similar in spirit to bug 711043 (which was fixed a few years ago).

(Also, I'm tagging comment 3 as "typo" to make it autocollapse, since it's huge from having the contents of the testcase copypasted in as a comment.  People can still click the "+" to open it if they like -- or probably-better, they can just view its attached testcase [and its source] directly.)
I cannot reproduce locally, using Firefox Nightly or Firefox 43 (previous release) in an Ubuntu 14.04 VM. For me, the "reconstruction" snapshot always seems to be solid-gray.

Your screenshots are definitely concerning, though. What OS & Firefox version are you testing with?
Flags: needinfo?(chromium.khalil)
(This might want a look from heycam, since he wrote the patch for bug 711043, and according to the attached screenshot, this seems to be getting around that patch's defenses.)
Flags: needinfo?(cam)
Doesn't work on my Mac either.
I assume we're looking at the software backend here: https://dxr.mozilla.org/mozilla-central/rev/709f559b5406e8555cf84dd09bdc747b076f142c/gfx/2d/FilterProcessingSIMD-inl.h#1017.

Markus, as you rewrote the filter implementations since bug 711043, can you take a look?
(Transferring needinfo to mstange per comment 9)
Flags: needinfo?(cam) → needinfo?(mstange)
(In reply to Daniel Holbert [:dholbert] (offline 2/18-2/21) from comment #6)
> I cannot reproduce locally, using Firefox Nightly or Firefox 43 (previous
> release) in an Ubuntu 14.04 VM. For me, the "reconstruction" snapshot always
> seems to be solid-gray.
> 
> Your screenshots are definitely concerning, though. What OS & Firefox
> version are you testing with?

Firefox 44.0.2 on Windows 7
Flags: needinfo?(chromium.khalil)
I think the attached testcase uses -webkit-filter and the one in the collapsed comment 3 uses -moz-filter, and that's why the attached one doesn't work.
In Nightly at least, -webkit-filter should be supported as an alias for filter (and -moz-filter is rejected as invalid CSS).  So I'd expect the attached testcase to be more effective than comment 3, in Nightly.
Nice work.
Khalil, can you paste the graphics section from your about:support?

The software filters were specifically written with this attack in mind. Since the attack works on your Windows 7 machine, which should use the Direct2D 1.1 backend for filters (GPU accelerated) assuming we're not blacklisting the driver, it sounds like the vulnerability is in Direct2D.
Flags: needinfo?(mstange) → needinfo?(chromium.khalil)
I have attached a screenshot for graphics section.
Flags: needinfo?(chromium.khalil)
Attached image screenshot.png
Thanks. Your content backend is cairo, which means that your machine is using the software code.

Comment 15 can be ignored - I was describing the feConvolveMatrix filter, but your exploit uses the feComposite filter.
After looking at the exploit more closely, I don't think it can work in this state at all. Did you make changes to it after you recorded the Firefox results?

Your attackRepeatedly function (which is the only place where you measure times) only times style resolution, not painting. Painting only happens during the time when no JS is executing (i.e. in the time during which you wait for the setTimeout to fire). So you if you want to time painting, you need to take the first timestamp just before you exit your JS, and the next timestamp when your timeout callback is called.
In fact, Firefox never paints the "differentiate" filter at all, since attackRepeatedly()  always unsets the "-webkit-filter:url(#differentiate)" style before it exits the function.
Flags: needinfo?(chromium.khalil)
Sorry for delay, we made this exploit for target Chrome and we get the same result.
Flags: needinfo?(chromium.khalil)
(In reply to Khalil Zhani from comment #20)
> Sorry for delay, we made this exploit for target Chrome and we get the same
> result.

Could you make double-check that you've attached the correct testcase that definitely reproduces the issue for you in Firefox, and attach that testcase?   (Maybe you've already double-checked that; but given that nobody else here has been able to reproduce yet, and given that the file you attached has a bunch of -webkit stuff, seems worth checking.)
Flags: needinfo?(chromium.khalil)
Actually I couldn't repro it on Nightly, but I'm still able to reproduce it on 44.0.2 (stable).
Flags: needinfo?(chromium.khalil)
Attached image result.png
(In reply to Daniel Holbert [:dholbert] from comment #6)
> For me, the "reconstruction" snapshot always seems to be solid-gray.

My bad -- I didn't leave the testcase open for long enough (before reloading) to populate the snapshot.

I can confirm that I do see black & white pixels (not gray) eventually populated in the "reconstruction" frame, if I leave the testcase open for a minute or so.

However, the pattern looks random & not at all related to the reference. I'm testing on Windows; about:support says that I've got a direct2d backend. I also tested with about:config pref "layers.acceleration.disabled" set to false, which gives me a cairo backend. I'm using Firefox 44.02.

In any case, I'll assume for the moment that you've got some hardware/configuration difference that makes this reproducible on your machine (discounting Markus's concerns in comment 19 for the moment).  It's great to hear that this isn't reproducible in Nightly -- what is your threshold for "reproducible" vs. "not reproducible", out of curiosity?
(And, just to confirm, your most recent "result.png" is meant to show that this is not-reproducible in Nightly, correct? I can't tell whether you're using Nightly or Release in that screenshot, but it certainly looks like the reconstruction has no relation to the reference there.)
Yeah I used Release in that screenshot.
I'm confused. Are you saying the screenshot demonstrates the problem still happening, or not?

In comment 23, you said 44.0.2 (aka Release) still reproduces the problem. But that screenshot (attachment 8722655 [details]) does not look like it shows any correlation between the reconstruction & the reference.
Yes, the screenshot demonstrates the problem still happening, that's what I meant.
(In reply to Khalil Zhani from comment #29)
> Yes, the screenshot demonstrates the problem still happening, that's what I
> meant.

Why/how? There seems to be no correlation between the left and right image.
Flags: needinfo?(chromium.khalil)
AS in (In reply to :Gijs Kruitbosch from comment #30)
> (In reply to Khalil Zhani from comment #29)
> > Yes, the screenshot demonstrates the problem still happening, that's what I
> > meant.
> 
> Why/how? There seems to be no correlation between the left and right image.

But SVG feComposite filter should not produce intermediate pixel color values outside of the normal 0-255 byte range as in you can in (attachment 8722655 [details]).
Flags: needinfo?(chromium.khalil)
What intermediate stage are you referring to? How does the picture show that the color values are not clamped at the stage at which you think they should be clamped?
Flags: needinfo?(chromium.khalil)
Note that the attached exploit uses "-webkit-filter" to apply the filter, which means the filter has zero effect in Firefox release (44.0.2).

So if your test is assuming the filter is applied, that is a faulty assumption (except in Nightlies for the past ~5 weeks, when bug 1236506 landed).  That might explain why your filtered output is outside the range that you expect...

Your copypasted testcase (as distinct from your attached testcase) used "-moz-filter", but we don't support that either, and we never have. We've only ever supported the unprefixed "filter" property itself.

So I think your testcase's dependence on prefixed filters (and its assumption that they do anything) might be the problem here. You may be getting random data, which you think is being generated by your filter, but it's actually not...
(In reply to Daniel Holbert [:dholbert] from comment #33)
> We've only ever supported the unprefixed "filter" property itself.

(and we've very recently started supporting "-webkit-filter" for compat reasons -- but in newer builds than the ones that you say are affected)
For demonstration purposes -- here's the original exploit you attached, with "-webkit-filter" (which has no effect at all in Firefox 44) replaced with "filter" (which *does* actually do something).

(I'm letting this testcase populate its reconstruction locally, to see if it produces anything non-random-looking.)
Attachment #8719367 - Attachment description: exploit.html → exploit.html (note that the -webkit-filter styles here have zero effect in Firefox release builds)
Here's what the reconstruction looks like in Release (44.02) & Nightly (47) after viewing comment 35's modified exploit for 20 minutes.

Release seems to have finished its reconstruction, and there's no clear correlation between the reconstruction and the reference.

Nightly seems to be much slower for some reason, and it may have just gotten stuck; I'm not sure.

In any case, it's not at all clear to me that there's anything actually leaking here, particularly given that the previously-provided testcases were using prefixed filter CSS that had literally no effect in supposedly-affected builds...
(In reply to Markus Stange [:mstange] from comment #32)
> What intermediate stage are you referring to? How does the picture show that
> the color values are not clamped at the stage at which you think they should
> be clamped?

The color values should not be outside of the normal 0-255 byte range.
Flags: needinfo?(chromium.khalil)
(Khalil, I'm curious on your responses to my Comment 33 - 37 -- in particular, in your trials where you believe you got results in Firefox 44, were you using a testcase that had a *vendor-prefixed* "filter" property, like the testcase you attached & the slightly-different testecase that you pasted into a comment here?

If so, it seems likely that any apparent successful results may have been statistical anomalies, particularly if you ran multiple trials & picked the best one.  Or alternately, if you really do somehow reliably get results with a testcase that uses prefixed filters, then it must not actually depend on filters at all, and the nature of this bug is different...)
Flags: needinfo?(chromium.khalil)
Comment 0 also seems to be copypasted directly from the Chromium bug
 https://bugs.chromium.org/p/chromium/issues/detail?id=586820

...including this part:
> I performed the exploit a couple of times and the results
> and analysis are attached in the attached image results.png

And the results.png image that Khalil attached here is identical to the one on the Chromium bug. So, it's unclear to me whether that is a screenshot of Firefox's results, or Chromium's results.  (Given the webkit stuff in the testcase, I'm tending to think it's a screenshot of Chromium's results.)

Khalil, have you actually produced any concerning results in Firefox?  You mentioned a clamping issue, and I still don't understand how your testcase demonstrates that there's a clamping bug -- but more importantly, I don't see how it has any connection to a privacy/data-leakage issue, since you haven't shown any evidence of useful reconstructions in Firefox. (and we haven't been able to produce them on our end either, with your testcase & modified versions of your testcase)
(In reply to Daniel Holbert [:dholbert] from comment #38)
> (Khalil, I'm curious on your responses to my Comment 33 - 37 -- in
> particular, in your trials where you believe you got results in Firefox 44,
> were you using a testcase that had a *vendor-prefixed* "filter" property,
> like the testcase you attached & the slightly-different testecase that you
> pasted into a comment here?
> 
> If so, it seems likely that any apparent successful results may have been
> statistical anomalies, particularly if you ran multiple trials & picked the
> best one.  Or alternately, if you really do somehow reliably get results
> with a testcase that uses prefixed filters, then it must not actually depend
> on filters at all, and the nature of this bug is different...)

Actually I used "-webkit-filter" property and I used also I used the testcase in (Comment 3), and I got the same result.
Flags: needinfo?(chromium.khalil)
OK. As noted in comment 33, neither "-webkit-filter" nor "-moz-filter" (what you had in comment 3) have any effect in Firefox 44.  So it seems that your testing never actually exercised Firefox's filter code at all.

Can you clarify whether you actually got any data for Firefox that was concerning? All signs seem to point to "no" right now.
(In reply to Daniel Holbert [:dholbert] from comment #41)
> OK. As noted in comment 33, neither "-webkit-filter" nor "-moz-filter" (what
> you had in comment 3) have any effect in Firefox 44.  So it seems that your
> testing never actually exercised Firefox's filter code at all.
> 
> Can you clarify whether you actually got any data for Firefox that was
> concerning? All signs seem to point to "no" right now.

Excuse me Daniel, what do you think about what I got and what you got in the screenshot ("Reconstruction")?
Maybe I'm misunderstanding, I don't see any correlation between the pixels in the reconstruction and the pixels in the reference image that it's trying to reconstruct.
(Your original screenshot here -- results.png -- is the only thing here that seems like compelling results, but I'm pretty sure that was taken using Chrome, as I noted in comment 39.)
Could you clarify what's concerning about the Firefox screenshots?
Flags: needinfo?(chromium.khalil)
I'm not sure if there is any correlation between the reconstruction & the reference. but I'm comfortable about what happens with the pixels in the reconstruction, I'm confused.
Flags: needinfo?(chromium.khalil)
(In reply to Khalil Zhani from comment #46)
> but I'm comfortable about what happens with the pixels in the
> reconstruction, I'm confused.

I assume that here, you meant to say "But if you're comfortable..." (i.e. you're confused about why we're comfortable)

Explaining why I don't understand what's concerning here, since it seems to not clear yet: I admit that I haven't fully read grokked every step that your testcase is doing, but as far as I understand, it does:
 (1) set some filter styles, which *actually have no effect* in Firefox per comment 33
 (2) measure the time that takes to flush layout.
 (3) repeat that a bunch to produce a sum of a bunch of time measurements.
 (4) Somehow from those measurements, decide either to draw a white pixel or a black pixel into the "reconstruction".

And this produces a random assortment of white & black pixels.

I don't understand everything between (3) and (4), but I'm confused about what's significant/concerning here. Asking one more time: could you please
[hit save too early] ...could you please explain where the bug / privacy vulnerability is here?
Flags: needinfo?(chromium.khalil)
(In reply to Daniel Holbert [:dholbert] from comment #48)
> [hit save too early] ...could you please explain where the bug / privacy
> vulnerability is here?

The SVG filter can be applied to an iframe, and by looking at how long it takes to apply the filter a large number of times, you can learn information about the contents of the iframe, even if it is from another origin.
Flags: needinfo?(chromium.khalil)
And as you can in my screenshot.png (my result) since it's based on timing, the results it's a bit noisy, and vary from machine to machine.
(In reply to Khalil Zhani from comment #50)
> And as you can in my screenshot.png (my result) since it's based on timing,
> the results it's a bit noisy, and vary from machine to machine.

Noise is just noise....

Sorry, dude...did you actually read my questions/comments?

I understand that that "measure filter timing to figure out the contents of an iframe" is the *theoretical* attack... but:
 - as noted in comment 33, your testcases here don't even apply a filter that Firefox understands, so it's impossible that you're measuring any filtering operations.
 - Even if you *were* applying a filter correctly, comment 19 indicates that you're not actually measuring the result of the filtering operation.
 - Even if you were doing everything right, we've already mitigated this class of attack earlier in bug 711043 & and as described in comment 14 here, as far as we know -- so we'd need to see compelling proof (something like your original "result.png" attachment which seems to actually be a screenshot of Chrome taken from someone else's bug) to demonstrate that firefox is still vulnerable to this sort of attack.

So at this point, I don't really know what you've been hoping to achieve with this bug, but it seems like you didn't actually test Firefox and don't actually understand what the testcase is doing (which isn't too surprising given that it's taken from a Chromium bug that someone else filed).  I appreciate you trying to contribute to Firefox by reporting a bug, but please make sure you're not wasting people's time.

If I'm wrong about any of this, please correct me, but it seems this bug is INVALID, and 100% safe to open because the Chrome bug (& testcase) is already public and it seems like there's no Firefox bug here.
Group: layout-core-security
Status: UNCONFIRMED → RESOLVED
Closed: 8 years ago
Resolution: --- → INVALID
Sorry Daniel. I didn't mean to wasting people's time, actually I was only trying to donate. I wasn't sure about if it hits Firefox. Sorry again and thanks for everything :)
OK. I do absolutely appreciate your intent to contribute, but keep in mind that we take security bugs very seriously, so it's counterproductive & wastes people's time if you copypaste a Chrome security bug into Mozilla's bug tracker (as you did here & in bug 1221681) without really knowing what's going on.

In the future, if you are concerned that Firefox is affected by a security bug from Chrome's bug tracker, do feel free to report it to us, BUT:

 (1) Please be *very clear* about when you're quoting someone else vs. when you're speaking for yourself. (Both in this bug and in bug 1221681, your initial comment included "I think..." or "I performed..." statements, which were actually statements from the original researcher -- not from you.  And it confused the issue a lot here -- e.g. we didn't realize your initial screenshot was bogus until 
comment 39.)

 (2) Please be clear, up-front, that you're simply re-reporting someone else's Chrome bug.  (Posting a link to the bug as an afterthought is a nice gesture, but it buries the attribution a bit.)

 (3) Please be clear about your confidence-level about whether Firefox is affected, and your level of understanding of the testcase.  (Your comment 0 here originally implied that both your confidence in Firefox being affected & your understanding of the testcase were strong, which was confusing and wasted time when we found reasons why the testcase shouldn't work in Firefox.)

 (4) Please make sure to answer questions clearly.

Thanks - I do hope you continue to contribute, but do keep the above guidelines in mind.
You need to log in before you can comment on or make changes to this bug.

Attachment

General

Creator:
Created:
Updated:
Size: