# Facebook GraphQL CSRF

Path traversal in business.instagram.com's /business/:id route let a crafted link smuggle a GraphQL mutation into a GET request, enabling CSRF-style arbitrary mutations as the victim.

- Payout: $7,500
- Severity: high
- Program: Meta / Facebook
- Credited: Philippe Harewood
- Disclosed: 2017-10-08
- Write-up quality: 8/10
- Original: https://philippeharewood.com/facebook-graphql-csrf/
- Library page: https://bugbountylibrary.com/r/blog%3Aphilippeharewood-com-facebook-graphql-csrf
- Tags: domain:web domain:web3 impact:integrity kind:bounty platform:blog sev:high tech:graphql technique:js-analysis technique:source-review vuln:csrf

**Target:** business.instagram.com GraphQL endpoint via /business/:id route
**Root cause:** The /business/:id route passed the id slug into a Graph API URL builder that appended `?fields=...`; by URL-encoding a full `graphql?q=...&query_params=...` payload into the id and appending `&fixend`, the attacker could override the query and issue a GraphQL mutation over a GET request.
**Steps:**
1. Visit business.instagram.com/login and observe redirect to /select; inspect BusinessToolsEntrypoint.instagram module in DevTools
2. Enumerate routes including /business/:id and trace BusinessHome.instagram -> BusinessStore.instagram -> graph.instagram fetch
3. Note the URL is built as `id + '?fields=' + fields`, so the id is concatenated before the query string
4. Craft id as URL-encoded `graphql?q=Mutation SyncAddMutations : SyncAddMutationsResponse {story_create(<input>){client_mutation_id}}&query_params={'input':'{...actor_id...message...}'}` and append `&fixend` to absorb the trailing `?fields=`
5. Send victim the link `https://business.instagram.com/business/<encoded-payload>`; victim's authenticated IG token executes the mutation
**How found:** Started from the login/select flow, used Chrome DevTools to locate BusinessToolsEntrypoint.instagram, enumerated client-side routes, then traced the /business/:id data flow into BusinessStore.instagram and the graph.instagram fetch. Noticed the id was concatenated into the URL before `?fields=`, and that the IG access token had GraphQL scope, so tried injecting an encoded graphql query into the id.
**Exploitation:** Attacker sends a victim-specific link (actor_id set to victim) to business.instagram.com/business/<encoded GraphQL mutation>. The victim, authenticated to the Instagram business app, loads the page; the frontend passes the id into the Graph API URL, the encoded payload overrides the query, and the GraphQL mutation (e.g. story_create with attacker-controlled message) executes as the victim over GET.
**Insight:** When a client-side route param is concatenated into an API URL before the query string, try URL-encoding a full alternate endpoint/query (e.g. `graphql?q=...`) into the param and append a dummy `&fixend` to swallow the trailing `?fields=`. Also check whether the page's access token carries GraphQL scope, turning a GET-rendered page into a CSRF vector for mutations.
