CVE-2016-7780 (published 2017-03-07T16:59Z)

SQL injection vulnerability in cron/find_help.php in Exponent CMS 2.3.9 and earlier allows remote attackers to execute arbitrary SQL commands via the version parameter.

Severity: HIGH
CVSS v2 base score: 7.5 (AV:N/AC:L/Au:N/C:P/I:P/A:P)
CVSS v3 base score: 9.8, CRITICAL (CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H)

Weakness information:
cwe_id: CWE-89
cwe_name: Improper Neutralization of Special Elements used in an SQL Command ('SQL Injection')
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.
extended_description: 
            
            
         
         

Fix information:
repo_url: https://github.com/exponentcms/exponent-cms
commit_details: repo_url: https://github.com/exponentcms/exponent-cms
author: dleffler
author_date: 2016-09-29 23:30:24-04:00
msg: second effort to greatly enhance system security (xss, sql inject, file exploit, rce, etc...), thanks to felixk3y
repository: repo_url: https://github.com/exponentcms/exponent-cms
repo_name: exponentcms/exponent-cms
description: Content Management, Simple.
repo_language: PHP
owner: exponentcms
file_changes: filename: find_help.php
change_type: ModificationType.MODIFY
diff: @@ -39,7 +39,7 @@
         }
         if (!empty($_GET['version'])) {
             $version_title = $_GET['version'];
-            $version = $db->selectValue('help_version', 'id', 'version="' . expString::sanitize($_GET['version']) . '"');
+            $version = $db->selectValue('help_version', 'id', 'version="' . expString::escape($_GET['version']) . '"');
         }
     }
     /**

programming_language: PHP
filename: cartController.php
change_type: ModificationType.MODIFY
diff: @@ -74,12 +74,12 @@ class cartController extends expController {
                 if (in_array($qkey, $this->params['prod-check'])) {
                     //this might not be working...FJD
                     $child = new $product_type($qkey);
-                    /*if ($quantity < $child->minimum_order_quantity)                       
+                    /*if ($quantity < $child->minimum_order_quantity)
                     {
-                        flash('message', $child->title . " - " . $child->model . " has a minimum order quantity of " . $child->minimum_order_quantity . 
+                        flash('message', $child->title . " - " . $child->model . " has a minimum order quantity of " . $child->minimum_order_quantity .
                         '. Your quantity has been adjusted accordingly.');
                         $quantity = $child->minimum_order_quantity;
-                        
+
                     }*/
                     $this->params['children'][$qkey] = $quantity;
                 }
