In the module “Xen Forum” (xenforum) edited by App1pro, an authenticated user can perform SQL injection in affected versions. 2.13.0 fixed vulnerabilities.

Summary

  • CVE ID: CVE-2023-24763
  • Published at: 2023-03-06
  • Advisory source: Friends-of-Presta.org
  • Platform: PrestaShop
  • Product: xenforum
  • Impacted release: < 2.13.0
  • Product author: App1pro
  • Weakness: CWE-89
  • Severity: high (8.8)

Description

In Xen Forum module for PrestaShop up to 2.13.0, multiple sensitives SQL calls in class XenForumAttachment::getListAttachments() (or XenForumCat, XenForumPost, …) can be executed with a trivial http call and exploited to forge a bind SQL injection.

CVSS base metrics

  • Attack vector: network
  • Attack complexity: low
  • Privilege required: low
  • User interaction: none
  • Scope: unchanged
  • Confidentiality: high
  • Integrity: high
  • Availability: high

Vector string: CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H

Possible malicious usage

  • Technical and personal data leaks
  • Obtain admin access
  • Remove all data of the linked PrestaShop
  • Display sensitives tables to front-office to unlock potential admin’s ajax scripts of modules protected by token on the ecosystem

Proof of concept

curl -v --cookie-jar cookie.txt 'https://domain.tld/authentification?submitLogin=1&emailXXXX&password=YYY && \
curl -v --cookie cookie.txt 'http://domain.tld/index.php?fc=module&module=xenforum&id=194&controller=editpost&edit_post=1&attachments[]=3%29%3BSELECT%20SLEEP%2825%29%23'

Patch

--- a/classes/XenForumAttachment.php
+++ b/classes/XenForumAttachment.php
@@ -55,7 +55,7 @@ class XenForumAttachment extends ObjectM
     public static function getListAttachments($ids)
     {
         $sql = 'SELECT *, id_xenforum_attachment as id
-            FROM '._DB_PREFIX_.'xenforum_attachment WHERE id_xenforum_attachment IN ('.implode(',', $ids).');';
+            FROM '._DB_PREFIX_.'xenforum_attachment WHERE id_xenforum_attachment IN ('.implode(', ', array_map('intval', $ids)).');';
         return DB::getInstance()->executeS($sql);
     }
 }
diff -bpru a/classes/XenForumCat.php b/classes/XenForumCat.php
--- a/classes/XenForumCat.php
+++ b/classes/XenForumCat.php
@@ -60,7 +60,7 @@ class XenForumCat extends ObjectModel
             $ql .= ' AND (pc.`private` = 0';
             // Allow this user access to private forum
             if (!empty($validations)) {
-                $ql .= ' OR pc.`id_xenforum_cat` IN ('.pSQL($validations).')';
+                $ql .= ' OR pc.`id_xenforum_cat` IN ('.implode(', ', array_map('intval', $validations)).')';
             }
             $ql .= ')';
         }
@@ -82,7 +82,7 @@ class XenForumCat extends ObjectModel
             if (empty($availables)) {
                 return false;
             }
-            $sql .= ' AND pc.`id_xenforum_cat` IN ('.pSQL(implode(',', $availables)).')';
+            $sql .= ' AND pc.`id_xenforum_cat` IN ('. implode(', ', array_map('intval', $availables)) . ')';
         }
 
         $sql .= ' AND pc.`id_parent` = '.(int)$id_parent.';';
@@ -114,7 +114,7 @@ class XenForumCat extends ObjectModel
             if (empty($availables)) {
                 return false;
             }
-            $sql .= ' AND pc.`id_xenforum_cat` IN ('.pSQL(implode(',', $availables)).')';
+            $sql .= ' AND pc.`id_xenforum_cat` IN ('.implode(', ', array_map('intval', $availables)).')';
         }
 
         $sql .= ' AND pc.`id_parent` = '.(int)$id_parent.'
