Graphql API Vulnerabilities

7 min read
Graphql API Vulnerabilities

GraphQL, a query language for APIs, has become increasingly popular for its flexibility and developer-friendly features. However, its widespread adoption also brings attention to potential security vulnerabilities and threats. These vulnerabilities often stem from implementation or design flaws, allowing attackers to exploit the system for unauthorized data access or actions.

GraphiQL

GraphiQL is an interactive development environment (IDE) for constructing GraphQL queries. It provides auto-completion and real-time results, enhancing the developer experience. However, it is often enabled by default, making it a target for attackers who can use it to explore the API schema and gather sensitive information.By default graphQL does not implement authentication, this is put on the developer to implement. This means by default graphQL allows anyone to query it, any sensitive information will be available to attackers unauthenticated.

When performing your directory brute force attacks make sure to add the following paths to check for graphQL instances.

  • /graphql
  • /graphiql
  • /graphql.php
  • /graphql/console
  • /playground
  • /v1/graphql
  • /v2/graphql
  • /console
  • /api

Introspection

Once you find an open graphQL instance you need to know what queries it supports. This can be done by using the introspection system.More details about GraphQl queries can be found here:

The GraphQL schema acts as a contract between the server and the client, outlining the API's capabilities and defining how clients can request data. By default, introspection is enabled in most GraphQL instances, allowing clients to query the schema for information about supported queries. While introspection is a useful feature, it can be misused by attackers to gather information about the GraphQL implementation.Graphql usually supports GET, POST (x-www-form-urlencoded) and POST(json).

Enumeration

To use introspection to discover schema information, query the "__schema" field. This field is available on the root type of all queries. With this query you will find the name of all the types being used:

Terminal
query={__schema{types{name,fields{name}}}}

With the below query you can extract all the types, it's fields, and it's arguments (and the type of the args).

Terminal
query={__schema{types{name,fields{name, args{name,description,type{name, kind, ofType{name, kind}}}}}}}

f4f05ae7-5d55-4d9a-bcde-48bbecc601d3.png

Below is the inline query the schema of the full database using introspection.

Terminal
/?query=fragment%20FullType%20on%20Type%20{+%20%20kind+%20%20name+%20%2 0description+%20%20fields%20{+%20%20%20%20name+%20%20%20%20description+%20 %20%20%20args%20{+%20%20%20%20%20%20...InputValue+%20%20%20%20}+%20%20 %20%20type%20{+%20%20%20%20%20%20...TypeRef+%20%20%20%20}+%20%20}+%20 %20inputFields%20{+%20%20%20%20...InputValue+%20%20}+%20%20interfaces%20{+%20 %20%20%20...TypeRef+%20%20}+%20%20enumValues%20{+%20%20%20%20name+%20 %20%20%20description+%20%20}+%20%20possibleTypes%20{+%20%20%20%20...TypeRef +%20%20}+}++fragment%20InputValue%20on%20InputValue%20{+%20%20name+%20%2 0description+%20%20type%20{+%20%20%20%20...TypeRef+%20%20}+%20%20defaultValue +}++fragment%20TypeRef%20on%20Type%20{+%20%20kind+%20%20name+%20%20ofT ype%20{+%20%20%20%20kind+%20%20%20%20name+%20%20%20%20ofType%20{+%20 %20%20%20%20%20kind+%20%20%20%20%20%20name+%20%20%20%20%20%20ofTyp e%20{+%20%20%20%20%20%20%20%20kind+%20%20%20%20%20%20%20%20name+%2 0%20%20%20%20%20%20%20ofType%20{+%20%20%20%20%20%20%20%20%20%20kind +%20%20%20%20%20%20%20%20%20%20name+%20%20%20%20%20%20%20%20%20 %20ofType%20{+%20%20%20%20%20%20%20%20%20%20%20%20kind+%20%20%20%20 %20%20%20%20%20%20%20%20name+%20%20%20%20%20%20%20%20%20%20%20%2 0ofType%20{+%20%20%20%20%20%20%20%20%20%20%20%20%20%20kind+%20%20%2 0%20%20%20%20%20%20%20%20%20%20%20name+%20%20%20%20%20%20%20%20% 20%20%20%20%20%20ofType%20{+%20%20%20%20%20%20%20%20%20%20%20%20%2 0%20%20%20kind+%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20na me+%20%20%20%20%20%20%20%20%20%20%20%20%20%20}+%20%20%20%20%20%2 0%20%20%20%20%20%20}+%20%20%20%20%20%20%20%20%20%20}+%20%20%20%20 %20%20%20%20}+%20%20%20%20%20%20}+%20%20%20%20}+%20%20}+}++query%20In trospectionQuery%20{+%20%20schema%20{+%20%20%20%20queryType%20{+%20%20 %20%20%20%20name+%20%20%20%20}+%20%20%20%20mutationType%20{+%20%20%2 0%20%20%20name+%20%20%20%20}+%20%20%20%20types%20{+%20%20%20%20%20 %20...FullType+%20%20%20%20}+%20%20%20%20directives%20{+%20%20%20%20%20% 20name+%20%20%20%20%20%20description+%20%20%20%20%20%20locations+%20%20 %20%20%20%20args%20{+%20%20%20%20%20%20%20%20...InputValue+%20%20%20%2 0%20%20}+%20%20%20%20}+%20%20}+}The last code line is a graphql query that will dump all the meta-information from the graphql (objects names, parameters, types...)

