mirror of
https://github.com/carlospolop/hacktricks
synced 2024-12-22 11:03:24 +00:00
509 lines
28 KiB
Markdown
509 lines
28 KiB
Markdown
# GraphQL
|
|
|
|
<details>
|
|
|
|
<summary><strong>Lernen Sie AWS-Hacking von Grund auf mit</strong> <a href="https://training.hacktricks.xyz/courses/arte"><strong>htARTE (HackTricks AWS Red Team Expert)</strong></a><strong>!</strong></summary>
|
|
|
|
Andere Möglichkeiten, HackTricks zu unterstützen:
|
|
|
|
* Wenn Sie Ihr **Unternehmen in HackTricks bewerben möchten** oder **HackTricks als PDF herunterladen möchten**, überprüfen Sie die [**ABONNEMENTPLÄNE**](https://github.com/sponsors/carlospolop)!
|
|
* Holen Sie sich das [**offizielle PEASS & HackTricks-Merchandise**](https://peass.creator-spring.com)
|
|
* Entdecken Sie [**The PEASS Family**](https://opensea.io/collection/the-peass-family), unsere Sammlung exklusiver [**NFTs**](https://opensea.io/collection/the-peass-family)
|
|
* **Treten Sie der** 💬 [**Discord-Gruppe**](https://discord.gg/hRep4RUj7f) oder der [**Telegram-Gruppe**](https://t.me/peass) bei oder **folgen** Sie uns auf **Twitter** 🐦 [**@carlospolopm**](https://twitter.com/hacktricks_live)**.**
|
|
* **Teilen Sie Ihre Hacking-Tricks, indem Sie PRs an die** [**HackTricks**](https://github.com/carlospolop/hacktricks) und [**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud) GitHub-Repositories senden.
|
|
|
|
</details>
|
|
|
|
## Einführung
|
|
|
|
GraphQL wird als **effiziente Alternative** zu REST-APIs hervorgehoben und bietet einen vereinfachten Ansatz zum Abfragen von Daten vom Backend. Im Gegensatz zu REST, bei dem oft mehrere Anfragen an verschiedene Endpunkte erforderlich sind, um Daten zu sammeln, ermöglicht GraphQL das Abrufen aller erforderlichen Informationen über eine **einzige Anfrage**. Diese Vereinfachung kommt den Entwicklern erheblich zugute, da sie die Komplexität ihrer Datenabrufprozesse verringert.
|
|
|
|
## GraphQL und Sicherheit
|
|
|
|
Mit der Einführung neuer Technologien, einschließlich GraphQL, treten auch neue Sicherheitslücken auf. Ein wichtiger Punkt ist, dass **GraphQL standardmäßig keine Authentifizierungsmechanismen enthält**. Es liegt in der Verantwortung der Entwickler, solche Sicherheitsmaßnahmen zu implementieren. Ohne ordnungsgemäße Authentifizierung können GraphQL-Endpunkte sensible Informationen für nicht authentifizierte Benutzer freigeben und somit ein erhebliches Sicherheitsrisiko darstellen.
|
|
|
|
### Directory-Brute-Force-Angriffe und GraphQL
|
|
|
|
Um freigegebene GraphQL-Instanzen zu identifizieren, wird empfohlen, bestimmte Pfade in Directory-Brute-Force-Angriffen einzuschließen. Diese Pfade sind:
|
|
|
|
- `/graphql`
|
|
- `/graphiql`
|
|
- `/graphql.php`
|
|
- `/graphql/console`
|
|
- `/api`
|
|
- `/api/graphql`
|
|
- `/graphql/api`
|
|
- `/graphql/graphql`
|
|
|
|
Die Identifizierung offener GraphQL-Instanzen ermöglicht die Untersuchung unterstützter Abfragen. Dies ist entscheidend, um die über den Endpunkt zugänglichen Daten zu verstehen. Das Introspection-System von GraphQL erleichtert dies, indem es die Abfragen auflistet, die ein Schema unterstützt. Weitere Informationen hierzu finden Sie in der GraphQL-Dokumentation zur Introspection: [**GraphQL: Eine Abfragesprache für APIs.**](https://graphql.org/learn/introspection/)
|
|
|
|
### Fingerprint
|
|
|
|
Das Tool [**graphw00f**](https://github.com/dolevf/graphw00f) kann erkennen, welche GraphQL-Engine auf einem Server verwendet wird, und liefert dann einige hilfreiche Informationen für den Sicherheitsauditor.
|
|
|
|
#### Universelle Abfragen <a href="#universal-queries" id="universal-queries"></a>
|
|
|
|
Um zu überprüfen, ob eine URL einen GraphQL-Dienst darstellt, kann eine **universelle Abfrage**, `query{__typename}`, gesendet werden. Wenn die Antwort `{"data": {"__typename": "Query"}}` enthält, bestätigt dies, dass die URL einen GraphQL-Endpunkt hostet. Diese Methode basiert auf dem `__typename`-Feld von GraphQL, das den Typ des abgefragten Objekts angibt.
|
|
```javascript
|
|
query{__typename}
|
|
```
|
|
### Grundlegende Enumeration
|
|
|
|
Graphql unterstützt normalerweise **GET**, **POST** (x-www-form-urlencoded) und **POST**(json). Es wird jedoch empfohlen, nur json zuzulassen, um CSRF-Angriffe zu verhindern.
|
|
|
|
#### Introspektion
|
|
|
|
Um die Schema-Informationen mit Hilfe der Introspektion zu entdecken, fragen Sie das Feld `__schema` ab. Dieses Feld ist auf dem Wurzeltyp aller Abfragen verfügbar.
|
|
```bash
|
|
query={__schema{types{name,fields{name}}}}
|
|
```
|
|
Mit dieser Abfrage finden Sie den Namen aller verwendeten Typen:
|
|
|
|
![](<../../.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 %}
|
|
|
|
Mit dieser Abfrage können Sie alle Typen, ihre Felder und ihre Argumente (und den Typ der Argumente) extrahieren. Dies ist sehr nützlich, um zu wissen, wie die Datenbank abgefragt werden kann.
|
|
|
|
![](<../../.gitbook/assets/image (207) (3).png>)
|
|
|
|
**Fehler**
|
|
|
|
Es ist interessant zu wissen, ob die **Fehler** angezeigt werden, da sie nützliche **Informationen** liefern können.
|
|
```
|
|
?query={__schema}
|
|
?query={}
|
|
?query={thisdefinitelydoesnotexist}
|
|
```
|
|
![](<../../.gitbook/assets/image (205) (1).png>)
|
|
|
|
**Datenbankschema über Introspektion ermitteln**
|
|
|
|
{% hint style="info" %}
|
|
Wenn die Introspektion aktiviert ist, aber die obige Abfrage nicht ausgeführt wird, versuchen Sie, die Direktiven `onOperation`, `onFragment` und `onField` aus der Abfragestruktur zu entfernen.
|
|
{% 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
|
|
}
|
|
}
|
|
}
|
|
}
|
|
```
|
|
Inline-Introspektionsabfrage:
|
|
```
|
|
/?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}+}
|
|
```
|
|
Die letzte Codezeile ist eine GraphQL-Abfrage, die alle Metainformationen aus dem GraphQL (Objektnamen, Parameter, Typen...) ausgibt.
|
|
|
|
![](<../../.gitbook/assets/image (206).png>)
|
|
|
|
Wenn die Introspektion aktiviert ist, können Sie [**GraphQL Voyager**](https://github.com/APIs-guru/graphql-voyager) verwenden, um in einer GUI alle Optionen anzuzeigen.
|
|
|
|
### Abfragen
|
|
|
|
Nun, da wir wissen, welche Art von Informationen in der Datenbank gespeichert sind, versuchen wir, **einige Werte abzurufen**.
|
|
|
|
In der Introspektion können Sie herausfinden, **welches Objekt Sie direkt abfragen können** (weil Sie ein Objekt nicht einfach abfragen können, nur weil es existiert). Im folgenden Bild sehen Sie, dass der "_queryType_" "_Query_" genannt wird und dass eines der Felder des "_Query_"-Objekts "_flags_" ist, das auch ein Objekttyp ist. Daher können Sie das Flag-Objekt abfragen.
|
|
|
|
![](../../.gitbook/assets/screenshot-from-2021-03-13-18-17-48.png)
|
|
|
|
Beachten Sie, dass der Typ der Abfrage "_flags_" "_Flags_" ist und dieses Objekt wie folgt definiert ist:
|
|
|
|
![](../../.gitbook/assets/screenshot-from-2021-03-13-18-22-57.png)
|
|
|
|
Sie können sehen, dass die "_Flags_"-Objekte aus **Name** und **Wert** bestehen. Sie können also alle Namen und Werte der Flags mit der Abfrage erhalten:
|
|
```javascript
|
|
query={flags{name, value}}
|
|
```
|
|
Beachten Sie, dass im Falle des **zu abfragenden Objekts** ein **primitiver** **Typ** wie **String** ist, wie im folgenden Beispiel:
|
|
|
|
![](<../../.gitbook/assets/image (441).png>)
|
|
|
|
Sie können es einfach abfragen mit:
|
|
```javascript
|
|
query={hiddenFlags}
|
|
```
|
|
In einem anderen Beispiel, in dem es 2 Objekte innerhalb des "_Query_"-Typobjekts gab: "_user_" und "_users_". Wenn diese Objekte keine Argumente zur Suche benötigen, können Sie alle Informationen von ihnen abrufen, indem Sie einfach nach den gewünschten Daten fragen. In diesem Beispiel aus dem Internet könnten Sie die gespeicherten Benutzernamen und Passwörter extrahieren:
|
|
|
|
![](<../../.gitbook/assets/image (208).png>)
|
|
|
|
Jedoch erhalten Sie in diesem Beispiel bei dem Versuch, dies zu tun, folgenden Fehler:
|
|
|
|
![](<../../.gitbook/assets/image (210).png>)
|
|
|
|
Es scheint, dass auf irgendeine Weise das "_**uid**_"-Argument vom Typ _**Int**_ verwendet wird. Wie auch immer, wir wussten bereits in dem Abschnitt [Grundlegende Enumeration](graphql.md#basic-enumeration), dass eine Abfrage vorgeschlagen wurde, die uns alle benötigten Informationen zeigt: `query={__schema{types{name,fields{name, args{name,description,type{name, kind, ofType{name, kind}}}}}}}`
|
|
|
|
Wenn Sie das bereitgestellte Bild lesen, sehen Sie, dass "_**user**_" das **arg** "_**uid**_" vom Typ _Int_ hatte.
|
|
|
|
Daher habe ich durch eine leichte _**uid**_-Brute-Force herausgefunden, dass bei _**uid**=**1**_ ein Benutzername und ein Passwort abgerufen wurden:\
|
|
`query={user(uid:1){user,password}}`
|
|
|
|
![](<../../.gitbook/assets/image (211).png>)
|
|
|
|
Beachten Sie, dass ich herausgefunden habe, dass ich nach den Parametern "_**user**_" und "_**password**_" fragen konnte, weil ich bei dem Versuch, nach etwas zu suchen, das nicht existiert (`query={user(uid:1){noExists}}`), diesen Fehler erhalte:
|
|
|
|
![](<../../.gitbook/assets/image (213).png>)
|
|
|
|
Und während der **Enumeration-Phase** habe ich herausgefunden, dass das Objekt "_**dbuser**_" die Felder "_**user**_" und "_**password**_" hatte.
|
|
|
|
**Trick zum Dumpen von Abfragezeichenfolgen (danke an @BinaryShadow\_)**
|
|
|
|
Wenn Sie nach einem Zeichenfolgentyp suchen können, wie z.B. `query={theusers(description: ""){username,password}}` und Sie nach einer leeren Zeichenfolge suchen, werden alle Daten abgerufen. (Beachten Sie, dass dieses Beispiel nicht mit dem Beispiel der Tutorials zusammenhängt. Für dieses Beispiel nehmen Sie an, dass Sie mit "**theusers**" nach einem String-Feld namens "**description**" suchen können).
|
|
|
|
### Suche
|
|
|
|
In dieser Konfiguration enthält eine **Datenbank** **Personen** und **Filme**. **Personen** werden anhand ihrer **E-Mail** und ihres **Namens** identifiziert; **Filme** anhand ihres **Namens** und ihrer **Bewertung**. **Personen** können miteinander befreundet sein und auch Filme haben, was Beziehungen in der Datenbank anzeigt.
|
|
|
|
Sie können **Personen** **nach** dem **Namen** suchen und ihre E-Mails erhalten:
|
|
```javascript
|
|
{
|
|
searchPerson(name: "John Doe") {
|
|
email
|
|
}
|
|
}
|
|
```
|
|
Sie können Personen **nach** ihrem **Namen** suchen und ihre **abonnierten** **Filme** erhalten:
|
|
```javascript
|
|
{
|
|
searchPerson(name: "John Doe") {
|
|
email
|
|
subscribedMovies {
|
|
edges {
|
|
node {
|
|
name
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
```
|
|
Beachten Sie, wie angegeben wird, den `name` der `subscribedMovies` der Person abzurufen.
|
|
|
|
Sie können auch **mehrere Objekte gleichzeitig suchen**. In diesem Fall wird eine Suche nach 2 Filmen durchgeführt:
|
|
```javascript
|
|
{
|
|
searchPerson(subscribedMovies: [{name: "Inception"}, {name: "Rocky"}]) {
|
|
name
|
|
}
|
|
}r
|
|
```
|
|
Oder sogar **Beziehungen mehrerer verschiedener Objekte unter Verwendung von Aliassen**:
|
|
```javascript
|
|
{
|
|
johnsMovieList: searchPerson(name: "John Doe") {
|
|
subscribedMovies {
|
|
edges {
|
|
node {
|
|
name
|
|
}
|
|
}
|
|
}
|
|
}
|
|
davidsMovieList: searchPerson(name: "David Smith") {
|
|
subscribedMovies {
|
|
edges {
|
|
node {
|
|
name
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
```
|
|
### Mutationen
|
|
|
|
**Mutationen werden verwendet, um Änderungen auf der Serverseite vorzunehmen.**
|
|
|
|
In der **Introspektion** können Sie die **deklarierten** **Mutationen** finden. Im folgenden Bild wird "_MutationType_" als "_Mutation_" bezeichnet und das Objekt "_Mutation_" enthält die Namen der Mutationen (wie "_addPerson_" in diesem Fall):
|
|
|
|
![](../../.gitbook/assets/screenshot-from-2021-03-13-18-26-27.png)
|
|
|
|
In dieser Konfiguration enthält eine **Datenbank** **Personen** und **Filme**. **Personen** werden anhand ihrer **E-Mail** und ihres **Namens** identifiziert, **Filme** anhand ihres **Namens** und ihrer **Bewertung**. **Personen** können miteinander befreundet sein und auch Filme haben, was Beziehungen innerhalb der Datenbank anzeigt.
|
|
|
|
Eine Mutation zum **Erstellen neuer** Filme in der Datenbank könnte wie folgt aussehen (in diesem Beispiel wird die Mutation `addMovie` genannt):
|
|
```javascript
|
|
mutation {
|
|
addMovie(name: "Jumanji: The Next Level", rating: "6.8/10", releaseYear: 2019) {
|
|
movies {
|
|
name
|
|
rating
|
|
}
|
|
}
|
|
}
|
|
```
|
|
**Beachten Sie, wie sowohl die Werte als auch der Datentyp in der Abfrage angegeben sind.**
|
|
|
|
Darüber hinaus unterstützt die Datenbank eine **Mutation**-Operation namens `addPerson`, mit der **Personen** erstellt werden können, zusammen mit ihren Verbindungen zu vorhandenen **Freunden** und **Filmen**. Es ist wichtig zu beachten, dass die Freunde und Filme bereits in der Datenbank vorhanden sein müssen, bevor sie mit der neu erstellten Person verknüpft werden können.
|
|
```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
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
```
|
|
### Stapelbruteforce in 1 API-Anfrage
|
|
|
|
Diese Informationen wurden von [https://lab.wallarm.com/graphql-batching-attack/](https://lab.wallarm.com/graphql-batching-attack/) übernommen.\
|
|
Authentifizierung über die GraphQL-API durch **gleichzeitiges Senden vieler Abfragen mit unterschiedlichen Anmeldeinformationen**, um sie zu überprüfen. Es handelt sich um einen klassischen Brute-Force-Angriff, bei dem jetzt aufgrund der GraphQL-Batch-Funktion mehr als ein Anmelde-/Passwortpaar pro HTTP-Anfrage gesendet werden kann. Mit diesem Ansatz kann externe Rate-Monitoring-Anwendungen getäuscht werden, indem sie denken, dass alles in Ordnung ist und es keinen Brute-Force-Bot gibt, der Passwörter errät.
|
|
|
|
Unten finden Sie die einfachste Demonstration einer Anforderung zur Anwendungsberechtigung mit **3 verschiedenen E-Mail-/Passwortpaaren gleichzeitig**. Natürlich ist es auf die gleiche Weise möglich, Tausende in einer einzigen Anfrage zu senden:
|
|
|
|
![](<../../.gitbook/assets/image (182) (1).png>)
|
|
|
|
Wie wir aus dem Antwort-Screenshot sehen können, haben die ersten und dritten Anfragen _null_ zurückgegeben und die entsprechenden Informationen im _error_-Abschnitt reflektiert. Die **zweite Mutation hatte die richtigen Authentifizierungsdaten** und die Antwort enthält das richtige Authentifizierungssitzungstoken.
|
|
|
|
![](<../../.gitbook/assets/image (119) (1).png>)
|
|
|
|
## GraphQL ohne Introspektion
|
|
|
|
Immer mehr **GraphQL-Endpunkte deaktivieren die Introspektion**. Die Fehler, die GraphQL wirft, wenn eine unerwartete Anfrage empfangen wird, reichen jedoch aus, damit Tools wie [**clairvoyance**](https://github.com/nikitastupin/clairvoyance) den Großteil des Schemas rekonstruieren können.
|
|
|
|
Darüber hinaus beobachtet die Burp Suite-Erweiterung [**GraphQuail**](https://github.com/forcesunseen/graphquail) die durch Burp gehenden GraphQL-API-Anfragen und **erstellt** ein internes GraphQL-**Schema** mit jeder neuen Abfrage, die sie sieht. Es kann auch das Schema für GraphiQL und Voyager freigeben. Die Erweiterung gibt eine gefälschte Antwort zurück, wenn sie eine Introspektionsabfrage erhält. Dadurch zeigt GraphQuail alle Abfragen, Argumente und Felder an, die in der API verwendet werden können. Weitere Informationen finden Sie [**hier**](https://blog.forcesunseen.com/graphql-security-testing-without-a-schema).
|
|
|
|
Eine gute **Wortliste**, um [**GraphQL-Entitäten zu entdecken, finden Sie hier**](https://github.com/Escape-Technologies/graphql-wordlist?).
|
|
|
|
### Umgehung von GraphQL-Introspektionsabwehrmaßnahmen <a href="#bypassing-graphql-introspection-defences" id="bypassing-graphql-introspection-defences"></a>
|
|
|
|
### **Umgehung von GraphQL-Introspektionsabwehrmaßnahmen**
|
|
|
|
Um Einschränkungen bei Introspektionsabfragen in APIs zu umgehen, ist es wirksam, nach dem Schlüsselwort `__schema` ein **spezielles Zeichen einzufügen**. Diese Methode nutzt häufige Entwicklerfehler in Regex-Mustern aus, die darauf abzielen, die Introspektion durch Fokussierung auf das Schlüsselwort `__schema` zu blockieren. Durch das Hinzufügen von Zeichen wie **Leerzeichen, Zeilenumbrüche und Kommas**, die von GraphQL ignoriert werden, aber möglicherweise nicht in Regex berücksichtigt werden, können Einschränkungen umgangen werden. Zum Beispiel kann eine Introspektionsabfrage mit einem Zeilenumbruch nach `__schema` solche Abwehrmaßnahmen umgehen:
|
|
```bash
|
|
# Example with newline to bypass
|
|
{
|
|
"query": "query{__schema
|
|
{queryType{name}}}"
|
|
}
|
|
```
|
|
Wenn dies nicht erfolgreich ist, sollten alternative Anfragemethoden wie **GET-Anfragen** oder **POST mit `x-www-form-urlencoded`** in Betracht gezogen werden, da Einschränkungen möglicherweise nur für POST-Anfragen gelten.
|
|
|
|
### **Aufdecken von freigelegten GraphQL-Strukturen**
|
|
|
|
Wenn die Introspektion deaktiviert ist, ist es eine nützliche Strategie, den Quellcode der Website nach vorab geladenen Abfragen in JavaScript-Bibliotheken zu untersuchen. Diese Abfragen können mithilfe des `Sources`-Tabs in den Entwicklertools gefunden werden und geben Einblicke in das Schema der API und enthüllen potenziell **sensible freigelegte Abfragen**. Die Befehle zum Suchen in den Entwicklertools lauten:
|
|
```javascript
|
|
Inspect/Sources/"Search all files"
|
|
file:* mutation
|
|
file:* query
|
|
```
|
|
## CSRF in GraphQL
|
|
|
|
Wenn Sie nicht wissen, was CSRF ist, lesen Sie die folgende Seite:
|
|
|
|
{% 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 %}
|
|
|
|
Dort finden Sie mehrere GraphQL-Endpunkte, die **ohne CSRF-Token konfiguriert sind**.
|
|
|
|
Beachten Sie, dass GraphQL-Anfragen normalerweise über POST-Anfragen mit dem Content-Type **`application/json`** gesendet werden.
|
|
```javascript
|
|
{"operationName":null,"variables":{},"query":"{\n user {\n firstName\n __typename\n }\n}\n"}
|
|
```
|
|
Jedoch unterstützen die meisten GraphQL-Endpunkte auch **`form-urlencoded` POST-Anfragen:**
|
|
```javascript
|
|
query=%7B%0A++user+%7B%0A++++firstName%0A++++__typename%0A++%7D%0A%7D%0A
|
|
```
|
|
Daher ist es möglich, CSRF-Anfragen wie die vorherigen **ohne Preflight-Anfragen** zu senden, um Änderungen in GraphQL durch Missbrauch eines CSRF durchzuführen.
|
|
|
|
Beachten Sie jedoch, dass der neue Standardwert für das `samesite`-Flag von Chrome `Lax` ist. Dies bedeutet, dass das Cookie nur von einer Drittanbieter-Website in GET-Anfragen gesendet wird.
|
|
|
|
Beachten Sie auch, dass es in der Regel möglich ist, die **Abfrageanfrage** auch als **GET-Anfrage** zu senden und das CSRF-Token möglicherweise nicht in einer GET-Anfrage validiert wird.
|
|
|
|
Es ist auch möglich, durch Ausnutzung eines [**XS-Search**](../../pentesting-web/xs-search.md)-Angriffs Inhalte aus dem GraphQL-Endpunkt unter Ausnutzung der Anmeldeinformationen des Benutzers zu exfiltrieren.
|
|
|
|
Weitere Informationen finden Sie im [**Originalbeitrag hier**](https://blog.doyensec.com/2021/05/20/graphql-csrf.html).
|
|
|
|
## Autorisierung in GraphQL
|
|
|
|
Viele in der Endpunkt definierte GraphQL-Funktionen überprüfen möglicherweise nur die Authentifizierung des Anfragenden, jedoch nicht die Autorisierung.
|
|
|
|
Die Änderung der Eingabevariablen der Abfrage könnte zum [Leck](https://hackerone.com/reports/792927) sensibler Kontodetails führen.
|
|
|
|
Mutationen könnten sogar zu Account-Übernahmen führen, wenn versucht wird, andere Kontodaten zu ändern.
|
|
```javascript
|
|
{
|
|
"operationName":"updateProfile",
|
|
"variables":{"username":INJECT,"data":INJECT},
|
|
"query":"mutation updateProfile($username: String!,...){updateProfile(username: $username,...){...}}"
|
|
}
|
|
```
|
|
### Umgehung der Autorisierung in GraphQL
|
|
|
|
Das Verketten von Abfragen kann ein schwaches Authentifizierungssystem umgehen.
|
|
|
|
Im folgenden Beispiel sehen Sie, dass die Operation "forgotPassword" ist und dass nur die damit verbundene forgotPassword-Abfrage ausgeführt werden sollte. Dies kann umgangen werden, indem am Ende eine zusätzliche Abfrage hinzugefügt wird. In diesem Fall fügen wir "register" hinzu und eine Benutzervariable, um das System als neuen Benutzer zu registrieren.
|
|
|
|
<figure><img src="../../.gitbook/assets/GraphQLAuthBypassMethod.PNG" alt=""><figcaption></figcaption></figure>
|
|
|
|
## Umgehung von Rate Limits mit Aliases in GraphQL
|
|
|
|
In GraphQL sind Aliases ein leistungsstarkes Feature, das es ermöglicht, **Eigenschaften explizit zu benennen**, wenn eine API-Anfrage gestellt wird. Diese Funktion ist besonders nützlich, um **mehrere Instanzen desselben Objekttyps** in einer einzigen Anfrage abzurufen. Aliases können verwendet werden, um die Einschränkung zu umgehen, dass GraphQL-Objekte nicht mehrere Eigenschaften mit demselben Namen haben können.
|
|
|
|
Für ein detailliertes Verständnis von GraphQL-Aliases wird die folgende Ressource empfohlen: [Aliases](https://portswigger.net/web-security/graphql/what-is-graphql#aliases).
|
|
|
|
Obwohl der Hauptzweck von Aliases darin besteht, die Notwendigkeit für zahlreiche API-Aufrufe zu reduzieren, wurde ein unbeabsichtigter Anwendungsfall identifiziert, bei dem Aliases genutzt werden können, um Brute-Force-Angriffe auf einen GraphQL-Endpunkt auszuführen. Dies ist möglich, da einige Endpunkte durch Rate-Limiter geschützt sind, die Brute-Force-Angriffe durch die Begrenzung der **Anzahl der HTTP-Anfragen** verhindern sollen. Diese Rate-Limiter berücksichtigen jedoch möglicherweise nicht die Anzahl der Operationen in jeder Anfrage. Da Aliases das Hinzufügen mehrerer Abfragen in einer einzigen HTTP-Anfrage ermöglichen, können sie solche Rate-Limiting-Maßnahmen umgehen.
|
|
|
|
Betrachten Sie das untenstehende Beispiel, das veranschaulicht, wie aliased Abfragen verwendet werden können, um die Gültigkeit von Rabattcodes für einen Online-Shop zu überprüfen. Diese Methode könnte das Rate Limiting umgehen, da sie mehrere Abfragen in einer HTTP-Anfrage zusammenfasst und somit die Überprüfung zahlreicher Rabattcodes gleichzeitig ermöglicht.
|
|
```bash
|
|
# 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
|
|
}
|
|
}
|
|
```
|
|
## Werkzeuge
|
|
|
|
### Schwachstellen-Scanner
|
|
|
|
* [https://github.com/gsmith257-cyber/GraphCrawler](https://github.com/gsmith257-cyber/GraphCrawler): Toolkit, das zum Abrufen von Schemas und zum Suchen nach sensiblen Daten, zum Testen der Autorisierung, zum Brute-Forcen von Schemas und zum Finden von Pfaden zu einem bestimmten Typ verwendet werden kann.
|
|
* [https://blog.doyensec.com/2020/03/26/graphql-scanner.html](https://blog.doyensec.com/2020/03/26/graphql-scanner.html): Kann eigenständig oder als [Burp-Erweiterung](https://github.com/doyensec/inql) verwendet werden.
|
|
* [https://github.com/swisskyrepo/GraphQLmap](https://github.com/swisskyrepo/GraphQLmap): Kann auch als CLI-Client verwendet werden, um Angriffe zu automatisieren.
|
|
* [https://gitlab.com/dee-see/graphql-path-enum](https://gitlab.com/dee-see/graphql-path-enum): Tool, das die verschiedenen Möglichkeiten auflistet, einen bestimmten Typ in einem GraphQL-Schema zu erreichen.
|
|
* [https://github.com/doyensec/inql](https://github.com/doyensec/inql): Burp-Erweiterung für fortgeschrittenes GraphQL-Testing. Der _**Scanner**_ ist der Kern von InQL v5.0, mit dem Sie einen GraphQL-Endpunkt oder eine lokale Introspection-Schema-Datei analysieren können. Es generiert automatisch alle möglichen Abfragen und Mutationen und organisiert sie in einer strukturierten Ansicht für Ihre Analyse. Die Komponente _**Attacker**_ ermöglicht das Ausführen von Stapel-GraphQL-Angriffen, die nützlich sein können, um schlecht implementierte Rate-Limits zu umgehen.
|
|
|
|
### Clients
|
|
|
|
* [https://github.com/graphql/graphiql](https://github.com/graphql/graphiql): GUI-Client
|
|
* [https://altair.sirmuel.design/](https://altair.sirmuel.design/): GUI-Client
|
|
|
|
### Automatische Tests
|
|
|
|
{% embed url="https://graphql-dashboard.herokuapp.com/" %}
|
|
|
|
* Video, das AutoGraphQL erklärt: [https://www.youtube.com/watch?v=JJmufWfVvyU](https://www.youtube.com/watch?v=JJmufWfVvyU)
|
|
|
|
## Referenzen
|
|
|
|
* [**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><strong>Lernen Sie AWS-Hacking von Grund auf mit</strong> <a href="https://training.hacktricks.xyz/courses/arte"><strong>htARTE (HackTricks AWS Red Team Expert)</strong></a><strong>!</strong></summary>
|
|
|
|
Andere Möglichkeiten, HackTricks zu unterstützen:
|
|
|
|
* Wenn Sie Ihr **Unternehmen in HackTricks bewerben möchten** oder **HackTricks als PDF herunterladen möchten**, überprüfen Sie die [**ABONNEMENTPLÄNE**](https://github.com/sponsors/carlospolop)!
|
|
* Holen Sie sich das [**offizielle PEASS & HackTricks-Merchandise**](https://peass.creator-spring.com)
|
|
* Entdecken Sie [**The PEASS Family**](https://opensea.io/collection/the-peass-family), unsere Sammlung exklusiver [**NFTs**](https://opensea.io/collection/the-peass-family)
|
|
* **Treten Sie der** 💬 [**Discord-Gruppe**](https://discord.gg/hRep4RUj7f) oder der [**Telegramm-Gruppe**](https://t.me/peass) bei oder **folgen** Sie uns auf **Twitter** 🐦 [**@carlospolopm**](https://twitter.com/hacktricks_live)**.**
|
|
* **Teilen Sie Ihre Hacking-Tricks, indem Sie PRs an die** [**HackTricks**](https://github.com/carlospolop/hacktricks) und [**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud) GitHub-Repositories senden.
|
|
|
|
</details>
|