in Advisories

CS-Cart v4.2.0 Session Hijacking and Other Vulnerabilities

Vendor: CS-Cart

Affected versions: up to v4.2.0

Patched: v4.2.1 released

CS-Cart is a semi-popular open source e-commerce shopping cart application. It contains a homebrew session management system that utilizes an insecure source of randomness to generate session tokens. The poor source of randomness combined with other bugs makes it possible to hijack an administrators session with a small brute-force window.

The exploit involves a number of steps, and i’ll set them all out below along with background on each step.

Part 1: Weak Session ID Generation

In the file ./app/Tygh/Session.php line 49:

    private static function _generateId()
    {
        $cmp = uniqid(time());
        return md5($cmp);
    }

A session key based on uniqid, which is not cryptographically secure. This is what the PHP manual says about uniqid:

Warning This function does not create random nor unpredictable strings. This function must not be used for security purposes. Use a cryptographically secure random function/generator and cryptographically secure hash functions to create unpredictable secure IDs.

What might be understated is just how non-random uniqid really is. Here is the relevant source code for the PHP function:

gettimeofday((struct timeval *) &tv, (struct timezone *) NULL);
sec = (int) tv.tv_sec;
usec = (int) (tv.tv_usec % 0x100000);

/* The max value usec can have is 0xF423F, so we use only five hex
* digits for usecs.
*/
if (more_entropy) {
    spprintf(&uniqid, 0, "%s%08x%05x%.8F", prefix, sec, usec, php_combined_lcg(TSRMLS_C) * 10);
} else {
    spprintf(&uniqid, 0, "%s%08x%05x", prefix, sec, usec);
}

RETURN_STRING(uniqid, 0);

It turns out uniqid is just a call to gettimeofday and formatted back in hex. The parameter passed to uniqid is not a seed, it is just a prefix. Passing time() to uniqid() effectively just prints the time twice.

To better understand just how non-random this is, here is a pure PHP implementation of uniqid to make it a little easier to understand:

function homebrew_uniqid($prefix='') {
  $time = gettimeofday();
  $sec = $time['sec'];
  $usec = $time['usec'];
  return sprintf("%s%08x%05x", $prefix, $sec, $usec);
}

Example output:

print uniqid() . PHP_EOL;
print homebrew_uniqid() . PHP_EOL;

5395c0796bc60
5395c0796c427

CS-Cart generating md5(uniqid(time())) as a session ID is simply the md5 hash of: the current time as an integer in seconds plus the current time as a hexadecimal number plus the current number of usecs in hex.

You can probably see already where this is heading. There are 999,999 usec’s per second, so if we can work out at what time a session was generated within a second we can take a shot at brute-forcing it.

Exploiting this will require finding a way to force the user into recreating a session or figuring out when their session was created.

Part 2: Regenerating the Administrators Session

In the same Session.php file in the start method that is called on every request there is this little block of code:

// Force transfer session id to cookies if it passed via url
if (!empty($request[self::$_name])) {
    self::setId($request[self::$_name], false);
}

The $request variable simply stores all the applications request variables (it mixes GET and POST vars into one array, replicating PHP’s $_REQUEST).

What this code says is – if there is a request variable that is the same name as the session name, then set the value of the session to the value of the request.

We don’t know the value, but what this allows an attacker to do is to set the session variable to an invalid value, thus killing the session.

The session name is unique to each install, but is easy to retrieve by simply visiting the admin portal login page and checking the name of the cookie.

In my example install the name of the cookie is sid_admin_e66cd.

We can then craft a URL that when visited will log the admin user out since it isn’t a valid session (there is also a header inject bug here):

http://cscart.local/admin.php?sid_admin_e66cd=a

The value of the cookie doesn’t matter. When an administrator visits that URL they will be logged out of their session and sent back to the login page.

We now have a way to force the admin user to re-crate the session. If we send the admin that link and hope they log in again we can narrow down the search field for the brute-force.

Part 3: User-Agent Checking

One problem with exploiting it is that the CS-Cart application stores the administrators user-agent in the session and checks it on every request. If the user-agent does not match the user agent stored for the user then the session is expired.

There is a way around this – we simply wrap the URL around a URL shortener that we control. That gives us not only the user-agent but also the correct UTC (or relevant timezone) datetime where we can begin our session search from.

I have my own evil shortener that I use – it is hosted on a couple of innocent looking domain names. I crafted the admin logout URL, shortened the link to look like an image and then sent a spearphish email to the target admin:

Hi, I was wondering if you have this in stock:

http://www.adomainsimilartothetarget.com/images/leather-sofa.jpg

The URL would redirect to log them out on the CS-Cart admin backend. Hopefully the admin user would login again after seeing their session logout. Looking at the logs on the shortener, we now have the exact time the admin clicked the link and their exact user-agent.

Part 4: The exploit

We now have all the components we need to brute-force the session. Here is a simple python exploit (I used a multithreaded version that uses gevent)

Exploit on GitHub

Suggested improvements would be to distribute the load across more machines. I successfully hijacked a session using a small number of servers.

Issue #2: secret_key is not random.

During install CS-Cart generates a secret key that is used in encryption routines and other security routines. The generation method is not random and is rather weak.

Here is the source:

substr(str_shuffle('abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890'), 0, 10);

str_shuffle uses rand, which is also not secure.

Disclosure Timeline

10th June 2014 – Reported to CS-Cart
17th June 2014 – Bugs confirmed by CS-Cart, offered a free license of their software. I replied asking when the fix will be out – no response.
20th June 2014 – Sent them an email telling them I have more bugs – no response.
21st July 2014CS-Cart announce v4.2.1 with no mention of security patches, bury the details in the changelog without mentioning security (no credit).
7th Aug 2014 – I find out they patched the bugs weeks ago only because I checked the latest version myself.

  1. Hi Nik,

    Nice work. I also have a couple of security issues to send to them and nice to know that they have THAT kind of tech support.

    CS-Cart seems to have lot’s of security issues…

Comments are closed.