# Craft CMS has IDOR via GraphQL @parseRefs

Craft CMS GraphQL @parseRefs directive resolves {user:1:email}-style reference tags without canView checks, letting guests read any element's attributes.

- Payout: Research
- Severity: high
- Program: Craft CMS
- Found by: z3rco
- Disclosed: 2026-03-03
- CVE: CVE-2026-28696
- Write-up quality: 7/10
- Original: https://github.com/advisories/GHSA-7x43-mpfg-r9wj
- Library page: http://bugbountylibrary.com/r/ghsa%3AGHSA-7x43-mpfg-r9wj
- Tags: area:web3 domain:web impact:data-leak impact:pii-leak kind:research platform:ghsa sev:high tech:graphql tech:php technique:source-review vuln:access-control vuln:idor vuln:info-disclosure

**Target:** GraphQL API @parseRefs directive / Elements::parseRefs
**Root cause:** Elements::parseRefs resolves reference tags via _getRefTokenReplacement and reads $element->$attribute without calling canView(), and getElementTypeByRefHandle permits any element type plus __get() custom field handles.
**Steps:**
1. Enable Public Schema with element queries for a section (e.g. News).
2. Create an entry whose title contains `{user:1:username}` or `{user:1:email}`.
3. POST `{"query": "{ entries { title @parseRefs } }"}` to `/index.php?action=graphql/api`.
4. Response returns resolved attribute, e.g. `{"data":{"entries":[{"title":"admin"}]}}`; `{user:1:authKey}` leaks server paths via stack trace.
**How found:** Reporter inspected Elements::parseRefs and _getRefTokenReplacement, noticed no canView() call and that getElementTypeByRefHandle accepts arbitrary element types, then tested reference tags in a public schema field.
**Exploitation:** Unauthenticated guest queries a public schema field containing a reference tag; @parseRefs resolves it server-side and returns the target element's attribute, enabling PII enumeration, admin username disclosure, private entry/asset field reads, and path disclosure via error stack traces.
**Escalation:** Polymorphic targeting of any element type (user, entry, asset, volume) and custom field handles via __get(), plus stack-trace path leakage from erroring attributes like authKey.
**Insight:** GraphQL directives that resolve templated reference strings are a blind spot for authorization: check whether the resolver re-applies canView() per resolved element, and probe __get() custom field handles plus erroring attributes for stack-trace leakage.

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

**Unauthenticated GraphQL @parseRefs exploit request**

```
curl -X POST \
-H "Content-Type: application/json" \
-d '{"query": "{ entries { title @parseRefs } }"}'
```

