# The authenticity_token can be reversed and used to forge valid per_form_csrf_tokens for arbitrary routes

Rails authenticity_token embeds its one-time pad, letting an attacker recover session[:csrf_token] and forge per-form CSRF tokens for arbitrary routes.

- Payout: hidden
- Severity: medium
- Program: Ruby on Rails
- Found by: jregele
- Disclosed: 2020-08-27
- CVE: CVE-2020-8166
- Write-up quality: 8/10
- Original: https://hackerone.com/reports/732415
- Library page: https://bugbountylibrary.com/r/h1%3A732415
- Tags: area:web domain:web impact:integrity kind:bounty platform:hackerone sev:medium tech:ruby technique:chained technique:source-review vuln:csrf vuln:secrets-leak

**Target:** Rails CSRF token generation (per_form_csrf_tokens)
**Root cause:** authenticity_token is Base64 of one_time_pad | (one_time_pad XOR session[:csrf_token]); the pad is transmitted in cleartext, so XORing the halves recovers the session CSRF secret used as the HMAC key for per-form tokens.
**Steps:**
1. Enable per_form_csrf_tokens and have two routes accepting only per-form tokens.
2. Confirm a token from form A returns HTTP 422 on form B.
3. Extract authenticity_token from the <meta> tag of any page in the session.
4. XOR first half (pad) with second half to recover session[:csrf_token].
5. Compute `HMAC(session[:csrf_token], "/articles/2#patch")` for the target route and method.
6. URL-encode the forged token and send it as authenticity_token in the POST with matching _method.
**How found:** Code-reading of the token construction: noticed the one-time pad is the first half of the token, making it non-secret and reversible to the session CSRF secret.
**Exploitation:** Attacker needs a valid global authenticity_token (readable from page, not HTTP-Only). Recover session[:csrf_token] via XOR, forge per-form token for any route/method, submit cross-site POST to perform password change, deletion, or user creation.
**Escalation:** Chains with XSS on a form-less page to bypass per-form CSRF on unrelated sensitive forms.
**Insight:** When a token is masked with a one-time pad, check whether the pad itself is shipped alongside the masked value; if so the underlying secret is trivially recoverable and any HMAC keyed on it is forgeable.

