CVE-2016-7400 (PHP, published 2017-02-07T15:59Z)
Description: value: Multiple SQL injection vulnerabilities in Exponent CMS before 2.4.0 allow remote attackers to execute arbitrary SQL commands via the (1) id parameter in an activate_address address controller action, (2) title parameter in a show blog controller action, or (3) content_id parameter in a showComments expComment controller action.
Severity: HIGH (CVSS v3 base score: 9.8)
Weakness: CWE-89 \u2014 Improper Neutralization of Special Elements used in an SQL Command ('SQL Injection')
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.
Repository: https://github.com/exponentcms/exponent-cms
Files changed:
framework/core/subsystems/database/mysqli.php
framework/core/subsystems/expDatabase.php
framework/core/subsystems/expRouter.php
framework/core/subsystems/expString.php
framework/core/subsystems/expTheme.php
Commit message: security fix several sql injection vulnerabilities reported by Manuel Garcia Cardenas and PKAV TEAM


Vulnerable code:
        $res = @mysqli_query($this->connection, $sql);
        $res = @mysqli_query($this->connection, $sql);
                $values .= "'" . mysqli_real_escape_string($this->connection, $val) . "'";
                    $sql .= "`$var`='".mysqli_real_escape_string($this->connection,$val)."',";
            $sql .= $where;
        $res = @mysqli_query($this->connection, $sql);
        $res = @mysqli_query($this->connection, $sql);
        // conventional method to ensure the 'id' is an id
        if (isset($_GET['id'])) {
            $_GET['id'] = intval($_GET['id']);
            } elseif (count($this->url_parts) == 1 || $db->selectObject('section', "sef_name='" . substr($db->escapeString($this->sefPath),1) . "'") != null) {
                if (($db->selectObject('section', "sef_name='" . $db->escapeString($this->url_parts[0]) . "'") != null) && (in_array(array('printerfriendly','exportaspdf','ajax_action'), $this->url_parts))) {
        return expString::sanitize($url);
        $this->sefPath = expString::sanitize($sefPath[0]);
            if(0) {
                // remove whitespaces and tags
//            $data = strip_tags(trim($data));
                // remove whitespaces and script tags
                $data = self::strip_tags_content(trim($data), '<script>', true);
//            $data = self::strip_tags_content(trim($data), '<iframe>', true);

                // apply stripslashes if magic_quotes_gpc is enabled
                if (get_magic_quotes_gpc()) {
                    $data = stripslashes($data);
                }

                $data = self::escape($data);

                // re-escape newlines
                $data = str_replace(array('\r', '\n'), array("\r", "\n"), $data);
            }
                $tmp =1;  //FIXME we've already sanitized at this point
            if (!$user->isAdmin()) expString::sanitize($_GET);

Fixed code:
        else
            $where = $this->injectProof($where);
        $res = @mysqli_query($this->connection, $this->injectProof($sql));
        $res = @mysqli_query($this->connection, $this->injectProof($sql));
        else
            $where = $this->injectProof($where);
        $where = $this->injectProof($where);
                $values .= "'" . $this->escapeString($val) . "'";
                    $sql .= "`$var`='" . $this->escapeString($val) . "',";
            $sql .= $this->injectProof($where);
        else
            $where = $this->injectProof($where);
        $res = @mysqli_query($this->connection, $this->injectProof($sql));
        else
            $where = $this->injectProof($where);
        else
            $where = $this->injectProof($where);
        $res = @mysqli_query($this->connection, $this->injectProof($sql));
    /**
   	 * Unescape a string based on the database connection
   	 * @param $string
   	 * @return string
   	 */
   	function injectProof($string) {
   	    $quotes = substr_count("'", $string);
        if ($quotes % 2 != 0)
            $string = $this->escapeString($string);
        $dquotes = substr_count('"', $string);
        if ($dquotes % 2 != 0)
            $string = $this->escapeString($string);
        return $string;
    }

        // conventional method to ensure the 'id' is only an id
        if (isset($_REQUEST['id'])) {
            if (isset($_GET['id']))
                $_GET['id'] = intval($_GET['id']);
            if (isset($_POST['id']))
                $_POST['id'] = intval($_POST['id']);

        // do the same for the other id's
        foreach ($_REQUEST as $key=>$var) {
            if (is_string($var) && strrpos($key,'_id',-3) !== false) {
                if (isset($_GET[$key]))
                    $_GET[$key] = intval($_GET[$key]);
                if (isset($_POST[$key]))
                    $_POST[$key] = intval($_POST[$key]);

                $_REQUEST[$key] = intval($_REQUEST[$key]);
            }
        }
            } elseif (count($this->url_parts) == 1 || $db->selectObject('section', "sef_name='" . substr($this->sefPath,1) . "'") != null) {
                if (($db->selectObject('section', "sef_name='" . $this->url_parts[0] . "'") != null) && (in_array(array('printerfriendly','exportaspdf','ajax_action'), $this->url_parts))) {
        return expString::escape(expString::sanitize($url));
        $this->sefPath = expString::escape(expString::sanitize($sefPath[0]));
//            if(0) {
//                // remove whitespaces and tags
////            $data = strip_tags(trim($data));
//                // remove whitespaces and script tags
//                $data = self::strip_tags_content(trim($data), '<script>', true);
////            $data = self::strip_tags_content(trim($data), '<iframe>', true);
//
//                // apply stripslashes if magic_quotes_gpc is enabled
//                if (get_magic_quotes_gpc()) {
//                    $data = stripslashes($data);
//                }
//
//                $data = self::escape($data);
//
//                // re-escape newlines
//                $data = str_replace(array('\r', '\n'), array("\r", "\n"), $data);
//            }
//                $tmp =1;  //FIXME we've already sanitized at this point
            if (!$user->isAdmin())
                expString::sanitize($_GET);