How to exploit GraphQL endpoint: introspection, query, mutations & tools

March 24, 2021

From many years, GraphQL has been presented as a good alternative for REST APIs. We will not debate if this is true or not, what are con or pro, but we will see how we can proceed when we found a GraphQL endpoint in our target. How GraphQL works? How to perform Introspection? How to use Mutations? Exploitation of GraphQL endpoint can have a big impact and can bring high rewards, let’s take a look!

Just for a history part, GraphQL is a query language developed by Facebook and used internally since 2012, it has been open-source licensed since 2015. For technical reasons, more and more company use GraphQL and switch their backend to this new system, but, while this query language can have many advantages, it also has a number of security issues as the queries become more complex. Information Disclosure (PII leak), Business Logic Errors, IDORs and Improper Access Control are the most vulnerabilities which can be found on GraphQL endpoint.

This article aims to list some possible attacks on GraphQL and is not a development tutorial, if you want to learn more, I invite you to read this.

Examples of GraphQL endpoints

It’s difficult to list all possible endpoints to find a GraphQL instance but many of them use a framework like “Appollo” and they use common GraphQL endpoints:

/v1/explorer
/v1/graphiql
/graph
/graphql
/graphql/console/
/graphql.php
/graphiql
/graphiql.php
(...)

You can find more complete list on SecLists. Another way to identify an hidden endpoint by searching some keywords in JavaScripts files like “query“, “mutation“, “graphql” and it could be reveal the presence of GraphQL decommissioned/unofficial endpoint.

Introspection

Now, you have identified a GraphQL endpoint, the first attempt that you can do and which could be helpful is: introspection. You said what?

Introspection is the ability to query which resources are available in the current API schema. Given the API, via introspection, we can see the queries, types, fields, and directives it supports.

So, if introspection is authorized on your target it’s a good news, you will have the possibility to see all useful information to inspect and go deeper on it.

How to perform introspection in GraphQL ?

This is the full request to perform you GraphQL introspection on your target (if enabled):

{__schema{queryType{name}mutationType{name}subscriptionType{name}types{...FullType}directives{name description locations args{...InputValue}}}}fragment FullType on __Type{kind name description fields(includeDeprecated:true){name description args{...InputValue}type{...TypeRef}isDeprecated deprecationReason}inputFields{...InputValue}interfaces{...TypeRef}enumValues(includeDeprecated:true){name description isDeprecated deprecationReason}possibleTypes{...TypeRef}}fragment InputValue on __InputValue{name description type{...TypeRef}defaultValue}fragment TypeRef on __Type{kind name ofType{kind name ofType{kind name ofType{kind name ofType{kind name ofType{kind name ofType{kind name ofType{kind name}}}}}}}}

The server should response with the full schema (query, mutation, objects, fields…). Even if schema is displayed in JSON, it can be quickly unreadable. In my opinion, once you have the schema, the best way is to import it in a tool like “GraphQL Voyager” (I talk about below in “Tools” chapter).

Introspection is disabled ? Fuzz!

If introspection is disabled on your target (it should be in a safe world), this is a good opportunity to start finding out what they don’t want us to see. By default, GraphQL backend have a feature for fields and operations suggestions. If you try to query a field but you have made a typo, GraphQL will attempt to suggest fields that are similar to the initial attempt.

Field suggestions is not a vulnerability, but from hacker’s side, this feature can be abused to gain more insight into GraphQL’s schema, especially when Introspection is not allowed.

To perform this suggestion abuse, I highly recommend to use tools. You could use Intruder tool in Burp Suite but not always appropriate for this step.

Query flaws

The principal problem in GraphQL is: by design, you don’t have any control access system, developer has to write “resolvers” which will map the data to the queries for the database of his choice.

This is why the most commons issues which happens in Query are authorization logic flaw like Improper Access Control and IDOR.

For example, you have a initial legit Query called “currentUser” which take a variable parameter “internalId“. This request should only return information from current connected user.

query {
  currentUser(internalId: 1337) {
    role
    name
    email
    token
  }
}

Try to replace the value of internalId by another one, and check if you can fetch information from another users. Classic IDOR, but common in GraphQL.

Query are also interesting, as it is sometimes possible to use a legitimate query and add fields to get juicy stuff. For example, you have a Query “listPosts” which is used by a newsletter web application.

query {
  listPosts(postId: 13) {
    title
    description
  }
}

By using introspection (the best case) or fuzzing, you could also discover a “user” object in this Query. Which could be used to fetch additional information:

query {
  listPosts(postId: 13) {
    title
    description
  }
user {
    username
    email
    firstName
    lastName
    }
}

Mutations flaws

Mutations are used when web application perform modification actions on data. And like Query, mutations suffers of same problems and can also have others flaws, like mass assignment vulnerability.

Let’s assume you have a mutation called “registerAccount” which is used by your target to create a simple user account. This mutation have these fields: nickname, email, password.

In addition, we can also observe that a field “role” is on returned values by GraphQL in “user” object once the mutation is sent.

mutation {
    registerAccount(nickname:"hacker", email:"hacktheplanet@yeswehack.ninja", password:"StrongP@ssword!") {
        token {
             accessToken
        }
        user {
           email
           nickname
           role
           } 
       }
    }
}

In this case, it’s a good opportunity to see what happen if we add a field “role” in our mutation!

mutation {
    registerAccount(nickname:"hacker", email:"hacktheplanet@yeswehack.ninja", password:"StrongP@ssword!", role:"Admin") {
        token {
             accessToken
        }
        user {
           email
           nickname
           role
           } 
       }
    }
}

As mentioned earlier, the most difficult part of GraphQL for developers is having a granular access control for each request and implementing a resolver for that will integrate with the appropriate access controls.

SQL injection, debug information, batching attack (brute force and rate-limit bypass)

  • SQL Injection: simple but classic, try SQL and NoSQL injection in fields values,
  • Debug & information disclosure: Insert bad characters in object or fields name, sometimes DEBUG mode is activated and even if you have a 403 status, you could have a good surprise,
  • Batching Attack: Batching is the process of taking a group of requests, combining them into one, and making a single request with the same data that all of the other queries would have made (more here). When authentication process is used with GraphQL, batch attack can be performed to simultaneously sending many queries with different credentials, it’s like a bruteforce attack but only with one request. Also, batch attack can be used against 2FA authentication, to bypass rate-limit (if it’s based on number of query by IP for example).

If you want to know more about Batching Attack, I invite you to read this excellent article

Tools

More and more tools dedicated to GraphQL attacks are developed, but I would like to recommend two of them in addition to those I’ve indicated in the previous chapters.

GraphQL Voyager

Event if you’re a master of JSON, I think we will be OK to said when you have a GraphQL schema in front of your eyes, to have a clear idea about each object, each mutation and each query, it’s not the simplest.

I think this screenshot is enough to understand the point.

InQL (Burp Suite)

InQL is originally a command line tool to facilitate certain attacks against a GraphQL endpoint. Luckily, a Burp Suite extension has also been developed and I recommend you to install it (available in BurpApp Store).

It allows you to directly perform an introspection query (if authorized, of course) and to have all the queries and mutations in Burp, in a readable format.

GitHub repository : https://github.com/doyensec/inql

Need some training?

If you need to practice with GraphQL before you start digging into your target, I highly recommend using these docker projects “Damn Vulnerable GraphQL Application” and “PoC graphql” which are two great projects available on GitHub. They will allow you to use many different attacks and approaches with GraphQL that I mentioned in this article.

References