CVE-2017-15733: Cross-Site Request Forgery (CSRF) (CWE-352)
value: In phpMyFAQ before 2.9.9, there is Cross-Site Request Forgery (CSRF) in admin/ajax.attachment.php and admin/att.main.php.
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-10-22T18:29Z
Repository: https://github.com/thorsten/phpMyFAQ
Language: PHP
Files changed: phpmyfaq/admin/ajax.attachment.php, phpmyfaq/admin/att.main.php
Commit date: 2017-10-08 11:00:38 +0200
Commit message: Added missing CSRF protection

Diff: diff --git a/phpmyfaq/admin/ajax.attachment.php b/phpmyfaq/admin/ajax.attachment.php
index 304eaca8d..a7ca90cdd 100644
--- a/phpmyfaq/admin/ajax.attachment.php
+++ b/phpmyfaq/admin/ajax.attachment.php
@@ -8,15 +8,13 @@
  * This Source Code Form is subject to the terms of the Mozilla Public License,
  * v. 2.0. If a copy of the MPL was not distributed with this file, You can
  * obtain one at http://mozilla.org/MPL/2.0/.
  *
  * @category  phpMyFAQ
- *
  * @author    Anatoliy Belsky <anatoliy.belsky@mayflower.de>
  * @copyright 2010-2017 phpMyFAQ Team
  * @license   http://www.mozilla.org/MPL/2.0/ Mozilla Public License Version 2.0
- *
  * @link      http://www.phpmyfaq.de
  * @since     2010-12-20
  */
 if (!defined('IS_VALID_PHPMYFAQ')) {
     $protocol = 'http';
@@ -27,19 +25,26 @@ if (!defined('IS_VALID_PHPMYFAQ')) {
     exit();
 }
 
 $ajaxAction = PMF_Filter::filterInput(INPUT_GET, 'ajaxaction', FILTER_SANITIZE_STRING);
 $attId = PMF_Filter::filterInput(INPUT_GET, 'attId', FILTER_VALIDATE_INT);
+$csrfToken = PMF_Filter::filterInput(INPUT_GET, 'csrf', FILTER_SANITIZE_STRING);
 
 $att = PMF_Attachment_Factory::create($attId);
 
 if ($att) {
     switch ($ajaxAction) {
         case 'delete':
+
+            if (!isset($_SESSION['phpmyfaq_csrf_token']) || $_SESSION['phpmyfaq_csrf_token'] !== $csrfToken) {
+                echo $PMF_LANG['err_NotAuth'];
+                exit(1);
+            }
+
             if ($att->delete()) {
-                print $PMF_LANG['msgAttachmentsDeleted'];
+                echo $PMF_LANG['msgAttachmentsDeleted'];
             } else {
-                print $PMF_LANG['ad_att_delfail'];
+                echo $PMF_LANG['ad_att_delfail'];
             }
             break;
     }
 }
diff --git a/phpmyfaq/admin/att.main.php b/phpmyfaq/admin/att.main.php
index 238c17848..c83cf7df9 100644
--- a/phpmyfaq/admin/att.main.php
+++ b/phpmyfaq/admin/att.main.php
@@ -68,11 +68,11 @@ $pagination = new PMF_Pagination(
                             <td><?php echo $item->filename ?></td>
                             <td><?php echo $item->record_lang ?></td>
                             <td><?php echo $item->filesize ?></td>
                             <td><?php echo $item->mime_type ?></td>
                             <td>
-                                <a href="javascript:deleteAttachment(<?php echo $item->id ?>); void(0);"
+                                <a href="javascript:deleteAttachment(<?php echo $item->id ?>, '<?php echo $user->getCsrfTokenFromSession() ?>'); void(0);"
                                    class="btn btn-danger" title="<?php echo $PMF_LANG['ad_gen_delete'] ?>">
                                     <i aria-hidden="true" class="fa fa-trash-o"></i>
                                 </a>
                             </td>
                         </tr>
@@ -90,19 +90,20 @@ $pagination = new PMF_Pagination(
         <script type="text/javascript">
         /**
          * Ajax call for deleting attachments
          *
          * @param att_id Attachment id
+         * @apram csrf CSRF token
          */
-        function deleteAttachment(att_id)
+        function deleteAttachment(att_id, csrf)
         {
             if (confirm('<?php echo $PMF_LANG['msgAttachmentsWannaDelete'] ?>')) {
                 $('#saving_data_indicator').html('<i aria-hidden="true" class="fa fa-spinner fa-spin"></i> Deleting ...');
                 $.ajax({
                     type:    "GET",
                     url:     "index.php?action=ajax&ajax=att&ajaxaction=delete",
-                    data:    {attId: att_id},
+                    data:    { attId: att_id, csrf: csrf},
                     success: function(msg) {
                         $('.att_' + att_id).fadeOut('slow');
                         $('#saving_data_indicator').html('<p class="alert alert-success">' + msg + '</p>');
                     }
                 });

Vulnerable code:  *
 *
                print $PMF_LANG['msgAttachmentsDeleted'];
                print $PMF_LANG['ad_att_delfail'];
                                <a href="javascript:deleteAttachment(<?php echo $item->id ?>); void(0);"
        function deleteAttachment(att_id)
                    data:    {attId: att_id},
Fixed code: $csrfToken = PMF_Filter::filterInput(INPUT_GET, 'csrf', FILTER_SANITIZE_STRING);

            if (!isset($_SESSION['phpmyfaq_csrf_token']) || $_SESSION['phpmyfaq_csrf_token'] !== $csrfToken) {
                echo $PMF_LANG['err_NotAuth'];
                exit(1);
            }

                echo $PMF_LANG['msgAttachmentsDeleted'];
                echo $PMF_LANG['ad_att_delfail'];
                                <a href="javascript:deleteAttachment(<?php echo $item->id ?>, '<?php echo $user->getCsrfTokenFromSession() ?>'); void(0);"
         * @apram csrf CSRF token
        function deleteAttachment(att_id, csrf)
                    data:    { attId: att_id, csrf: csrf},