Skip to content

Commit 8f5bd21

Browse files
committed
[Security] Address 2FA bypass in password reset functionality
Thanks to Trixter#0001 on Discord for this security report. There was a two-factor authentication bypass present in all previous versions of Pterodactyl that would allow a user to login without providing a token by going through the password reset process. A person would still have to have access to the targeted account's email, but if they did manage to get a password reset link they would be able to reset the account password and then proceede to login without a token being required. This logic has since been changed to check if 2FA is enabled on an account, and if so they will NOT be logged in when their password is changed. This will force them to continue through the normal login pathway where a token will be needed. Overall the impact of this issue is minor, but I am still addressing it and disclosing the mechanism behind it.
1 parent 422e5dd commit 8f5bd21

File tree

1 file changed

+92
-0
lines changed

1 file changed

+92
-0
lines changed

app/Http/Controllers/Auth/ResetPasswordController.php

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,14 @@
22

33
namespace Pterodactyl\Http\Controllers\Auth;
44

5+
use Illuminate\Support\Str;
6+
use Prologue\Alerts\AlertsMessageBag;
7+
use Illuminate\Contracts\Hashing\Hasher;
8+
use Illuminate\Auth\Events\PasswordReset;
9+
use Illuminate\Contracts\Events\Dispatcher;
510
use Pterodactyl\Http\Controllers\Controller;
611
use Illuminate\Foundation\Auth\ResetsPasswords;
12+
use Pterodactyl\Contracts\Repository\UserRepositoryInterface;
713

814
class ResetPasswordController extends Controller
915
{
@@ -16,6 +22,47 @@ class ResetPasswordController extends Controller
1622
*/
1723
public $redirectTo = '/';
1824

25+
/**
26+
* @var bool
27+
*/
28+
protected $hasTwoFactor = false;
29+
30+
/**
31+
* @var \Prologue\Alerts\AlertsMessageBag
32+
*/
33+
private $alerts;
34+
35+
/**
36+
* @var \Illuminate\Contracts\Events\Dispatcher
37+
*/
38+
private $dispatcher;
39+
40+
/**
41+
* @var \Illuminate\Contracts\Hashing\Hasher
42+
*/
43+
private $hasher;
44+
45+
/**
46+
* @var \Pterodactyl\Contracts\Repository\UserRepositoryInterface
47+
*/
48+
private $userRepository;
49+
50+
/**
51+
* ResetPasswordController constructor.
52+
*
53+
* @param \Prologue\Alerts\AlertsMessageBag $alerts
54+
* @param \Illuminate\Contracts\Events\Dispatcher $dispatcher
55+
* @param \Illuminate\Contracts\Hashing\Hasher $hasher
56+
* @param \Pterodactyl\Contracts\Repository\UserRepositoryInterface $userRepository
57+
*/
58+
public function __construct(AlertsMessageBag $alerts, Dispatcher $dispatcher, Hasher $hasher, UserRepositoryInterface $userRepository)
59+
{
60+
$this->alerts = $alerts;
61+
$this->dispatcher = $dispatcher;
62+
$this->hasher = $hasher;
63+
$this->userRepository = $userRepository;
64+
}
65+
1966
/**
2067
* Return the rules used when validating password reset.
2168
*
@@ -29,4 +76,49 @@ protected function rules(): array
2976
'password' => 'required|confirmed|min:8',
3077
];
3178
}
79+
80+
/**
81+
* Reset the given user's password. If the user has two-factor authentication enabled on their
82+
* account do not automatically log them in. In those cases, send the user back to the login
83+
* form with a note telling them their password was changed and to log back in.
84+
*
85+
* @param \Illuminate\Contracts\Auth\CanResetPassword|\Pterodactyl\Models\User $user
86+
* @param string $password
87+
*
88+
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
89+
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
90+
*/
91+
protected function resetPassword($user, $password)
92+
{
93+
$user = $this->userRepository->update($user->id, [
94+
'password' => $this->hasher->make($password),
95+
$user->getRememberTokenName() => Str::random(60),
96+
]);
97+
98+
$this->dispatcher->dispatch(new PasswordReset($user));
99+
100+
// If the user is not using 2FA log them in, otherwise skip this step and force a
101+
// fresh login where they'll be prompted to enter a token.
102+
if (! $user->use_totp) {
103+
$this->guard()->login($user);
104+
}
105+
106+
$this->hasTwoFactor = $user->use_totp;
107+
}
108+
109+
/**
110+
* Get the response for a successful password reset.
111+
*
112+
* @param string $response
113+
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Http\JsonResponse
114+
*/
115+
protected function sendResetResponse($response)
116+
{
117+
if ($this->hasTwoFactor) {
118+
$this->alerts->success('Your password was successfully updated. Please log in to continue.')->flash();
119+
}
120+
121+
return redirect($this->hasTwoFactor ? route('auth.login') : $this->redirectPath())
122+
->with('status', trans($response));
123+
}
32124
}

0 commit comments

Comments
 (0)