hacktricks/network-services-pentesting/pentesting-web/graphql.md
2023-06-03 01:46:23 +00:00

24 KiB

GraphQL

☁️ HackTricks Cloud ☁️ -🐦 Twitter 🐦 - 🎙️ Twitch 🎙️ - 🎥 Youtube 🎥

Introducción

GraphQL actúa como una alternativa a las API REST. Las API REST requieren que el cliente envíe múltiples solicitudes a diferentes puntos finales en la API para consultar datos de la base de datos del backend. Con GraphQL solo necesitas enviar una solicitud para consultar el backend. Esto es mucho más simple porque no tienes que enviar múltiples solicitudes a la API, una sola solicitud puede ser utilizada para recopilar toda la información necesaria.

GraphQL

A medida que surgen nuevas tecnologías, también lo harán nuevas vulnerabilidades. Por defecto, GraphQL no implementa autenticación, esto es responsabilidad del desarrollador para implementar. Esto significa que por defecto GraphQL permite a cualquiera consultarla, cualquier información sensible estará disponible para los atacantes no autenticados.

Al realizar tus ataques de fuerza bruta de directorios, asegúrate de agregar las siguientes rutas para verificar las instancias de GraphQL.

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

Una vez que encuentres una instancia de GraphQL abierta, necesitas saber qué consultas admite. Esto se puede hacer utilizando el sistema de introspección, se pueden encontrar más detalles aquí: GraphQL: un lenguaje de consulta para API.
A menudo es útil preguntar a un esquema de GraphQL por información sobre qué consultas admite. GraphQL nos permite hacerlo…

Huella digital

La herramienta graphw00f es capaz de detectar qué motor de GraphQL se utiliza en un servidor y luego imprime información útil para el auditor de seguridad.

Enumeración básica

GraphQL generalmente admite GET, POST (x-www-form-urlencoded) y POST (json).

query={__schema{types{name,fields{name}}}}

Con esta consulta encontrarás el nombre de todos los tipos que se están utilizando:

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

Con esta consulta puedes extraer todos los tipos, sus campos y sus argumentos (y el tipo de los argumentos). Esto será muy útil para saber cómo consultar la base de datos.

Errores

Es interesante saber si los errores se van a mostrar ya que contribuirán con información útil.

?query={__schema}
?query={}
?query={thisdefinitelydoesnotexist}

Enumerar el esquema de la base de datos a través de la introspección

La introspección es una técnica que permite a los clientes de GraphQL descubrir el esquema de la API GraphQL. Esto significa que podemos usar la introspección para descubrir información sobre la base de datos subyacente. Podemos hacer esto enviando una consulta especial llamada IntrospectionQuery. Esta consulta devuelve información sobre los tipos de datos, las consultas y las mutaciones disponibles en la API GraphQL. Podemos usar esta información para descubrir información sobre la base de datos subyacente, como los nombres de las tablas y los campos, y las relaciones entre ellas.

