Clickjacking Chrome Extensions
Some webpages embed resources from other domains. Because of the web
browser's storied and sordid history, it's possible for those
resources to carry invisible, ambient authority without their
creators' knowledge. One concrete example is Cross-Site Request
Forgery: the JavaScript on a particular webpage can make an
XMLHttpRequest
to a resource on another domain and the browser will
(in some circumstances) transmit its cookie along with that
XMLHttpRequest
. This allows an attacker's website to act with all of
the user's authority on the second domain.
Another example is "clickjacking": by visually integrating an embedded resource into a webpage, for example by creating an iframe and styling it to be mostly transparent, you can trick a user to click on a button or link in the embedded document. Because embedded iframes carry ambient authority from their domains, clicking a button in a transparent iframe is the same as clicking a button on the original domain. In this way an attacker can trick a victim into visiting a webpage and performing some behavior on a webpage on an unrelated domain that they ordinarily wouldn't.
With ordinary webpages the mitigation for clickjacking is to include
headers like X-Frame-Options
or some Content-Security-Policy
settings in the response from the server. Last year Blake Griffith and
I realized that clickjacking Chrome extensions was possible by
discovering a vulnerability in PrivacyBadger, a privacy-preserving
browser extension published by the EFF for Chrome and Firefox.
Chrome extensions are compressed collections of files. One of those is
a manifest.json
; that JSON file has a field
web_accessible_resources
. Here's what the Chrome docs say about it:
These resources would then be available in a webpage via the URL
chrome-extension://[PACKAGE ID]/[PATH]
, which can be generated with the extension.getURL method. Allowlisted resources are served with appropriate CORS headers, so they're available via mechanisms like XHR.1A navigation from a web origin to an extension resource will be blocked unless the resource is listed as web accessible. Note these corner cases:
- When an extension uses the webRequest or declarativeWebRequest APIs to redirect a public resource request to a resource that is not web accessible, such request is also blocked.
- The above holds true even if the resource that is not web accessible is owned by the redirecting extension.
In addition to being web accessible, the resources in the
web_accessible_resources
run with the ambient authority of the
extension: they can alter state, load other resources, and modify the
browser in certain ways. If a document in web_accessible_resources
can perform any interesting behavior, an attacker can embed it in a
webpage and trick visitors into triggering it.
In this case, in PrivacyBadger, the contents of the directory skin/
were web_accessible_resources
.2 By loading
skin/popup.html
3, the document that gets rendered when you
click the the PrivacyBadger icon in the browser, in an iframe we could
fool the user into clicking "Disable PrivacyBadger for this Website",
opening up the user to additional tracking and undermining the
function of PrivacyBadger.4
A video demonstrating the transparent iframe overlaid over an attacker-controlled website, fading in and out. (In a real-life scenario an attacker could make it totally transparent, i.e. invisible).
The fix is easy: remove /skin/*
from the
web_accessible_resources
. Blake wrote a PR for that and it was
merged on June 5th, a day after we reported the issue.
The proof-of-concept Blake and I wrote is as follows. It will work on
release 2017.5.9
5 or earlier.
<style> iframe { width: 430px; height: 300px; opacity: 0.01; float: top; position: absolute; } #stuff { float: top; position: absolute; } button { float: top; position: absolute; top: 168px; left: 100px; } </style> <div id="stuff"> <h1> Click the button </h1> <button id="button"> click me </button> </div> <iframe src="chrome-extension://ablpimhddhnaldgkfbpafchflffallca/skin/popup.html"> </iframe>
Footnotes:
A CSRF-like attack isn't very interesting here, since the
behavior we'd like to trigger will almost always require running a
particular piece of JavaScript in the extension context. But a
CSRF-like attack has been used to fingerprint browsers based on the
web_accessible_resources
of the collection of extensions they have
installed.
web_accessible_resources
before the fix, at commit ec90ae52
:
"web_accessible_resources": [ "skin/*", "icons/*" ]
Figure 1: popup.html
and the "Disable" button.
An attacker could also e.g. add a long list of sites to
the whitelist through skin/options.html
; this is only the simplest
example, but as far as we could tell, disabling PrivacyBadger's
functionality is the most serious thing an attacker could do.
You can install it by following
PrivacyBadger's
developer installation instructions if you'd like to reproduce our
work. You may need to edit the chrome-extension://
URL in the
proof-of-concept to match the installed version.