DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Please enter at least three characters to search
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Zones

Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

Last call! Secure your stack and shape the future! Help dev teams across the globe navigate their software supply chain security challenges.

Modernize your data layer. Learn how to design cloud-native database architectures to meet the evolving demands of AI and GenAI workloads.

Releasing software shouldn't be stressful or risky. Learn how to leverage progressive delivery techniques to ensure safer deployments.

Avoid machine learning mistakes and boost model performance! Discover key ML patterns, anti-patterns, data strategies, and more.

Related

  • A Hybrid Approach to Continuous Application Authorization
  • C# Applications Vulnerability Cheatsheet
  • Understanding IEEE 802.11(Wi-Fi) Encryption and Authentication: Write Your Own Custom Packet Sniffer
  • Power BI Embedded Analytics — Part 2: Power BI Embedded Overview

Trending

  • Enhancing Avro With Semantic Metadata Using Logical Types
  • Understanding Java Signals
  • Evolution of Cloud Services for MCP/A2A Protocols in AI Agents
  • The Role of Retrieval Augmented Generation (RAG) in Development of AI-Infused Enterprise Applications

Using Session Puzzling to Bypass Two-Factor Authentication

See how one security expert was able to get around two-factor authentication, and learn how to stop such vulnerabilities in your code.

By 
Ziyahan Albeniz user avatar
Ziyahan Albeniz
·
Jan. 21, 19 · Tutorial
Likes (3)
Comment
Save
Tweet
Share
9.9K Views

Join the DZone community and get the full member experience.

Join For Free

Sessions are an essential part of most modern web applications. This is why session-related vulnerabilities often have a sizable impact on the overall security of a web application. They frequently allow the impersonation of other users and can have other dangerous side effects.

What Are Session Variables?

For those not familiar with session variables, they are server-side variables whose value is tied to the current session. This means that if a user visits the website, you could store their username in the session variable as they log in and it will be available until the session expires or the user logs out. If another user logs in, that triggers a new session and the session variable will return a different username for that particular user.

Session Variable Example

Let's take a look at an example of how session variables work. Be aware that this example uses stripped-down pseudocode to help illustrate an otherwise complicated concept. Do not use anything like this in production!

Login

user = getUser(input['username']);
if(compare_hash(input['password'], user.hash) === true) {
        session['username'] = user.name;
        session['logged_in'] = true;
        return true;
} else {
        session['logged_in'] = false;
        return false;
}

Index

if(session['logged_in'] === true) {
        print('Hello ' + sanitize(session['username']))
}

If a user called Alice logged in, she would be greeted with "Hello Alice." If Bob was logged in at the same time and opened the same page, he would see "Hello Bob" instead. The session variable is available across different files and isn't restricted to file it is declared in. This can lead to a complication.

Session Puzzling

In this example, we're going to look at three different files. Try to spot the problem in the code before you continue reading the article. Also, not that this example contains vulnerable pseudocode. Do not use anything like this in production!

Login (snippet)

// check if phone number is confirmed
  if(user.phone_number_confirmed === true) {
   // set the `confirmed` session variable to true since we need to check it later
   session['confirmed'] = true;
  }
  // the user wants to get notified if somebody logs in to their account?
  if(user.notify_on_login === true) {
   // we need to check this as well
   session['notify_on_login'] = true;
  }

Index (snippet)

// we handle the login notifications here
  if(session['notify_on_login'] === true) {
   var message = 'Somebody just logged into your account.';
   // if the phone number is confirmed...
   if(session['confirmed'] === true) {
           // we send an SMS text message
           sendTextMessage(message);
   // if the phone number is not confirmed
   } else {
           // we send an email instead
           sendEmail(message);
   }
  }

Admin (snippet)

// if the user submitted a password
  if(input['password'] !== null) {
   // check if it matches the one that's required to access the admin panel
   var result = check_admin_password(input['password']);
   // if it's correct...
   if(result === true) {
           // confirm that the user was logged in
           session['confirmed'] = true;
   } else {
          // set the confirmation to false
          session['confirmed'] = false;
   }
  }
  // if the user didn't supply the correct password
  if(session['confirmed'] !== true) {
   print('You must proof that you are allowed to visit the admin section.')
   print('Please type in the password.');
   // generate a password form and just exit
   generatePasswordForm();
   exit();
  // if the user supplied the right password....
  } else {
   // load the admin section and grant access
   loadAdminSection();
  }

