CVE-2016-9182 (PHP, published 2016-11-04T10:59Z)
Description: value: 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 v3 base score: 7.5)
Weakness: CWE-284 \u2014 Improper Access Control
The product does not restrict or incorrectly restricts access to a resource from an unauthorized actor.
Repository: https://github.com/exponentcms/exponent-cms
Files changed:
framework/core/controllers/expController.php
framework/core/expFramework.php
framework/core/subsystems/expString.php
framework/modules/file/controllers/fileController.php
framework/modules/forms/controllers/formsController.php
framework/modules/navigation/controllers/navigationController.php
framework/modules/pixidou/controllers/pixidouController.php
Commit message: fix security vulnerability to bypass permissions using method name in wrong case, reported by fyth


Vulnerable code:
    public $requires_login = array();  // actions/methods which ONLY require user be logged in to access...$permissions take priority
    if (array_key_exists($perm_action, $perms)) {
                flash('error', gt("You don't have permission to")." ".$perms[$perm_action]);
    } elseif (array_key_exists($common_action, $perms)) {
                flash('error', gt("You don't have permission to")." ".$perms[$common_action]);
    } elseif (array_key_exists($perm_action, $controller->requires_login)) {
            $msg = empty($controller->requires_login[$perm_action]) ? gt("You must be logged in to perform this action") : gt($controller->requires_login[$perm_action]);
    } elseif (array_key_exists($common_action, $controller->requires_login)) {
        // check if the action requires the user to at least be logged in
            $msg = empty($controller->requires_login[$common_action]) ? gt("You must be logged in to perform this action") : gt($controller->requires_login[$common_action]);
     * @param $data string
     * @return string
        'batchDelete'=>'You must be logged in to perform this action',
        'createFolder'=>'You must be logged in to perform this action',
        'quickUpload'=>'You must be logged in to perform this action',
     * @param null $rptcols
    protected $remove_permissions = array(
    );
        'exitEditor'=>'You must be logged in to perform this action',

Fixed code:
    public $requires_login = array();  // actions/methods (lower case ONLY) which ONLY require user be logged in to access...$permissions take priority
        //note every invalid command gets converted to 'showall'
    // 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);
    if (array_key_exists($lc_perm_action, $lc_perms)) {
                flash('error', gt("You don't have permission to")." ".$lc_perms[$lc_perm_action]);
    } elseif (array_key_exists($lc_common_action, $lc_perms)) {
                flash('error', gt("You don't have permission to")." ".$lc_perms[$lc_common_action]);
    } elseif (array_key_exists($lc_perm_action, $controller->requires_login)) {
            $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]);
    } elseif (array_key_exists($lc_common_action, $controller->requires_login)) {
        // check if the common action requires the user to at least be logged in
            $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]);
     * @param $data string|array
     * @return string|array
        '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',
        'quickupload'=>'You must be logged in to perform this action',
     * @param array|null $rptcols
//    protected $remove_permissions = array(
//    );
        'exiteditor'=>'You must be logged in to perform this action',