New issue
Advanced search Search tips
Note: Color blocks (like or ) mean that a user may not be available. Tooltip shows the reason.

Complicated CSS effects and :visited selector leak browser history through paint timing

Reported by mds...@gmail.com, Apr 21 2018

Issue description

VULNERABILITY DETAILS

An attacker can combine complicated CSS effects and the :visited selector to
reliably determine the visited status of arbitrary URLs, by forcing the browser
to complete expensive repaint operations if a test URL is visited, and comparing
performance measurements with those taken for a known-unvisited "control" URL.

(1) The attacker constructs an element that is complicated to render using CSS
    effects.

In the attached exploit, we use the following inline style (crafted through
manual trial and error) to produce a link element that is costly to paint:

    display: inline-block;
    width: 5px; font-size: 2px; text-align: center;
    outline-width: 24px; text-shadow: 16px 16px 10px #fefffe;
    filter: contrast(200%) drop-shadow(16px 16px 10px #fefefe) saturate(200%);
    transform: perspective(100px) rotateY(37deg);

To further increase the burden placed on the browser's rendering engine, the
element is filled with a long string of Chinese characters. The exact
configuration is less important than the overall idea that the element is made
costly to draw by stacking on these challenges.

(2) The attacker forces the browser to repeatedly repaint the element if the
    test URL is visited.

We use the CSS :visited selector to apply different color styles to the link
element if it points to a visited URL. For example:

    a { color: white; background-color: white; outline-color: white; }
    a:visited { color: #feffff; background-color: #fffeff;
                outline-color: #fffffe; }

In the attached attack, we use the DOM to oscillate the link element's href
attribute between the test URL and a randomly-generated known-unvisited URL,
forcing a repaint with each oscillation if the test URL is visited.

An attacker could alternatively point the link to the test URL, then rotate the
colors of the CSS rules under :visited through a set of different values. This
similarly forces the browser to repeatedly repaint the element if the test uRL
is visited.

(3) The attacker measures paint performance during the process in step (2) and
    compares it with measurements taken for a known-unvisited control URL.

We measure the frequency of `requestAnimationFrame` callback executions over
time. The forced repaints cause a drop in this measurement if the test URL is
visited, relative to one collected for the control URL; thus we can make
a visited vs unvisited determination.

Experimentally, 70-80% fewer `requestAnimationFrame` callbacks fire in the case
of a visited test URL vs the control URL, with the effect observable down to
100ms measurement periods (producing measurements on the order of 4 vs 15). An
attacker using an intelligent search strategy could rapidly extract
visited/unvisited status for each of a set of test URLs.

VERSION (tested on 2 systems)

Chrome Version: 66.0.3359.117 stable
Operating System: Windows 10 Pro Version 1709 (OS Build 16299.371)

Chrome Version: 65.0.3325.181 stable
Operating System: macOS 10.10.5

REPRODUCTION CASE

`attack.html` demonstrates inferring visited status of arbitrary URLs through
the technique described above. If it does not seem to work, ensure that your
Chrome installation can properly display Chinese characters.
 
attack.html
8.7 KB View Download

Comment 1 by vakh@chromium.org, Apr 22 2018

Components: UI>Browser>History Blink>DOM Privacy
Labels: Security_Severity-Low Security_Impact-Stable OS-Chrome OS-Linux OS-Mac OS-Windows
Owner: msramek@chromium.org
Status: Assigned (was: Unconfirmed)
This looks similar to issue 712246 and issue 713521.

Setting Security_Severity-Low since this looks more like a Privacy issue, than a security issue.

msramek@ -- assigning to you for now to make a call on whether this is materially different than the linked issues and for further triage.

Comment 2 by mds...@gmail.com, Apr 23 2018

This is entirely different from 712246, which tricks the user into revealing visited status by clicking on images that are hidden or not depending on their browsing history. The attack presented here requires no user interaction and can be performed in bulk.

And as with 835589, I would have thought this would be considered medium severity at least.

https://chromium.googlesource.com/chromium/src/+/master/docs/security/severity-guidelines.md

"Example Bugs: ... A bug that allows an attacker to reliably read or infer browsing history (381808)."
Cc: palmer@chromium.org jochen@chromium.org mkwst@chromium.org rbyers@chromium.org tnagel@chromium.org
+palmer@ FYI, since you've been looking into timing attacks
+tnagel@ FYI
+rbyers@, +mkwst@, jochen@: This bug serves as more evidence to what is discussed in issue 713521.

We could theoretically fix this by *always* processing the :visited computations, and only at the end, before assigning a color to the element, checking if it was actually visited. But I'd be interested to again gauge the support in the OWP community to make :visited origin-scoped.
Project Member

Comment 4 by sheriffbot@chromium.org, Apr 23 2018

Labels: Pri-2
Any opinions on the above? Running both :visited and :not(:visited) branches to achieve constant time computation?

I assume that the computation is done once per CSS rule, not once per each occurrence of <a>. So a regular site with a single link style would only incur a small performance loss.

The only websites which this would hurt would be the ones using a different style for each link, which are likely the ones trying to sniff history. And even there, the worst case performance would only worsen by a factor of 2.

Comment 6 by mds...@gmail.com, May 23 2018

A variant of this attack is possible with a complex SVG image inside the link instead of placing CSS transforms on the link itself. The signal is again observable down to 100ms measurement periods, and is even stronger than the CSS variant over higher durations. A demo file is attached (most of the 2 MB size is the embedded SVG image).
attack.html
2.1 MB View Download

Comment 7 by palmer@chromium.org, May 30 2018

Labels: -Security_Severity-Low Security_Severity-Medium
Note that the Severity Guidelines call out history leaking as Medium:

https://chromium.googlesource.com/chromium/src/+/master/docs/security/severity-guidelines.md#Medium-severity
Project Member

Comment 8 by sheriffbot@chromium.org, May 31 2018

msramek: Uh oh! This issue still open and hasn't been updated in the last 28 days. This is a serious vulnerability, and we want to ensure that there's progress. Could you please leave an update with the current status and any potential blockers?

If you're not the right owner for this issue, could you please remove yourself as soon as possible or help us find the right one?

If the issue is fixed or you can't reproduce it, please close the bug. If you've started working on a fix, please set the status to Started.

Thanks for your time! To disable nags, add the Disable-Nags label.

For more details visit https://www.chromium.org/issue-tracking/autotriage - Your friendly Sheriffbot
Project Member

Comment 9 by sheriffbot@chromium.org, May 31 2018

Labels: M-67
Project Member

Comment 10 by sheriffbot@chromium.org, May 31 2018

Labels: -Pri-2 Pri-1
Cc: tdres...@chromium.org
+tdresser@ PTAL at comments #3, #5
Cc: ojan@chromium.org
"I assume that the computation is done once per CSS rule, not once per each occurrence of <a>. So a regular site with a single link style would only incur a small performance loss."

I don't think this is necessarily true. We need to do all the work associated with painting the link. That includes rendering complex filters etc, which need to be done on a per-link basis, right? Those cases should be quite rare though. 

If performance is a big issue, maybe we could even have a heuristic to identify if there's expensive logic associated with displaying a visited link, and draw things normally if there isn't, and draw things twice if there is? 

I know Ojan has claimed in the past that anywhere there's a leak with :visited, there's probably also a leak in other contexts. It feels to me like rAF should be usable to determine if a user is logged into a site embedded in a cross origin iframe (although potentially not in the context of OOPIF).
Project Member

Comment 13 by sheriffbot@chromium.org, Jul 4

msramek: Uh oh! This issue still open and hasn't been updated in the last 14 days. This is a serious vulnerability, and we want to ensure that there's progress. Could you please leave an update with the current status and any potential blockers?

If you're not the right owner for this issue, could you please remove yourself as soon as possible or help us find the right one?

If the issue is fixed or you can't reproduce it, please close the bug. If you've started working on a fix, please set the status to Started.

Thanks for your time! To disable nags, add the Disable-Nags label.

For more details visit https://www.chromium.org/issue-tracking/autotriage - Your friendly Sheriffbot
Components: -Blink>DOM Blink>CSS
Project Member

Comment 15 by sheriffbot@chromium.org, Jul 25

Labels: -M-67 Target-68 M-68
Project Member

Comment 16 by sheriffbot@chromium.org, Sep 5

Labels: -M-68 M-69 Target-69
Project Member

Comment 17 by sheriffbot@chromium.org, Oct 17

Labels: -M-69 Target-70 M-70
Labels: OS-Android OS-Fuchsia
Any reason that Android and Fuchsia would not be affected?
Nope, they should be.
Now that Spectre is in our threat-model, I think we should be planning for a solution for this which completely closes this hole (not just makes it harder). Whether it's directed visitedness (my personal preference), or elimination of :visisted entirely (eg. replacing the use cases with some sort of browser-level UI) I believe it will be possible to make a compelling argument for fundamentally changing the web here within the framework of https://bit.ly/blink-compat.

Of course, it may make sense to do some shorter-term mitigations - but the cost/benefit tradeoff there will depend on the longer term plan.
FWIW, I agree that spectre changes our threat model and negates what I said above about this problem.

My preference is that we at least do an initial exploration browser-level UI and treat directed visitedness as a fallback if that doesn't pan out. In particular, I think browser level UI would open up the door for a whole class of link annotations aside from visited that I'd really like to see us experiment with (e.g. annotating links to pages that use a lot of data).
Issue 904239 has been merged into this issue.
Cc: talo@chromium.org bengr@chromium.org aposner@chromium.org
We've considered annotating links with the expected page size a long time ago, but only recently developed the infrastructure (go/cacao) to provide annotation details to Chrome. If adding annotations is high priority for anyone, let me know.
Cc: pdr@chromium.org reed@google.com mtklein@chromium.org futhark@chromium.org chrishtr@chromium.org schenney@chromium.org fmalita@chromium.org
 Issue 891712  has been merged into this issue.
Project Member

Comment 26 by sheriffbot@chromium.org, Dec 5

Labels: -M-70 Target-71 M-71
Labels: reward-ineligible
(reward-ineligible as this issue has been published https://www.usenix.org/system/files/conference/woot18/woot18-paper-smith.pdf)
Labels: -Restrict-View-SecurityTeam allpublic

Sign in to add a comment