CVE-2016-9182 (published 2016-11-04T10:59Z)

Exponent CMS 2.4 uses PHP reflection to call a method of a controller class, and then uses the method name to check user permission. But, the method name in PHP reflection is case insensitive, and Exponent CMS permits undefined actions to execute by default, so an attacker can use a capitalized method name to bypass the permission check, e.g., controller=expHTMLEditor&action=preview&editor=ckeditor and controller=expHTMLEditor&action=Preview&editor=ckeditor. An anonymous user will be rejected for the former but can access the latter.

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

Weakness information:
cwe_id: CWE-284
cwe_name: Improper Access Control
description: The product does not restrict or incorrectly restricts access to a resource from an unauthorized actor.
extended_description: 
            Access control involves the use of several protection mechanisms such as:
            
               Authentication (proving the identity of an actor)
               Authorization (ensuring that a given actor can access a resource), and
               Accountability (tracking of activities that were performed)
            
            When any mechanism is not applied or otherwise fails, attackers can compromise the security of the product by gaining privileges, reading sensitive information, executing commands, evading detection, etc.
            There are two distinct behaviors that can introduce access control weaknesses:
               
                  Specification: incorrect privileges, permissions, ownership, etc. are explicitly specified for either the user or the resource (for example, setting a password file to be world-writable, or giving administrator capabilities to a guest user). This action could be performed by the program or the administrator.
                  Enforcement: the mechanism contains errors that prevent it from properly enforcing the specified access control requirements (e.g., allowing the user to specify their own privileges, or allowing a syntactically-incorrect ACL to produce insecure settings). This problem occurs within the program itself, in that it does not actually enforce the intended security policy that the administrator specifies.
               
         
		 

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-11-03 09:23:12-04:00
msg: fix security vulnerability to bypass permissions using method name in wrong case, reported by fyth
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: expController.php
change_type: ModificationType.MODIFY
diff: @@ -53,7 +53,7 @@ abstract class expController {
     protected $remove_permissions = array();  // $permissions not applicable for this module from above list
     protected $add_permissions = array();  // additional $permissions processed and visible  for this module
     protected $manage_permissions = array();  // additional actions requiring manage permission in addition to $m_permissions
-    public $requires_login = array();  // actions/methods which ONLY require user be logged in to access...$permissions take priority
+    public $requires_login = array();  // actions/methods (lower case ONLY) which ONLY require user be logged in to access...$permissions take priority
 
     public $filepath = ''; // location of this controller's files
     public $viewpath = ''; // location of this controllers views; defaults to controller file location

programming_language: PHP
filename: expFramework.php
change_type: ModificationType.MODIFY
diff: @@ -277,6 +277,7 @@ function renderAction(array $parms=array()) {
 		$meth = $controllerClass->getMethod($action);
         if ($meth->isPrivate()) expQueue::flashAndFlow('error', gt('The requested action could not be performed: Action not found'));*/
     } elseif ($controllerClass->hasMethod('showall')) {
+        //note every invalid command gets converted to 'showall'
         $parms['action'] = 'showall';
         $action = 'showall';
     } else {
@@ -402,39 +403,43 @@ function renderAction(array $parms=array()) {
         }
     }
 
+    // deal with lower case only to prevent hacking reflection function names
+    $lc_perms = array_change_key_case($perms);
+    $lc_perm_action = strtolower($perm_action);
+    $lc_common_action = strtolower($common_action);
     //FIXME? if the assoc $perm doesn't exist, the 'action' will ALWAYS be allowed, e.g., default is to allow action
-    if (array_key_exists($perm_action, $perms)) {
+    if (array_key_exists($lc_perm_action, $lc_perms)) {
         if (!expPermissions::check($perm_action, $controller->loc)) {
             if (expTheme::inAction()) {
-                flash('error', gt("You don't have permission to")." ".$perms[$perm_action]);
+                flash('error', gt("You don't have permission to")." ".$lc_perms[$lc_perm_action]);
                 notfoundController::handle_not_authorized();
                 expHistory::returnTo('viewable');
             } else {
                 return false;
             }
         }
-    } elseif (array_key_exists($common_action, $perms)) {
+    } elseif (array_key_exists($lc_common_action, $lc_perms)) {
         if (!expPermissions::check($common_action, $controller->loc)) {
             if (expTheme::inAction()) {
-                flash('error', gt("You don't have permission to")." ".$perms[$common_action]);
+                flash('error', gt("You don't have permission to")." ".$lc_perms[$lc_common_action]);
                 notfoundController::handle_not_authorized();
                 expHistory::returnTo('viewable');
             } else {
                 return false;
             }
         }
-    } elseif (array_key_exists($perm_action, $controller->requires_login)) {
+    } elseif (array_key_exists($lc_perm_action, $controller->requires_login)) {
         // check if the action requires the user to at least be logged in
         if (!$user->isLoggedIn()) {
-            $msg = empty($controller->requires_login[$perm_action]) ? gt("You must be logged in to perform this action") : gt($controller->requires_login[$perm_action]);
+            $msg = empty($controller->requires_login[$lc_perm_action]) ? gt("You must be logged in to perform this action") : gt($controller->requires_login[$lc_perm_action]);
             flash('error', $msg);
             notfoundController::handle_not_authorized();
             expHistory::redirecto_login();
         }
-    } elseif (array_key_exists($common_action, $controller->requires_login)) {
-        // check if the action requires the user to at least be logged in
+    } elseif (array_key_exists($lc_common_action, $controller->requires_login)) {
+        // check if the common action requires the user to at least be logged in
         if (!$user->isLoggedIn()) {
-            $msg = empty($controller->requires_login[$common_action]) ? gt("You must be logged in to perform this action") : gt($controller->requires_login[$common_action]);
+            $msg = empty($controller->requires_login[$lc_common_action]) ? gt("You must be logged in to perform this action") : gt($controller->requires_login[$lc_common_action]);
             flash('error', $msg);
             notfoundController::handle_not_authorized();
             expHistory::redirecto_login();

programming_language: PHP
filename: expString.php
change_type: ModificationType.MODIFY
diff: @@ -509,8 +509,8 @@ class expString {
      * Scrub input string for possible security issues.
      *
      * @static
-     * @param $data string
-     * @return string
+     * @param $data string|array
+     * @return string|array
      */
     public static function sanitize(&$data) {
 //        return $data;

programming_language: PHP
filename: fileController.php
change_type: ModificationType.MODIFY
diff: @@ -35,12 +35,13 @@ class fileController extends expController {
         'picker'=>'You must be logged in to perform this action',
         'adder'=>'You must be logged in to perform this action',
         'addit'=>'You must be logged in to perform this action',
-        'batchDelete'=>'You must be logged in to perform this action',
-        'createFolder'=>'You must be logged in to perform this action',
+        'batchdelete'=>'You must be logged in to perform this action',
+        'createfolder'=>'You must be logged in to perform this action',
+        'delete'=>'You must be logged in to perform this action',
         'deleter'=>'You must be logged in to perform this action',
         'deleteit'=>'You must be logged in to perform this action',
         'edit'=>'You must be logged in to perform this action',
-        'quickUpload'=>'You must be logged in to perform this action',
+        'quickupload'=>'You must be logged in to perform this action',
         'upload'=>'You must be logged in to perform this action',
         'uploader'=>'You must be logged in to perform this action',
     );

programming_language: PHP
filename: formsController.php
change_type: ModificationType.MODIFY
diff: @@ -1500,7 +1500,7 @@ class formsController extends expController {
      *
      * @param      $items
      *
-     * @param null $rptcols
+     * @param array|null $rptcols
      *
      * @return string
      */

programming_language: PHP
filename: navigationController.php
change_type: ModificationType.MODIFY
diff: @@ -25,12 +25,12 @@ class navigationController extends expController {
         'showall' => 'Show Navigation',
         'breadcrumb' => 'Breadcrumb',
     );
-    protected $remove_permissions = array(
+//    protected $remove_permissions = array(
 //        'configure',
 //        'create',
 //        'delete',
 //        'edit'
-    );
+//    );
     protected $add_permissions = array(
         'manage'    => 'Manage',
         'view'      => "View Page"

programming_language: PHP
filename: pixidouController.php
change_type: ModificationType.MODIFY
diff: @@ -25,7 +25,7 @@ class pixidouController extends expController {
 	public $cacheDir = "tmp/pixidou/";
     public $requires_login = array(
         'editor'=>'You must be logged in to perform this action',
-        'exitEditor'=>'You must be logged in to perform this action',
+        'exiteditor'=>'You must be logged in to perform this action',
     );
 
     static function displayname() { return gt("Pixidou Image Editor"); }

programming_language: PHP