diff -bpru a/classes/XenForumPost.php b/classes/XenForumPost.php
--- a/classes/XenForumPost.php
+++ b/classes/XenForumPost.php
@@ -161,7 +161,7 @@ class XenForumPost extends ObjectModel
     {
         $sql = 'SELECT * FROM '._DB_PREFIX_.'xenforum_post p
             LEFT JOIN '._DB_PREFIX_.'xenforum x ON (p.id_xenforum = x.id_xenforum)
-            WHERE p.active= 1 AND x.id_xenforum_cat IN ('.pSQL(implode(',', $category_ids)).')';
+            WHERE p.active= 1 AND x.id_xenforum_cat IN ('.implode(', ', array_map('intval', $category_ids)).')';
 
         if (!$posts = Db::getInstance()->executeS($sql)) {
             return 0;
@@ -219,7 +219,7 @@ class XenForumPost extends ObjectModel
             if (empty($availables)) {
                 return false;
             }
-            $sql .= ' AND b.`id_xenforum_cat` IN ('.pSQL(implode(',', $availables)).')';
+            $sql .= ' AND b.`id_xenforum_cat` IN ('.implode(', ', array_map('intval', $availables)).')';
         }
 
         $sql .= ' ORDER BY a.`created` DESC LIMIT '.(int)$limit.';';
diff -bpru a/classes/XenForumTopic.php b/classes/XenForumTopic.php
--- a/classes/XenForumTopic.php
+++ b/classes/XenForumTopic.php
@@ -210,7 +210,7 @@ class XenForumTopic extends ObjectModel
     public function getTotalByCat($category_ids = null)
     {
         $sql = 'SELECT COUNT(id_xenforum) FROM `'._DB_PREFIX_.'xenforum`
-            WHERE `active` = 1 AND `id_xenforum_cat` IN ('.pSQL(implode(',', $category_ids)).')';
+            WHERE `active` = 1 AND `id_xenforum_cat` IN ('.implode(', ', array_map('intval', $category_ids)).')';
 
         return Db::getInstance()->getValue($sql);
     }
@@ -234,7 +234,7 @@ class XenForumTopic extends ObjectModel
             if (empty($availables)) {
                 return false;
             }
-            $sql .= ' AND a.`id_xenforum_cat` IN ('.pSQL(implode(',', $availables)).')';
+            $sql .= ' AND a.`id_xenforum_cat` IN ('.implode(', ', array_map('intval', $availables)).')';
         }
 
         $sql .= ' ORDER BY a.`highlight` DESC, a.`id_xenforum` DESC LIMIT '.(int)$limit_start.', '.(int)$limit;
@@ -264,7 +264,7 @@ class XenForumTopic extends ObjectModel
             if (empty($availables)) {
                 return false;
             }
-            $sql .= ' AND a.`id_xenforum_cat` IN ('.pSQL(implode(',', $availables)).')';
+            $sql .= ' AND a.`id_xenforum_cat` IN ('.implode(', ', array_map('intval', $availables)).')';
         }
 
         $sql .= ' ORDER BY a.`highlight` DESC, a.`id_xenforum` DESC LIMIT '.(int)$limit_start.', '.(int)$limit;
@@ -282,7 +282,7 @@ class XenForumTopic extends ObjectModel
         $sql = 'SELECT a.*, u.* FROM `'._DB_PREFIX_.'xenforum` a
                 LEFT JOIN `'._DB_PREFIX_.'customer` c ON (a.`id_author` = c.`id_customer`)
                 LEFT JOIN '._DB_PREFIX_.'xenforum_user u ON (u.`id_xenforum_user` = c.`id_customer`)
-                WHERE a.`active` = 1 AND c.`active` = 1 AND a.`id_xenforum_cat` IN ('.pSQL(implode(',', $category_ids)).')';
+                WHERE a.`active` = 1 AND c.`active` = 1 AND a.`id_xenforum_cat` IN ('.implode(', ', array_map('intval', $category_ids)).')';
         $sql .= ' ORDER BY a.`id_xenforum` DESC LIMIT 1';
 
         if ($values = Db::getInstance()->executeS($sql)) {
@@ -383,7 +383,7 @@ class XenForumTopic extends ObjectModel
             if (empty($availables)) {
                 return false;
             }
-            $sql .= ' AND a.`id_xenforum_cat` IN ('.pSQL(implode(',', $availables)).')';
+            $sql .= ' AND a.`id_xenforum_cat` IN ('.implode(', ', array_map('intval', $availables)).')';
         }
 
         if (!$posts = Db::getInstance()->executeS($sql)) {

Other recommandations

  • It’s recommended to upgrade the module beyong 2.13.0.
  • Upgrade PrestaShop to the latest version to disable multiquery executions (separated by “;”)
  • Change the default database prefix ps_ by a new longer arbitrary prefix. Nethertheless, be warned that this is useless against blackhat with DBA senior skill because of a design vulnerability in DBMS
  • Activate OWASP 942’s rules on your WAF (Web application firewall), be warned that you will probably break your backoffice and you will need to pre-configure some bypasses against these set of rules.

Timeline

Date Action
2022-10-24 Issue discovered during a code reviews by 202 ecommerce
2022-10-24 Contact the author
2022-11-08 Retry to obtain a response from the author
2022-11-17 Contact PrestaShop Addons Team
2023-01-16 Fix published on addons PrestaShop marketplace
2023-01-28 Request a CVE ID
2023-03-06 Publication of this security advisory