Vulnerability: In phpMyFAQ before 2.9.9, there is Cross-Site Request Forgery (CSRF) in admin/stat.adminlog.php.
Vulnerability ID: CVE-2017-15731
CWE: CWE-352
Severity score: 6.4
Project: https://github.com/thorsten/phpMyFAQ
Published: 2017-10-22
File: phpmyfaq/admin/stat.adminlog.php
Commit message: Added missing CSRF protection
Commit author: Thorsten Rinne

Vulnerable version (before fix):
     $perpage = 15;     $pages = PMF_Filter::filterInput(INPUT_GET, 'pages', FILTER_VALIDATE_INT);     $page = PMF_Filter::filterInput(INPUT_GET, 'page', FILTER_VALIDATE_INT, 1);      if (is_null($pages)) {         $pages = round(($logging->getNumberOfEntries() + ($perpage / 3)) / $perpage, 0);     }      $start = ($page - 1) * $perpage;     $ende = $start + $perpage;                  <i aria-hidden="true" class="fa fa-tasks"></i> <?php echo $PMF_LANG['ad_menu_adminlog'];     ?>                 <div class="pull-right">                     <a class="btn btn-danger" href="?action=deleteadminlog">                         <i aria-hidden="true" class="fa fa-trash"></i> <?php echo $PMF_LANG['ad_adminlog_del_older_30d'] ?>                     </a>                 </div>         ?></td>             <td><small><?php             $text = $logging_value['text'];         $text = str_replace('Loginerror', $PMF_LANG['ad_log_lger'], $text);         $text = str_replace('Session expired', $PMF_LANG['ad_log_sess'], $text);         $text = str_replace('Useredit, ', $PMF_LANG['ad_log_edit'], $text);         $text = str_replace('Beitragcreatesave', $PMF_LANG['ad_log_crsa'], $text);         $text = str_replace('Beitragcreate', $PMF_LANG['ad_log_crea'], $text);         $text = str_replace('Usersave, ', $PMF_LANG['ad_log_ussa'], $text);         $text = str_replace('Userdel, ', $PMF_LANG['ad_log_usde'], $text);         $text = str_replace('Beitragedit, ', $PMF_LANG['ad_log_beed'], $text);         $text = str_replace('Beitragdel, ', $PMF_LANG['ad_log_bede'], $text);         echo $text;         ?></small>             </td>         </tr> <?php  <?php  } elseif ($user->perm->checkRight($user->getUserId(), 'adminlog') && 'deleteadminlog' == $action) {     if ($logging->delete()) {         printf('<p class="alert alert-success">%s</p>', $PMF_LANG['ad_adminlog_delete_success']);     } else {

Fixed version (after fix):
     $perpage = 15;     $pages = PMF_Filter::filterInput(INPUT_GET, 'pages', FILTER_VALIDATE_INT);     $page = PMF_Filter::filterInput(INPUT_GET, 'page', FILTER_VALIDATE_INT, 1);     $csrfToken = PMF_Filter::filterInput(INPUT_GET, 'csrf', FILTER_SANITIZE_STRING);      if (is_null($pages)) {         $pages = round(($logging->getNumberOfEntries() + ($perpage / 3)) / $perpage, 0);     }      if (!isset($_SESSION['phpmyfaq_csrf_token']) || $_SESSION['phpmyfaq_csrf_token'] !== $csrfToken) {         $deleteLog = false;     } else {         $deleteLog = true;     }      $start = ($page - 1) * $perpage;     $ende = $start + $perpage;                  <i aria-hidden="true" class="fa fa-tasks"></i> <?php echo $PMF_LANG['ad_menu_adminlog'];     ?>                 <div class="pull-right">                     <a class="btn btn-danger"                        href="?action=deleteadminlog&csrf=<?php echo $user->getCsrfTokenFromSession() ?>">                         <i aria-hidden="true" class="fa fa-trash"></i> <?php echo $PMF_LANG['ad_adminlog_del_older_30d'] ?>                     </a>                 </div>         ?></td>             <td><small><?php             $text = $logging_value['text'];             $text = str_replace('Loginerror', $PMF_LANG['ad_log_lger'], $text);             $text = str_replace('Session expired', $PMF_LANG['ad_log_sess'], $text);             $text = str_replace('Useredit, ', $PMF_LANG['ad_log_edit'], $text);             $text = str_replace('Beitragcreatesave', $PMF_LANG['ad_log_crsa'], $text);             $text = str_replace('Beitragcreate', $PMF_LANG['ad_log_crea'], $text);             $text = str_replace('Usersave, ', $PMF_LANG['ad_log_ussa'], $text);             $text = str_replace('Userdel, ', $PMF_LANG['ad_log_usde'], $text);             $text = str_replace('Beitragedit, ', $PMF_LANG['ad_log_beed'], $text);             $text = str_replace('Beitragdel, ', $PMF_LANG['ad_log_bede'], $text);             echo $text;             ?></small>             </td>         </tr> <?php  <?php  } elseif ($user->perm->checkRight($user->getUserId(), 'adminlog') && 'deleteadminlog' == $action && $deleteLog) {     if ($logging->delete()) {         printf('<p class="alert alert-success">%s</p>', $PMF_LANG['ad_adminlog_delete_success']);     } else {

Patch:
patch: @@ -35,11 +35,18 @@
     $perpage = 15;
     $pages = PMF_Filter::filterInput(INPUT_GET, 'pages', FILTER_VALIDATE_INT);
     $page = PMF_Filter::filterInput(INPUT_GET, 'page', FILTER_VALIDATE_INT, 1);
+    $csrfToken = PMF_Filter::filterInput(INPUT_GET, 'csrf', FILTER_SANITIZE_STRING);
 
     if (is_null($pages)) {
         $pages = round(($logging->getNumberOfEntries() + ($perpage / 3)) / $perpage, 0);
     }
 
+    if (!isset($_SESSION['phpmyfaq_csrf_token']) || $_SESSION['phpmyfaq_csrf_token'] !== $csrfToken) {
+        $deleteLog = false;
+    } else {
+        $deleteLog = true;
+    }
+
     $start = ($page - 1) * $perpage;
     $ende = $start + $perpage;
 
@@ -66,7 +73,8 @@
                 <i aria-hidden="true" class="fa fa-tasks"></i> <?php echo $PMF_LANG['ad_menu_adminlog'];
     ?>
                 <div class="pull-right">
-                    <a class="btn btn-danger" href="?action=deleteadminlog">
+                    <a class="btn btn-danger"
+                       href="?action=deleteadminlog&csrf=<?php echo $user->getCsrfTokenFromSession() ?>">
                         <i aria-hidden="true" class="fa fa-trash"></i> <?php echo $PMF_LANG['ad_adminlog_del_older_30d'] ?>
                     </a>
                 </div>
@@ -122,17 +130,17 @@
         ?></td>
             <td><small><?php
             $text = $logging_value['text'];
-        $text = str_replace('Loginerror', $PMF_LANG['ad_log_lger'], $text);
-        $text = str_replace('Session expired', $PMF_LANG['ad_log_sess'], $text);
-        $text = str_replace('Useredit, ', $PMF_LANG['ad_log_edit'], $text);
-        $text = str_replace('Beitragcreatesave', $PMF_LANG['ad_log_crsa'], $text);
-        $text = str_replace('Beitragcreate', $PMF_LANG['ad_log_crea'], $text);
-        $text = str_replace('Usersave, ', $PMF_LANG['ad_log_ussa'], $text);
-        $text = str_replace('Userdel, ', $PMF_LANG['ad_log_usde'], $text);
-        $text = str_replace('Beitragedit, ', $PMF_LANG['ad_log_beed'], $text);
-        $text = str_replace('Beitragdel, ', $PMF_LANG['ad_log_bede'], $text);
-        echo $text;
-        ?></small>
+            $text = str_replace('Loginerror', $PMF_LANG['ad_log_lger'], $text);
+            $text = str_replace('Session expired', $PMF_LANG['ad_log_sess'], $text);
+            $text = str_replace('Useredit, ', $PMF_LANG['ad_log_edit'], $text);
+            $text = str_replace('Beitragcreatesave', $PMF_LANG['ad_log_crsa'], $text);
+            $text = str_replace('Beitragcreate', $PMF_LANG['ad_log_crea'], $text);
+            $text = str_replace('Usersave, ', $PMF_LANG['ad_log_ussa'], $text);
+            $text = str_replace('Userdel, ', $PMF_LANG['ad_log_usde'], $text);
+            $text = str_replace('Beitragedit, ', $PMF_LANG['ad_log_beed'], $text);
+            $text = str_replace('Beitragdel, ', $PMF_LANG['ad_log_bede'], $text);
+            echo $text;
+            ?></small>
             </td>
         </tr>
 <?php
@@ -144,7 +152,7 @@
 
 <?php
 
-} elseif ($user->perm->checkRight($user->getUserId(), 'adminlog') && 'deleteadminlog' == $action) {
+} elseif ($user->perm->checkRight($user->getUserId(), 'adminlog') && 'deleteadminlog' == $action && $deleteLog) {
     if ($logging->delete()) {
         printf('<p class="alert alert-success">%s</p>', $PMF_LANG['ad_adminlog_delete_success']);
     } else {
path: phpmyfaq/admin/stat.adminlog.php