How I bypassed Facebook CSRF once again!

I discovered a vulnerability in Facebook Lead Ads that allowed me to create an arbitrary form capable of sending a POST request containing Facebook's anti-CSRF token to Facebook endpoints or external hosts.

The issue was similar in concept to a CSRF bypass I had found previously, but this time the vulnerable functionality existed inside Facebook's Lead Ads Continued Flow feature.

Product Facebook Lead Ads
Feature Continued Flow
Issue CSRF protection bypass / insufficient server-side validation
Impact Send authenticated POST requests with the victim's Facebook CSRF token
Bounty $7,500
Facebook CSRF research

Facebook's CSRF Protection

Facebook used the fb_dtsg parameter as an anti-CSRF token for many state-changing requests.

For protected actions, Facebook expected the token to be included in the request and validated server-side. Without a valid token, the request should normally be rejected.

The important question was whether any Facebook functionality could be abused to generate and submit requests containing a valid fb_dtsg token on behalf of the user.

Lead Ads Continued Flow

I found the vulnerable functionality in Facebook's Continued Flow feature of Lead Ads.

A Continued Flow Lead Ad allowed the final step of the form to be completed on the advertiser's website. Facebook could collect the user's submitted data and send it to a destination URL using either a hash or a POST request.

The interesting part was Facebook's POST implementation: when Continued Flow submitted the request, Facebook automatically added the user's fb_dtsg token.

Root cause of Facebook Lead Ads CSRF bypass
The root cause: Continued Flow could submit a POST request containing Facebook's anti-CSRF token.

Turning the Feature Into a CSRF Primitive

If the destination of the Continued Flow request could be controlled, the feature effectively became a way to generate authenticated POST requests containing a valid Facebook CSRF token.

That meant an attacker-controlled Lead Ad form could potentially submit requests directly to Facebook endpoints while carrying the victim's own fb_dtsg.

Facebook Lead Ads CSRF attack scenario
The attack scenario using Continued Flow to send an authenticated request.

Bypassing the Continued Flow Whitelist

There was one obstacle: according to Facebook's documentation, Continued Flow was only available to whitelisted users.

While reviewing how Lead Ad forms were created, I noticed that Facebook sent the form configuration as a JSON object to the form-creation endpoint.

I then found another endpoint that returned previously created Lead Ad forms as JSON. Comparing the returned structure revealed additional fields related to Continued Flow.

Facebook Lead Ads form JSON containing Continued Flow fields
Lead Ads form JSON exposing fields related to Continued Flow.

I manually added those fields to the normal form-builder JSON and changed their values.

The server accepted the modified structure and created a Continued Flow form even though my account was not whitelisted for the feature.

There was no effective server-side validation enforcing the whitelist restriction.

Example: Disabling Timeline Review

As a proof of concept, I used the issue against Facebook's Timeline Review setting.

Target endpoint:

https://facebook.com/ajax/settings/timeline/review.php

Request body:

tag_approval_enabled=0

The resulting destination could be constructed as:

https://facebook.com/ajax/settings/timeline/review.php?tag_approval_enabled=0&__a=1

I tested the resulting Lead Ad using Facebook's own Lead Ads testing tool, and the request was successfully executed.

Proof of Concept

Another Interesting Case

During the research, I also tested what happened when the name of a custom Lead Ads field itself was set to fb_dtsg.

Facebook Lead Ads custom field named fb_dtsg
A custom Lead Ads field using fb_dtsg as its name.

Resolution

Facebook acknowledged and fixed the primary issue, and later fixed the additional whitelist-related issue as well.

Disclosure Timeline

March 29, 2016 Initial vulnerability reported.
April 6, 2016 Facebook requested additional information.
April 6, 2016 Additional technical details provided.
April 7, 2016 Issue acknowledged by Facebook's security team.
April 7, 2016 Additional fb_dtsg field behavior reported.
April 12, 2016 Primary vulnerability fixed.
April 13, 2016 $7,500 bounty awarded.
April 18, 2016 Additional details about the whitelist bypass provided.
May 6, 2016 Second issue fixed.