# CSRF on cards API

Twitter cards poll-vote API enforced CSRF token only on the .json path; requesting /i/cards/api/v1 without extension bypassed the check, letting attackers cast votes as victims.

- Payout: hidden
- Severity: medium
- Program: X / xAI
- Found by: filedescriptor
- Disclosed: 2017-04-11
- Write-up quality: 6/10
- Original: https://hackerone.com/reports/95555
- Library page: https://bugbountylibrary.com/r/h1%3A95555
- Tags: area:web domain:web impact:integrity kind:bounty platform:hackerone sev:medium technique:source-review vuln:auth-bypass vuln:csrf

**Target:** POST /i/cards/api/v1.json poll-vote endpoint
**Root cause:** CSRF authenticity_token check was scoped to the exact path /i/cards/api/v1.json, while the router also accepted /i/cards/api/v1 without the extension and served it without the CSRF filter.
**Steps:**
1. Send `POST https://twitter.com/i/cards/api/v1.json?tweet_id=...&card_name=poll2choice_text_only&forward=false&capi_uri=...` with body `{"twitter:string:card_uri":"card://...","twitter:long:original_tweet_id":"...","twitter:string:selected_choice":"2"}` and no CSRF token -> HTTP 403.
2. Resend the same request to `/i/cards/api/v1` (drop `.json`) without token -> HTTP 200 and vote recorded.
3. Host an auto-submitting form/page that issues the extensionless POST to silently cast the victim's vote.
**How found:** Noticed the CSRF check applied only to the exact .json path, hypothesized the server relaxes path extensions, and tested trimming v1.json to v1 to see if the filter was skipped.
**Exploitation:** Attacker lures a logged-in victim to a page that auto-submits the extensionless POST to /i/cards/api/v1 with the target poll's tweet_id, card_uri and selected_choice; the victim's session cookie authenticates the vote without any CSRF token.
**Insight:** When a framework's CSRF/route filter matches an exact path string, try alternate path forms the router still resolves (extension stripped, trailing slash, case, encoded chars) to find a variant that skips the filter but reaches the same handler.

