Ruby on Rails · Report #732415
The authenticity_token can be reversed and used to forge valid per_form_csrf_tokens for arbitrary routes
How it was found
When per_form_csrf_tokens is set to true, each form should protected against CSRF with a unique token that is not predictable by an attacker. Theper_form_csrf_token is generated using a HMAC SHA-256 using a key that is exposed in a reversed authenticity_token. The authenticity_token is a Base64 encoding of
one_time_pad | (one_time_pad XOR session[:csrf_token])
Because the one_time_pad is the first half of the authenticity_token, it is not secret and an attacker can reverse the token to learn session[:csrf_token].
From there the attacker can forge a hash for an arbitrary route (e.g. /articles/2):
HMAC ( session[:csrf_token], "/articles/2#patch")
To reproduce:
- Have two Rails routes that accept only per form csrf tokens
- Validate that the
authenticity_tokensent in the POST data returns an HTTP 422 when sent in the other form - Forge a per form token with the attached exploit script
- the
authenticity_tokenparameter is taken from the <meta> tag in the header of any page for the session - the
routeparameter is the action of the target HTML form (e.g /articles/2) - the
methodparameter is the value from_methodparameter sent in the POST data (e.g. patch)
- the
- Take the forged token from the exploit script, URL-encode it, and send it as the
authenticity_tokenin the POST data. For reliability, ensure that:- the route parameter matches the endpoint,
- the
_methodparameter in the POST data matches what used formethodin the exploit script - the forged
authenticity_tokenin the POST data is properly URL-encoded
Impact
Exploitation allow an attacker to forge valid per-form CSRF tokens even in hardened situations where the global authenticity_token itself is not allowed.
For the attack to be successful, an attacker would need a valid global authenticity_token. This can be extracted out of a web page without any protections that cookies have (such as HTTP-Only). An attacker could leverage an XSS vulnerability to bypass per-form CSRF on unrelated pages. Because the token can be forged for any form, code execution on a page without forms could still lead to attackers bypassing CSRF protections of forms related to password changes, deletion of data, creation of new users, etc.