2500 Dollars in bounties, Hacking GraphQL


Hi, amazing hackers! I hope you're doing well.
In this article, I’ll share my learning process and experiences in finding multiple vulnerabilities in GraphQL APIs application.
In February, I started learning GraphQL API security using the book Black Hat GraphQL as a reference, along with PortSwigger labs. While studying, I picked a private bug bounty program on HackerOne. To my surprise, its entire infrastructure was built on GraphQL—a perfect target to put my learning into practice!
So, let's get into the two bugs I discovered!
First One, IDOR (My favvv)
The first thing you can do while testing a GraphQL API application is check whether introspection is enabled or not.
A quick note about GraphQL introspection from PortSwigger:
“Introspection is a built-in GraphQL function that enables you to query a server for information about the schema. It is commonly used by applications such as GraphQL IDEs and documentation generation tools.”
Having introspection enabled allows us to make all requests and test further for bugs!
I used InQL, a well-known Burp Suite extension, to check for it. As you can see, I got an error :(, meaning that introspection was disabled by the developer. So, I decided to find queries and mutations from JavaScript files instead. It was time-consuming to find and construct requests, but what else could we do? :)
It turns out, we could do something! While reading PortSwigger's page about GraphQL, I noticed that developers often disable the __schema
keyword in queries.
However, if the developer has only excluded __schema
, then the introspection query below would not be blocked, allowing us to access the schema!
For Queries:
{
__type(name: "Query") {
fields {
name
args {
name
type {
name
kind
}
}
}
}
}
For Mutations:
{
__type(name: "Mutation") {
fields {
name
args {
name
type {
name
kind
}
}
}
}
}
By doing this, I was able to find all mutations, queries, and fields. Now, it was time to construct them all together, one by one. I used AI (ChatGPT) to automate this process, and it was amazing!
Then, I found the mutation below, which takes an ID as its argument. The value was numeric, making it a great candidate for testing IDOR 🙂.
I tried 1, 2, 3, but all returned 404. I initially thought it wasn’t vulnerable, but thankfully, I decided to fuzz a bit. Finally, I found that 1105 responded! and 1110, 1120, and so on…
As Stripe's documentation mentions, the client_secret is used to complete a payment from the frontend. It should not be stored, logged, or exposed to anyone other than the customer.
If leaked, an attacker could use the client_secret to confirm payments on behalf of victims, leading to unauthorized transactions and financial fraud. Ref
I initially submitted the report with this impact description, but I now realize it may not have fully explained the severity. The report was marked as Medium by the H1 analyst. However, I believe it should have been considered HIGH. Unfortunately, it was rated Medium, and I received $500.
Second, Non-JSON CSRF
Yes, you read it right! Sometimes, the server fails to validate the request content type and accepts non-JSON queries and mutations, which is the opposite of GraphQL’s expected behavior (accepting only application/json
content-type). When this happens, and there is no CSRF token, an attacker can forge requests and trick users into performing authenticated state-changing actions on behalf of victims. In my case, I was able to add admin, change users email, in a simple word, i could do any update/edit action.
I used GraphQL-Cop, an open-source tool for testing various security vulnerabilities in GraphQL APIs. As shown in the image below, the tool confirms that requests can be sent using non-JSON queries, meaning the server accepts requests with the application/x-www-form-urlencoded
content type.
Now, it's time to forge the request. Here's how I do it:
I change the Content-Type and remove the request body. Then, I add a query parameter. This makes Burp Suite recognize the request as GraphQL, adding a new GraphQL tab in the Repeater. By clicking on this tab, you can reinsert the query or mutation that you removed from the body.
The images below demonstrate how it looks in action.
The image above shows how it looks. From here, the rest is straightforward. just like a normal CSRF attack, where you craft a PoC (Proof of Concept) to exploit the vulnerability.
To be honest, I used Burp Suite's PoC generator to simplify the process. I created three different PoCs and recorded a video demonstrating a sensitive action being performed without user consent. After a long wait, my report was finally triaged with a HIGH severity rating. Then, two weeks later, I received a $2,000 bounty as a reward! 🚀
Last Words
For me, bug bounty is all about the fun of learning something new and applying it in the real world. My experience with GraphQL was no different. This month (March), I started diving into Android hacking, and guess what? The company’s Android application is also in scope, so let’s give it a shot and see what comes out of it!
I hope you enjoyed reading this post and learned something along the way. If you'd like to follow me, feel free to connect with me on X (Twitter) and LinkedIn.
Be Happy, Be Nice:)
Subscribe to my newsletter
Read articles from Ali Hussainzada directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by

Ali Hussainzada
Ali Hussainzada
Senior Student of Computer Science | 21 y/o Web Application Pentester My HackerOne Profile: https://hackerone.com/amir_shah