CVE-ID:CVE-2026-71625

Summary

The password reset endpoint validates only channel, code, and account when checking an email/SMS verification code. It does not verify whether the code is still within its allowed validity window. As a result, any historical verification code that remains undeleted in the database can be reused to reset the victim account password even after the code has expired.

This can lead to account takeover if an attacker obtains an old verification code through SMS/email leakage, logs, client-side storage, or weak operational processes.

Affected Code

app/Http/Controllers/APIs/V2/ResetPasswordController.php

$verificationCode = $verificationCodeModel->where('channel', $request->input('verifiable_type'))
    ->where('code', $request->input('verifiable_code'))
    ->where('account', $account)
    ->first();

The query does not call byValid() or any equivalent expiry check.

For comparison, the API login flow does enforce a validity window:

$verify = VerificationCode::query()->where('account', $login)
    ->where('channel', $field == 'phone' ? 'sms' : 'mail')
    ->where('code', $code)
    ->byValid(120)
    ->orderby('id', 'desc')
    ->first();

The registration flow has the same expiry-validation issue:

$verify = VerificationCode::where('account',
    $channel == 'mail' ? $email : $phone)
    ->where('channel', $channel)
    ->where('code', $code)
    ->orderby('id', 'desc')
    ->first();

Attack Preconditions