@@ -171,7 +171,7 @@ class cartController extends expController {
             $item    = new orderitem($id);
             $updates = new stdClass();
             if (!empty($item->id)) {
-                //$newqty = $item->product->updateQuantity($this->params['value']);                  
+                //$newqty = $item->product->updateQuantity($this->params['value']);
                 $newqty = $item->product->updateQuantity($this->params['value']);
                 if ($newqty > $item->product->quantity) {
                     if ($item->product->availability_type == 1) {
@@ -217,10 +217,10 @@ class cartController extends expController {
                     if ($orderItem->product_id == $item->product_id) $qCheck += $orderItem->quantity;
                 }
                 //eDebug("Done",true);
-                //}                           
-                /*eDebug($item->quantity);   
-                eDebug($item->product->quantity); 
-                eDebug($qCheck);                  
+                //}
+                /*eDebug($item->quantity);
+                eDebug($item->product->quantity);
+                eDebug($qCheck);
                 eDebug($newqty,true);  */
                 //check minimum quantity
                 $qtyMessage = '';
@@ -243,7 +243,7 @@ class cartController extends expController {
                         //$updates->message = 'Only '.$item->product->quantity.' '.$item->products_name.' are currently in stock. Shipping may be delayed on the other '.$diff;
                     } elseif ($item->product->availability_type == 2) {
                         flash('error', $item->products_name . ' ' . gt('only has') . ' ' . $item->product->quantity . ' ' . gt('on hand. You can not add any more than that to your cart.'));
-                        /*$updates->message = $item->products_name.' only has '.$item->product->quantity.' on hand. You can not add any more to your cart.';                        
+                        /*$updates->message = $item->products_name.' only has '.$item->product->quantity.' on hand. You can not add any more to your cart.';
                         $updates->cart_total = '$'.number_format($order->getCartTotal(), 2);
                         $updates->item_total = '$'.number_format($item->quantity*$item->products_price, 2);
                         $updates->item_id = $id;
@@ -302,7 +302,7 @@ class cartController extends expController {
 
             //eDebug($order,true);
             //check to see if we have calculate shipping yet - if shipping_total_before_discounts is set
-            //to something other than 0, then we have, but we'll set the estimtae to shipping_total to 
+            //to something other than 0, then we have, but we'll set the estimtae to shipping_total to
             //accomodate any applied discounts
             //if (!empty($order->shipping_total_before_discounts))
             //{
@@ -310,7 +310,7 @@ class cartController extends expController {
             //}
             //otherwise we'll grab an estimate
             //else
-            //{    
+            //{
             //$estimated_shipping = shipping::estimateShipping($order);
             /* $shipping = new shipping();
           $shipping->getRates();
@@ -332,7 +332,7 @@ class cartController extends expController {
                 ));
                 $discounts = null;
             } else {
-                // get all current discount codes that are valid and applied                
+                // get all current discount codes that are valid and applied
                 $discounts = $order->validateDiscounts();
             }
         } else {
@@ -703,7 +703,7 @@ class cartController extends expController {
         if (empty($result->errorCode)) {
             // if ($result->errorCode === "0" || $result->errorCode === 0)
             // {
-            // save out the cart total to the database		
+            // save out the cart total to the database
             $billing->billingmethod->update(array('billing_cost'=> $order->grand_total));
 
             // set the invoice number and purchase date in the order table..this finializes the order
@@ -1069,11 +1069,11 @@ class cartController extends expController {
         //this will change once we allow more than one coupon code
 
         $discount = new discounts();
-        $discount = $discount->getCouponByName($this->params['coupon_code']);
+        $discount = $discount->getCouponByName(expString::escape($this->params['coupon_code']));
 
         if (empty($discount)) {
             flash('error', gt("This discount code you entered does not exist."));
-            //redirect_to(array('controller'=>'cart', 'action'=>'checkout'));       
+            //redirect_to(array('controller'=>'cart', 'action'=>'checkout'));
             expHistory::back();
         }
 
@@ -1100,7 +1100,7 @@ class cartController extends expController {
         } else {
             flash('error', $validateDiscountMessage);
         }
-        //redirect_to(array('controller'=>'cart', 'action'=>'checkout'));                           
+        //redirect_to(array('controller'=>'cart', 'action'=>'checkout'));
         expHistory::back();
     }
 
@@ -1141,11 +1141,11 @@ class cartController extends expController {
     //this is ran after we alter the quantity of the cart, including
     //delete items or runing the updatequantity action
     private function rebuildCart() {
-        //group items by type and id               
+        //group items by type and id
         //since we can have the same product in different items (options and quantity discount)
         //remove items and readd?
         global $order;
-        //eDebug($order,true); 
+        //eDebug($order,true);
         $items = $order->orderitem;
         foreach ($order->orderitem as $item) {
             $item->delete();
@@ -1185,12 +1185,12 @@ class cartController extends expController {
         }
         $order->save();
         /*eDebug($items);
-        
-        
-        $options = array();  
+
+
+        $options = array();
         foreach ($this->optiongroup as $og) {
             if ($og->required && empty($params['options'][$og->id][0])) {
-                
+
                 flash('error', $this->title.' '.gt('requires some options to be selected before you can add it to your cart.'));
                 redirect_to(array('controller'=>store, 'action'=>'show', 'id'=>$this->id));
             }
@@ -1198,7 +1198,7 @@ class cartController extends expController {
                 foreach ($params['options'][$og->id] as $opt_id) {
                     $selected_option = new option($opt_id);
                     $cost = $selected_option->modtype == '$' ? $selected_option->amount :  $this->getBasePrice() * ($selected_option->amount * .01);
-                    $cost = $selected_option->updown == '+' ? $cost : $cost * -1;                      
+                    $cost = $selected_option->updown == '+' ? $cost : $cost * -1;
                     $price += $cost;
                     $options[] = array($selected_option->id,$selected_option->title,$selected_option->modtype,$selected_option->updown,$selected_option->amount);
                 }
@@ -1212,7 +1212,7 @@ class cartController extends expController {
         //eDebug($item, true);
         $item->products_price = $price;
         $item->options = serialize($options);
-        
+
         $sm = $order->getCurrentShippingMethod();
         $item->shippingmethods_id = $sm->id;
         $item->save();                            */

programming_language: PHP
filename: purchaseOrderController.php
change_type: ModificationType.MODIFY
diff: @@ -119,7 +119,7 @@ class purchaseOrderController extends expController {
 	public function getPurchaseOrderByJSON() {
 
 		if(!empty($this->params['vendor'])) {
-			$purchase_orders = $this->purchase_order->find('all', 'vendor_id=' . $this->params['vendor']);
+			$purchase_orders = $this->purchase_order->find('all', 'vendor_id=' . expString::escape($this->params['vendor']));
 		} else {
 			$purchase_orders = $this->purchase_order->find('all');
 		}

programming_language: PHP
filename: storeController.php
change_type: ModificationType.MODIFY
diff: @@ -854,7 +854,7 @@ class storeController extends expController {
 //        $classname = $db->selectValue('product', 'product_type', 'id=' . $this->params['id']);
 //        $product   = new $classname($this->params['id'], true, true);
 
-        $id = isset($this->params['title']) ? $this->params['title'] : $this->params['id'];
+        $id = isset($this->params['title']) ? expString::escape($this->params['title']) : $this->params['id'];
         $product = new product($id);
         $product_type = new $product->product_type($product->id);
         $product_type->title = expString::parseAndTrim($product_type->title, true);
@@ -908,7 +908,7 @@ class storeController extends expController {
         //need to add a check here for child product and redirect to parent if hit directly by ID
         expHistory::set('viewable', $this->params);
 
-        $product = new product(addslashes($this->params['title']));
+        $product = new product(expString::escape($this->params['title']));
         $product_type = new $product->product_type($product->id);
         $product_type->title = expString::parseAndTrim($product_type->title, true);
         $product_type->image_alt_tag = expString::parseAndTrim($product_type->image_alt_tag, true);
@@ -952,7 +952,7 @@ class storeController extends expController {
 
         expHistory::set('viewable', $this->params);
         $product = new product();
-        $model = $product->find("first", 'model="' . $this->params['model'] . '"');
+        $model = $product->find("first", 'model="' . expString::escape($this->params['model']) . '"');
         //eDebug($model);
         $product_type = new $model->product_type($model->id);
         //eDebug($product_type);
@@ -973,7 +973,7 @@ class storeController extends expController {
         expHistory::set('viewable', $this->params);
 //        $parent = isset($this->params['cat']) ? $this->params['cat'] : expSession::get('catid');
         $catid = expSession::get('catid');
-        $parent = !empty($catid) ? $catid : (!empty($this->params['cat']) ? $this->params['cat'] : 0);
+        $parent = !empty($catid) ? $catid : (!empty($this->params['cat']) ? intval($this->params['cat']) : 0);
         $category = new storeCategory($parent);
         $categories = $category->getEcomSubcategories();
         $ancestors = $category->pathToNode();
@@ -1650,7 +1650,7 @@ class storeController extends expController {
 
     function searchByModelForm() {
         // get the search terms
-        $terms = $this->params['search_string'];
+        $terms = expString::escape($this->params['search_string']);
 
         $sql = "model like '%" . $terms . "%'";
 
@@ -1688,6 +1688,7 @@ class storeController extends expController {
         if (!($user->isAdmin())) $sql .= '(p.active_type=0 OR p.active_type=1) AND ';
 
         //if first character of search is a -, then we do a wild card, else from beginning
+        $this->params['query'] = expString::escape($this->params['query']);
         if ($this->params['query'][0] == '-') {
             $sql .= " p.model LIKE '%" . $this->params['query'];
         } else {
@@ -1709,6 +1710,7 @@ class storeController extends expController {
     public function search() {
         global $db, $user;
 
+        $this->params['query'] = expString::escape($this->params['query']);
         if (SAVE_SEARCH_QUERIES && INCLUDE_AJAX_SEARCH == 1) {  // only to add search query record
             $qry = trim($this->params['query']);
             if (!empty($qry)) {
@@ -1808,6 +1810,8 @@ class storeController extends expController {
      */
     public function searchNew() {
         global $db, $user;
+
+        $this->params['query'] = expString::escape($this->params['query']);
         //$this->params['query'] = str_ireplace('-','\-',$this->params['query']);
         $sql = "select DISTINCT(p.id) as id, p.title, model, sef_url, f.id as fileid, ";
         $sql .= "match (p.title,p.model,p.body) against ('" . $this->params['query'] . "*' IN BOOLEAN MODE) as relevance, ";

programming_language: PHP
filename: fileController.php
change_type: ModificationType.MODIFY
diff: @@ -252,12 +252,12 @@ class fileController extends expController {
 
         // How many records to get?
         if(strlen($this->params['results']) > 0) {
-            $results = $this->params['results'];
+            $results = intval($this->params['results']);
         }
 
         // Start at which record?
         if(strlen($this->params['startIndex']) > 0) {
-            $startIndex = $this->params['startIndex'];
+            $startIndex = intval($this->params['startIndex']);
         }
 
         // Sorted?
@@ -265,7 +265,7 @@ class fileController extends expController {
             if ($this->params['sort'] == 'cat') {
                 $sort = 'id';
             } else {
-                $sort = $this->params['sort'];
+                $sort = expString::escape($this->params['sort']);
             }
 //            if ($sort = 'id') $sort = 'filename';
         }
@@ -281,6 +281,7 @@ class fileController extends expController {
         $totalrecords = 0;
 
         if (!empty($this->params['query'])) {
+            $this->params['query'] = expString::escape($this->params['query']);
             $filter = '';
             if (!$user->isAdmin()) {
                 $filter = "(poster=".$user->id." OR shared=1) AND ";

programming_language: PHP
filename: usersController.php
change_type: ModificationType.MODIFY
diff: @@ -842,17 +842,17 @@ class usersController extends expController {
 
         // How many records to get?
         if (strlen($this->params['results']) > 0) {
-            $results = $this->params['results'];
+            $results = intval($this->params['results']);
         }
 
         // Start at which record?
         if (strlen($this->params['startIndex']) > 0) {
-            $startIndex = $this->params['startIndex'];
+            $startIndex = intval($this->params['startIndex']);
         }
 
         // Sorted?
         if (strlen($this->params['sort']) > 0) {
-            $sort = $this->params['sort'];
+            $sort = expString::escape($this->params['sort']);
             if ($sort = 'id') $sort = 'username';
         }
 
@@ -893,11 +893,10 @@ class usersController extends expController {
 
         if (!empty($this->params['query'])) {
 
-//            $this->params['query'] = $this->params['query'];
+            $this->params['query'] = expString::escape($this->params['query']);
             $totalrecords = $this->$modelname->find('count', (empty($filter) ? '' : $filter . " AND ") . "(username LIKE '%" . $this->params['query'] . "%' OR firstname LIKE '%" . $this->params['query'] . "%' OR lastname LIKE '%" . $this->params['query'] . "%' OR email LIKE '%" . $this->params['query'] . "%')");
 
             $users = $this->$modelname->find('all', (empty($filter) ? '' : $filter . " AND ") . "(username LIKE '%" . $this->params['query'] . "%' OR firstname LIKE '%" . $this->params['query'] . "%' OR lastname LIKE '%" . $this->params['query'] . "%' OR email LIKE '%" . $this->params['query'] . "%')", $sort . ' ' . $dir, $results, $startIndex);
-
             for ($i = 0, $iMax = count($users); $i < $iMax; $i++) {
                 if (ECOM == 1) {
                     $users[$i]->usernamelabel = "<a href='viewuser/{$users[$i]->id}'  class='fileinfo'>{$users[$i]->username}</a>";

programming_language: PHP