browser.identity.launchWebAuthFlow() exposes redirect_url no matter what it is.
Categories
(WebExtensions :: Request Handling, defect, P2)
Tracking
(firefox-esr68- wontfix, firefox73 wontfix, firefox74+ wontfix, firefox75+ fixed)
People
(Reporter: levissie, Assigned: mixedpuppy)
References
Details
(4 keywords, Whiteboard: sec-high impact on affected users [reporter-external] [client-bounty-form] [verif?][post-critsmash-triage][adv-main75+])
Attachments
(5 files)
browser.identity.launchWebAuthFlow() always returns the URL to where is redirected in the opened webview. This is a big security issue.
When a Firefox Add-On uses the browser.identity.launchWebAuthFlow() method, the service provider's authorization URL is passed as mandatory parameter. This URL contains among other things the redirect_uri to where the service provider should redirect the user after the user has authenticated at the service provider. The authentication part of the OAuth flow all happens in a webview that Firefox automatically launches after calling launchWebAuthFlow().
The redirect_url used, should be a registered, and thus known, url at the service provider.
This redirect_url can be the url of the add-on itself (obtained by browser.identity.getRedirectURL()), in which case the user will be redirected back to the Add-On. This redirect_url can also be something else.
The current behaviour in Firefox is that the Promise returned by browser.identity.launchWebAuthFlow(), always resolves with the URL to where is redirected. Also in the case this redirect_url is not the same url as obtained by browser.identity.getRedirectURL() (the Add-On url). The result is that I can obtain the code passed in the redirect_url's queryparams, even when the redirect goes to another domain.
Possible exploit:
An attacker can exploit this when he/she knows at least 1 registered redirect_url of an app that uses OAuth (which does not have to be under his control). He can just call the browser.identity.launchWebAuthFlow("https://auth.service.provider.com/oauth/authorize?client_id=ClientId&redirect_uri=https://the.registered.redirect_uri.org/") with given parameter. It doesn't matter where the service provider redirects the users after authenticating, the Promise returned by browser.identity.launchWebAuthFlow() resolves with the redirect URL, containing the auth_code (assuming the user authenticated correctly)
How to reproduce:
- Setup up basic OAuth 2 server
- Register a redirect_uri at the server
Step 1 and 2 represent the OAuth server of an existing (not our own) application
Next steps are the Attackers actions:
- Make a new Firefox Add-On (only valid manifest.json and background page necessary)
- Call browser.identity.launchWebAuthFlow({url: "https://authserver.com/auth?redirect_uri=https://the_registered_redirect_url.com&client_id=registeredClientId", interactive: true}).then((url)=>{console.log(url)})
- Authenticate correctly with OAuth app
- See the redirect_url logged in console.
Using this method the Attacker can counterfeit an Add-On, and use the same redirect_uri as the real Add-On. But after the redirect, use the Auth code obtained. Even though the Add-On's0 id is not registered at the service provider.
Reporter | ||
Comment 1•5 years ago
|
||
Firefox version: 72.0.2 (64-bit)
os:
Ubuntu 18.04.4 LTS
Reporter | ||
Comment 2•5 years ago
|
||
The url https://eodcacbkjencjlinmmdhdmflokbmbbka.chromiumapp.org/
is registered at the OAuth provider.
After redirection, the OAuth provider redirects the user to https://eodcacbkjencjlinmmdhdmflokbmbbka.chromiumapp.org/, and the Promise resolves with "https://eodcacbkjencjlinmmdhdmflokbmbbka.chromiumapp.org/?state=6cx5s&code=DRUbG6dVa41Vq8OFDTS4TM83L58jN" which contains the code.
This allows a Firefox Add-On to obtain auth codes from redirect url's which did not redirect to the domain of the Add-On but to a totally different one.
Comment 3•5 years ago
|
||
Luca/Shane, can you take a look? I don't know enough about browser.identity
to follow what's going on here.
Reporter | ||
Comment 4•5 years ago
|
||
Also confirmed on Firefox 73.0 (64-bits)
Reporter | ||
Comment 5•5 years ago
|
||
As you can see, following the same flow in Google Chrome, with the redirect_url registered at the service provider, but from an Extension with another id. The resolved return from the Promise is undefined and an error is thrown.
Reporter | ||
Comment 6•5 years ago
|
||
The documentation (https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/identity/launchWebAuthFlow) does explain the current flow:
Return value:
A Promise. If the extension is authorized successfully, this will be fulfilled with a string containing the redirect URL. The URL will include a parameter that either is an access token or can be exchanged for an access token, using the documented flow for the particular service provider.
But this should only occur, when the user is indeed redirected to the Add-On (https://<add-on-id>.extension.allizom.org/), not when user gets redirected to any URL.
Reporter | ||
Comment 7•5 years ago
|
||
The same documentation says that redirect_uri is an optional parameter, but when I use this property, an error is thrown: Error: Type error for parameter details (Unexpected property "redirect_uri") for identity.launchWebAuthFlow.
Comment 8•5 years ago
|
||
(In reply to Leon Visscher from comment #7)
Created attachment 9126224 [details]
Screenshot from 2020-02-12 21-47-03.pngThe same documentation says that redirect_uri is an optional parameter, but when I use this property, an error is thrown: Error: Type error for parameter details (Unexpected property "redirect_uri") for identity.launchWebAuthFlow.
This part seems to have been wrongly documented when the mdn API doc page has been created:
redirect_uri
isn't an actual parameter of the launchWebAuthFlow
method (in both Firefox, see API schema here, and Chrome, see API docs here)
The redirect_url
is instead an optional parameter of the url
passed from the extension into the identity.launchWebAuthFlow
method.
Shane did work on this API, I'm going to discuss about this issue with Shane and we will look more deeply into it
Assignee | ||
Comment 9•5 years ago
|
||
The issue here is specifically that an addon can pass redirect_uri as a part of the url passed into launchWebAuthFlow. If the oauth service redirects to that uri, then we return the results to the addon. The api does not return information from any redirect, just redirects to the url that is provided in redirect_uri. This is by design. So the question is really whether this design is a problem.
An extension could much more easily use a proxy or webrequest listener to intercept oauth codes. It's also possible to just open a tab with an extension page that does oauth on its own without using this api and probably do exactly the same thing. Given that we generally trust addons as a part of the browser I don't really see this as a problem.
I also seem to recall that some oauth services would simply not work with the generated domains so allowing the passing of redirect_uri was necessary.
nonetheless I'll try to dig up a second opinion.
Assignee | ||
Comment 10•5 years ago
|
||
Ok, with a lengthy chat with dveditz the second opinion is that we should force redirect_uri to be the extensions uri, disallowing extensions to pass that in.
Comment 11•5 years ago
|
||
I agree with the reporter and apparently the Chrome team. If the redirect_url isn't a match[*} of the result of getRedirectURL() then the OAuth server did not mean to give the token to that WebExtension and we should return an error. For compatibility we should match Chrome -- I assume they reject the promise, but maybe they resolve with something that is an error rather than a URL.
If a WebExtension wants to authorize as FooSite.com then it should do it from a content script running in a FooSite context.
[*] "match" is probably something like /^{getRedirectURL() result}
[/?].*/ -- whatever is Chrome compat (which will be easier to figure out from their code than by experiment).
I also seem to recall that some oauth services would simply not work with the generated domains so allowing the passing of redirect_uri was necessary.
Do we know why? Would the same extension code in Chrome have the same problem?
Updated•5 years ago
|
Assignee | ||
Comment 12•5 years ago
|
||
Assignee | ||
Comment 13•5 years ago
|
||
(In reply to Daniel Veditz [:dveditz] from comment #11)
I also seem to recall that some oauth services would simply not work with the generated domains so allowing the passing of redirect_uri was necessary.
Do we know why? Would the same extension code in Chrome have the same problem?
It was over 3 years ago, so memory could be bad. It might actually have been that we always included redirect_uri in the url and some services reject that.
Reporter | ||
Comment 14•5 years ago
|
||
Thanks for the quick responses and fix! I have two questions:
Is it known when/what Firefox version the fix will be released?
What is the further process for classifying the eligibility for the Client Bug Bounty program?
Comment 15•5 years ago
|
||
The fix has not been checked in yet (when it is this bug will be RESOLVED FIXED). Assuming that's soon this should be fixed in Firefox 75. Because it's a lesser severity security bug and has a risk of site breakage we are not likely to rush this into an earlier release. That also assumes that testing (particularly real-world Beta testing) goes well in terms of web compat -- if it breaks too many sites/extensions it could be back to the drawing board.
The bug bounty folks usually wait until bugs are fixed to take a look, since at that point we know exactly what the scope of the problem is. The nomination flag is on this bug so it'll get looked at. If you have further bounty questions they are best addressed in mail to security@mozilla.org since the folks working in the bug won't know.
Assignee | ||
Comment 16•5 years ago
|
||
pushing to autoland now, and will request uplift to beta after.
Comment 17•5 years ago
|
||
https://hg.mozilla.org/integration/autoland/rev/691affa7618084c260364dbdc25e9e29f95bfb91
https://hg.mozilla.org/mozilla-central/rev/691affa76180
Reporter | ||
Comment 18•5 years ago
|
||
(In reply to Daniel Veditz [:dveditz] from comment #15)
The fix has not been checked in yet (when it is this bug will be RESOLVED FIXED). Assuming that's soon this should be fixed in Firefox 75. Because it's a lesser severity security bug and has a risk of site breakage we are not likely to rush this into an earlier release. That also assumes that testing (particularly real-world Beta testing) goes well in terms of web compat -- if it breaks too many sites/extensions it could be back to the drawing board.
The bug bounty folks usually wait until bugs are fixed to take a look, since at that point we know exactly what the scope of the problem is. The nomination flag is on this bug so it'll get looked at. If you have further bounty questions they are best addressed in mail to security@mozilla.org since the folks working in the bug won't know.
I am curious how the severity of this bug (sec-moderate) is established. When I look at the security severty rating Authentication Flaws (which lead to account compromise) is listed under sec-critical.
I do believe this bug is more severe than is established right now. Exploiting this bug I can for example make an "Official Google Black Friday search extension", and make users log in with their Google accounts. I can intercept the access tokens and gain access to their account.
Comment 19•5 years ago
|
||
AFAICT, this also affects ESR68? Patch grafts cleanly there too, FWIW.
Comment 20•5 years ago
•
|
||
(In reply to Leon Visscher from comment #18)
I am curious how the severity of this bug (sec-moderate) is established. When I look at the security severty rating Authentication Flaws (which lead to account compromise) is listed under sec-critical.
I do believe this bug is more severe than is established right now. Exploiting this bug I can for example make an "Official Google Black Friday search extension", and make users log in with their Google accounts. I can intercept the access tokens and gain access to their account.
If you could pull this off from the general web, for all users, then this would definitely be sec-high. In this case you would first have to convince your victims to install your malicious extension. We've generally interpreted that requirement as capping the severity at sec-moderate since it would slow the spread of an attack and would get blackholed once discovered.
Updated•5 years ago
|
Updated•5 years ago
|
Comment 21•5 years ago
|
||
(In reply to Leon Visscher from comment #18)
I do believe this bug is more severe than is established right now. Exploiting this bug I can for example make an "Official Google Black Friday search extension", and make users log in with their Google accounts. I can intercept the access tokens and gain access to their account.
I'm not sure I understand the severity. Full extent of this vulnerability is a bad guy convincing the user to install an extension (which doesn't ask for google.com
or <all_urls>
permissions), and hoping that user already installed one of X other extensions with IDs known to the attacker, and that they went through the auth flow with one of them. Is that about right?
I'm not sure we've ever seen a malicious extension that doesn't already ask for <all_urls>
, or at least google.com
if they are named "Official Google something...".
Reporter | ||
Comment 22•5 years ago
|
||
(In reply to :Tomislav Jovanovic :zombie from comment #21)
(In reply to Leon Visscher from comment #18)
I do believe this bug is more severe than is established right now. Exploiting this bug I can for example make an "Official Google Black Friday search extension", and make users log in with their Google accounts. I can intercept the access tokens and gain access to their account.
I'm not sure I understand the severity. Full extent of this vulnerability is a bad guy convincing the user to install an extension (which doesn't ask for
google.com
or<all_urls>
permissions), and hoping that user already installed one of X other extensions with IDs known to the attacker, and that they went through the auth flow with one of them. Is that about right?I'm not sure we've ever seen a malicious extension that doesn't already ask for
<all_urls>
, or at leastgoogle.com
if they are named "Official Google something...".
Hi Tomislav,
This vulnerability has nothing to do with manifest host permissions. Also there is no need for the victim to have any other extension installed.
The vulnerability is that when the attacker knows a valid redirect url and client id (and sometimes client secret) of an OAuth provider (like Google), he can develop an extension implementing this OAuth flow, even though the extension's ID is not registered (and known) at this OAuth provider.
And with OAuth implemented in browser based apps like SPAs and webextensions, these redirect url, client id and client secret are all public information.
Comment 23•5 years ago
|
||
Please request uplift to beta (and maybe esr68) when you get a chance.
Assignee | ||
Comment 24•5 years ago
|
||
(In reply to Julien Cristau [:jcristau] from comment #23)
Please request uplift to beta (and maybe esr68) when you get a chance.
We're debating whether this should be uplifted. This breaks some set of extensions, currently researching the situation.
Updated•5 years ago
|
Comment 26•5 years ago
|
||
Shane, any update on your research? our last beta builds in a few hours.
Assignee | ||
Comment 27•5 years ago
|
||
We are not going to uplift to beta. We'll let it ride the trains, but we will keep the patch applied.
Updated•5 years ago
|
Comment 28•5 years ago
|
||
Hi Richard, putting this on your radar for this week. We'll need to update the documentation (and likely the BCD) for the identity API.
Comment 29•5 years ago
|
||
Thanks Caitlin, I'll await more information (i.e. my initial read of the comments didn't identify what changes might be required)
Comment 30•5 years ago
|
||
Was a decision made on esr68 uplift, or not just yet?
Assignee | ||
Comment 31•5 years ago
|
||
(In reply to Julien Cristau [:jcristau] from comment #30)
Was a decision made on esr68 uplift, or not just yet?
I think it's fine to let this ride the train, the next ESR is not far away.
Updated•5 years ago
|
Updated•5 years ago
|
Updated•5 years ago
|
Comment 32•5 years ago
|
||
Shane, does this advisory look accurate?
Assignee | ||
Comment 33•5 years ago
|
||
Roughly, yes.
Assignee | ||
Updated•5 years ago
|
Updated•5 years ago
|
Comment 34•4 years ago
|
||
It looks like we'll need to update the documentation regarding the redirect_uri
option, setting dev-doc-needed. Also, can we make this bug public?
Updated•4 years ago
|
Comment 35•3 years ago
|
||
Could I get some advice on the documentation changes needed?
Comment 36•3 years ago
|
||
Documentation review requested: MDN documentation changes have already been completed (as per Slack discussion), changes to the compatibility data are in PR 12016
Comment 37•3 years ago
|
||
Now also updated the release notes and tidied up the API documentation, see PR 8363
Updated•3 years ago
|
Updated•2 years ago
|
Assignee | ||
Updated•10 months ago
|
Updated•6 months ago
|
Description
•