Shoplift (CVE-2015-1397): the Magento 1 SQL injection that was weaponised in 24 hours
Magekwik Security 6 min read
CVE-2015-1397 is the SQL injection at the centre of the Magento 1 "Shoplift" chain — an unauthenticated grid-export flaw that let attackers write admin accounts straight into the database. Patched by SUPEE-5344 in February 2015, it was mass-exploited within a day of disclosure. The mechanism, the indicators of compromise, and the fix.
What it is
"Shoplift" is the community nickname for the vulnerability chain that Magento's SUPEE-5344 patch closed on 9 February 2015. (Check Point researchers found and reported it; the "Shoplift" name came from the patch and community coverage as stores were exploited, not from the original advisory.) It affects Magento 1 CE up to and including 1.9.1.0 and EE up to and including 1.14.1.0. The chain is three linked CVEs: an admin authentication bypass (CVE-2015-1398), a SQL injection in the admin grid export path (CVE-2015-1397), and a remote file inclusion primitive (CVE-2015-1399). Individually they are unremarkable. Composed, they give an unauthenticated remote attacker arbitrary SQL execution, and from there code execution, against any exposed storefront.
Practical impact
admin_user row, which is enough to install a module, drop a PHP shell, and skim cards. NVD scores CVE-2015-1397 at CVSS v2 6.5, but that number describes the isolated injection, not the chained, pre-auth reality.Root cause
The injection lives in Mage_Adminhtml_Block_Widget_Grid::getCsvFile(), reached through the admin grid range filters. When a grid column filter is a range (a from/to pair), the value is passed as an associative array into Varien_Db_Adapter_Pdo_Mysql::prepareSqlCondition(). That method builds the SQL fragment from the array keys — and it honours an extra field_expr key that is treated as a raw column expression and interpolated into the statement without quoting or binding. The search-terms report grid exposes this path via its popularity column filter — the popularity[field_expr], popularity[from], and popularity[to] parameters.
None of this matters if the request never reaches an admin controller. That is what CVE-2015-1398 supplies. The admin pre-dispatch authentication check only rewrites the request to the login/denied action when a specific condition is met; when it is not met the controller is left unchanged and execution continues. Supplying a crafted forwarded parameter — logic meant for an alternate dispatch flow — flips that condition, so the auth check falls through and the grid export action runs unauthenticated. The RFI stage then abuses the core_file_storage table plus a phar:// wrapper to turn a written media file into executed PHP.
How attackers abuse it
The public proof of concept (Exploit-DB 37977) is a single POST to the CMS WYSIWYG directive route, carrying the forwarded bypass and a popularity[field_expr] payload whose subquery writes directly into the admin tables.
POST /index.php/admin/Cms_Wysiwyg/directive/index/ HTTP/1.1
Content-Type: application/x-www-form-urlencoded
___directive=e3tibG9jayB0eXBlPUFkbWlu... # base64 admin grid block
&filter=<url-encoded popularity[field_expr] SQLi>
&forwarded=1
Who got hit
Shoplift became the textbook example of how fast a public Magento bug is weaponised. SecurityWeek reported working exploits within 24 hours of the technical write-up; Sucuri saw them in the wild days later, injecting rogue administrator accounts and card-skimming JavaScript into checkout. Because SUPEE-5344 lagged on a huge share of the install base, well over a hundred thousand stores were exposed at the same moment — a single, largely automated campaign against the whole ecosystem.
How to check
Confirm the version and whether SUPEE-5344 is applied, then look for the persistence attackers leave behind — most often an unexpected admin account.
# Version — anything <= 1.9.1.0 (CE) / 1.14.1.0 (EE) unpatched is in range
php -r "require 'app/Mage.php'; echo Mage::getVersion(), PHP_EOL;"
grep -l "SUPEE-5344" app/etc/applied.patches.list 2>/dev/null
# Rogue admins created around the compromise window (run in the DB):
# SELECT user_id, username, email, created FROM admin_user ORDER BY created DESC;
How to fix it
- Apply SUPEE-5344 immediately. If you cannot apply raw SUPEE patches, move to OpenMage LTS, which bundles every SUPEE fix on a maintained Magento 1 codebase.
- Assume compromise if you were unpatched during the wave: audit
admin_userfor accounts you did not create, reviewcore_config_dataand CMS/layout tables for injected scripts, and hunt for PHP shells undermedia/and web roots. - Rotate all admin credentials and API keys, then restore code from a known-good build rather than deleting individual injected files.
- Magento 1 is end of life — the durable fix is migrating to a supported Adobe Commerce / Magento 2.4.x release.
The real lesson
References & sources
Primary sources — advisories, vendor research, and the CVE record. Verify against these, not us.