Excessive Errors/Fields Suggestions

GraphQL is known for providing detailed error messages, which can be exploited by attackers to craft valid queries or attacks. It is important for GraphQL APIs in production to avoid returning verbose stack traces or operating in debug mode. Developers should implement middleware to control the errors returned by the server, ensuring that only necessary information is exposed to API consumers.

87160b7b-3b6b-4fc2-810a-bbb041974dd7.png

Querying

In the introspection you can find which object you can directly query for (because you cannot query an object just because it exists). In the following image you can see that the "queryType" is called "Query" and that one of the fields of the "Query" object is "flags", which is also a type of object. Therefore you can query the flag object.

64ed2c33-8f17-4b9c-9227-adb2d4f5b383.png

Note that the type of the query "flags" is "Flags", and this object is defined as below:

a1017762-8326-468d-b957-9768ed7c6a40.png

You can see that the "Flags" objects are composed by name and .value Then you can get all the names and values of the flags with the query:

Terminal
query={flags{name, value}}

Mutations

While Introspection is for querying the database, Mutations are for making changes in the database. In the introspection you can find the declared mutations. In the following image the "MutationType" is called "Mutation" and the "Mutation" object contains the names of the mutations (like "addPerson" in this case):

aa038a9c-909d-46b0-8c09-993b8935b068.png

For this example imagine a data base with persons identified by the email and the name and movies identified by the name and rating. A person can be friend with other persons and a person can have movies.

A mutation to create new movies inside the database can be like the following one (in this example the mutation is called "addmovie".

Terminal
mutation {
  addMovie(name: "Jumanji: The Next Level", rating: "6.8/10", releaseYear: 2019) {
    movies {
      name
      rating
    }
  }
}

Denial of Service (DoS)

GraphQL APIs can be susceptible to Denial of Service attacks, where attackers send requests designed to overwhelm the application server. Several types of DoS attacks are possible:

Batching Attacks

GraphQL supports query batching, allowing multiple queries to be sent in a single HTTP request. Attackers can exploit this to overload the server. An attacker can brute force authentication credentials in order to get access tokens. Instead of doing so in many requests, which might be blocked by a network security measure like a web application firewall or a rate limiter like Nginx, these requests may be batched. This means there would only be a couple of requests, which may allow for efficient brute forcing without being detected.An example batched query would look like this-

Terminal
query {
  Veterinary(id: "1") {
    name
  }
  second:Veterinary(id: "2") {
    name
  }
  third:Veterinary(id: "3") {
    name
  }
}

Alias Overloading

If batching is disabled there is another way to overwhelm the server by sending the same query by using aliases.An alias is used to give a different name to a field in a query or mutation. This can be useful in cases where you want to request the same field multiple times with different arguments or to avoid naming conflicts when querying multiple fields that have the same name.

Attackers can use this feature for batching, where one request will be sent over the network and then all queries will be executed sequentially.

03548bfe-fb7c-4825-8e0e-f75e887e9483.png

Injection Attacks

Injection attacks in GraphQL are a type of security vulnerability that allow an attacker to inject and execute arbitrary code or commands within a GraphQL API. These attacks exploit vulnerabilities in the way user-supplied input is handled by the GraphQL server.

SQL Injection

GraphQL SQL injection (SQLi) attacks are a type of security vulnerability in GraphQL APIs that allow an attacker to execute malicious SQL queries against a backend database. In GraphQL, SQLi attacks occur when user-supplied input is not properly sanitized or validated before being used in a database query. Attackers can exploit this vulnerability by injecting malicious SQL code into a GraphQL query, which is then executed by the backend database.

This type of attack can be used to bypass authentication and authorization controls, execute arbitrary SQL queries, and access or modify sensitive data. Following image provides an example SQLi query.

b474eb37-fe86-4b13-89fc-349570c387c2.png

Cross Site Scripting

XSS attacks in GraphQL occur when user-supplied input is not properly sanitized or validated and the output of the response is getting reflected on the screen on a web page. Both reflected and stored XSS are possible depending upon the vulnerable operation used (query/mutation).

Reflected XSS occurs when an application takes user input and immediately sends it back to the browser without properly sanitizing it first. For example, if a GraphQL query operation takes user input and directly includes it in the error messages or other parts of the response that are rendered in the end user’s browser, an attacker could craft a malicious input that results in arbitrary script execution in the victim’s browser when the response is rendered.

Stored XSS (also known as persistent XSS) is a more dangerous variant where the injected script is permanently stored on the target server (for example, in a database), and then served as part of a webpage to users. In the context of GraphQL, this might occur if a mutation operation is used to store user input on the server, and this input is not properly sanitized before being stored and subsequently served to users.

Authorization

After authentication, authorization determines what data and actions a user can access. Since GraphQL does not provide built-in authorization, developers must implement this logic. Attackers can exploit weaknesses in this logic, such as by manipulating identifiers to access unauthorized data—a vulnerability known as Broken Object Level Authorization (BOLA).

Tools

References

Follow us on social media

Cyber Unfolded Light Logo
Copyright © 2024 CYUN. All rights reserved.