Insufficient GraphQL API vulnerability due to lack of validation of Authorization Bearer token
January 20th 2023. I've decided to hunt on some private program on HackerOne. Usually I don't hunt on private programs. Only on public bug bounty programs such as Yahoo!, Spotify, Twitter etc.
That day I got one private invitation from one private bug bounty program on HackerOne. And unexpectedly it was really fresh program with only few resolved reports. So, far by this I've decided to hunt on this program. Program's front-end is written using Next.js JavaScript framework. It's really clean.
January 21th 2023
While exploring on that program. I saw that they have pretty detailed API documentation. And whole API in their documentation is actually GraphQL API. And I started reading it, hence the time passing. I didn't find that day anything.
January 22th 2023
So, while I was exploring their features, I've came across with one specific feature. And obviously I went to their API documentation again and I found that feature in their API documentation and it was pretty detailed, so it took me some time to understand it.
January 23th 2023
So, after I was done with reading API documentation about that feature, I started testing that feature. Also feature was about Authorization Controls / Rule Types. It was for bank stuff.
January 24th 2023
What I've noticed is that Authorization
header with Bearer
token is being invalidated from server-side. Basically it wasn't checking for Authorization. This Rules Types feature was using same GraphQL API query FindSpendRule
for every Rule type. Let's take one type as a example which was vulnerable obviously, as all of them were vulnerable to this vulnerability since it was affecting whole feature. So, this is the type that I was talking about Street Address
Now let's see it's GraphQL API mutation:
So, this is a GraphQL mutation that creates a Street Address Rule Type and it's using the CreateStreetAddressSpendRuleInput
input type. The mutation returns many different fields, depends on the result of the operation such as the rule's ID, name, version, and timestamps if the rule is created successfully, or it will return an AccessDeniedError
from back-end side. Which means we can't access it.
Also I forgot to include our variables of our GraphQL mutation:
So, you can see it's a JSON object with a key input
and a value that is also a object too. The input
object has two keys which is the name
and allowed
. The value of name
is a string test
and the value of allowed
is an array containing a single string MATCH
. Also it used as a variable for the above GraphQL mutation CreateStreetAddressRule
Now let's execute this GraphQL mutation and let's see what happens on back-end:
So, here we have a data
object and a value. The data
object has a mutationcreateStreetAddressSpendRule
and a value that represents a street address spend rule. The object has some few fields including __typename
, id
, name
, version
, allowed
, blocked
, createdAt
, updatedAt
and the values of these fields are string, string, string, array, array, string, string
. Also this JSON object has a key extensions
with a requestId
field and a string value that is a unique identifier for the request.
So, by this logic the implementation of the createStreetAddressSpendRule
function does not properly check for the authorization before creating a new rule, and therefore attacker is able to gain sensitive information about another user. Basically there's a bad sanitization and it's not well validated from server-side.
I will provide here how I was able to gain another's users sensitive information. I couldn't post id
within variables directly since they don't have that in their API documentation but I've made it and I will show you how this happened:
So, In this JSON object, there's a object called input
with a value of another object. The input object has three keys: spendRuleId
, allowed
, and blocked
. The spendRuleId
key has a string value of AAAAAAAAAAAAA
, which is a unique identifier for a spend rule. The allowed
key has an array value with a single string AUTOMATED_FUEL_DISPENSERS
, which represents a category that is allowed under the spend rule type. The blocked
key also has an empty array value, which means that no spend categories are blocked under the spend rule.
Basically this will return user's information via FindSpendRule
GraphQL API query. And via this way all we need to do is to manipulate ID with another's users spend rule type ID in order to gain his sensitive information which is bypassing access controls.
January 25th 2023
- I reported this
January 30th 2023
HackerOne's triager marked it as
needs-more-info
January 31st 2023
Triager change status to
Pending program review
February 1st 2023
Program's staff member triaged my report and immediately awarded me with a $700 bounty.
Thank you for reading this. Follow me on Twitter: https://twitter.com/intlulz
Last updated