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

Fix username enumeration #1

Merged
merged 1 commit into from
Aug 3, 2020
Merged

Fix username enumeration #1

merged 1 commit into from
Aug 3, 2020

Conversation

mufeedvh
Copy link

@mufeedvh mufeedvh commented Jul 29, 2020

馃搳 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: pagekit#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.

@JamieSlome
Copy link

@mufeedvh - make sure you have your bounty URL included in your comments!

Copy link

@Mik317 Mik317 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @mufeedvh 馃槃
great fix!!! The only problem I think could make the fix bypassable is the color of the message (if the frontend receives a exception, won't the message color be red and if successfull green even if the message is the same?).

Regards,
Mik

Copy link

@toufik-airane toufik-airane left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice. 馃憤

huntr

@JamieSlome JamieSlome merged commit f925b8f into 418sec:develop Aug 3, 2020
@huntr-helper
Copy link
Member

Congratulations mufeedvh - your fix has been selected! 馃帀

Thanks for being part of the community & helping secure the world's open source code.
If you have any questions, please respond in the comments section. Your bounty is on its way - keep hunting!

@humantex
Copy link

humantex commented Aug 3, 2020

Referenced in my response here: pagekit#935 (comment)

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