In a world driven by data and interconnectedness, the evolution of web technologies has been nothing short of remarkable. From the days of static HTML pages to the dynamic, content-rich web applications we use today, one technology has consistently risen to the forefront, promising a more efficient and flexible approach to data retrieval and manipulation: GraphQL.
GraphQL, often hailed as the future of API development, has been gaining immense popularity in recent years. Its elegant and adaptable architecture enables developers to request exactly the data they need, reducing the over-fetching of information commonly encountered with traditional REST APIs. GraphQL's ability to revolutionise data access and management has made it a favourite among developers and businesses seeking to create scalable and data-efficient applications.
But as with any innovative technology, the allure of GraphQL has also attracted the attention of those with less noble intentions. As we delve into hacking GraphQL, we will explore the fascinating world of ethical hacking, shedding light on how security professionals are working to safeguard GraphQL APIs from potential threats. We'll also touch on the darker side, where hackers exploit vulnerabilities for malicious purposes.
Join us on this short journey as we uncover some inner workings of GraphQL, and learn how to enumerate, hack and harden your GraphQL APIs. Whether you're a seasoned developer, cyber security professional or just starting your journey in the world of web technologies, this exploration of hacking GraphQL promises to be an eye-opening read.
Having read the introduction, let's now delve into it from the perspective of a Penetration Tester.
One of our initial priorities is to enumerate the GraphQL engine. To achieve this, we recommend leveraging this great tool.
Here is an example, and we enumerate it’s using Hasura.
Following that, we aim to gain an understanding of how the GraphQL Schema is formatted. The GraphQL Schema is a fundamental concept that defines the structure, types, and capabilities of the data.
For those utilising Hasura, there's some encouraging news for hackers: Introspection is enabled by default. Unless developers have intentionally disabled this feature, you can readily retrieve the schema (authentication is usually required).
Here's an extract from a GitHub illustrating the default settings for Hasura.
For newcomers to GraphQL, its unique format can be a bit overwhelming, especially when it comes to the first-time penetration testing the technology. The following section will demystify GraphQL in a straightforward manner, making it accessible even for those taking their first steps testing the API technology.
Here is the blog's first look at a GraphQL request, highlighting the endpoint discovered and the query.
It’s worth noting the endpoint maybe different than the above, and content discovery could be required. Here is a nice list to utilise.
Now, back to retrieving the schema. All we have to do if Introspection is enabled is replace the request body in the above with the following:
{"query":"\n query IntrospectionQuery {\r\n __schema {\r\n queryType { name }\r\n mutationType { name }\r\n subscriptionType { name }\r\n types {\r\n ...FullType\r\n }\r\n directives {\r\n name\r\n description\r\n locations\r\n args {\r\n ...InputValue\r\n }\r\n }\r\n }\r\n }\r\n\r\n fragment FullType on __Type {\r\n kind\r\n name\r\n description\r\n fields(includeDeprecated: true) {\r\n name\r\n description\r\n args {\r\n ...InputValue\r\n }\r\n type {\r\n ...TypeRef\r\n }\r\n isDeprecated\r\n deprecationReason\r\n }\r\n inputFields {\r\n ...InputValue\r\n }\r\n interfaces {\r\n ...TypeRef\r\n }\r\n enumValues(includeDeprecated: true) {\r\n name\r\n description\r\n isDeprecated\r\n deprecationReason\r\n }\r\n possibleTypes {\r\n ...TypeRef\r\n }\r\n }\r\n\r\n fragment InputValue on __InputValue {\r\n name\r\n description\r\n type { ...TypeRef }\r\n defaultValue\r\n }\r\n\r\n fragment TypeRef on __Type {\r\n kind\r\n name\r\n ofType {\r\n kind\r\n name\r\n ofType {\r\n kind\r\n name\r\n ofType {\r\n kind\r\n name\r\n ofType {\r\n kind\r\n name\r\n ofType {\r\n kind\r\n name\r\n ofType {\r\n kind\r\n name\r\n ofType {\r\n kind\r\n name\r\n }\r\n }\r\n }\r\n }\r\n }\r\n }\r\n }\r\n }\r\n ","variables":null}
Here is an image showing the code from above in the request body, and the server returning a 200 OKAY with a snippet of the schema returned.
The data returned is often huge, encompassing thousands of lines of code. Now, the question is how to efficiently handle this vast volume of data. Fortunately, the solution is quite straightforward – you simply need to copy the response body to obtain a user-friendly graphical interface (GUI) representing the active schema.
To achieve this, we can use the following tool - click here.
Here is an snippet of an example GraphQL Schema shown as an interactive graph. You can see how it all works including the structure, types, and capabilities of the data, with a nice graphical flow.
In the end, this approach empowers testers to attain a higher level of testing coverage and focus on specific queries, thereby enhancing the overall testing effectiveness.
It’s worth noting if Introspection is disabled, fair play to the developers. For any testers, I would recommend asking the client for their GraphQL Schema to facilitate more effective testing if it can not be retrieved
If you’re a developer and using Hasura we recommend disabling Introspection, here is a nice guide.
First off, we want to reformat queries to be more legible for testing. The image below shows an example of a query, which can be obtained from the previously obtained schema, or by interacting with the application and proxying the traffic. As you see it isn’t easy to read let alone test.
To make it more legible we recommend using GraphQL Raider. Which is a Burp Suite Extension for testing endpoints implementing GraphQL.
Here is a screenshot of the tool description.
Let us show you how it works. The first image shows a query in the standard GraphQL format.
Using GraphQL Raider we can simply select the highlighted tab in the image below to format the data in a nice legible format.
Better still we can see the variables and injection points to interact with. The image below shows the userId field.
Now, that it’s more legible you can start the more familiar testing, for example, based on the above, Insecure direct object references (IDOR). Along with all the usual tests: SQL Injection, Cross-Site Scripting, Command Injection, Excessive Data Retrieval, Authorization Bypass, etc.
Rather than going into specific exploits due to the sheer volume here are some more useful and in-depth guides for testing and security GraphQL:
With that being said, no Cybaverse blog would be complete without a demonstration of a security challenge! During our GraphQL testing endeavours, we have noticed segmentation between user levels is typically well-executed (although we have found a few exceptions), with clearly defined entities and granular permissions in place. However, numerous times we have discovered the same security configurations are not implemented across the whole application and authorisation between accounts is not well-configured on requests that don’t utilise GraphQL but are used on the same application.
Here is an example of the commonly found OWASP Top 10, number 1, Broken Access Control. It was possible to carry out requests as a non-privileged user that should be limited to a privileged user, which would allow them to read, modify or delete other users' data. We could create an admin account with a non-privileged account, a feature that was not part of the GraphQL APIs.
The first image highlights the account type used to create the privileged user is not an admin.
The next image shows the admin user created, using the Bearer token belonging to the non-privileged user, which was successful and the admin account was created!
We could then log in as admin, and privilege escalation was achieved, nice warm feeling for us and it turned out in this instance a change to the granular permissions was a trivial fix for the developers. So, a win, win!
Unfortunately, as GraphQL's popularity soars, it also catches the attention of those who seek to exploit its potential vulnerabilities. In this blog, we've shed some light on the efforts of security professionals working diligently to safeguard GraphQLAPIs. But we've also glimpsed the darker side, where hackers attempt to exploit weaknesses for malicious purposes.
We've learned how to reveal the inner workings of GraphQL, reformat queries for legibility, and conduct comprehensive testing using the links provided. We've uncovered security challenges, demonstrated the importance of authorisation across whole applications, and dipped our toe in the rich world of GraphQL testing and security.
The resources and tools provided in this blog are invaluable for anyone looking to secure their GraphQL APIs and understand the potential exploits. Whether you're a seasoned developer, a cybersecurity expert, or just beginning your journey in the web technology landscape, we hope this exploration of hacking GraphQL offers a thought-provoking and enlightening adventure.
We extend our gratitude to all those who have contributed to GraphQL testing, and we'd like to give a big shout-out to the creators and maintainers of the documentation mentioned in this blog. Your efforts and expertise are greatly appreciated.