A while back I was playing around with the OAuth2 spec and discovered a flaw in how Vimeo associates Facebook accounts. Their Facebook connect callback URL was vulnerable to a Cross Site Request Forgery, allowing an attacker to connect their Facebook account with a victim’s Vimeo account.
If you try to connect a Facebook account to your Vimeo account, Vimeo sends you to the following URL:
Once you accept the authorization prompt, Facebook returns an HTTP 302, redirecting you back to Vimeo’s
redirect_uri along with a
code that Vimeo uses to access your Facebook info and associate the accounts.
Along with the
code, Facebook sends back the
state value from the URL above so that Vimeo can verify that the callback request is authentic and originated from the same browser that started the flow.
Vimeo correctly set a
state parameter, but used code similar to the following to verify the state:
def callback if params[:state] == session[:state] # associate accounts else # error csrf detected! end end
But what is
session[:state] when the request is not authentic?
session[:state] will be
The attacker logs into their own Vimeo account and beings the flow to connect their Facebook account.
The attacker accepts all permissions, but uses a tool like NoRedirect to block their browser from making the final redirect back to Vimeo:
- The attacker uses this URL, omitting the
stateparameter as part of a CSRF attack on a page that the victim (who is currently logged into Vimeo) is likely to view.
<img src='https://vimeo.com/settings/apps?action=connect&service=facebook&code=<CODE>#_=_' />
- The victim’s Vimeo account is now associated with the attacker’s Facebook account. The attacker can now use their Facebook to login to the victim’s Vimeo account.
The callback must ensure that the
state query parameter matches the persisted
state token AND that both values are non-nil.