12 ms·
Oh-Auth – Abusing OAuth to take over millions of accounts
- Spivak 3y agoWhat am I missing here? How do you mess this up? Like you redirect to the OAuth provider with a url that has your client_id and ask for a postback and you get it with a code, you use that code plus your app's client_id/secret to get user-creds and then query some API to figure out who just authed. The code is tied to your client_id/secret. You have to implicitly verify the code you got just to figure out who logged in.
- michaeljx 3y agoAn attacker could call the post back with a code generated for another client_id. When you use the code to query who logged in, Facebook will still respond, even if the code was not generated for your client_id
- rosswilson 3y agoThis shouldn't be possible as the server-to-server request to Facebook to exchange the Authorization Code for an Access Token requires the client_id and client_secret to be provided. Facebook should (though I haven't actually confirmed this) verify that the code was issued to the given client_id. If the code was issued for client 123, when client 456 tries to exchange it for an Access Token Facebook should throw an error.
- michaeljx 3y agoYup that's the thing, Facebook doesn't do that. The token is the only thing needed, no validation on their half if it matches a client id
- jorams 3y agoWhat you describe is the explicit grant flow and the exchange of the code for an access token automatically protects against this vulnerability. The bugs here are in the implicit grant flow where the redirect doesn't include an opaque code but an actual access token. There the application is responsible for validating that the token is for that application, but there's nothing that requires it to do that.
- dboreham 3y agoI'm a little confused about this. The article seems to say that the application (the server that is the target of the attack) makes a call to FB where the application believes that a successful response indicates that the user's credentials are valid. It says that FB responds with success (and the user's email). But in fact FB would respond with success regardless of whether the supplied token was valid (for that application). This seems like either a serious bug in FB's implementation, or very bad API design. Presumably it's the latter because FB is responding correctly to a request that has nothing to do with auth -- application (server) asked for the email for one of its users, providing its (the server's) credentials for that request. The problem is that the API doc/design has fooled the application developer into believing that that call was an auth check. But it wasn't. Is that it? Also...why is this not wrapped in a library such that it wouldn't be possible to make such a mistake?
- locallost 3y ago"Facebook" (or whoever) just sends a token that says the user authorized the use of its account for the application. What happens in this 'hack' is that the token is valid, but was not actually intended for that application, but for another one (e.g. a malicious one, that is now trying to reuse the token to access other services of the same Facebook user). I don't know how Facebook tokens look like, but JWTs have a property called aud/audience indicating who the token was intended for. The application developer must verify that the token was actually intended for their application. That is my understanding, sorry if I got it wrong.
- dboreham 3y agoI get that. You're not wrong. I'm asking why Facebook tells the application anything other than "nope, that's not valid for you"? It doesn't seem to be a case of the application will accept random noise as a valid token. It does send the token to Facebook and Facebook responds with something that looks like "OK". But when they said "OK" they really meant "we gave you some valid information about the target user, but forgot to mention that the token you provided was not valid". I'm saying that Facebook should in all cases respond with "Nope, that's not a valid token". Their API just shouldn't have a way to get data on a user without providing a valid token (by valid, we mean valid for that purpose, not stolen from somewhere).
- TazeTSchnitzel 3y ago> Vidio, an online streaming platform with 100 million monthly active users, the researchers found that when logging into the site through Facebook, the site did not verify the token — which the website developers and not OAuth must do. Because of this, an attacker could manipulate the API calls to insert an access token generated for a different application, the researchers found. How does this work? I thought the token was an opaque hex string and only had meaning when sent back to the original server?
- vladvasiliu 3y agoIIRC, the user woud get an access token from Facebook for the target site, e.g. Vidio. Vidio then has to make sure that not only is the token actually signed by Facebook, but also that Vidio is the token's audience. The token shouldn't be opaque to Vidio. I'm in no way an authority on OAuth, but I think the "opaque token" you're talking about is in the situation when Vidio would want to access Facebook on the user's behalf: they would get an access token for Facebook. The token can be opaque to Vidio.
- Incipient 3y agoNot being a full expert on it either, that flow is roughly right. Vidio does have to validate it. The entire pkce flow is bloody painful to implement, and it's actually quite hard to do it perfectly, and easy to accidentally skip a part of it and have it seemingly work ok.
- tommiegannert 3y agoThe original article [1] explains it much further: > As you read previously, according to the Facebook documentation, when Vidio.com receives the access token from the user, Vidio should verify that the access token was generated to its App ID (92356) by calling the https://graph.facebook.com/debug_token https://graph.facebook.com/debug_token API. Confirming what vladvasiliu said. 1. https://salt.security/blog/oh-auth-abusing-oauth-to-take-over-millions-of-accounts https://salt.security/blog/oh-auth-abusing-oauth-to-take-ove...
- 3y ago
- duttonw 3y agosomething something jwt token for implicit flow OAuth I'd guess.
- Incipient 3y agoShould be pkce now. I think implicit flow is entirely dead? But could be wrong.
- duttonw 3y agoit should be but auth0 was not forcing it to be off, just highly suggesting to turn off that 'feature' unsure now.
- duttonw 3y agoAnother option is pkce spa, if they did not do 'auth' checks that the jwt token was indeed signed by auth0 or similar, a carefully crafted js alteration would let you take control of the front end. Then you could give it a incorrectly signed token with all the correct details for another user. Usually they would only use the email for matching which makes things even more trivial. You would hope they verified the signing of the jwt token on the backend, but seems thats too difficult for many dev's.
- tansan 3y agoPKCE should only be necessary if you're using app linking or have some client app in-between. If you completely trust the server than implicit is fine.
- krooj 3y agoNo. This is wrong. Implicit is deprecated in favor of authorization_code + PKCE
- krooj 3y agoThe important thing with PKCE is that it's not completely secure, either. A malicious actor can create an app that uses your client_id and its own code_challenge and verifier. In the event that there are any issues with redirect jacking, such as may be the case with custom schemes on mobile, you're hosed. The only way around this is to use intents (on Android) and OS pinning in the client configuration of your authorization server.
- geraldhh 3y agofinally the "login as facebook" feature everybody has been waiting for
- 3abiton 3y agoBut this seems to require access to a different oauth token already, ie the victim could have had their tokens from other websites leaked.
- usrbinbash 3y ago> But this seems to require access to a different oauth token already The article explans quite well how that works. The attacker makes an outwardly legitimate service, which is registered at the OAuth authentication provider, and waits for people to access it. All it has to do, is store the tokens on its backend, and try them, with the same username, against other services, using the same auth provider.
- davedx 3y agoAnother issue with OAuth is the assumption that the browser part of the flow (where the real user enters their username and password on the issuer website) is actually done by a real user. When I was trying to automate EV charging, I looked at quite a few OAuth implementations and many of them were doing things like storing the user's credentials (including their password) then doing the entire OAuth flow with browser automation. I imagine this was the tip of the iceberg. These days with a lot of non-standards-compliant OAuth implementations behind me, my conclusion is a lot of it is a giant waste of time, and it often adds completely pointless complexity for many of the cases it's used for. And as shown in the article, all the complexity often adds exploit vectors. For 90% of API use cases just issue a revokable Bearer Token and be done with it. You are not an app platform!!! [Qualifier: the EV API's I experimented with were a good use-case for OAuth, as they were user-first, based on mobile apps authenticated with user/password credentials. But I've worked with a lot of third party vendor API's where it was purely server-to-server access, but for some reason wrapped in OAuth... because... architects and compliance something something]
- deleted 3y ago[deleted]
- lathiat 3y agoThe oaith spec requires a callback URL to work and usually that url has to be “registered”/authorised with the server. Most of these apps are using the api “unofficially” and the only way to do that is with browser automation. Otherwise once the login is complete the redirect with the authorised token won’t get to your app - it will go to the official website or app. They then pluck the authorised token out of the cookie store.
- davedx 3y agoNot really. The libraries should do what my own app does: render the vendor website (Tesla or whoever) in a browser window and let the user enter their creds securely. You can then capture the auth code from the redirect just fine. There are right and wrong ways to do OAuth both in server and client, and many, many implementations do it wrong.
- mgaunard 3y agoOr you can just use JWT which instead of asking the identity provider whether a token is valid simply requires you to check its signature against a public key.
- camkego 3y agoSome of the vulnerabilities in the article are due to validating the signature of the JWT properly but not checking the "aud" or audience claim. Honestly, I would be tempted to call back to check the token due to how easy it is to make a mistake with JWT.
- mgaunard 3y agoOf course, it just checks that the token was indeed generated by the identity provider. You still need to check that its contents match what you expect for your application. Issuing a request to another server from my backend would be a non-starter, completely incompatible with any sensible performance or security model.
- mooreds 3y ago> it just checks that the token was indeed generated by the identity provider. This! I have given a talk on JWTs dozens of times and always emphasize that you must do two things: * verify the signature * validate the claims (including standard ones like `aud` and non-standard, business specific ones) You must must must do both to securely trust the token. This isn't just a JWT thing, either. If you provide a token to an introspect endpoint like what Facebook provides, only the first item is taken care of. You must still inspect the claims. Libraries will sometimes help with the standard claims, but you're on your own for non-standard ones.
- 29athrowaway 3y agotl;dr: it was an open redirect.
- talkin 3y agoNo, it wasn’t. It’s not validating reused codes or tokens from an application owned by attacker.
- 29athrowaway 3y agoTrue, thanks for noticing. I re-read it more carefully
- bjt12345 3y ago.
- raselhanout 3y ago> OAuth also uses an “explicit grant type,” which is similar to “implicit grant type” but the server (randomsite.com) receives a code instead of a token and needs to make an additional request to Facebook to exchange it for a token. We’re using the implicit grant type example here because it is easier to understand and relevant to this post. Are the first two attacks possible with explicit grant ? Wouldn't the auth server return an error if the client tried to request the access token with their secret and the code from another client ?
- sigwinch28 3y agoNo, they’re not possible. Yes, the auth server would return an error because the token is retrieved by the server out-of-band (Mallory can’t intercept via the browser) and must provide its credentials to Facebook to retrieve said token. Additionally, Facebook SHOULD check that the client using the code is the same client that initiated the OAuth flow.
- agentultra 3y agoSecuring an OAuth implementation is hard. There’ve been several appendices added to the spec that many developers are probably not aware of [0]. Remember to check for current best practices to learn about the latest mitigations and protocol updates! [0] https://www.ietf.org/id/draft-ietf-oauth-security-topics-24.html https://www.ietf.org/id/draft-ietf-oauth-security-topics-24....
- vinckr 3y agoYou probably dont need OAuth2/OIDC. While the two are very powerful protocols when used correctly, and have many advantages and use cases, the truth is that they are not always necessary. In fact, you most likely do not need them. - https://www.ory.sh/oauth2-openid-connect-do-you-need-use-cases-examples/ https://www.ory.sh/oauth2-openid-connect-do-you-need-use-cas...
- kriops 3y agoI don't disagree, but I want to expand upon your point in a cautionary direction: Using OIDC/OAuth2 does prevent you from making several classes of rudimentary mistakes, entirely for free. The nonces and whitelists aren't there just to look pretty!
- fastest963 3y agoYou can send appsecret_proof instead to validate the token and the app. See https://developers.facebook.com/docs/facebook-login/security/#proof https://developers.facebook.com/docs/facebook-login/security...
- nstart 3y agoHang on... So this means that when I check the token as per the guide at https://developers.facebook.com/docs/facebook-login/guides/advanced/manual-flow/#checktoken https://developers.facebook.com/docs/facebook-login/guides/a... , it returns the information regardless of the access_token parameter matching the app id or not? My assumption without reading the docs would have been that if the input_token was from another app, FB would refuse to debug it if the access_token was not associated with the app id. What's the point in asking for the access_token at all for that endpoint otherwise? Am I misunderstanding the bug? This feels like a foot gun that was happily handed over to a lot of people all this time.
- wunderwuzzi23 3y agoDon't forget about ROPC!! There is also an MFA bypass that I see often, it's the Resource Owner Password Credentials (ROPC) flow in OAuth. Especially when you have an Microsoft M365/Azure tenant. Pretty much every client that I have ever tested had this issue. When ROPC is configured (which is the default) then you can just use a simple password to logon (and it bypasses MFA). More details and a tool to test your own tenant: https://embracethered.com/blog/posts/2022/ropci-so-you-think-you-have-mfa-azure-ad/ https://embracethered.com/blog/posts/2022/ropci-so-you-think... If you use M365/Azure test and see if you can logon without MFA, you might be surprised.
- doakes 3y agoI see this a lot too. It makes password spraying very easy. I don't see anything in the article about remediation. Do you have any info for disabling it?
- charles_f 3y ago> the OAuth protocol itself is indeed correctly designed and is secure by nature. However, to use it with a web service requires integrating it into that service’s existing platform, which is where the trouble starts That's my main beef with OAuth. I like having a standard in the middle, I also like OAuth, and I think I understand some of the flows well (I have enough knowledge to know I'm not an expert). The main issue is that it is complex enough that you can botch the implementation in multiple places (eg misuse of the refresg_token), flexible enough that you can take the wrong shortcuts (eg use the wrong flow), and most implementations have semi-transparent intermediary points (eg Jwt that you can see into) that make you feel like you understand the whole thing while you only get half of it - or once again use a component for something it should not be used (id_token for authorization being the most common). It's better than inventing your own scheme, which is certain to fail. But most people don't spend or don't get the time they need to understand properly. It doesn't result in gaps necessarily, but it can.
- candiddevmike 3y agoYou can use your existing API tokens as the OAuth access_token. Refresh tokens are optional. None of it has to be a JWT. Doing this prevents having to support multiple API auth methods. OIDC on the other hand...
- charles_f 3y agoThat's precisely the point. You can do those things, or you can not do them. And there are paths in the combinatorial that leave you with an open door. Re: OIDC, that's yet another one. The average dev can't tell you the difference between both - or won't even know the term OIDC and call it OAuth.
- technion 3y agoI did a lot of the Pentesterlab Pro exercises: https://www.pentesterlab.com/exercises https://www.pentesterlab.com/exercises And was astounded by how many of them came in the class of "OAuth Exploits". And far from a list of specific CVEs in some random app, you seemed to be able to go from one common implementation bug to the next. You walk away wondering how anyone ever gets it right.
- foxyv 3y agoI recently implemented some code to: Sign into an account using username/password/MFA, authenticate with an API using OAuth to get a SAML assertion, authenticate with another API using the SAML assertion to get a token, then sign into an API using the token... Why do we do this to ourselves?
- jeroenhd 3y agoNot having to deal with auth yourself makes your life so much easier. For my self hosted stuff, I've out a simple OIDC requirement in my Apache config (https://github.com/OpenIDC/mod_auth_openidc https://github.com/OpenIDC/mod_auth_openidc) and have set up a plain Keycloak server (should've gone with something simpler but oh well). Apache now sets the user ID in a header (that doesn't get passed on from the client for obvious security reasons) and all the application on the other end needs to do is look up the user ID. Multi factor authentication, password recovery, session expiration and everything else is all handled by Keycloak and no unauthenticated requests can make it through Apache. Tokens are signed and verified, of course, so there's no faking anything. I think Caddy has a similar system built in, including a login page and everything. Give or six lines of config are all you need to handle authentication, plus you get automatic security updates for your authentication flow in case something happens. There's a reason Okta and its competitors are selling auth as a service: doing auth yourself properly is a massive pain that you need to stay on top of. You need to set up secure hashes, integrate TOTP/FIDO2 with appropriate recovery mechanisms, set up login flows, deal with session expiration, and all that stuff, when all you want is "give me a user ID so I know what data to load". These systems are annoying to develop against (though plenty of libraries are available to do the hard parts for you) but they're great if you just want to secure a backend or web application. I don't know why you needed two extra assertions when you were already authenticated, though. That's sounds like bad integration to me.
- foxyv 3y agoSigning into AWS API through OKTA using a SAML Identity Provider. Just the way it was implemented unfortunately. OKTA's API is okay for this sort of stuff, but they need a SAML Assertion API that doesn't require scraping HTML. Also it would be nice if their JSON formatting weren't so all over the place. So it went: OKTA Assertion -> OKTA SAML Assertion -> AWS STS Token > That's sounds like bad integration to me. No arguments here.
- fulafel 3y agoAre these cases all implicit flow, except the Grammarly case where a bug/misconfiguration made it accept the implicit flow param anyway?
- Foobar8568 3y agoI read a lot of things in oauth, implemented several Saas/API access for server to server communication, and I feel it's so insecure that I am sure I have missed something...
- krooj 3y agoThis article is not as impressive as one might imagine - the exploit appears to use the implicit grant flow, which is officially deprecated and should be replaced by the authorization_code + PKCE flow.
- gerardnico 3y agoIt should not happen if you have a proper state that is local, unknown from the attacker and temporarily valid. https://datacadamia.com/iam/oauth/state https://datacadamia.com/iam/oauth/state
- woranl 3y agoWhich version of OAuth implementation is at risk? 2.0 and 1.0a are quite different.