CVE-2026-55416: SQL Injection

Published Sep 10, 2026
·
Updated

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 reportsconfig 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, LOADFILE(), 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 reportsconfig 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 reportsconfig permission + valid admin session) [Controller] CustomReportController::updateAction() ↓ $configuration = decodeJson($request->request->getString('configuration')) [Config Store] Configuration saved to customreports 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] pregmatch('/(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) ↓ jsondecode() → stdClass [Store] Persistent in database (customreports 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 reportsconfig 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: <target-host> Content-Type: application/x-www-form-urlencoded Cookie: PHPSESSID=<validadminsession>

name=maliciousreport&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 TABLENAME, TABLESCHEMA, 1 FROM INFORMATIONSCHEMA.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

Affected Software

3 affected componentsFixes available
composer/pimcore/pimcore<11.5.18
11.5.19
composer/pimcore/pimcore>=12.0.0-RC1<=12.3.9
12.3.10
composer/pimcore/pimcore>=2026.1.0<=2026.1.5
2026.1.6

Remediation

Recommended actions to resolve this vulnerability, in priority order.

  1. Upgrade

    Upgrade composer/pimcore/pimcore to a version that resolves this vulnerability.

    Fixed in 11.5.19
  2. Upgrade

    Upgrade composer/pimcore/pimcore to a version that resolves this vulnerability.

    Fixed in 12.3.10
  3. Upgrade

    Upgrade composer/pimcore/pimcore to a version that resolves this vulnerability.

    Fixed in 2026.1.6
  4. Upgrade

    Upgrade Pimcore Custom Reports bundle (Sql adapter) to a version that resolves this vulnerability.

    Fixed in 2026.1.6
  5. Upgrade

    Upgrade Pimcore Custom Reports bundle (Sql adapter) to a version that resolves this vulnerability.

    Fixed in 12.3.10
  6. Upgrade

    Upgrade Pimcore Custom Reports bundle (Sql adapter) to a version that resolves this vulnerability.

    Fixed in 11.5.19
  7. Configuration

    Restrict the 'reports_config' permission to only highly trusted administrators, since an authenticated attacker with this permission can inject arbitrary SQL via report configuration fields ('sql', 'from', 'where', 'groupby') that are concatenated into SQL queries.

    Pimcore Custom Reports (CustomReportController/updateAction → CustomReportsBundle Tool Adapter Sql) reports_config permission scope = restrict to highly trusted administrators
  8. Configuration

    Cast LIMIT/OFFSET to integers before interpolating into the SQL string (the advisory notes the LIMIT clause directly interpolates '$offset' and '$limit' without integer casting, creating a secondary injection point).

    Pimcore Custom Reports (Sql::buildQueryString / getBaseQuery) LIMIT/OFFSET handling = cast to integers
  9. Configuration

    Replace the custom SQL concatenation approach with a parameterized query builder approach (Doctrine QueryBuilder), using parameterized queries for all user-supplied values (including WHERE conditions), instead of concatenating $config fields into SQL.

    Pimcore Custom Reports (Sql adapter) SQL query construction = use parameterized queries / Doctrine QueryBuilder for WHERE conditions
  10. Configuration

    Only allow predefined table names from a whitelist for report configuration fields such as 'from', rather than using attacker-controlled table identifiers directly.

    Pimcore Custom Reports (Sql adapter) table selection = whitelist-based approach
  11. Compensating control

    Deploy a WAF rule to block requests to /admin/bundle/customreports/custom-report/update containing SQL keywords in the 'configuration' parameter (HTTP POST body).

  12. Compensating control

    Restrict access to the report update endpoint (/admin/bundle/customreports/custom-report/update) to trusted users/sessions with the required 'reports_config' permission; the advisory notes exploitation requires an authenticated admin session with that permission.

Event History

Sep 10, 2026
Advisory Published
via GitHub·07:25 PM
Data Sourced
via GitHub·07:25 PM
DescriptionSeverityWeaknessAffected Software

Frequently Asked Questions

1

Who can exploit this issue?

An attacker must be authenticated and have the reports_config permission. Restricting that permission to only highly trusted administrators reduces exposure.

2

Which report settings are dangerous?

The sql, from, where, and groupby report configuration fields are concatenated directly into database queries. The offset and limit values in the LIMIT clause are also directly interpolated without integer casting.

3

What can an attacker do after exploiting it?

The advisory states that exploitation can allow an attacker to read, modify, or delete all database data. It also identifies techniques such as UNION SELECT, INSERT, LOAD_FILE(), INTO OUTFILE, stacked queries, subqueries, and MySQL comment injection as not blocked by the keyword blacklist.

4

Which releases contain patches?

The advisory lists patched versions 2026.1.6, 12.3.10, and 11.5.19.

Contact

SecAlerts Pty Ltd.
132 Wickham Terrace
Fortitude Valley,
QLD 4006, Australia
info@secalerts.co
By using SecAlerts services, you agree to our services end-user license agreement. This website is safeguarded by reCAPTCHA and governed by the Google Privacy Policy and Terms of Service. All names, logos, and brands of products are owned by their respective owners, and any usage of these names, logos, and brands for identification purposes only does not imply endorsement. If you possess any content that requires removal, please get in touch with us.
© 2026 SecAlerts Pty Ltd.
ABN: 70 645 966 203, ACN: 645 966 203