CubeCart 6.1.12 - Admin Authentication Bypass


CubeCart

CubeCart is an open source e-commerce solution for an easy to install webshop package. In one of our latest security analysis we found two flaws in this web application that allow an attacker to circumvent the authentication mechanism required to login as an administrator. Once bypassed, an attacker can execute arbitrary code on the web server and steal all sensitive files and data. In this technical blog post we will take a closer look at these interesting vulnerabilities and learn how a custom database abstraction layer can turn against you.

I Forgot My Password!

Both vulnerabilities are exploitable through CubeCarts “I forgot my Password!” functionality. It is implemented in the file classes/cubecart.class.php, in the method _recovery(). When a user forgot his password, he can use this feature to enter his email address, a valid password reset token he received via email, and his new password for reset.

classes/cubecart.class.php

27612762276327642765276627672768
private function _recovery() {
	if (isset($_POST['email']) 
	&& isset($_POST['validate']) 
	&& isset($_POST['password'])) {
		$GLOBALS['user']->passwordReset($_POST['email'], 
						$_POST['validate'], 
						$_POST['password']);
	}

At the beginning of this method, these three user controlled parameters are passed to the passwordReset() method of the User class located in classes/user.class.php. The method is responsible for the account retrieval.

classes/user.class.php

679680681682683684685686687688689690691692693694695
public function passwordReset($email, $verification, $password) {
	if (filter_var($email, FILTER_VALIDATE_EMAIL) 
	&& !empty($verification) &&!empty($password['password'])
	&& !empty($password['passconf']) 
	&& ($password['password'] === $password['passconf'])) {

		if (($check = $GLOBALS['db']->select('CubeCart_customer', 
		array('customer_id', 'email'),
		array('email'=>$email, 'verify'=>$verification)))!==false) {
			
				// Password reset successful
			
		}
	}	
	
	return false;	// Password reset failed
}

The passwordReset() method starts to check if the email is a valid email address, if all parameters are non-empty, and if the passwords are equal on line 680-683. If one of those checks fails the password reset progress will fail on line 694. Otherwise, the next check is a database query issued by a select() call in the lines 685-687. Here, the user supplied $email and $verification token is used as arguments.

classes/database.class.php

569570571572573574575576
public function select($table, $columns = false, $where = false) {
	$table_where = $table;
	
	$parent_query = "SELECT $sql_cache $calc_rows ".
	implode(', ', $cols).	" FROM $wrapper{$prefix}$table$wrapper ".
	$this->where($table_where, $where)." $group $orderString $limit;";
	
	$this->_execute($cache);

The select() method constructs a SQL query which is then sent to the database (line 576). To construct the WHERE clause of the SELECT query, the application uses the vulnerable method where() in line 574. In the next two sections we will analyze this where() method and present two individually detected vulnerabilities.

Unauthenticated Blind SQL Injection

The where() method of the database.class.php sanitizes values provided in the second parameter $whereArray perfectly fine with the PHP built-in function mysql_real_escape_string(). However, if the value is an array (line 811), then each value of the array is concatenated unsanitized into the SQL query on line 816.

classes/database.class.php

807808809810811812813814815816817818819820821822823824
public function where($table, $whereArray = null, $label = false) {
	
	foreach ($whereArray as $key => $value) {
		
		if (is_array($value)) {
			foreach ($value as $val) {
				
				$or[] = "`$key` IN (".implode(',', $value).')';
				
			}
			if (isset($or) && is_array($or)) {
				$where[] = implode(' OR ', $or);
				unset($or);
			}
		}
	
	}
	return 'WHERE '.implode(' AND ', $where);

As an attacker we can now pass an array as our user input. This will allow us to inject SQL syntax into the constructed SQL query and to perform SQL injection attacks to extract sensitive information from the database. A malicious POST request could look like the following:

email=contact@ripstech.com
validate[]=0)+OR+sleep(10
password[password]=secretnewpassword
password[passconf]=secretnewpassword
token=15f84b621a9982d65f82d6f12764ecdb

Note how the validate input parameter now is an array not containing a valid password reset token anymore but our SQL payload. The constructed SQL query can be seen below (the injected part is marked red).

SELECT `customer_id`, `email` FROM `cc6111_CubeCart_customer` WHERE 
cc6111_CubeCart_customer.email = 'contact@ripstech.com' 
AND `verify` IN (0) OR sleep(10);

As a result, an adversary can modify the SQL query and start to extract the password hashes of all users, or aim for more sophisticated attacks pivotting on the database to the underyling operating system. But there is an even easier way to authenticate than to steal and crack the administrator’s password hash.

  See RIPS report

Authentication Bypass

Our second vulnerability is only a few lines away from our SQL injection vulnerability showing that we actually do not need to inject SQL syntax to gain access as an administrator. The where() method of the database.class.php file also introduces search modifiers.

classes/cubecart.class.php

807808809810811812813814815816817818819820821822823824825826827828829830831
public function where($table, $whereArray = null, $label = false) {
	
	foreach ($whereArray as $key => $value) {
		
		if (isset($value) && !ctype_alnum($value) || $value=='NULL' || 
			is_null($value) || $value=='NOT NULL') {
			if(preg_match('#^([<>!~\+\-]=?)(.+)#',$value, $match)){
				switch ($match[1]) {
					case '~':
						// Fuzzy searching
						$symbol = 'LIKE';
						$value = "%{$match[2]}%";
						break;
					default:
						$symbol = $match[1];
						$value = trim($match[2]);
				}
			}
		}
		$full_key = ($label ? $label : $this->_prefix.$table).".".$key;
		
		$where[] = "$full_key $symbol ".$this->sqlSafe($value, true);
		
	
	return 'WHERE '.implode(' AND ', $where);

Basically the where() method checks the input values for special characters (< > ~ ! + -) ultimately effecting which comparison operator will be used in the WHERE clause of the SQL query. For example, a prefixed tilde character (~) in a value will construct a SQL query with a LIKE syntax (line 817-818). A LIKE operation does not require an exact match in the database but allows wildcard characters (%). This can be abused to bypass the check for a valid password reset token. All we have to do is to prefix our password reset token with a ~ character and to put as many wildcard characters into the password reset token as the expected token length is. This will result in the following SELECT query:

   
select * from CubeCart_customer where email = 'contact@ripstech.com' 
and verify LIKE '%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%'

The WHERE condition that requires a correct verify token will evaluate to true almost all the time with our crafted verification token and is thus bypassed. This allows an adversary to reset the password of an administrator in a matter of seconds and to login as admin. In the administration panel, an attacker can then abuse admin features to execute arbitrary PHP code.

Time Line

DateWhat
2017/10/11Provided vulnerability details and PoC to vendor
2017/10/11Vendor confirmed security issue
2017/10/16Vendor released 6.1.12 version
2017/11/23Vendor informed about additional issues
2017/11/29Vendor released 6.1.13 fixed version

Summary

We detected two critical issues that allow an attacker to bypass CubeCart’s authentication and to login as an administrator. The security issues base on a custom database abstraction layer that compiles SQL queries in an unsafe manner. Due to the absence of prepared statements and custom SQL concatenation features, an attacker can malform the SQL query that is used for authentication in order to bypass it.

We would like to thank the CubeCart team for their very fast and professional handling of these issues. They responded immediately to our report and released a fixed version rapidly. We recommend to update to CubeCart 6.1.13 immediately.


Tags: robin peraglie, php, security, authentication bypass, sql injection,

Author: Robin Peraglie

Security Researcher

Robin is a passionate bug hunter and security researcher at RIPS Technologies. Since he was young he experimented with web security, cryptography and lockpicking. He received a degree in IT Security at the Ruhr-University Bochum and collected industrial experience in penetration tests and professional code audits.

Comments

comments powered by Disqus