Knocking Down the Big Door
How We Bypassed the Auth0 Authentication
With more than 2000 enterprise customers and managing 1.5 billion logins every single day, Auth0 is one of the biggest Identity Platforms. In this post we will tell the story of how we, at Cinta Infinita, found an Authentication Bypass Vulnerability that affected any application using Auth0 (for username and password authentication) in the context of an independent non-profitable research.
We will demonstrate the flaw attacking the Auth0 Management Console (used as one exploitable example application).
One important fact to mention is that the described vulnerability would allow malicious users to run cross-company attacks, allowing them to access any portal / application protected with Auth0 with minimum knowledge. The only thing a malicious user needed to perform the attack was administrative access to any Auth0 account and, since registration is free, this requirement could be trivially fulfilled.
To fully understand what follows, you need to know some basic concepts about JSON Web Tokens (JWT). If you already know how they work, you can skip this section.
JSON Web Token (JWT) is a compact, URL-safe means of representing claims to be transferred between two parties, described in RFC 7519. The claims in a JWT are encoded as a JSON object that is used as the payload of a JSON Web Signature (JWS) structure or as the plaintext of a JSON Web Encryption (JWE) structure, enabling the claims to be digitally signed or integrity protected with a Message Authentication Code (MAC) and/or encrypted.
So, basically, a JWT is a JSON object containing a number of claims that is signed server side with a secret key (or a private certificate). Its security relies on the fact that only the issuer knows the key (or holds the private certificate) to sign and verify the tokens.
1. Motivation
The story begins in September 2017, while we were pentesting an application which we will call “SecureApp”. The application was already in production but we were testing in a DEV environment, and it used Auth0 for authentication.
The authentication flow looked like the following:
The response marked in red contains a signed JWT with the following claims in its payload:
{
"id": 48,
"user_id": 48,
"username": "user1@test.com",
"iat": 1516146210,
"exp": 1516146270,
"aud": "urn:auth0:secureapp:secureapp-dev",
"iss": "urn:auth0"
}
We couldn’t modify this payload because it had been signed, but we could try to reuse it. So, armed with a proxy, we captured a valid “wresult” JWT from the DEV environment and injected it into a login flow in PROD, and it worked! We were able to access the account for that user in the production environment.
The question is then, are DEV and PROD environments using the same signing keys / certificates? What else is wrong?
After we found this, we tried using this kind of attack to jump through different apps within the organization. Think of a “user_id” value that identifies an internal user, and multiple applications that rely on that identifier. We could now access all of them even when we didn’t have valid credentials for them.
How can we take this a step further?
Since the Auth0 Management Console uses this type of authentication, we tried to reproduce our attack there.
2. Attacking the Auth0 Management Console
We used the same approach to analyze the Management Console login flow, and we received the following JWT in the “wresult” parameter.
{
"user_id": "59c5a39c5315152c967cc031",
"email": "nahuel@cintainfinita.com.ar",
"email_verified": true,
"iat": 1506952673,
"exp": 1506952733,
"aud": "urn:auth0:auth0:auth0",
"iss": "urn:auth0"
}
In order to hijack an account, we would need to forge a valid JWT with that user’s information. At this point, there are two pieces of information we don’t have access to:
- the “user_id” (not trivial like an email address or an incremental integer, but for other applications this could be the case — see our first example -)
- the signing key (or private certificate)
We found a way to obtain the “user_id” for any user with a little bit of “social engineering”. You would need to invite the victim to one of your tenants and, after the invitation is accepted, delete that user from the tenant. The “delete” HTTP request contains the “user_id” for that user (this was just one possible technique and there might be other ways to get the “user_id”).
We were not able to extract the signing key nor obtain the private certificate, but we found a functionality that could be used (or abused) as an oracle to generate valid JWTs with arbitrary payloads. The Management Console allows you to create Database Action Scripts that are executed every time a user logs in. We created a simple “Database Action Script” that returned the needed values for the profile:
From the above picture you can observe the “user_id” 59d60fef8025c603ce735e02 (obtained using the “social engineering” technique we described) and also the “email” for the selected victim. This “user_id” looks like a Mongo ObjectId (thanks @andresriancho).
After hitting the “Try Connection” button, and filling the login form with arbitrary values, the following signed JWT was returned.
{
"user_id": "59d60fef8025c603ce735e02",
"email": "nahuel+victima@cintainfinita.com.ar",
"email_verified": true,
"iat": 1507203410,
"exp": 1507203470,
"aud": "urn:auth0:atacante:Username-Password-Authentication",
"iss": "urn:auth0"
}
Take into account that the values of “aud”, “iat” and “exp” are fixed by the Auth0 server in the response, bound to the tenant and database (even if they are specified in the forged JSON they are omitted by the server). In this case the tenant is “atacante” (attacker in Spanish) and the database is “Username-Password-Authentication”.
So, now we had the ability to forge a valid signed JWT with the “email” and “user_id” of the victim. It was time to try the attack. Will it work? Well, grab some popcorn and watch the video.
First, some details to help you follow the video:
- “Atacante” is the attacker account
- “nahuel+victima@cintainfinita.com.ar” will be the victim email address and its user_id (the important one) is 59d60fef8025c603ce735e02
- We use the very well-known tool “Burp Suite” to manipulate the HTTP traffic and forge the requests
It worked!! Why? The audience claim was not being checked and JWTs generated from our test application were accepted by the Management Console app (same signing key / private certificate).
Could we use this attack to access arbitrary applications? Yes, as long as we know the expected fields and values for the JWT. There is no need of social engineering in most of the cases we saw. Authentication for applications that use an email address or an incremental integer for user identification would be trivially bypassed.
3. Vendor Response
We disclosed this finding to the Auth0 Security Team in October 2017 following our Responsible Disclosure Policy and they acted really fast. We worked together with their team and the vulnerability was fixed in less than 4 hours in their Public SaaS.
The “audience” claim is now being correctly validated and the attack is no longer possible.
We waited for six months before publicly disclosing this issue so that Auth0 could update all their Private SaaS Appliances (on-premise) as well.
Auth0 published a blog post about their internal vulnerability management and remediation process where they mention our finding and the assistance we provided: https://auth0.com/blog/managing-and-mitigating-security-vulnerabilities-at-auth0/
We would like to thank the Auth0 Team, specially Joan Pepin, Matias Woloski and Duncan Godfrey, for the way they treated us and how they handled this finding. Great team!
4. More to come?
Yes!. We are preparing some similar ideas and recommendations for .NET Web applications running on IIS or Azure using SAML authentication (Federation). Stay tuned!
5. About Cinta Infinita
Hope you liked the post! If you would like to contact us, please visit http://cintainfinita.com or write to contact@cintainfinita.com.ar.