This vulnerability was reported earlier directly to both the Cart66 team and to Semper Fi Web Design (the maintainers of the GitHub clone). This post follows a 30-day responsible disclosure window.
Last month, a friend pointed me to the no-longer-in-development Cart66 Pro module for WordPress. The plugin ceased development about two years ago when the team moved to a hosted SaaS model, but it’s still available by way of a GitHub mirror for anyone still actively using the tool. Just one problem: it has some significant security holes.
The first critical flaw in the plugin is the way protects it fails to protect user credentials. WordPress isn’t widely known for its strong stance on user password hashing, but at least leverages multiple hashing rounds and a random salt to prevent the creation of rainbow tables for brute forcing a login.
Cart66, however, uses a single round of MD5 hashing on the password. Without a cryptographic salt.
This fails to offer much protection at all on a user password. Further, MD5 is considered a broken hash and it’s relatively easy to use an aforementioned rainbow table to reverse from a hash to a user-specified string.
Considering how frequently end users reuse passwords, getting a copy of the unsalted single-round MD5 hashes from a Cart66 database would give any attacker a solid starting point for trying to steal a customers’ account elsewhere. Perhaps they reused the same password for PayPal? For another ecommerce site? For their email address?
The weak state of password hashing on Cart66 Pro-powered sites results in databases ripe for abuse if they can be stolen by a malicious party.
Like all good ecommerce and marketing tools, Cart66 Pro presents an efficient way both to email customers and for customers to opt-out of receiving emails. Unfortunately, the opt-out mechanism is vulnerable to a somewhat trivial SQL injection vulnerability!
The opt-out system is powered by a WordPress shortcode. A WordPress administrator adds this shortcode to a standard post or page in WordPress, then visitors hit that page when they click an opt-out link in the footer of an email. PHP code running on the page will then extract certain query parameters from the URL to identify and flag the user as opted out of messaging.
One of these parameters is the customer’s email address (as that’s their primary identifier in the database). By default, the email address is passed as a Base64-encoded, URL-encoded parameter in the request.
For example, firstname.lastname@example.org would be encoded as ZXJpYyU0MHRoaXNpc2FyZWFsbHliYWRpZGVhLmNvbQ==.
However, there is zero sanitization on this input. While a typical user would click a link in their email client, an attacker could craft their own URL with whatever parameters they want specified. Imagine instead that an attacker passed JTI3JTNCVFJVTkNBVEUrd3BfY2FydDY2X2FjY291bnRzJTNCLS0= as the email parameter in the request.
This decodes to ';TRUNCATE wp_cart66_accounts;--, which is obviously not an email address. To understand why it’s an issue, you have to understand that, after decoding the email parameter, Cart66 Pro passes the value directly into a SQL statement:
$itemsTable = Cart66Common::getTableName('accounts');
$sql = "SELECT id from $itemsTable where email = '$email'";
$id = $this->_db->get_var($sql);
Running through the interpolation, this will have the effect of running the following query directly against the WordPress database:
SELECT id from wp_cart66_accounts where email = '';TRUNCATE wp_cart66_accounts;--'
The first SELECT statement will obviously fail to match any accounts, but the second TRUNCATE statement will drop all account data from the database!
A well-armed attacker could also craft a request to insert arbitrary information into the database, manipulate the data already present, or even extract whatever contents they want from the WordPress database (including a list of all account logins and weakly-hashed passwords)!
If you are using Cart66 Pro and have not already patched this issue, please do so immediately as your database (and your customers’ information) is at risk!
SQL injection attacks are one of the most prevalent in the open source software community. It’s incredibly easy to write insecure code and trust that the only inputs to our functions are legitimate. But every developer needs to occasionally put on their “how would someone break my code” hat and protect against the worst case scenario.
WordPress in particular has extensive documentation about how to properly prepare SQL statements to prevent this kind of injection attack. If you’re using WordPress and writing code that queries the database directly, use this approach to avoid a malicious party abusing your code to attack the platform.
If you’re writing PHP but not using WordPress, leverage tools like PDO that support prepared, parameterized statements to protect your code from abuse.
There is literally no excuse to not prepare your SQL statements in modern code. Anything less is reckless and opens not only your customers but also your business to attack and abuse. It’s worth it to take the time and do things right the first time. Security engineers will often find these issues in the wild and give you a head’s up; but usually a malicious party has already found and exploited the hole by then.
For more advice on writing secure PHP code, I’d encourage you to pick up a copy of my new book, Security Principles for PHP Applications. I’ll walk you through all of the updated OWASP Top Ten, and specifically through properly avoiding both SQL and other forms of injection attacks. It’s a great place to start to keep your project secure.