CVE-2016-9124 (published 2017-03-28T02:59Z)

Revive Adserver before 3.2.3 suffers from Improper Restriction of Excessive Authentication Attempts. The login page of Revive Adserver is vulnerable to password-guessing attacks. An account lockdown feature was considered, but rejected to avoid introducing service disruptions to regular users during such attacks. A random delay has instead been introduced as a countermeasure in case of password failures, along with a system to discourage parallel brute forcing. These systems will effectively allow the valid users to log in to the adserver, even while an attack is in progress.

Severity: MEDIUM
CVSS v2 base score: 5.0 (AV:N/AC:L/Au:N/C:P/I:N/A:N)
CVSS v3 base score: 9.8, CRITICAL (CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H)

Weakness information:
cwe_id: CWE-287
cwe_name: Improper Authentication
description: When an actor claims to have a given identity, the product does not prove or insufficiently proves that the claim is correct.
extended_description: 
            
			
         
         

Fix information:
repo_url: https://github.com/revive-adserver/revive-adserver
commit_details: repo_url: https://github.com/revive-adserver/revive-adserver
author: Matteo Beccati
author_date: 2016-01-27 08:30:50+01:00
msg: Mitigate h1 report 96115

Improper Restriction of Excessive Authentication Attempts
---------------------------------------------------------

Karan M. Tank and Smit B. Shah have reported via HackerOne that the login
page of Revive Adserver was vulnerable to password-guessing attacks. An
account lockdown feature was taken into account, but rejected to avoid
introducing service disruptions to regular users during such attacks.
A random delay has been introduced as a counter-measure in case of password
failures, along with a system to discourage parallel brute forcing. Such
system will effectively allow the actual user to log in to the adserver even
while an attack is in progress.

A CVE-ID has been requested, but not assigned yet.

CWE: CWE-307
CVSSv2: 8.5 (AV:N/AC:L/Au:N/C:C/I:P/A:N)
repository: repo_url: https://github.com/revive-adserver/revive-adserver
repo_name: revive-adserver/revive-adserver
description: The world's most popular free, open source ad serving system. You can download the latest release at:
repo_language: PHP
owner: revive-adserver
file_changes: filename: Auth.php
change_type: ModificationType.MODIFY
diff: @@ -83,7 +83,6 @@ class OA_Auth
             $doUser = OA_Auth::authenticateUser();
 
             if (!$doUser) {
-                sleep(3);
                 OA_Auth::restart($GLOBALS['strUsernameOrPasswordWrong']);
             }
 

programming_language: PHP
filename: authentication.php
change_type: ModificationType.MODIFY
diff: @@ -15,6 +15,7 @@ require_once LIB_PATH . '/Plugin/Component.php';
 require_once 'Date.php';
 require_once MAX_PATH . '/lib/max/language/Loader.php';
 require_once MAX_PATH . '/lib/pear/HTML/QuickForm/Rule/Email.php';
+require_once MAX_PATH . '/lib/OA/DB/AdvisoryLock.php';
 
 Language_Loader::load('settings');
 
@@ -98,16 +99,55 @@ class Plugins_Authentication extends OX_Component
      */
     function checkPassword($username, $password)
     {
+        // Introduce a random delay in case of failures, as recommended by:
+        // https://www.owasp.org/index.php/Blocking_Brute_Force_Attacks
+        //
+        // The base delay is 1-5 seconds.
+        $waitMs = mt_rand(1000, 5000);
+
+        $oLock = OA_DB_AdvisoryLock::factory();
+
+        // Username check is case insensitive
+        $username = strtolower($username);
+
+        // Try to acquire an excusive advisory lock for the username
+        $lock = $oLock->get('auth.'.md5($username));
+
+        if (!$lock) {
+            // We couldn't acquire the lock immediately, which means that
+            // another authentication process for the same username is underway.
+            //
+            // This might mean that the account is being targeted by a
+            // multi-threaded brute force attack, so we try to discourage such
+            // behaviour by increasing the delay time by 4x.
+            //
+            // However, if the actual user tries to log in while their account
+            // is being attacked, we will allow them in, they'd just have to
+            // be patient (max 20 seconds).
+            usleep($waitMs * 4000);
+        }
+
         $doUser = OA_Dal::factoryDO('users');
-        $doUser->username = strtolower($username);
+        $doUser->username = $username;
         $doUser->password = md5($password);
+
         $doUser->find();
 
         if ($doUser->fetch()) {
+            $oLock->release();
+
             return $doUser;
-        } else {
-            return false;
         }
+
+        if ($lock) {
+            // The password was wrong, but no other login attempt was in place
+            // so we apply just the base delay time.
+            usleep($waitMs * 1000);
+        }
+
+        $oLock->release();
+
+        return false;
     }
 
     /**
@@ -517,4 +557,7 @@ class Plugins_Authentication extends OX_Component
     }
 }
 
-?>
\ No newline at end of file
+
+class Plugins_Authentication_Exception extends Exception
+{
+}

programming_language: PHP