CVE-2015-10084: Improper Neutralization of Special Elements used in an SQL Command ('SQL Injection') (CWE-89)
value: A vulnerability was found in irontec klear-library chloe and classified as critical. Affected by this issue is the function _prepareWhere of the file Controller/Rest/BaseController.php. The manipulation leads to sql injection. Upgrading to version marla is able to address this issue. The name of the patch is b25262de52fdaffde2a4434fc2a84408b304fbc5. It is recommended to upgrade the affected component. The identifier of this vulnerability is VDB-221504.
CWE description: The product constructs all or part of an SQL command using externally-influenced input from an upstream component, but it does not neutralize or incorrectly neutralizes special elements that could modify the intended SQL command when it is sent to a downstream component. Without sufficient removal or quoting of SQL syntax in user-controllable inputs, the generated SQL query can cause those inputs to be interpreted as SQL instead of ordinary user data.
CVSS v3 base score: 9.8
Published: 2023-02-21T15:15Z
Repository: https://github.com/irontec/klear-library
Language: PHP
Files changed: Controller/Rest/BaseController.php
Commit date: 2015-10-15 16:30:12 +0200
Commit message: Fix sql injection && Permitir la ejecución de sentencias sql complejas predefinidas en controladores REST vía Zend_Db_Expr

Diff: diff --git a/Controller/Rest/BaseController.php b/Controller/Rest/BaseController.php
index 94aa3ee..f3a4af5 100644
--- a/Controller/Rest/BaseController.php
+++ b/Controller/Rest/BaseController.php
@@ -377,26 +377,28 @@ class Iron_Controller_Rest_BaseController extends \Zend_Rest_Controller
         if ($search === false || trim($search) === '') {
             return NULL;
         }
 
         $search = json_decode($search);
+        return implode(" AND ", $this->_parseWhere($search));
+    }
 
