CVE-2016-9127: Cross-Site Request Forgery (CSRF) (CWE-352)
value: Revive Adserver before 3.2.3 suffers from Cross-Site Request Forgery (CSRF). The password recovery form in Revive Adserver is vulnerable to CSRF attacks. This vulnerability could be exploited to send a large number of password recovery emails to the registered users, especially in conjunction with a bug that caused recovery emails to be sent to all the users at once. Both issues have been fixed.
CWE description: The web application does not, or can not, sufficiently verify whether a well-formed, valid, consistent request was intentionally provided by the user who submitted the request.
CVSS v3 base score: 8.8
Published: 2017-03-28T02:59Z
Repository: https://github.com/revive-adserver/revive-adserver
Language: PHP
Files changed: lib/OA/Admin/PasswordRecovery.php
Commit date: 2016-03-01 16:23:09 +0100
Commit message: Fix h1 report 99452

Cross-Site Request Forgery (CSRF)
---------------------------------

An undisclosed user has reported via HackerOne that the password recovery form
in Revive Adserver was vulnerable to CSRF attacks. Such vulnerability could be
exploited to send a large number of password recovery emails to the
registered users, especially in conjunction with a bug that caused recovery
emails to be sent to all the users at once. Both issues have been fixed.

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

CWE: CWE-352
CVSSv2: 5 (AV:N/AC:L/Au:N/C:N/I:N/A:P)

Diff: diff --git a/lib/OA/Admin/PasswordRecovery.php b/lib/OA/Admin/PasswordRecovery.php
index 91455ed2e1..a293e9d50e 100644
--- a/lib/OA/Admin/PasswordRecovery.php
+++ b/lib/OA/Admin/PasswordRecovery.php
@@ -97,10 +97,12 @@ class OA_Admin_PasswordRecovery
      * This method, combined with handleGet allows semantic, REST-style
      * actions.
      */
     function handlePost($vars)
     {
+        OA_Permission::checkSessionToken();
+
         $this->pageHeader();
         if (empty($vars['id'])) {
             if (empty($vars['email'])) {
                 $this->displayRecoveryRequestForm($GLOBALS['strEmailRequired']);
             } else {
@@ -150,10 +152,12 @@ class OA_Admin_PasswordRecovery
             echo "<span class='tab-r'>{$errormessage}</span></div>";
         }
 
         echo "<form method='post' action='password-recovery.php'>\n";
 
+        echo "<input type='hidden' name='token' value='".phpAds_SessionGetToken()."'/>\n";
+
         echo "<div class='install'>".$GLOBALS['strPwdRecEnterEmail']."</div>";
         echo "<table cellpadding='0' cellspacing='0' border='0'>";
         echo "<tr><td colspan='2'><img src='" . OX::assetPath() . "/images/break-el.gif' width='400' height='1' vspace='8'></td></tr>";
         echo "<tr height='24'><td>".$GLOBALS['strEMail'].":&nbsp;</td><td><input type='text' name='email' /></td></tr>";
         echo "<tr height='24'><td>&nbsp;</td><td><input type='submit' value='".$GLOBALS['strProceed']."' /></td></tr>";
@@ -175,10 +179,11 @@ class OA_Admin_PasswordRecovery
             echo "<span class='tab-r'>{$errormessage}</span></div>";
         }
 
         echo "<form method='post' action='password-recovery.php'>\n";
         echo "<input type='hidden' name='id' value=\"".htmlspecialchars($id)."\" />";
+        echo "<input type='hidden' name='token' value='".phpAds_SessionGetToken()."'/>\n";
 
         echo "<div class='install'>".$GLOBALS['strPwdRecEnterPassword']."</div>";
         echo "<table cellpadding='0' cellspacing='0' border='0'>";
         echo "<tr><td colspan='2'><img src='" . OX::assetPath() . "/images/break-el.gif' width='400' height='1' vspace='8'></td></tr>";
         echo "<tr height='24'><td>".$GLOBALS['strPassword'].":&nbsp;</td><td><input type='password' name='newpassword' /></td></tr>";

Fixed code:         OA_Permission::checkSessionToken();

        echo "<input type='hidden' name='token' value='".phpAds_SessionGetToken()."'/>\n";

        echo "<input type='hidden' name='token' value='".phpAds_SessionGetToken()."'/>\n";