Cross-origin read SOP violation by extension via search provider
Categories
(Firefox :: Search, defect, P3)
Tracking
()
Tracking | Status | |
---|---|---|
firefox87 | --- | fixed |
People
(Reporter: arminius, Assigned: standard8)
References
Details
(Keywords: csectype-other, reporter-external, sec-low, Whiteboard: [reporter-external] [client-bounty-form] [verif?][post-critsmash-triage][adv-main87+])
Attachments
(4 files)
A malicious extension can read cross-origin resources by installing a specially crafted search provider and using the browser.search
API.
This happens as the search engine manager fetches any absolute favicon URL, and converts the content to a data:
URI which is made available to extension code, regardless of the resource's actual content type (e.g. text/html).
Proof of concept
- Create an extension which adds a search engine via the
chrome_settings_overrides.search_provider
manifest key. Setfavicon_url
to the web resource that should be read cross-origin. The target's content-type is irrelevant. - Use
browser.search.get()
to retrieve the installed search engine. - Base64-decode the engine's
favIconUrl
data:
URI payload.
Analysis
The manifest format for the favicon URL, relativeUrl
, permits absolute URLs. In parent/ext-search.js#34-41
, only extension-relative icons are converted to data:
URIs (engine.iconURI.schemeIs("moz-extension") && engine.iconURI.host !== context.extension.uuid
). However, icon conversion is also done earlier in SearchEngine.jsm#766-813
without checking origin or content type. Hence, when ext-search.js
queries the search service, the icons are already converted to data:
URIs.
Security impact
The search
permission does not show up in the addon details ("This extension doesn’t require any permissions.") or during installation, allowing an attack to go unnoticed. But while any http[s] URL can be targeted, cookies aren't transmitted. This makes the attack most useful against e.g. local interfaces/intranet resources, but doesn't allow an attacker to trivially tap the user's current web mail session.
Reporter | ||
Comment 1•4 years ago
|
||
This proof-of-concept extension fetches and displays the source of https://example.com/
in a new tab upon startup. I attached it as a signed XPI.
manifest.json
:
{
"manifest_version": 2,
"name": "searchprovider sop leak",
"version": "2.0",
"background": { "scripts": [ "background.js" ] },
"permissions": [ "search" ],
"chrome_settings_overrides": {
"search_provider": {
"name": "mysearch",
"search_url": "https://example.com/$1",
"favicon_url": "https://example.com/"
}
}
}
background.js
:
browser.search.get().then((res) => {
let provider = res.find(x => x.name === "mysearch")
let source = atob(provider.favIconUrl.split(',')[1]);
let url = URL.createObjectURL(new Blob([source], {type : 'text/plain'}));
browser.tabs.create({url: url})
})
Updated•4 years ago
|
Comment 2•4 years ago
|
||
Thanks for the report and PoC. This bug offers the following capabilities:
- An extension can read the response of one GET request, without control over when the request is sent.
- No control over request headers, no cookies/credentials are sent in particular.
- Response bodies exceeding 20000 (
SearchUtils.MAX_ICON_SIZE
) cannot be read. - The URL has to be hardcoded in a
manifest.json
file in a signed extension, but as redirects are followed, any http(s) URL can be read.
This is not a bug with the implementation in the extension framework, but with the search engine implementation. The resolved URL should be a valid image URL before it is stored.
Assignee | ||
Comment 3•4 years ago
|
||
I've been looking into this a bit and discussing with Johann.
We should change the search service to accept content types starting with "image/" only. The fallback would have probably been accounting for images with incorrect content types being served. This seems to be largely unnecessary now, and I just checked what appear to be the most popular servers for OpenSearch and they are either providing data uris, or urls to images which are served correctly.
That doesn't do enough to fix the cross-origin side of the issue. Whilst we can assume websites don't declare sensitive resources as favicons, I think there is still potential for data to be encoded in images for a site controlled by the add-on author.
We think that there's a few choices here:
- Remove the favicon from the
browser.search.get
API (though this may not be popular with extension authors). - Require some sort of additional permission.
- Have the search service save the original URL as well as the data URL. Presumably the API could do cross-origin checks on the original URL, but still serve the data URL to the add-on. (Note: serving the data url, means we wouldn't be pinging the server, which is better for the user's privacy).
I think these last points really need input from the WebExtensions team. Rob, can you look at this, or redirect to whoever's appropriate?
Comment 4•4 years ago
|
||
(In reply to Mark Banner (:standard8) from comment #3)
That doesn't do enough to fix the cross-origin side of the issue. Whilst we can assume websites don't declare sensitive resources as favicons, I think there is still potential for data to be encoded in images for a site controlled by the add-on author.
We think that there's a few choices here:
- Remove the favicon from the
browser.search.get
API (though this may not be popular with extension authors).- Require some sort of additional permission.
- Have the search service save the original URL as well as the data URL. Presumably the API could do cross-origin checks on the original URL, but still serve the data URL to the add-on. (Note: serving the data url, means we wouldn't be pinging the server, which is better for the user's privacy).
I think these last points really need input from the WebExtensions team. Rob, can you look at this, or redirect to whoever's appropriate?
I don't think that further restrictions are needed once the SearchEngine implementation validates/sanitizes the image response.
The fact that the request doesn't include credentials is a mitigating factor; as a result only publicly available images can be read (except possibly for intranet URLs, which can normally not be read, but can be by this bug).
Extensions with the right permissions already have another way to read image URLs, via the favIconUrl
property of a tab. An extension could create a page with a favicon pointing to a desired location, and the extension would be able to read it. I discussed this with my team, and it's an acceptable feature (especially because the image is already public, and presumably sanitized/shrunk).
Reporter | ||
Comment 5•4 years ago
|
||
I don't think that further restrictions are needed once the SearchEngine implementation validates/sanitizes the image response.
I second this. Reading intranet images (without sending credentials + sufficiently small/scaled down) seems to be an acceptable risk given the various intranet fingerprinting techniques that exist already.
Extensions with the right permissions already have another way to read image URLs, via the
favIconUrl
property of a tab.
There isn't even a permission needed. Any extension can read any (non-credentials-protected, favicon-able) cross-origin image because moz-extension://{id}/page.html
has an implicit host permission for {id}
, thus can do <link rel="icon" href="https://cross-origin.example/image.png">
from page.html
and access the remote image via its own favicon, without declaring tabs
or any other permission.
Comment 6•4 years ago
|
||
(In reply to Arminius (Armin Razmjou) from comment #5)
Extensions with the right permissions already have another way to read image URLs, via the
favIconUrl
property of a tab.There isn't even a permission needed. Any extension can read any (non-credentials-protected, favicon-able) cross-origin image because
moz-extension://{id}/page.html
has an implicit host permission for{id}
, thus can do<link rel="icon" href="https://cross-origin.example/image.png">
frompage.html
and access the remote image via its own favicon, without declaringtabs
or any other permission.
This statement is true for Chromium-based browsers, but wasn't true for Firefox until recently. Prior to bug 1679688, only the "tabs" permission would allow an extension to read url/title/favIconUrl; as of Firefox 86, what you're describing is true too. But again, this is an accepted trade-off given that favicons are already presumably sanitized. If you are able to show that this is not the case (i.e. tabs.Tab.favIconUrl
holds unsanitized & private data), please file a new bug with a test case.
Comment 7•4 years ago
|
||
Another option may be to require the search icons to be contained in the extension. That would require some lead time to allow extensions to update, so shouldn't be considered a fix for this.
Assignee | ||
Comment 8•4 years ago
|
||
Updated•4 years ago
|
Assignee | ||
Comment 9•4 years ago
|
||
Depends on D105483
Updated•4 years ago
|
Comment 10•4 years ago
|
||
https://hg.mozilla.org/integration/autoland/rev/d8bc9d97caead4467b1f8230dbb5df37c7c1699d
https://hg.mozilla.org/mozilla-central/rev/d8bc9d97caea
Assignee | ||
Updated•4 years ago
|
Updated•4 years ago
|
Reporter | ||
Comment 11•4 years ago
|
||
Sorry, I wasn't in time for the patch review on Phabricator.
The patch is insufficient. The favicon target can send an image/*
content type header plus a 30x redirect to bypass the check.
I suspect this is because SearchEngine
holds its own reference to the channel for the favicon request which is later used to check the contentType
.
https://searchfox.org/mozilla-central/rev/7bb1cc6abf6634b2a20f71935e1e519e73402b63/toolkit/components/search/SearchEngine.jsm#769,794
So when SearchUtils.LoadListener.asyncOnChannelRedirect
follows a redirect and updates to a new channel, SearchEngine
still checks the initial channel's content type.
https://searchfox.org/mozilla-central/rev/7bb1cc6abf6634b2a20f71935e1e519e73402b63/toolkit/components/search/SearchUtils.jsm#86-89
A passing response may look like this:
HTTP/1.1 302
Location: http://example.com/
Content-type: image/whatever
Content-length: 0
Hope it's fine to reopen based on that.
Updated•4 years ago
|
Assignee | ||
Comment 12•4 years ago
|
||
(In reply to Arminius (Armin Razmjou) from comment #11)
Sorry, I wasn't in time for the patch review on Phabricator.
The patch is insufficient. The favicon target can send an
image/*
content type header plus a 30x redirect to bypass the check.
Thank you for commenting about that case. I've just been verifying it and it is indeed wrong. It'd also be potentially broken for images loaded from redirects regardless of the security issue.
The original fix on this bug is on 87 which has just merged to beta. For me to fix this, I'll need to land for 88 and uplift. As a result, to make it easier on tracking, I've created bug 1694183 for the redirect follow-up.
Updated•4 years ago
|
Assignee | ||
Comment 13•4 years ago
|
||
Should be able to test this with the extension and steps from comment 0.
Updated•4 years ago
|
Comment 14•4 years ago
|
||
Updated•4 years ago
|
Comment 15•3 years ago
|
||
Add tests to ensure that search engine icons which are not images are correctly ignored. r=mak
https://hg.mozilla.org/integration/autoland/rev/16cf1f0beed1dc66a5749ad129c1c2cf8e59812d
https://hg.mozilla.org/mozilla-central/rev/16cf1f0beed1
Updated•3 years ago
|
Updated•6 months ago
|
Description
•