/?query=fragment%20FullType%20on%20Type%20{+%20%20kind+%20%20name+%20%20description+%20%20fields%20{+%20%20%20%20name+%20%20%20%20description+%20%20%20%20args%20{+%20%20%20%20%20%20...InputValue+%20%20%20%20}+%20%20%20%20type%20{+%20%20%20%20%20%20...TypeRef+%20%20%20%20}+%20%20}+%20%20inputFields%20{+%20%20%20%20...InputValue+%20%20}+%20%20interfaces%20{+%20%20%20%20...TypeRef+%20%20}+%20%20enumValues%20{+%20%20%20%20name+%20%20%20%20description+%20%20}+%20%20possibleTypes%20{+%20%20%20%20...TypeRef+%20%20}+}++fragment%20InputValue%20on%20InputValue%20{+%20%20name+%20%20description+%20%20type%20{+%20%20%20%20...TypeRef+%20%20}+%20%20defaultValue+}++fragment%20TypeRef%20on%20Type%20{+%20%20kind+%20%20name+%20%20ofType%20{+%20%20%20%20kind+%20%20%20%20name+%20%20%20%20ofType%20{+%20%20%20%20%20%20kind+%20%20%20%20%20%20name+%20%20%20%20%20%20ofType%20{+%20%20%20%20%20%20%20%20kind+%20%20%20%20%20%20%20%20name+%20%20%20%20%20%20%20%20ofType%20{+%20%20%20%20%20%20%20%20%20%20kind+%20%20%20%20%20%20%20%20%20%20name+%20%20%20%20%20%20%20%20%20%20ofType%20{+%20%20%20%20%20%20%20%20%20%20%20%20kind+%20%20%20%20%20%20%20%20%20%20%20%20name+%20%20%20%20%20%20%20%20%20%20%20%20ofType%20{+%20%20%20%20%20%20%20%20%20%20%20%20%20%20kind+%20%20%20%20%20%20%20%20%20%20%20%20%20%20name+%20%20%20%20%20%20%20%20%20%20%20%20%20%20ofType%20{+%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20kind+%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20name+%20%20%20%20%20%20%20%20%20%20%20%20%20%20}+%20%20%20%20%20%20%20%20%20%20%20%20}+%20%20%20%20%20%20%20%20%20%20}+%20%20%20%20%20%20%20%20}+%20%20%20%20%20%20}+%20%20%20%20}+%20%20}+}++query%20IntrospectionQuery%20{+%20%20schema%20{+%20%20%20%20queryType%20{+%20%20%20%20%20%20name+%20%20%20%20}+%20%20%20%20mutationType%20{+%20%20%20%20%20%20name+%20%20%20%20}+%20%20%20%20types%20{+%20%20%20%20%20%20...FullType+%20%20%20%20}+%20%20%20%20directives%20{+%20%20%20%20%20%20name+%20%20%20%20%20%20description+%20%20%20%20%20%20locations+%20%20%20%20%20%20args%20{+%20%20%20%20%20%20%20%20...InputValue+%20%20%20%20%20%20}+%20%20%20%20}+%20%20}+}

La última línea de código es una consulta graphql que volcará toda la meta-información de graphql (nombres de objetos, parámetros, tipos...)

Si la introspección está habilitada, puedes usar GraphQL Voyager para ver en una GUI todas las opciones.

Consultando

Ahora que sabemos qué tipo de información se guarda dentro de la base de datos, intentemos extraer algunos valores.

En la introspección puedes encontrar qué objeto puedes consultar directamente (porque no puedes consultar un objeto solo porque existe). En la siguiente imagen puedes ver que el "queryType" se llama "Query" y que uno de los campos del objeto "Query" es "flags", que también es un tipo de objeto. Por lo tanto, puedes consultar el objeto flag.

Ten en cuenta que el tipo de consulta "flags" es "Flags", y este objeto se define como sigue:

Puedes ver que los objetos "Flags" están compuestos por nombre y valor. Luego puedes obtener todos los nombres y valores de las banderas con la consulta:

query={flags{name, value}}

Ten en cuenta que en caso de que el objeto a consultar sea un tipo primitivo como una cadena de texto como en el siguiente ejemplo

Puedes consultarla simplemente con:

query={hiddenFlags}

En otro ejemplo donde había 2 objetos dentro del objeto "Query": "user" y "users".
Si estos objetos no necesitan ningún argumento para buscar, se puede obtener toda la información de ellos simplemente pidiendo los datos que se desean. En este ejemplo de Internet se podrían extraer los nombres de usuario y contraseñas guardados:

Sin embargo, en este ejemplo si se intenta hacer esto se obtiene este error:

Parece que de alguna manera buscará usando el argumento "uid" de tipo Int.
De todas formas, ya sabíamos que en la sección de Enumeración Básica se propuso una consulta que nos mostraba toda la información necesaria: query={__schema{types{name,fields{name, args{name,description,type{name, kind, ofType{name, kind}}}}}}}

Si se lee la imagen proporcionada cuando se ejecuta esa consulta se verá que "user" tenía el argumento "uid" de tipo Int.

Así que, realizando una fuerza bruta ligera de uid encontré que en uid=1 se recuperó un nombre de usuario y una contraseña:
query={user(uid:1){user,password}}

Tenga en cuenta que descubrí que podía pedir los parámetros "user" y "password" porque si intento buscar algo que no existe (query={user(uid:1){noExists}}) obtengo este error:

Y durante la fase de enumeración descubrí que el objeto "dbuser" tenía como campos "user" y "password.

Truco de volcado de cadena de consulta (gracias a @BinaryShadow_)