Did you spot the vulnerability? If you found it, then congratulations! But don't worry if you couldn't spot it right away. We'll explain what went wrong in the above code.

  • Let's summarize the login snippet. What happens here is that we check whether or not the user wants to be notified whenever someone logs into their account. It also checks whether or not the phone number has been confirmed in line 2. If that's the case, the confirmed session variable is assigned the value 'true'.
  • In the index snippet, we see why this session variable is used. If the user wants to get notified when someone logs into their account, it checks the confirmed variable in line 5 and sends an SMS in line 7 if the user has confirmed their phone number. If there is no confirmed phone number, it will send an email instead.
  • The actual problem arises in the admin snippet. In the lines 2-13, it checks whether or not a password was supplied and sets a session variable to 'true' if the password matches the one that's needed to access the admin section of the website. However, if you take a closer look you will see that this session variable has the same name ('confirmed') as the one that's being used to check whether or not the user confirmed their phone number. In line fifteen, it checks whether or not the confirmed session variable is 'false' (or undefined). And if it's set to 'true' it will load the admin section in line twenty-four.

We don't need to know the password here. When we confirm our phone number, the confirmed session variable will be set to 'true' in line 4 of the login snippet. So if our phone number is confirmed, we can also access the admin panel without typing in a password. The problem here is that session variables are valid across files and that we use the same variable name for different functionality. This is called Session Puzzling.

Bypassing Two-Factor Authentication by Taking Advantage of Missing Access Controls

Two-Factor Authentication (2FA) is a security feature that prevents your account from being stolen if an attacker knows your password. The website you're logging into requires you to provide a second code, in addition to your normal password. Ideally, this code has been generated by using a Time-based One-Time Password (TOTP) algorithm. In most cases, if you enable 2FA, the website provides you with a string of letters and numbers, or a QR code that you need to scan or type into an app on your phone. It will also provide you with some backup code, in case you lose access to your phone.

The app will then continuously generate a new, additional password based on the secret code and the current UNIX timestamp. Usually, these additional passwords are regenerated every 30 seconds (think Google Authenticator). The idea behind this is that it may be possible for an attacker to retrieve your password by various means, but it's often infeasible for them to gain possession of the device on which your second code (2FA) is generated. In addition to smartphones, there are also dedicated hardware devices that can be used for generating these codes.

The question for an attacker is: can 2FA be bypassed?

In a lot of cases, the answer is 'yes'. TOTP is not the only method websites use to implement 2FA. Some use emails that contain the code, while others use an SMS or a phone call. Because consumers reuse passwords, the website password and the email account password are often the same word. Therefore, an attacker can simply log into the email account and read the code. Using different techniques and tricks, attackers can also intercept SMS text messages and phone calls. My conclusion is that TOTP is the way to go.

What About the Server-Side Implementation?

However, it also depends on the server-side implementation of both the algorithm and the 2FA prompt. The possibility of bypassing 2FA is not a totally new concept, but it was once again proven by Nikhil Mittal. He found a way to bypass it without even touching the underlying token generation algorithm. Instead, he used a server-side bug that was present due to careless session handling. In this instance, it led to an access control problem.

Since it was a private bug bounty program, Nikhil Mittal was unable to disclose its name. What he was allowed to tell was how he was able to bypass Two Factor Authentication. First, he outlined what a typical 2FA login flow on the website looked like:

  1. The user provides an email address and a password.
  2. A valid 2FA code is sent to the user's registered telephone number.
  3. The website asks for the 2FA code.
  4. The user types in the code.
  5. The user is logged in.

We briefly mentioned that sometimes there are backup codes. Users who lose their device or SIM card have an alternative: select the backup code option in Step 3 and use one of their backup codes.

Nikhil noticed that the websites session basically has two states:

  1. Username and password have been supplied correctly, but the 2FA code has not yet been provided.
  2. Username, password, and the 2FA code have all been supplied correctly.

Obviously, you have unlimited access to all settings if are in the second state. You can regenerate your backup codes and edit other settings too. But are users in the first state really limited to typing in their 2FA token?

Nikhil Mittal was curious about whether he would be able to access other functionality in the first state. So, he issued a request to the website that would return the backup codes if he were in the second state. The expected behavior is that the application would throw an error due to missing privileges. The surprise was that it returned the backup codes! That meant that he was simply able to log in with the correct username and password, retrieve the user's backup codes, select the option to use them instead of the actual 2FA code and then supply one of the stolen ones. This immediately granted him access to the account – Two Factor Authentication was bypassed.

For further information about the vulnerability he found, and what requests he issued in order to retrieve the backup tokens, we highly recommend reading Nikhil Mattal's writeup, How I bypassed 2-Factor Authentication in a bug bounty program.

Session (web analytics) authentication

Published at DZone with permission of Ziyahan Albeniz, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

Related

  • A Hybrid Approach to Continuous Application Authorization
  • C# Applications Vulnerability Cheatsheet
  • Understanding IEEE 802.11(Wi-Fi) Encryption and Authentication: Write Your Own Custom Packet Sniffer
  • Power BI Embedded Analytics — Part 2: Power BI Embedded Overview

Partner Resources

×

Comments
Oops! Something Went Wrong

The likes didn't load as expected. Please refresh the page and try again.

ABOUT US

  • About DZone
  • Support and feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends:

Likes
There are no likes...yet! 👀
Be the first to like this post!
It looks like you're not logged in.
Sign in to see who liked this post!