Account Takeover Techniques and Security Best Practices

Account takeover is a common practice that threatens the security of users and their data. The impact on victims depends on the type of account targeted. It can be minor if it’s a customer loyalty account but becomes critical for a corporate administrator account.

Attacks use a variety of techniques, often based on large-scale campaigns to steal as many credentials as possible. However, there are also application vulnerabilities enabling more targeted account takeover. The presence of these vulnerabilities represents a major risk for companies, especially if an administrator account is compromised.

In this article, we detail the main techniques used to steal user accounts. With concrete case studies, we will also present best practices and measures to implement to reduce the risks of account takeover.

Credential Theft Campaigns

Most account takeover are the result of large-scale campaigns aimed at obtaining the authentication information of numerous victims. These operations rely on several techniques:

  • Data leaks: attackers exploit lists of credentials obtained through security breaches on various websites. This enables them to, not only access these sites, but also other accounts if the victims use the same credentials.
  • Stealer malware: this malware infects victims’ computers to exfiltrate sensitive data, such as passwords or session cookies. Attackers spread these stealers via phishing campaigns or websites, encouraging victims to install them unwittingly.
  • Phishing: phishing campaigns, by e-mail or SMS, encourage victims to disclose their authentication information by posing as legitimate applications. These articles dedicated to phishing (by e-mail) and smishing (SMS phishing) detail the associated risks and how to protect yourself.

These techniques enable attackers to create vast lists of valid credentials, facilitating subsequent targeted attacks. They exploit the mistakes or inattention of victims, whether by reusing credentials or being gullible in front of a fraudulent e-mail. However, attackers can also steal accounts by exploiting loopholes on targeted sites, without direct interaction with victims.

Application Vulnerabilities that can Lead to Account Takeover

Exploiting vulnerabilities in web applications to steal accounts requires more time and effort than mass campaigns. Indeed, each case is unique and requires specific analysis by the attacker. However, this approach enables more targeted attacks, giving systematic access to all the accounts concerned.

Vulnerabilities leading to account takeover take two forms. The first (more intuitive), involves stealing session cookies or guessing login information via brute force.

The second form, specific to web applications, targets password or email reset functionalities. The aim is to exploit a vulnerability or bypass processes to replace a victim’s login information with that controlled by the attacker.

To be successful, this attack requires the targeted accounts to use relatively weak credentials.

Let’s look at a concrete case encountered during a web application pentest.

The login process was not based on a password, but exclusively on the use of a 6-digit code sent by SMS, valid for 10 minutes.

This process is illustrated in the diagram below:

However, the application was not protected by any form of rate limiting. It was therefore possible to take advantage of the fact that a 6-digit code represents only 1 million combinations – a small number for a brute force attack.

Load testing showed that the server could handle around 2,000 connection requests per minute (step 5). It would therefore have taken around 19 hours to compromise an account (with a 90% probability of success).

Although this process was time-consuming, the vulnerability enabled to target any account. A single well-chosen account (such as an administrator account) would have enabled the highest levels of privilege to be acquired.

Thus, our attack proceeded as follows:

  • In the first few seconds, generate as many codes as possible (20 in our case). Each generated code triggered the sending of an e-mail, which considerably slowed down the server in case of excessive load.
  • Perform a brute force attack with random codes during the 10-minute validity period of the generated codes.
  • Repeat these two steps as many times as necessary.

At 2,000 requests per minute, with 20 valid codes out of a million possibilities, it takes around 1 hour to have a 90% probability of finding a valid code.

This attack can be automated using Burp‘s Intruder tool, launched every 10 minutes.

After a few tries, we obtained a valid session cookie, indicating that the account theft was successful (see image below).

This vulnerability is not exclusive to account takeover. However, it can enable it if adequate protections are not in place. We won’t go into the basics of XSS here, but rather the different scenarios that can lead to account takeover.

For a more in-depth exploration of XSS, we refer you to our dedicated article: XSS (Cross-Site Scripting) vulnerabilities: principles, types of attacks, exploitations and security best practices.

When an attacker succeeds in injecting an XSS into a website, there are several scenarios in which he can steal accounts:

  • If cookie security is misconfigured, it is possible to exfiltrate the victim’s cookies directly to an external server. In this case, the attacker can access the victim’s account without being stopped by protections such as MFA. However, account takeover is limited by the validity of the exfiltrated session.
  • If authentication information such as a “username/password” pair or an API key is visible in clear text in part of the application, the attacker can develop a JavaScript payload to make requests in place of the victim, retrieve this information and exfiltrate it to an external server.
  • If the first two scenarios are not possible, it is sometimes possible to steal victims’ accounts by directly modifying their login information instead of exfiltrating it. For example, if the e-mail address change functionality does not require password verification, the attacker can use the XSS flaw to simply change the victims’ e-mail address to one he controls (see payload example below). All the attacker then has to do is use the password reset feature to change the passwords and steal the victims’ accounts.
