Broken Authentication
My notes about authentication vulnerabilities
Category: [Server-Side]
Severity: [High to Critical]
Impact: [ATO, Unauthorized Access, Data Expose, Privilege Escalation, etc...]
“In modern OWASP standards (OWASP Top 10:2021), this category is also referred to as Identification and Authentication Failures.”
1. Concept
Authentication is the process of verifying the identity of a user or client, so application ensures that an account like Sample_account belongs to user Jack (for example).
Types of Authentication
There are 3 types of authentication:
- Something you know, such as password or the answer to a security question, also called Knowledge Factors
- Something you have, such as a physical device like mobile or security token, also called Possession Factors
- Something you ARE or DO, such as your biometrics or pattern of behavior, also called Inherence Factors
2. Authentication vs Authorization
Authentication: Who’s this user? Authorization: what’s this user allowed to do?
For example:
Whether someone who’s trying to access a website with username sam, really is the same person who created the account or not. Once sam is authenticated, their permissions determine what’s they authorized to do.
3. Broken authentication impact
Most authentication vulnerabilities occurs in one of two ways:
- The authentication mechanism is weak and can be attacked with a simple Brute-Force attack.
- Logic flaws or poor coding allow the attacker to bypass the authentication entirely.
If an attacker can access to an account or bypass authentication, they’ll have access to all data of the account, if they’re able to takeover a high-privileged account such as an admin, they could take full control over the entire infrastructure. Even a low-privileged account can open some new pages to attacker, so they can find and exploit new bugs, or may be able them to access some sensitive data by gaining access.
4. Password based authentication
In such a websites, users either create an account or login to an existing account, which is created by user or admin. This account has a unique username & password which is being used by user to authenticate and access their account.
Brute-force attack
A brute-force attack is when an attacker uses a system trial or error message to guess usernames and passwords. These attacks are typically automated using a wordlist contains usernames and passwords, attacker uses a tool to make a vast number of login requests to find correct credentials. This kind of attack isn’t always about making some blind login attempts, hackers may use a customized wordlist and special knowledge to target their victims more accurately, so websites that rely on password-based login as their sole method can be highly vulnerable if they don’t implement sufficient brute-force protection.
Brute-forcing usernames
Usernames are especially easy to guess if they comfort to a recognizable pattern such as business logins like name.lastname@company.com, However if they don’t use such a pattern, sometimes even high-privileged accounts use predictable usernames like admin or administrator.
It’s common on social media’s websites to see the username of the account publicly. sometimes the name of an account is the username too. You should also check HTTP responses to see if any email address is disclosed, sometimes it may contain the email of a high-privileged account like administrator.
Brute-forcing passwords
Passwords can similarly be brute-forced, with the difficulty varying based on the strength of password. Many websites adopt some kind of policy which forces users to choose a strong password based on their policy, which make it harder to crack. For example:
- Numbers
- A mixture of lower and uppercase characters.
- At least one special character, like
! @ # $ % ^ & * _ + = < > / ?
However cracking these passwords is difficult, we can use some human’s behavior to exploit vulnerabilities that users may introduce to this system. They often use a password that they can remember, so they’ll fit their password to website’s policy. For example: if mypassword isn’t allowed, user may try something like Mypassword1! or Myp4$$word instead.
In cases where the policy requires the user to change the password after a period of time, user may make just a minor and predictable change, for example: Mypassword1! → Mypassword2?.
Username enumeration
Username enumeration occurs when an attacker can determine whether a username or account exists by observing differences in an application’s behavior. It commonly appears on login pages when the application responds differently to a valid username with an incorrect password versus an unknown username. It can also occur in registration, password-reset, account-recovery, invitation, or user-search functionality.
For example, a registration endpoint might return:
HTTP/1.1 200 OK
Content-Type: application/json
Cache-Control: no-store
{
"username": "sam",
"is_available": false,
"message": "Username is already taken"
}
This response directly reveals that sam is already registered. An attacker could automate requests for many candidate usernames and build a list of existing accounts.
Common indicators of username enumeration include:
- Different HTTP status codes
- Different response bodies or error messages
- Different response lengths
- Redirect differences
- Differences in response timing
Response time is the amount of time between sending a request and receiving the server’s response. Timing-based enumeration can occur when the server performs expensive password-hash verification for an existing account but quickly rejects an unknown username. If this difference is consistent across repeated measurements, it may reveal whether the username exists. However, timing results can also be affected by network latency, server load, caching, and rate limiting, so they should be analyzed across multiple requests rather than inferred from one response.
Flawed brute-force protection
While attacker trying to login to an account and starts a brute-force attack, they’ll log a wide range of failed login attempts, which is a sign of brute-force. There are 2 most common ways of preventing brute-force:
- Locking the targeted account
- Blocking IP address of attacker
Both of these ways might have their special bugs, especially if they have a flawed logic bug. Example: In some applications, the counter for the number of failed login attempts resets after a successful login, so attacker just login to their own account to continue brute-force
Account locking
One way that websites try to prevent brute-forcing is to lock the account if certain suspicious criteria are met, usually a set number of failed login attempts. Just like normal login errors, responses from the server which indicated that the an account is locked can be a way to enumerate usernames. Locking an account offers a certain amount of protection, However this is not useful when attacker is just trying gaining access to any random account.
Attackers can do severe methods to work around these kind of protections:
- Establish a list of valid usernames (using username enumeration)
- Decide on a very small shortlist of passwords that they think at least one user is likely to have, for example: if allowed login attempts limited to 3, they just switch to next account after reach that limit
- Try each of the selected passwords with each of the usernames
User rate-limiting
In this protection, making too many login requests in a short period of time causes your IP address to be blocked, it just can be unblocked if one of the following situations happens:
- Automatically after a period of time
- Manually by admin
- Manually by user after solving a CAPTCHA As the rate-limit is based on rate of HTTP requests sent by user’s IP, it’s also possible to bypass it if you workout how to guess multiple passwords with a single request.
For example: attacker may use X-Forwarded-For for IP-based rate-limit to bypass it:
POST /api/login HTTP/1.1
Host: api.example.com
Content-Type: application/json
X-Forwarded-For: 203.0.113.45
{
"username": "user123",
"password": "Mypassword1!"
}
If server uses X-Forwarded-For as rate-limit key, it’ll be bypassed easily just by changing the value.
HTTP basic authentication
Most basic authentication mechanism which uses the username:password combination as base64 encoded text to authenticate the user called HTTP basic authentication, sample request:
GET /profile HTTP/2
Host: example.com
Authorization: Basic dXNlcm5hbWU6cGFzc3dvcmQK
...
dXNlcm5hbWU6cGFzc3dvcmQK is base64-encoded text of username:password
This is not a secure option to use
5. Multi-factor authentication
Some websites requires user to authenticate using multiple factors, for instance: 2FA (2 Factor Authentication), Biometrics, OTP (One Time Passcode), etc.
2FA tokens
Verification codes are usually read by the user from a physical device. Many websites now provide users with a dedicated device for this purpose, such as a RSA token or keypad device. It’s also common that a website uses a mobile app like Google Authenticator for the same purpose. Some websites send verification code to user’s mobile phone as a text message. This might enable the attacker to use a MITM attacks and intercept SMS or obtain a SIM card with the victim’s phone number, so they can receive all messages being send to victim’s phone number.
Bypassing 2FA
Sometimes 2FA can be bypassed entirely. If user first prompted to enter a password and then enter a verification code on a separate page, the user is effectively in a logged-in state before they enter the verification code, In such a case, it’s worth to test if you can directly navigate to profile page without entering the code.
For example: the request for password looks like this:
POST /login HTTP/1.1
Host: account.example.com
Content-Type: application/json
{
"username": "user123",
"password": "Mypassword1!"
}
after sending this request, user enters the verification code and send the following request:
POST /login/2fa HTTP/1.1
Host: account.example.com
Content-Type: application/json
{
"OTP": "123456"
}
Imagine that the profile endpoint is /account, attacker may simply go to /account after sending the first request which is /api/login and bypass 2FA mechanism in case of a flawed logic in verification mechanism.
Flawed 2FA verification logic
In some cases, flawed logic in 2FA means that after a user has completed the initial login step, the website doesn’t verify whether the same user is completing the next step or not.
Example: user may send username=sam&password=1234 in first step, site set a cookie Set-cookie: account=sam before completing the next step, so attacker may change the username and brute-force verification code:
POST /login/2fa HTTP/1.1
Host: account.example.com
Set-Cookie: account=victim → Intended victim
Content-Type: application/x-www-form-urlencoded
OTP={BRUTE FORCE HERE}
In some applications changing the OTP value type to array can be used to send all possible codes and bypass the verification, This exploits type confusion—the application expects a string value but fails to validate the input type properly.:
POST /login/2fa HTTP/1.1
Host: account.example.com
Content-Type: application/json
{
"OTP": ["000000", "000001", ..., "123456", ... , "999999"]
}
If the server accepts and processes all values in the array, the attacker can bypass verification by submitting all possible OTP codes in one or more requests. The server will validate the code if any value in the array matches the correct OTP.
6. Other authentication mechanisms
Some websites provide severe functionality which allows users to switch account, reset or change password or any other user controlled functionality which can lead to some kinds of vulnerabilities in application.
Keeping users logged-in
Most websites have a feature labeled Remember Me or something like that, which usually generates a session cookie to keep user logged-in after they close the browser. Possessing this cookie can enable the attacker to bypass the login page, so it must be impractical to guess. However some websites use a predictable concatenation to generate this cookie, for instance a combination of username and timestamp or even password! So, once attacker workout the formula, they might use it to brute-force other accounts.
For example: application uses a JWT token for session cookie:
GET /account HTTP/2
Host: example.com
Cookie: session=eyJ1c2VyIjoic2FtIiwiaWF0IjoxNzI1Njg2NDAwLCJzaWQiOiJhN2YzYzllMmQxYjVmOGU0YzZhOWQyZjFlOGIzYzVhNyJ9Cg;
attacker will decode the session and get this:
{"user":"sam","iat":1725686400,"sid":"a7f3c9e2d1b5f8e4c6a9d2f1e8b3c5a7"}
user defines the username, iat is the timestamp and sid is session id. If the JWT is not properly signed or validated, an attacker could modify the user field. But more realistically, if the sid is predictable (like a sequential number or based on iat), the attacker could generate valid session tokens for other usernames and brute-force them.
In practice, attackers would prioritize high-privileged usernames like admin or administrator to maximize impact, but we’re not discussing about that here.
Some websites assume that if the cookie is encrypted in some way it won’t be guessable even with static values. while this may be true if done correctly, encrypting the cookie using some 2-way encodings like base64 offers no protection. Even using a one-way hashing algorithm can’t make it safe if website doesn’t use salting, attacker simply hashes their wordlist and brute-force accounts. Even if attacker is not able to create an account, they may still use other vulnerabilities like XSS to steal user’s session cookie, or if the website uses an open source framework, attacker may find the documented cookie generation algorithm and abuses it.
In some rare cases, it may be possible to see user’s password in plaintext even with hashing. This happens when user uses a common password which is available in online hash databases and application doesn’t use any salting to protect such a passwords.
Resetting user passwords
As password-based authentication is impossible when user can not remember the password, websites use other ways to identify that the user is resetting their own password or not:
- Sending password by email
- Reset using a URL
- Other mechanisms like OTP
Sending password by email
Sending users their current password would never be possible of website handles passwords securely. Instead, some websites generate a new password and sent it via email to user, but sending passwords over unsecure channels have to be avoided and email inbox is not a safe place to keep passwords.
Resetting password using a URL
A more robust method is to send a unique URL to the users, so they can reset their password. In this case, application generates a unique token for URL:
https://account.example.com/reset-password?token=kdjfioewu9238u2riwuwifs
Using a guessable token can make it vulnerable, for example, using such a syntax is ridiculous:
https://account.example.com/reset-password?user=sam
So application should use a safe algorithm to generate unique and one-time usable tokens. When a user visits the intended URL, the system should check whether the token exists on the back-end and which user it belongs to the token. The token must expire after a short period of time and immediately after resetting the password.
However some websites fail to validate the token, so attacker can abuse this to hack someone’s account.
If the password reset URL is generated dynamically based on some headers like Host, it’s may also be vulnerable to password reset poisoning.
Look at the example: a website’s password reset URL is generated dynamically using Host header, the main request is something like this:
POST /reset-password HTTP/2
Host: example.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 51
_token=ksjdflkdjfoisuefweu8riwkjf&email=user@email.com
The generated URL to reset password:
https://example.com/reset-password?token=kdjfioewu9238u2riwuwifs
Attacker changes the Host header:
POST /reset-password HTTP/2
Host: attacker.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 51
_token=ksjdflkdjfoisuefweu8riwkjf&email=user@email.com
The generated URL changes to this:
https://attacker.com/reset-password?token=kdjfioewu9238u2riwuwifs
So the token will be logged on attacker’s server, they’ll use it to takeover the victim’s account.
Even if server rejects any Host changes, attacker may override that header:
POST /reset-password HTTP/2
Host: example.com
X-Forwarded-Host: attacker.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 51
_token=ksjdflkdjfoisuefweu8riwkjf&email=user@email.com
Consequently, if the server doesn’t validate the Host header or blocks overriding headers, it can lead to a 1 Click Account Takeover.