CVE-2019-18986: Improper Restriction of Excessive Authentication Attempts (CWE-307)
value: Pimcore before 6.2.2 allow attackers to brute-force (guess) valid usernames by using the 'forgot password' functionality as it returns distinct messages for invalid password and non-existing users.
CWE description: The product does not implement sufficient measures to prevent multiple failed authentication attempts within a short time frame, making it more susceptible to brute force attacks.
CVSS v3 base score: 7.5
Published: 2019-11-15T05:15Z
Repository: https://github.com/pimcore/pimcore
Language: PHP
Files changed: bundles/AdminBundle/Controller/Admin/LoginController.php, bundles/AdminBundle/Resources/views/Admin/Login/lostpassword.html.php, bundles/CoreBundle/Resources/translations/en.json
Commit date: 2019-10-21 16:47:15 +0200
Commit message: Lost password service: do not expose info whether a user exists or not (avoid brute-force attacks)

Diff: diff --git a/bundles/AdminBundle/Controller/Admin/LoginController.php b/bundles/AdminBundle/Controller/Admin/LoginController.php
index bd4a96d5af8..fc211623e32 100644
--- a/bundles/AdminBundle/Controller/Admin/LoginController.php
+++ b/bundles/AdminBundle/Controller/Admin/LoginController.php
@@ -20,10 +20,11 @@ use Pimcore\Bundle\AdminBundle\Security\BruteforceProtectionHandler;
 use Pimcore\Config;
 use Pimcore\Controller\Configuration\TemplatePhp;
 use Pimcore\Controller\EventedControllerInterface;
 use Pimcore\Event\Admin\Login\LostPasswordEvent;
 use Pimcore\Event\AdminEvents;
+use Pimcore\Logger;
 use Pimcore\Model\User;
 use Pimcore\Templating\Model\ViewModel;
 use Pimcore\Tool;
 use Pimcore\Tool\Authentication;
 use Symfony\Component\HttpFoundation\RedirectResponse;