fetch("/change-email/", {
    method: "POST",
    body: JSON.stringify({"new-email": "[email protected]"})
})

This last scenario differs from the previous examples, in that the victim loses access to his or her account as soon as the login is changed.

The APIs used by some web applications are frequently misconfigured, leaking sensitive information such as environment variables or other users’ personal data.

During our pentests, the routes used to list other users of the platform are sometimes affected, making it possible to steal any account using the password reset feature.

Let’s take the example of an e-commerce site where users can leave public reviews of items. When a user views an item’s page, requests are sent to the API to obtain information on other users who have also left reviews, such as their first and last names and profile photos.

A typical request of this kind might look like this:

GET /comment/1 HTTP/1.1
Host: not-vulnerable-site.com
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/119.0
Accept: */*

Response:

HTTP/1.1 200 OK 
Content-Length: 633
Content-Type: application/json
[...TRUNCATED DATA...]
{
    “content”: “Great article !”,
    “user”: {
        “name”: “foo”,
        “lastname”: “bar”,
        “profile_picture”: “http://...”
    }
}

In the above response, only the necessary information from the user object is returned. But in some cases we encounter, the API is misconfigured and returns all the fields of the table listing the users in the database.

Request:

GET /comment/1 HTTP/1.1
Host: vulnerable-site.com
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/119.0
Accept: */*

Response:

HTTP/1.1 200 OK 
Content-Length: 633
Content-Type: application/json
[...TRUNCATED DATA...]
{
    “content”: “Great article !”,
    “user”: {
        “id”: 10
        "createdAt":"2024-07-08T08:19:40.037Z",
        “name”: “foo”,
        “lastname”: “bar”,
        “email”: “[email protected]”,
        “phone”: “0101010101”,
        “password”: “$2y$15$KCZKk3HhrnXA0KJKwT3dUuI27mjoq7/Zl0jDFwdFCYM4GH5Obrfli”,
        “token_reset”: null,
        “profile_picture”: “http://...”
    }
}

Here we can quickly see that a lot of information is returned that is not relevant to the situation; in particular, the email, phone and password fields should not be passed on to all users.

However, the most dangerous piece of data here is the token_reset field. This is the field containing the random code that is sent to a user when he has forgotten his password.

All you have to do is initiate a password reset request for the e-mail address of the targeted account. Returning to the original article page and observing the request made to retrieve the target user’s information, we see that reset_token has been generated and returned to us.

HTTP/1.1 200 OK 
Content-Length: 633
Content-Type: application/json
[...TRUNCATED DATA...]
{
    “content”: “Great article !”,
    “user”: {
        “id”: 10
        "createdAt":"2024-07-08T08:19:40.037Z",
        “name”: “foo”,
        “lastname”: “bar”,
        “email”: “[email protected]”,
        “phone”: “0101010101”,
        “password”: “$2y$15$KCZKk3HhrnXA0KJKwT3dUuI27mjoq7/Zl0jDFwdFCYM4GH5Obrfli”,
        “token_reset”: “e1a59e94-5c9b-4f32-ad05-4a29858dd65e”,
        “profile_picture”: “http://...”
    }
}

Finally, you can continue the password reset procedure as if you had legitimately received the e-mail and clicked on the following link: https://vulnerable-site.com/reset-password?token= e1a59e94-5c9b-4f32-ad05-4a29858dd65e.

This example illustrates a password reset exploit, but there are other techniques. Our article on password reset vulnerabilities takes a deep dive at those exploits and the security measures to be implemented.

How to Prevent Account Takeover?

Various protective measures can be implemented at different levels of a web application to protect against the risk of account takeover: at the user-level itself, at the level of the overall configuration of the web server used, and finally at the level of the application’s functionality and internal operation.

From the user’s point of view, when it comes to using passwords, it is essential to put in place ‘strong password’ policies to ensure that users choose sufficiently strong passwords.

The main objective when implementing this type of policy should be to promote the use of long passwords (at least 15 characters, with no size limit). This helps to protect this information against the risk of brute force attacks on their credentials.

Next, it is crucial to configure the web server and the frameworks used correctly. Sensitive routes such as those for logging in or resetting passwords must be subject to a rate-limiting mechanism to prevent brute force attacks.

Cookies or authentication tokens must have a limited validity period and be declared as HttpOnly (in the case of cookies). This reduces the risk of session exfiltration via stealers or XSS vulnerabilities.

Finally, the most crucial point concerns the security of the application itself. Specific recommendations depend on the languages used and the functionalities. It is therefore difficult to define precise guidelines, but it is essential to ensure that the login and password reset processes cannot be circumvented or exploited under any circumstances.

Similarly, sensitive authentication fields (email, password, reset token, etc.) must be rigorously controlled to prevent accidental disclosure.

In general, security vulnerabilities often result from problems relating to configuration or interaction with the backends used for account management. It is therefore essential keep in mind best security practices relating to APIs throughout the development phases.

Author: Maël BRZUSZEK – Pentester @Vaadata