+    protected function _parseWhere($search) 
+    {
         $itemsSearch = array();
         foreach ($search as $key => $val) {
 
-            if (is_scalar($val)) {
+            if ($val instanceof \Zend_Db_Expr) {
+                $itemsSearch[] = $val->__toString();
+            } else if (is_scalar($val)) {
                 $itemsSearch[] = $this->_prepareScalarCondition($key, $val);
             } else if (is_object($val)){
                 $itemsSearch[] = $this->_prepareAdvancedCondition($key, $val);
             }
         }
-
-        if (empty($itemsSearch)) {
-            return '';
-        }
-        return implode(" AND ", $itemsSearch);
-    }
+        return $itemsSearch;
+    }   
 
     protected function _prepareScalarCondition($key, $val) {
 
         $dbAdapter = Zend_Db_Table::getDefaultAdapter();
         if ($val != '') {
@@ -410,36 +412,72 @@ class Iron_Controller_Rest_BaseController extends \Zend_Rest_Controller
     protected function _prepareAdvancedCondition($key, $val) {
 
         $dbAdapter = Zend_Db_Table::getDefaultAdapter();
         switch (strtolower(key($val))) {
 
+            case 'notEqual':
+                $key = $dbAdapter->quoteIdentifier($key) . " != ?";
+                return $dbAdapter->quoteInto($key, current($val));
+                break;
+
+            case 'isnull':
+                return $dbAdapter->quoteIdentifier($key) . " is null";
+                break;
+
+            case 'isnotnull':
+                return $dbAdapter->quoteIdentifier($key) . " is null";
+                break;
+
+            case 'gt':
+                $key = $dbAdapter->quoteIdentifier($key) . " > ?";
+                return $dbAdapter->quoteInto($key, current($val));
+                break;
+
+            case 'gte':
+                $key = $dbAdapter->quoteIdentifier($key) . " >= ?";
+                return $dbAdapter->quoteInto($key, current($val));
+                break;
+                
+            case 'lt':
+                $key = $dbAdapter->quoteIdentifier($key) . " < ?";
+                return $dbAdapter->quoteInto($key, current($val));
+                break;
+
+            case 'lte':
+                $key = $dbAdapter->quoteIdentifier($key) . " <= ?";
+                return $dbAdapter->quoteInto($key, current($val));
+                break;
+                
             case 'between':
-                $values = $this->_cleanArray($val);
+                $values = $this->_cleanArray(current($val));
                 return $dbAdapter->quoteIdentifier($key) . ' between '. $values[0] . ' AND ' . $values[1];
 
             case 'notin':
-
-                $values = $this->_cleanArray($val);
+                $values = $this->_cleanArray(current($val));
                 return $dbAdapter->quoteIdentifier($key) . ' not in ('. implode(",", $values) . ') ';
 
             case 'in':
-
-                $values = $this->_cleanArray($val);
+                $values = $this->_cleanArray(current($val));
                 return $dbAdapter->quoteIdentifier($key) . ' in ('. implode(",", $values) . ') ';
 
             case 'like':
-
-                $key = $dbAdapter->quoteIdentifier($key) . " like '%?%'";
-                return $dbAdapter->quoteInto($key, $val);
+                $key = $dbAdapter->quoteIdentifier($key) . " like ?";
+                return $dbAdapter->quoteInto($key, current($val));
 
             case 'notlike':
+                $key = $dbAdapter->quoteIdentifier($key) . " not like ?";
+                return $dbAdapter->quoteInto($key, current($val));
+                break;
 
-                $key = $dbAdapter->quoteIdentifier($key) . " not like '%?%'";
-                return $dbAdapter->quoteInto($key, $val);
+            case 'or':
+                $conditions = array();
+                foreach ($val as $subKey => $subVal) {
+                    $conditions += $this->_parseWhere($subVal);
+                }
+                return '('. implode(' OR ', $conditions) .')';
                 break;
         }
-
         return '';
     }
 
     protected function _cleanArray($obj) {
 

Vulnerable code:             if (is_scalar($val)) {

        if (empty($itemsSearch)) {
            return '';
        }
        return implode(" AND ", $itemsSearch);
    }
                $values = $this->_cleanArray($val);

                $values = $this->_cleanArray($val);

                $values = $this->_cleanArray($val);

                $key = $dbAdapter->quoteIdentifier($key) . " like '%?%'";
                return $dbAdapter->quoteInto($key, $val);
                $key = $dbAdapter->quoteIdentifier($key) . " not like '%?%'";
                return $dbAdapter->quoteInto($key, $val);

Fixed code:         return implode(" AND ", $this->_parseWhere($search));
    }
    protected function _parseWhere($search) 
    {
            if ($val instanceof \Zend_Db_Expr) {
                $itemsSearch[] = $val->__toString();
            } else if (is_scalar($val)) {
        return $itemsSearch;
    }   
            case 'notEqual':
                $key = $dbAdapter->quoteIdentifier($key) . " != ?";
                return $dbAdapter->quoteInto($key, current($val));
                break;

            case 'isnull':
                return $dbAdapter->quoteIdentifier($key) . " is null";
                break;

            case 'isnotnull':
                return $dbAdapter->quoteIdentifier($key) . " is null";
                break;

            case 'gt':
                $key = $dbAdapter->quoteIdentifier($key) . " > ?";
                return $dbAdapter->quoteInto($key, current($val));
                break;

            case 'gte':
                $key = $dbAdapter->quoteIdentifier($key) . " >= ?";
                return $dbAdapter->quoteInto($key, current($val));
                break;
                
            case 'lt':
                $key = $dbAdapter->quoteIdentifier($key) . " < ?";
                return $dbAdapter->quoteInto($key, current($val));
                break;

            case 'lte':
                $key = $dbAdapter->quoteIdentifier($key) . " <= ?";
                return $dbAdapter->quoteInto($key, current($val));
                break;
                
                $values = $this->_cleanArray(current($val));
                $values = $this->_cleanArray(current($val));
                $values = $this->_cleanArray(current($val));
                $key = $dbAdapter->quoteIdentifier($key) . " like ?";
                return $dbAdapter->quoteInto($key, current($val));
                $key = $dbAdapter->quoteIdentifier($key) . " not like ?";
                return $dbAdapter->quoteInto($key, current($val));
                break;
            case 'or':
                $conditions = array();
                foreach ($val as $subKey => $subVal) {
                    $conditions += $this->_parseWhere($subVal);
                }
                return '('. implode(' OR ', $conditions) .')';