# Strapi may leak sensitive data via relational filtering due to lack of query sanitization

Unauthenticated Strapi Content API filters traversed into admin_users relations, letting a boolean oracle extract admin resetPasswordToken and take over the admin account.

- Payout: Research
- Severity: critical
- Program: Strapi
- Found by: WildWestCyberSecurity
- Disclosed: 2026-05-14
- CVE: CVE-2026-27886
- Write-up quality: 7/10
- Original: https://github.com/advisories/GHSA-rjg2-95x7-8qmx
- Library page: https://bugbountylibrary.com/r/ghsa%3AGHSA-rjg2-95x7-8qmx
- Tags: area:web domain:api domain:web impact:ato impact:data-leak kind:research platform:ghsa sev:critical tech:nodejs technique:blind vuln:access-control vuln:account-takeover vuln:info-disclosure

**Target:** Strapi Content API query parameter filtering (where operator chains)
**Root cause:** Query parameter sanitization did not block operator chains traversing into relational target schemas the caller lacked read permission on, so filters on updatedBy/createdBy/publishedBy emitted WHERE clauses against joined admin_users columns.
**Steps:**
1. Identify a publicly-accessible content-type exposing an admin-relation field such as `updatedBy`.
2. Send `GET` to its Content API endpoint with `where[updatedBy][resetPasswordToken][$startsWith]=a`.
3. Observe the response count as a one-bit oracle: a match confirms the guessed prefix.
4. Iterate the trailing character over the hex alphabet (`0`-`9`, `a`-`f`) with progressively longer prefixes to recover the full token.
5. Use the recovered token via `POST /admin/reset-password` to set a new admin password.
**How found:** Reporter noticed the sanitization layer permitted operator chains reaching into relational target schemas without read permission, and that the generated LEFT JOIN against admin_users made response counts leak private column values.
**Exploitation:** Unauthenticated attacker repeatedly queries a public Content API endpoint with `where[updatedBy][resetPasswordToken][$startsWith]=<prefix>` filters, using the returned count as a boolean oracle to brute-force the admin reset token character by character, then submits it to `POST /admin/reset-password` to take over the admin account.
**Escalation:** Token extraction escalates an unauthenticated read oracle to full administrative account takeover.
**Insight:** On ORMs that auto-join relations for filters, test whether filter operators can traverse into relation targets the caller cannot read; response counts on list endpoints turn any joined private column into a boolean oracle.