Si se puede buscar por un tipo de cadena, como: query={theusers(description: ""){username,password}} y se busca una cadena vacía se volcarán todos los datos. (Tenga en cuenta que este ejemplo no está relacionado con el ejemplo de los tutoriales, para este ejemplo suponga que se puede buscar usando "theusers" por un campo de cadena llamado "description").

GraphQL es una tecnología relativamente nueva que está empezando a ganar terreno entre las startups y las grandes corporaciones. Además de la falta de autenticación por defecto, los puntos finales de graphQL pueden ser vulnerables a otros errores como IDOR.

Búsqueda

Para este ejemplo, imagine una base de datos con personas identificadas por el correo electrónico y el nombre y películas identificadas por el nombre y la calificación. Una persona puede ser amiga de otras personas y una persona puede tener películas.

Se pueden buscar personas por el nombre y obtener sus correos electrónicos:

{
  searchPerson(name: "John Doe") {
    email
  }
}

Puedes buscar personas por su nombre y obtener las películas a las que están suscritas:

{
  searchPerson(name: "John Doe") {
    email
    subscribedMovies {
      edges {
        node {
          name
        }
      }
    }
  }
}

Ten en cuenta cómo se indica recuperar el nombre de las subscribedMovies de la persona.

También puedes buscar varios objetos al mismo tiempo. En este caso, se realiza una búsqueda de 2 películas:

{
  searchPerson(subscribedMovies: [{name: "Inception"}, {name: "Rocky"}]) {
    name
  }
}r

O incluso relaciones de varios objetos diferentes usando alias:

{
  johnsMovieList: searchPerson(name: "John Doe") {
    subscribedMovies {
      edges {
        node {
          name
        }
      }
    }
  }
  davidsMovieList: searchPerson(name: "David Smith") {
    subscribedMovies {
      edges {
        node {
          name
        }
      }
    }
  }
}

Mutaciones

Las mutaciones se utilizan para realizar cambios en el lado del servidor.

En la introspección se pueden encontrar las mutaciones declaradas. En la siguiente imagen, el "MutationType" se llama "Mutation" y el objeto "Mutation" contiene los nombres de las mutaciones (como "addPerson" en este caso):

Para este ejemplo, imagina una base de datos con personas identificadas por el correo electrónico y el nombre y películas identificadas por el nombre y la calificación. Una persona puede ser amiga de otras personas y una persona puede tener películas.

Una mutación para crear nuevas películas dentro de la base de datos puede ser como la siguiente (en este ejemplo, la mutación se llama addMovie):

mutation {
  addMovie(name: "Jumanji: The Next Level", rating: "6.8/10", releaseYear: 2019) {
    movies {
      name
      rating
    }
  }
}

Nota cómo tanto los valores como el tipo de datos se indican en la consulta.

También puede haber una mutación para crear personas (llamada addPerson en este ejemplo) con amigos y archivos (nota que los amigos y películas tienen que existir antes de crear una persona relacionada con ellos):

mutation {
  addPerson(name: "James Yoe", email: "jy@example.com", friends: [{name: "John Doe"}, {email: "jd@example.com"}], subscribedMovies: [{name: "Rocky"}, {name: "Interstellar"}, {name: "Harry Potter and the Sorcerer's Stone"}]) {
    person {
      name
      email
      friends {
        edges {
          node {
            name
            email
          }
        }
      }
      subscribedMovies {
        edges {
          node {
            name
            rating
            releaseYear
          }
        }
      }
    }
  }
}

Ataque de fuerza bruta en lote en 1 solicitud de API

Esta información fue tomada de https://lab.wallarm.com/graphql-batching-attack/.
La autenticación a través de la API de GraphQL con el envío simultáneo de muchas consultas con diferentes credenciales para verificarla. Es un ataque de fuerza bruta clásico, pero ahora es posible enviar más de un par de inicio de sesión/contraseña por solicitud HTTP debido a la función de agrupación de GraphQL. Este enfoque engañaría a las aplicaciones externas de monitoreo de velocidad para que crean que todo está bien y que no hay un bot de fuerza bruta intentando adivinar contraseñas.

A continuación, se muestra la demostración más simple de una solicitud de autenticación de aplicación, con 3 pares de correo electrónico/contraseña diferentes a la vez. Obviamente, es posible enviar miles en una sola solicitud de la misma manera:

Como podemos ver en la captura de pantalla de la respuesta, las primeras y terceras solicitudes devolvieron null y reflejaron la información correspondiente en la sección de error. La segunda mutación tenía los datos de autenticación correctos y la respuesta tenía el token de sesión de autenticación correcto.

GraphQL sin introspección

Cada vez más los puntos finales de GraphQL están deshabilitando la introspección. Sin embargo, los errores que GraphQL arroja cuando se recibe una solicitud inesperada son suficientes para que herramientas como clairvoyance puedan recrear la mayor parte del esquema.

Además, la extensión de Burp Suite GraphQuail observa las solicitudes de API de GraphQL que pasan por Burp y construye un esquema interno de GraphQL con cada nueva consulta que ve. También puede exponer el esquema para GraphiQL y Voyager. La extensión devuelve una respuesta falsa cuando recibe una consulta de introspección. Como resultado, GraphQuail muestra todas las consultas, argumentos y campos disponibles para su uso dentro de la API. Para obtener más información, consulte esto.

CSRF en GraphQL

Si no sabe qué es CSRF, lea la siguiente página:

{% content-ref url="../../pentesting-web/csrf-cross-site-request-forgery.md" %} csrf-cross-site-request-forgery.md {% endcontent-ref %}

Allí podrá encontrar varios puntos finales de GraphQL configurados sin tokens CSRF.

Tenga en cuenta que las solicitudes de GraphQL generalmente se envían a través de solicitudes POST utilizando el tipo de contenido application/json.

{"operationName":null,"variables":{},"query":"{\n  user {\n    firstName\n    __typename\n  }\n}\n"}

Sin embargo, la mayoría de los puntos finales de GraphQL también admiten solicitudes POST form-urlencoded:

query=%7B%0A++user+%7B%0A++++firstName%0A++++__typename%0A++%7D%0A%7D%0A

Por lo tanto, como las solicitudes CSRF como las anteriores se envían sin solicitudes previas, es posible realizar cambios en el GraphQL abusando de un CSRF.

Sin embargo, tenga en cuenta que el nuevo valor predeterminado de la cookie de la bandera "samesite" de Chrome es "Lax". Esto significa que la cookie solo se enviará desde un sitio web de terceros en solicitudes GET.

También es posible enviar la solicitud de consulta como una solicitud GET y es posible que el token CSRF no se valide en una solicitud GET.

Además, mediante un ataque XS-Search, es posible exfiltrar contenido del punto final GraphQL abusando de las credenciales del usuario.

Para obtener más información, consulte la publicación original aquí.

Autorización en GraphQL

Muchas funciones de GraphQL definidas en el punto final solo pueden verificar la autenticación del solicitante pero no la autorización.

La modificación de las variables de entrada de la consulta podría llevar a la filtración de detalles de cuenta confidenciales.

La mutación incluso podría llevar a la toma de control de la cuenta intentando modificar los datos de otra cuenta.

{
  "operationName":"updateProfile",
  "variables":{"username":INJECT,"data":INJECT},
  "query":"mutation updateProfile($username: String!,...){updateProfile(username: $username,...){...}}"
}

Bypass de autorización en GraphQL

La concatenación de consultas puede saltarse un sistema de autenticación débil.

En el siguiente ejemplo se puede ver que la operación es "forgotPassword" y que solo debería ejecutar la consulta asociada a ella. Esto se puede saltar agregando una consulta al final, en este caso agregamos "register" y una variable de usuario para que el sistema registre un nuevo usuario.

Estructuras de GraphQL filtradas

Si la introspección está deshabilitada, intente buscar en el código fuente del sitio web. Las consultas a menudo se cargan previamente en el navegador como bibliotecas de JavaScript. Estas consultas preescritas pueden revelar información poderosa sobre el esquema y el uso de cada objeto y función. La pestaña Sources de las herramientas para desarrolladores puede buscar en todos los archivos para enumerar dónde se guardan las consultas. A veces, incluso las consultas protegidas por el administrador ya están expuestas.

Inspect/Sources/"Search all files"
file:* mutation
file:* query

Herramientas

Escáneres de vulnerabilidades

Clientes

Pruebas automáticas

{% embed url="https://graphql-dashboard.herokuapp.com/" %}

Referencias

☁️ HackTricks Cloud ☁️ -🐦 Twitter 🐦 - 🎙️ Twitch 🎙️ - 🎥 Youtube 🎥