Skip to content
This repository has been archived by the owner on Dec 1, 2023. It is now read-only.

Security Fix for User Enumeration - huntr.dev #954

Open
wants to merge 2 commits into
base: develop
Choose a base branch
from

Conversation

huntr-helper
Copy link

https://huntr.dev/users/mufeedvh has fixed the User Enumeration vulnerability 馃敤. mufeedvh has been awarded $25 for fixing the vulnerability through the huntr bug bounty program 馃挼. Think you could fix a vulnerability like this?

Get involved at https://huntr.dev/

Q | A
Version Affected | ALL
Bug Fix | YES
Original Pull Request | 418sec#1
GitHub Issue URL | #935
Vulnerability README | https://github.com/418sec/huntr/blob/master/bounties/packagist/pagekit/1/README.md

User Comments:

馃搳 Metadata *

Bounty URL: https://www.huntr.dev/bounties/1-packagist-pagekit

鈿欙笍 Description *

pagekit is vulnerable to Username Enumeration as the response message on the Reset Password page when an email exists or vise-versa differs making it easy for an attacker to assume an account exists or not.

馃捇 Technical Description *

An attacker can know if a certain email/user exists or not just by giving in a victim's email address.

If the email exists, the request responds with:

Check your email for the confirmation link.

If it doesn't, it responds with:

Unknown email address.

The function resides in the /pagekit/app/system/modules/user/src/Controller/ResetPasswordController.php:

public function requestAction($email)
{
    try {

        if (App::user()->isAuthenticated()) {
            return App::redirect();
        }

        if (!App::csrf()->validate()) {
            throw new Exception(__('Invalid token. Please try again.'));
        }

        if (empty($email)) {
            throw new Exception(__('Enter a valid email address.'));
        }

        if (!$user = User::findByEmail($email)) {
            throw new Exception(__('Unknown email address.'));
        }

        if ($user->isBlocked()) {
            throw new Exception(__('Your account has not been activated or is blocked.'));
        }

        $key = App::get('auth.random')->generateString(32);
        $url = App::url('@user/resetpassword/confirm', compact('key'), 0);

        try {

            $mail = App::mailer()->create();
            $mail->setTo($user->email)
                ->setSubject(__('Reset password for %site%.', ['%site%' => App::module('system/site')->config('title')]))
                ->setBody(App::view('system/user:mails/reset.php', compact('user', 'url', 'mail')), 'text/html')
                ->send();

        } catch (\Exception $e) {
            throw new Exception(__('Unable to send confirmation link.'));
        }

        $user->activation = $key;
        $user->save();

        App::message()->success(__('Check your email for the confirmation link.'));

        return App::redirect('@user/login');

    } catch (Exception $e) {
        return [
            '$view' => [
                'title' => __('Reset'),
                'name' => 'system/user/reset-request.php',
            ],
            'error' => $e->getMessage()
        ];
    }
}

As you can see, the response comes from these exceptions:

if (!$user = User::findByEmail($email)) {
    throw new Exception(__('Unknown email address.'));
}

and

App::message()->success(__('Check your email for the confirmation link.'));

Just changing these strings are enough to fix the issue. To not enable the attacker to enumerate users, we can change the strings to:

If this email exists, you will receive an email with the reset instructions.

馃悰 Proof of Concept (PoC) *

The PoC is nicely detailed in this issue: #935

poc-pagekit-1

poc-pagekit-2

As you can see it's just a string response, so changing them are enough to fix the issue.

馃敟 Proof of Fix (PoF) *

Changed the two strings to If this email exists, you will receive an email with the reset instructions. making it unable for an attacker to identify account existence.

if (!$user = User::findByEmail($email)) {
    throw new Exception(__('If this email exists, you will receive an email with the reset instructions.'));
}
App::message()->success(__('If this email exists, you will receive an email with the reset instructions.'));

馃憤 User Acceptance Testing (UAT)

Just a string change, no breaking changes are introduced.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants