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.1

A navigation from a web origin to an extension resource will be blocked unless the resource is listed as web accessible. Note these corner cases:

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.html3, 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.95 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:

1

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.

2

web_accessible_resources before the fix, at commit ec90ae52:

"web_accessible_resources": [
  "skin/*",
  "icons/*"
]
3

popup.png

Figure 1: popup.html and the "Disable" button.

4

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.

5

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.