@@ -114,33 +115,32 @@ class LoginController extends AdminController implements BruteforceProtectedCont
      * @TemplatePhp()
      */
     public function lostpasswordAction(Request $request, BruteforceProtectionHandler $bruteforceProtectionHandler)
     {
         $view = $this->buildLoginPageViewModel();
-        $view->success = false;
+        $error = null;
 
-        // TODO is the error on the view used somewhere?
         if ($request->getMethod() === 'POST' && $username = $request->get('username')) {
             $user = User::getByName($username);
 
             if ($user instanceof User) {
                 if (!$user->isActive()) {
-                    $view->error = 'user inactive';
+                    $error = 'user inactive';
                 }
 
                 if (!$user->getEmail()) {
-                    $view->error = 'user has no email address';
+                    $error = 'user has no email address';
                 }
 
                 if (!$user->getPassword()) {
-                    $view->error = 'user has no password';
+                    $error = 'user has no password';
                 }
             } else {
-                $view->error = 'user unknown';
+                $error = 'user unknown';
             }
 
-            if (!$view->error) {
+            if (!$error) {
                 $token = Authentication::generateToken($username, $user->getPassword());
 
                 $loginUrl = $this->generateUrl('pimcore_admin_login_check', [
                     'username' => $username,
                     'token' => $token,
@@ -161,18 +161,17 @@ class LoginController extends AdminController implements BruteforceProtectedCont
 
                     // directly return event response
                     if ($event->hasResponse()) {
                         return $event->getResponse();
                     }
-
-                    $view->success = true;
                 } catch (\Exception $e) {
-                    $view->error = 'could not send email';
+                    $error = 'could not send email';
                 }
             }
 
-            if ($view->error) {
+            if ($error) {
+                Logger::error('Lost password service: ' . $error);
                 $bruteforceProtectionHandler->addEntry($request->get('username'), $request);
             }
         }
 
         return $view;
diff --git a/bundles/AdminBundle/Resources/views/Admin/Login/lostpassword.html.php b/bundles/AdminBundle/Resources/views/Admin/Login/lostpassword.html.php
index 25ee4453fcc..65d4d353431 100644
--- a/bundles/AdminBundle/Resources/views/Admin/Login/lostpassword.html.php
+++ b/bundles/AdminBundle/Resources/views/Admin/Login/lostpassword.html.php
@@ -6,27 +6,20 @@ $this->get("translate")->setDomain("admin");
 
 ?>
 
 
 
-<?php if ($this->success) { ?>
-    <div class="text info">
+<?php if ($this->getRequest()->getMethod() === 'POST') { ?>
+    <div class="text error">
         <?= $this->translate("A temporary login link has been sent to your email address."); ?>
         <br/>
         <?= $this->translate("Please check your mailbox."); ?>
     </div>
 <?php } else { ?>
-    <?php if ($this->error) { ?>
-        <div class="text error">
-            <?= $this->translate('lostpassword_reset_error'); ?>
-        </div>
-    <?php } else { ?>
-        <div class="text info">
-            <?= $this->translate("Enter your username and pimcore will send a login link to your email address"); ?>
-        </div>
-    <?php } ?>
-
+    <div class="text info">
+        <?= $this->translate("Enter your username and pimcore will send a login link to your email address"); ?>
+    </div>
 
     <form method="post" action="<?= $view->router()->path('pimcore_admin_login_lostpassword') ?>">
         <input type="text" name="username" placeholder="<?= $this->translate("Username"); ?>" required autofocus>
         <button type="submit" name="submit"><?= $this->translate("Submit"); ?></button>
     </form>
diff --git a/bundles/CoreBundle/Resources/translations/en.json b/bundles/CoreBundle/Resources/translations/en.json
index 52ac2dfe8a6..08ccd1ceaee 100644
--- a/bundles/CoreBundle/Resources/translations/en.json
+++ b/bundles/CoreBundle/Resources/translations/en.json
@@ -145,11 +145,10 @@
   "reports": "Reports",
   "roles": "Roles",
   "send": "Send",
   "Password": "Password",
   "Forgot your password": "Forgot your password",
-  "lostpassword_reset_error": "There was an error while sending the lost password info. Please try again or contact your administrator.",
   "Back to Login": "Back to Login",
   "Enter your username and pimcore will send a login link to your email address": "Enter your username and Pimcore will send a login link to your email address",
   "Please check your mailbox.": "Please check your mailbox.",
   "Login": "Login",
   "Submit": "Submit",

Vulnerable code:         $view->success = false;
        // TODO is the error on the view used somewhere?
                    $view->error = 'user inactive';
                    $view->error = 'user has no email address';
                    $view->error = 'user has no password';
                $view->error = 'user unknown';
            if (!$view->error) {

                    $view->success = true;
                    $view->error = 'could not send email';
            if ($view->error) {
<?php if ($this->success) { ?>
    <div class="text info">
    <?php if ($this->error) { ?>
        <div class="text error">
            <?= $this->translate('lostpassword_reset_error'); ?>
        </div>
    <?php } else { ?>
        <div class="text info">
            <?= $this->translate("Enter your username and pimcore will send a login link to your email address"); ?>
        </div>
    <?php } ?>

  "lostpassword_reset_error": "There was an error while sending the lost password info. Please try again or contact your administrator.",
Fixed code: use Pimcore\Logger;
        $error = null;
                    $error = 'user inactive';
                    $error = 'user has no email address';
                    $error = 'user has no password';
                $error = 'user unknown';
            if (!$error) {
                    $error = 'could not send email';
            if ($error) {
                Logger::error('Lost password service: ' . $error);
<?php if ($this->getRequest()->getMethod() === 'POST') { ?>
    <div class="text error">
    <div class="text info">
        <?= $this->translate("Enter your username and pimcore will send a login link to your email address"); ?>
    </div>