CVE-2017-15735 (published 2017-10-22T18:29Z)

In phpMyFAQ before 2.9.9, there is Cross-Site Request Forgery (CSRF) for modifying a glossary.

Severity: MEDIUM
CVSS v2 base score: 6.8 (AV:N/AC:M/Au:N/C:P/I:P/A:P)
CVSS v3 base score: 8.8, HIGH (CVSS:3.0/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H)

Weakness information:
cwe_id: CWE-352
cwe_name: Cross-Site Request Forgery (CSRF)
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.
extended_description: When a web server is designed to receive a request from a client without any mechanism for verifying that it was intentionally sent, then it might be possible for an attacker to trick a client into making an unintentional request to the web server which will be treated as an authentic request. This can be done via a URL, image load, XMLHttpRequest, etc. and can result in exposure of data or unintended code execution.
         

Fix information:
repo_url: https://github.com/thorsten/phpMyFAQ
commit_details: repo_url: https://github.com/thorsten/phpMyFAQ
author: Thorsten Rinne
author_date: 2017-10-08 11:27:18+02:00
msg: Added missing CSRF protection
repository: repo_url: https://github.com/thorsten/phpMyFAQ
repo_name: thorsten/phpMyFAQ
description: phpMyFAQ - Open Source FAQ web application for PHP 8.1+ and MySQL, PostgreSQL and other databases
repo_language: PHP
owner: thorsten
file_changes: filename: glossary.add.php
change_type: ModificationType.MODIFY
diff: @@ -41,6 +41,8 @@ if (!defined('IS_VALID_PHPMYFAQ')) {
 if ($user->perm->checkRight($user->getUserId(), 'addglossary')) {
     ?>
                 <form class="form-horizontal" action="?action=saveglossary" method="post" accept-charset="utf-8">
+                    <input type="hidden" name="csrf" value="<?php echo $user->getCsrfTokenFromSession() ?>">
+
                     <div class="form-group">
                         <label class="col-lg-2 control-label" for="item"><?php echo $PMF_LANG['ad_glossary_item'];
     ?>:</label>

programming_language: PHP
filename: glossary.edit.php
change_type: ModificationType.MODIFY
diff: @@ -43,6 +43,7 @@ if ($user->perm->checkRight($user->getUserId(), 'editglossary')) {
     $glossaryItem = $glossary->getGlossaryItem($id);
     ?>
                 <form class="form-horizontal" action="?action=updateglossary" method="post" accept-charset="utf-8">
+                    <input type="hidden" name="csrf" value="<?php echo $user->getCsrfTokenFromSession() ?>">
                     <input type="hidden" name="id" value="<?php echo $glossaryItem['id'];
     ?>" />
                     <div class="form-group">

programming_language: PHP
filename: glossary.main.php
change_type: ModificationType.MODIFY
diff: @@ -9,11 +9,9 @@
  * obtain one at http://mozilla.org/MPL/2.0/.
  *
  * @category  phpMyFAQ
- *
  * @author    Thorsten Rinne <thorsten@phpmyfaq.de>
  * @copyright 2005-2017 phpMyFAQ Team
  * @license   http://www.mozilla.org/MPL/2.0/ Mozilla Public License Version 2.0
- *
  * @link      http://www.phpmyfaq.de
  * @since     2005-09-15
  */
@@ -42,12 +40,26 @@ if (!defined('IS_VALID_PHPMYFAQ')) {
         <div class="row">
             <div class="col-lg-12">
 <?php
+
+$csrfTokenFromPost = PMF_Filter::filterInput(INPUT_POST, 'csrf', FILTER_SANITIZE_STRING);
+$csrfTokenFromGet = PMF_Filter::filterInput(INPUT_GET, 'csrf', FILTER_SANITIZE_STRING);
+if (!isset($_SESSION['phpmyfaq_csrf_token']) || $_SESSION['phpmyfaq_csrf_token'] !== $csrfTokenFromPost) {
+    $csrfCheck = false;
+} else {
+    $csrfCheck = true;
+}
+if (!isset($_SESSION['phpmyfaq_csrf_token']) || $_SESSION['phpmyfaq_csrf_token'] !== $csrfTokenFromGet) {
+    $csrfCheckDelete = false;
+} else {
+    $csrfCheckDelete = true;
+}
+
 if ($user->perm->checkRight($user->getUserId(), 'addglossary') ||
     $user->perm->checkRight($user->getUserId(), 'editglossary') ||
     $user->perm->checkRight($user->getUserId(), 'delglossary')) {
     $glossary = new PMF_Glossary($faqConfig);
 
-    if ('saveglossary' == $action && $user->perm->checkRight($user->getUserId(), 'addglossary')) {
+    if ('saveglossary' == $action && $user->perm->checkRight($user->getUserId(), 'addglossary') && $csrfCheck) {
         $item = PMF_Filter::filterInput(INPUT_POST, 'item', FILTER_SANITIZE_SPECIAL_CHARS);
         $definition = PMF_Filter::filterInput(INPUT_POST, 'definition', FILTER_SANITIZE_SPECIAL_CHARS);
         if ($glossary->addGlossaryItem($item, $definition)) {
@@ -61,7 +73,7 @@ if ($user->perm->checkRight($user->getUserId(), 'addglossary') ||
         }
     }
 
-    if ('updateglossary' == $action && $user->perm->checkRight($user->getUserId(), 'editglossary')) {
+    if ('updateglossary' == $action && $user->perm->checkRight($user->getUserId(), 'editglossary') && $csrfCheck) {
         $id = PMF_Filter::filterInput(INPUT_POST, 'id', FILTER_VALIDATE_INT);
         $item = PMF_Filter::filterInput(INPUT_POST, 'item', FILTER_SANITIZE_SPECIAL_CHARS);
         $definition = PMF_Filter::filterInput(INPUT_POST, 'definition', FILTER_SANITIZE_SPECIAL_CHARS);
@@ -76,7 +88,7 @@ if ($user->perm->checkRight($user->getUserId(), 'addglossary') ||
         }
     }
 
-    if ('deleteglossary' == $action && $user->perm->checkRight($user->getUserId(), 'editglossary')) {
+    if ('deleteglossary' == $action && $user->perm->checkRight($user->getUserId(), 'editglossary') && $csrfCheckDelete) {
         $id = PMF_Filter::filterInput(INPUT_GET, 'id', FILTER_VALIDATE_INT);
         if ($glossary->deleteGlossaryItem($id)) {
             echo '<p class="alert alert-success"><a href="#" class="close" data-dismiss="alert">×</a>';
@@ -111,10 +123,12 @@ if ($user->perm->checkRight($user->getUserId(), 'addglossary') ||
             $items['definition']
         );
         printf(
-            '<td><a class="btn btn-danger" onclick="return confirm(\'%s\'); return false;" href="%s%d">',
+            '<td><a class="btn btn-danger" onclick="return confirm(\'%s\'); return false;" href="%s%d%s%s">',
             $PMF_LANG['ad_user_del_3'],
             '?action=deleteglossary&amp;id=',
-            $items['id']
+            $items['id'],
+            '&csrf=',
+            $user->getCsrfTokenFromSession()
         );
         printf(
             '<span title="%s"><i aria-hidden="true" class="fa fa-trash-o"></i></span></a></td>',

programming_language: PHP