Closed
Bug 1345045
(CVE-2017-15417)
Opened 8 years ago
Closed 7 years ago
Canvas composite operations and CSS blend modes leak cross-origin data via timing attacks
Categories
(Core :: Graphics, defect, P3)
Tracking
()
People
(Reporter: permutatorem, Assigned: lsalzman)
References
Details
(4 keywords, Whiteboard: [gfx-noted][pixel-stealing])
Attachments
(4 files)
User Agent: Mozilla/5.0 (X11; Linux x86_64; rv:54.0) Gecko/20100101 Firefox/54.0
Build ID: 20170125183131
Steps to reproduce:
"Minimized" reproduction cases for the globalCompositeOperation attack and the mix-blend-mode attack are in the first and second attachments, respectively. Just open them up and check the console output (after waiting a while, that is, potentially a long while in the case of the second one). You can have the devtools open, but don't open or close them while the exploits are running. You may have to serve them from a local Web server to get them to work.
While developing the exploit, I used an interface I'd built to make it easier to see how well it was working. A cleaned-up version of this interface is in the third attachment. If the first two don't seem to work for you, I recommend trying the third. Have fun with it. I actually wrote the JavaScript for it in ES6 module format and bundled it using a tool called Rollup, so look in the fourth attachment for the individual source files, which should be easier to navigate.
I tried to minimize the reproduction cases as much as possible without sacrificing the code's clarity, but it's a timing attack, which makes it a rather high-level bug, so I figured you're probably not going to be running it under gdb or anything. I hope it's in a good state to analyze.
Timing is fickle, so the exploit may or may not work on your system. In particular, neither of my current implementations of the exploits work on my Windows machine. I doubt there are any platforms that can't be targeted by both exploits with the proper tuning, however.
Actual results:
Many of the operations supported by CanvasRenderingContext2D.globalCompositeOperation and the CSS mix-blend-mode property take varying lengths of time to complete depending on the contents of the source and destination images. This allows an attacker to infer the contents of a cross-origin image or even an arbitrary HTML element, such as an iframe.
An attacker may read the pixels of a cross-origin image by following these steps:
1. Render a single pixel from the image onto a canvas.
2. Manipulate the canvas so that it may be in one of two states depending on whether that pixel's color has certain properties.
3. Time how long it takes to render the contents of the canvas, scaled up, onto another canvas with a globalCompositeOperation chosen to maximize the difference in timing between those two states.
4. Based on whether the operation from step 3 completed quickly or slowly, it can be determined whether the pixel's color has the properties selected for in step 2.
5. Return to step 1.
Reading the pixels from an HTML element is slower and slightly more complex but, in my experience, no less reliable. This paper from 2013 details a similar exploit targeting SVG filters: <https://www.contextis.com//documents/2/Browser_Timing_Attacks.pdf>
The basic procedure when targeting mix-blend-mode is as follows:
1. By applying the feColorMatrix filter to an HTML element, select a single pixel from that element and turn it red or black depending on whether one of its channels has a value greater than a certain threshold.
2. By applying the feTile filter to the previous filter's result, blow up that single pixel of red or black to take up a large area of the screen.
3. Layer many rectangular elements with random background colors and `mix-blend-mode: saturation` on top of the filtered element.
4. Call requestAnimationFrame. When the browser is finished rendering the changes that have just been made, it will call a callback, which will be able to determine how long the render took.
5. Based on whether the render completed quickly or slowly, it can be determined whether the selected channel of the selected pixel is greater than the selected threshold.
6. Return to step 1.
Expected results:
A Web page should not be able to infer cross-origin data.
See <https://drafts.fxtf.org/compositing-1/#security>. If browsers just followed the rule mentioned there, they wouldn't be vulnerable to this attack.
I think the fact that none of the browsers I've tested heed that section's warning suggests that browsers need to change their approach to mitigating timing attacks. There's a lot resting on the assumption that the pixels shown to the user aren't accessible to an attacker, but that assumption has been violated a number of times over the past four years by very similar attacks, at least on Chrome.
The solution in the past has been to modify the algorithms in question so that their timing appears to be independent of input, but as Paul Stone, the reporter of the 2013 exploit I linked to previously, wrote in a Bugzilla comment <https://bugzilla.mozilla.org/show_bug.cgi?id=711043#c5>:
> Beyond the source code, there's also things like compiler optimisations and the CPU cache that could affect timings in non-obvious ways.
An example of an attack that targeted differences in timing on the CPU level can be found here: <https://bugs.chromium.org/p/chromium/issues/detail?id=615851>
That attack was also patched, but there isn't much one can do to be certain that there aren't others like it still open, because at the end of the day, the C++ specification simply doesn't make any guarantees about the exact timing properties of a given function. Browsers are putting their carefully specified cross-origin security restrictions squarely in the hands of unspecified underlying behavior. And that's to say nothing of exploits like the one this bug report is actually about—looking at the affected Skia code, it doesn't seem to have even been considered as a potential attack vector.
Timing attacks are a sticky and unpleasant class of bug, which makes it important to have a structured approach to dealing with them. If there's a protocol and an infrastructure for them, it will be easier to keep them in mind as a risk when developing new features.
Timing attacks aren't new, and there's a fair amount of research into preventative measures. Rather than modifying the algorithm to appear to take a consistent amount of time, which is difficult and unreliable, these measures tend to involve padding the sensitive operations by sleeping after performing them. (This has the additional benefits of saving battery power and freeing up system resources, two things which as far as I know aren't viable side-channels in the case of browsers.)
In order for this strategy to work, an upper bound on time taken must be known—that's the hard part. However, by their performance-sensitive nature, rendering operations have the advantage of not taking potentially unbounded time based on sensitive information, which makes completely covering up those timing differences a bit less impossible.
The key is to understand that it's perfectly safe for the time taken to vary based on information the page's JavaScript already has access to. As a simple heuristic for calculating an upper bound on time, the renderer may time how long it takes to render a single pixel using the chosen filter/composite operation/etc, choosing a color that it expects to represent the worst case for that operation (possibly repeating the process with multiple colors and choosing the one that takes the longest), then multiply the time taken by the number of pixels being rendered.
A page's JavaScript is also, as far as I know, capable of determining which rendering operations involve data it doesn't have access to. An intelligent renderer could therefore distinguish between sensitive and non-sensitive rendering operations and pad out only the sensitive ones. Since these will likely make up a rather small subset of all rendering operations, and since Web pages will be perfectly capable of avoiding expensive operations on sensitive data if they know to do so, the impact on browser performance can be kept quite low.
The strategy I've just described still isn't guaranteed to work by any specification, of course, and still isn't a sure thing. But it would be a big step up from the way these sorts of vulnerabilities are currently handled.
That's my opinion on the matter of how best to address this bug, if you care for it, and I hope I'm not out of line. My SAT date is coming up, and this has been a rather draining project, so I probably won't be working on the PoC any more this week. I'll try to take my part in any discussion that may come up during that time, however.
For the record, the console output for globalCompositeOperation.html is supposed to look like this:
> The pixel at (10, 10) does not have a r value greater than 0.5.
> The pixel at (14, 14) does have a r value greater than 0.5.
And the console output for mix-blend-mode.html is supposed to look like this:
> The pixel at (1, 1) does have a r value greater than 0.99.
> The pixel at (1, 100) does not have a r value greater than 0.99.
I probably should have made those things more obvious. mix-blend-mode.html loads Wikipedia in an iframe, so if it doesn't work, it may just be because that iframe isn't loading.
Comment 6•8 years ago
|
||
mstange (or heycam), you've worked on these sorts of timing attacks in the past -- would you mind taking a look?
Group: firefox-core-security → core-security
Component: Untriaged → Graphics
Flags: needinfo?(mstange)
Product: Firefox → Core
Summary: Canvas composite operations and CSS blend modes leak cross-origin data via timing attacks. → Canvas composite operations and CSS blend modes leak cross-origin data via timing attacks
Comment 7•8 years ago
|
||
On platforms that are not Windows with acceleration, we use Skia for software graphics rendering. This bug is reported for Linux, and I can also reproduce the results on Mac.
So in order to fix these bugs (and make the pixel operations take time that's independent of the pixel color), we need to make changes to Skia's code. The Skia code is shared with Chrome. So Chrome should have these same bugs, and their fixes should also fix the bug in Firefox.
Max, did you file corresponding Chrome bugs for these two bugs?
Flags: needinfo?(mstange) → needinfo?(permutatorem)
Updated•8 years ago
|
Status: UNCONFIRMED → NEW
Ever confirmed: true
(In reply to Markus Stange [:mstange] from comment #7)
> On platforms that are not Windows with acceleration, we use Skia for
> software graphics rendering. This bug is reported for Linux, and I can also
> reproduce the results on Mac.
> So in order to fix these bugs (and make the pixel operations take time
> that's independent of the pixel color), we need to make changes to Skia's
> code. The Skia code is shared with Chrome. So Chrome should have these same
> bugs, and their fixes should also fix the bug in Firefox.
>
> Max, did you file corresponding Chrome bugs for these two bugs?
Yes, I filed one bug report there sharing most of the text of this one. I viewed it as one bug because the two exploits targeted the same code, but I see you're taking them as separate bugs.
Flags: needinfo?(permutatorem)
Comment 9•8 years ago
|
||
(In reply to Max May from comment #8)
> I viewed it as one bug because the two exploits targeted the same code, but I
> see you're taking them as separate bugs.
Ah, if it's really the same code, then I'm also viewing them as one bug. I wasn't sure that it was.
Could you let us know the URL of the Chrome bug? Even if we don't have access to it, it would be good to have something to reference if we contact the Skia team.
Reporter | ||
Comment 10•8 years ago
|
||
(In reply to Markus Stange [:mstange] from comment #9)
> Could you let us know the URL of the Chrome bug? Even if we don't have
> access to it, it would be good to have something to reference if we contact
> the Skia team.
<https://bugs.chromium.org/p/chromium/issues/detail?id=699028>
Updated•8 years ago
|
Updated•8 years ago
|
Group: core-security → gfx-core-security
Updated•8 years ago
|
status-firefox52:
--- → ?
status-firefox53:
--- → ?
status-firefox54:
--- → affected
status-firefox55:
--- → affected
status-firefox-esr45:
--- → ?
status-firefox-esr52:
--- → ?
Keywords: sec-high
Updated•8 years ago
|
Flags: sec-bounty?
Comment 11•8 years ago
|
||
Lee, can you check with your Skia contacts what the status of this bug is?
Flags: needinfo?(lsalzman)
Assignee | ||
Comment 12•8 years ago
|
||
(In reply to Markus Stange [:mstange] from comment #11)
> Lee, can you check with your Skia contacts what the status of this bug is?
Markus, you should have access to the chromium bug there now if you log into it with your mozilla email.
Flags: needinfo?(lsalzman)
Comment 13•8 years ago
|
||
Not much has happened over at the chromium bug. Their current conclusion seems to be that only software canvas is worth fixing (though it's not clear at which level), and that async painting should be sufficient to avoid the mix-blend-mode content rendering leak.
Comment 15•8 years ago
|
||
No changes since March 23rd, except for an automated comment on April 7th by a bot that frequently pokes open security bugs.
Flags: needinfo?(mstange)
Comment 16•8 years ago
|
||
The chromium bug was just assigned to hcm@chromium.org with the words
> Heather is planning to start discussions on this.
Comment 17•8 years ago
|
||
There was one interesting update on May 15th on the Chromium bug report:
Comment 41 by mtklein@chromium.org, May 15:
> For what it's worth, I've landed some new implementations of these blend modes
> that do not branch based on pixel values. They don't, however, do anything to
> protect against other data-dependent timing attacks like denorm values.
>
> The relevant code starts right around here:
> https://cs.chromium.org/chromium/src/third_party/skia/src/jumper/SkJumper_stages.cpp?rcl=9018952290a468886c819405c6d9495b4aa5d7d4&l=416
>
> If you like we could explore having Chromium prefer this new code path for
> these modes. The new code is primarily designed for color-correct drawing but
> is able to draw into legacy non-color-correct kN32_SkColorType buffers too.
> It'd draw somewhat differently (usually better) and at a different speed
> (probably slower, maybe faster... it's really machine dependent).
And there was some more discussion about whether this should be addressed at the browser level or in Skia. The last comment from that discussion was
Comment 39 by permutat...@gmail.com, May 2:
> I definitely think this vulnerability should be addressed at the browser level,
> but if it's done in a manner similar to the one I suggested (sleeping up to a
> supposed worst-case time based on a heuristic), it may be better implemented
> as a Skia feature that Blink turns on or off based on whether it's dealing with
> cross-origin data. This brings the heuristic closer to the algorithm whose
> performance it approximates, and it allows other security-sensitive
> applications using Skia (such as Firefox!) to benefit from the fix as well.
Comment 18•8 years ago
|
||
There have been no updates in the Chrome bug since May 24th.
Comment 19•8 years ago
|
||
Since the necessary Skia work has landed, I think we could start working on this bug and update Skia.
Here are the most important updates in the Chrome bug since the last update:
Comment 51 by fmalita@chromium.org, Jul 11
> We're in the process of switching Chrome to the branchless Skia impl for
> everything except Src & SrcOver (https://skia-review.googlesource.com/c/21372/).
>
> I can take the bug while we work on that.
Comment 52 by fmalita@chromium.org, Jul 11
> I was too optimistic about that change: we're holding it off for now
> due to perf regression concerns. Since it wouldn't necessarily close
> this attack vector anyway, it's prolly better to decouple from the bug.
People also realized that the updates from the Chrome bug have been forwarded to a public mailing list: https://groups.google.com/a/chromium.org/d/msg/paint-bugs/RIjWpTd_tf0/E6KWLm1zEAAJ
Comment 20•8 years ago
|
||
(In reply to Markus Stange [:mstange] from comment #19)
> Comment 51 by fmalita@chromium.org, Jul 11
> > We're in the process of switching Chrome to the branchless Skia impl for
> > everything except Src & SrcOver (https://skia-review.googlesource.com/c/21372/).
Actually, since the patch mentioned here is for Skia and not for Chrome, it looks like there is still work left to do on the Skia side.
Comment 21•8 years ago
|
||
Are we still waiting for Skia folks, Markus?
If not, is this something you can take, updating our code?
Flags: needinfo?(mstange)
Comment 22•8 years ago
|
||
From what I understood, Skia now offers branchless implementations for most blend modes.
But Chrome hasn't started switching to these implementations, because they're concerned about performance regressions.
Lee, do you have bandwidth to investigate switching us over to the branchless implementations? See comment 17 for a link to the relevant code.
Flags: needinfo?(mstange)
Assignee | ||
Comment 23•8 years ago
|
||
(In reply to Markus Stange [:mstange] from comment #22)
> From what I understood, Skia now offers branchless implementations for most
> blend modes.
> But Chrome hasn't started switching to these implementations, because
> they're concerned about performance regressions.
>
> Lee, do you have bandwidth to investigate switching us over to the
> branchless implementations? See comment 17 for a link to the relevant code.
A Skia update is somewhat of a major undertaking and the earliest I'd be able to get to it is October, and that in and of itself can take anywhere from a couple weeks to a month or more. We need to update Skia eventually, but it's not going to be in time for 57, maybe not in time for 58 either, unless it is a major enough priority to subsume the other work on WebRender I am trying to get done right now.
Updated•8 years ago
|
Whiteboard: [gfx-noted]
Updated•8 years ago
|
Keywords: csectype-sop,
testcase
Comment 24•8 years ago
|
||
Hi Milan:
I have assigned these security bugs to you to reassign them to appropriate developers in your team to investigate and fix them.
Thanks!
Wennie
Assignee: nobody → milan
Skia update in 59 is a possibility.
We can see what this is like with the latest Skia once we update. I don't expect any action on it otherwise.
Assignee: milan → lsalzman
Comment 27•8 years ago
|
||
The Chrome bug has now been closed as fixed:
> Comment 68 by chrishtr@chromium.org, Oct 31
>
> Update: the Skia team moved 'saturation' to the raster pipeline a while
ago:
>
> https://skia-review.googlesource.com/c/skia/+/17992
>
> which I believe means the particular exploit in this bug is likely fixed.
> A local test by fmalita@ last week seemed to confirm that.
So updating Skia is probably all we need to do here.
Updated•7 years ago
|
Priority: -- → P3
Comment 28•7 years ago
|
||
Is there any plan to update Skia in a near future? Is it supposed to be tracked by bug 1210886?
Flags: needinfo?(lsalzman)
Assignee | ||
Comment 29•7 years ago
|
||
(In reply to Stephanie Ouillon [:arroway] from comment #28)
> Is there any plan to update Skia in a near future? Is it supposed to be
> tracked by bug 1210886?
Eventually, but we have been so busy with WebRender work right now that Skia updating has to wait for a bit.
Flags: needinfo?(lsalzman)
Reporter | ||
Comment 30•7 years ago
|
||
The Chromium project has gone public with their version of the bug: <https://bugs.chromium.org/p/chromium/issues/detail?id=699028>
This seems like a dangerous situation!
Comment 31•7 years ago
|
||
Milan, what's the status of the Skia update? It looks like this sec-high bug is now public.
Flags: needinfo?(milan)
Good chance that it will happen in 60, but there are still test failures that we have to fix or explain.
Flags: needinfo?(milan)
Comment 33•7 years ago
|
||
(In reply to Milan Sreckovic [:milan] from comment #32)
> Good chance that it will happen in 60, but there are still test failures
> that we have to fix or explain.
Are there individual bugs, so we all get visibility in those blockers?
Flags: needinfo?(milan)
Update Skia is in 60 now. Whoever can reproduce this can retest, but if the fixes are in Chrome, we should have it as well.
Flags: needinfo?(milan)
Comment 35•7 years ago
|
||
(Specifically: it looks like the skia update happened in bug 1444506, which was indeed uplifted to 60 (beta) last week.)
Assignee | ||
Comment 36•7 years ago
|
||
The blend modes in Skia as of milestone 66 (bug 1444506) are now implemented with conditional moves, instead of branching, so that ideally the blend mode implementation should be closer to fixed timings. Or at least the impact of branching within these should be nullified, so that if there actually was still variability, it would be coming from within the hardware instructions themselves.
Updated•7 years ago
|
Whiteboard: [gfx-noted] → [gfx-noted][pixel-stealing]
Comment 37•7 years ago
|
||
Max, we think this might be fixed. Would you be willing to retest on Firefox 60?
Firefox 60 is currently in beta but will become the "normal" released Firefox version in a few hours.
Flags: needinfo?(permutatorem)
Reporter | ||
Comment 38•7 years ago
|
||
It doesn't look like Firefox 60 has hit the Arch repositories yet, so I still have 59 installed.
My PoC still works on 59, but I'm not getting any recognizable results when I try it on the latest Nightly (62.0a1, 2018-05-09). In other words, I can no longer reproduce the bug in Nightly.
I'll try again with 60 when it gets to the Arch repositories.
Flags: needinfo?(permutatorem)
Updated•7 years ago
|
status-firefox52:
? → ---
status-firefox53:
? → ---
status-firefox54:
affected → ---
status-firefox60:
--- → fixed
status-firefox-esr45:
? → ---
status-firefox-esr60:
--- → fixed
Reporter | ||
Comment 39•7 years ago
|
||
I'm sorry it took me so long to test the PoC on the release version. I've tried it in 61, and again nothing I try produces recognizable results, so the leak seems to be fixed.
I'll also note that it runs far faster than it did when I was developing the exploit back in 2017, which is the opposite of what I expected—nice!
Comment 40•7 years ago
|
||
Let's mark this as fixed
Status: NEW → RESOLVED
Closed: 7 years ago
Resolution: --- → FIXED
Updated•7 years ago
|
Updated•7 years ago
|
Flags: sec-bounty? → sec-bounty+
Comment 41•7 years ago
|
||
Since this was fixed upstream in Skia we should use the Google CVE CVE-2017-15417
Alias: CVE-2017-15417
Updated•7 years ago
|
Group: gfx-core-security → core-security-release
Updated•7 years ago
|
Group: core-security-release
Updated•1 year ago
|
Keywords: reporter-external
You need to log in
before you can comment on or make changes to this bug.
Description
•