mirror of
https://github.com/carlospolop/hacktricks
synced 2024-12-12 06:12:55 +00:00
513 lines
28 KiB
Markdown
513 lines
28 KiB
Markdown
# GraphQL
|
|
|
|
<details>
|
|
|
|
<summary><strong>Naucz się hakować AWS od zera do bohatera z</strong> <a href="https://training.hacktricks.xyz/courses/arte"><strong>htARTE (HackTricks AWS Red Team Expert)</strong></a><strong>!</strong></summary>
|
|
|
|
Inne sposoby wsparcia HackTricks:
|
|
|
|
* Jeśli chcesz zobaczyć swoją **firmę reklamowaną w HackTricks** lub **pobrać HackTricks w formacie PDF**, sprawdź [**PLANY SUBSKRYPCYJNE**](https://github.com/sponsors/carlospolop)!
|
|
* Zdobądź [**oficjalne gadżety PEASS & HackTricks**](https://peass.creator-spring.com)
|
|
* Odkryj [**Rodzinę PEASS**](https://opensea.io/collection/the-peass-family), naszą kolekcję ekskluzywnych [**NFT**](https://opensea.io/collection/the-peass-family)
|
|
* **Dołącz do** 💬 [**grupy Discord**](https://discord.gg/hRep4RUj7f) lub [**grupy telegramowej**](https://t.me/peass) lub **śledź** nas na **Twitterze** 🐦 [**@carlospolopm**](https://twitter.com/hacktricks\_live)**.**
|
|
* **Podziel się swoimi sztuczkami hakerskimi, przesyłając PR-y do** [**HackTricks**](https://github.com/carlospolop/hacktricks) i [**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud) na githubie.
|
|
|
|
</details>
|
|
|
|
## Wprowadzenie
|
|
|
|
GraphQL jest **wyróżniany** jako **efektywna alternatywa** dla interfejsu API REST, oferując uproszczone podejście do pobierania danych z backendu. W przeciwieństwie do REST, który często wymaga wielu żądań do różnych punktów końcowych w celu zebrania danych, GraphQL umożliwia pobranie wszystkich wymaganych informacji za pomocą **jednego żądania**. Ten proces usprawnia znacznie pracę programistów, zmniejszając złożoność ich procesów pobierania danych.
|
|
|
|
## GraphQL a Bezpieczeństwo
|
|
|
|
Wraz z pojawieniem się nowych technologii, w tym GraphQL, pojawiają się również nowe podatności bezpieczeństwa. Istotnym punktem jest to, że **GraphQL nie zawiera domyślnie mechanizmów uwierzytelniania**. Odpowiedzialność za wdrożenie takich środków bezpieczeństwa spoczywa na programistach. Bez odpowiedniego uwierzytelnienia punkty końcowe GraphQL mogą ujawniać poufne informacje nieuwierzytelnionym użytkownikom, stanowiąc znaczne ryzyko bezpieczeństwa.
|
|
|
|
### Ataki Brute Force na Katalogi i GraphQL
|
|
|
|
Aby zidentyfikować wystawione instancje GraphQL, zaleca się uwzględnienie określonych ścieżek w atakach brute force na katalogi. Te ścieżki to:
|
|
|
|
* `/graphql`
|
|
* `/graphiql`
|
|
* `/graphql.php`
|
|
* `/graphql/console`
|
|
* `/api`
|
|
* `/api/graphql`
|
|
* `/graphql/api`
|
|
* `/graphql/graphql`
|
|
|
|
Zidentyfikowanie otwartych instancji GraphQL umożliwia sprawdzenie obsługiwanych zapytań. Jest to kluczowe dla zrozumienia danych dostępnych poprzez punkt końcowy. System introspekcji GraphQL ułatwia to, szczegółowo opisując zapytania obsługiwane przez schemat. Aby uzyskać więcej informacji na ten temat, zapoznaj się z dokumentacją GraphQL dotyczącą introspekcji: [**GraphQL: Język zapytań dla interfejsów API.**](https://graphql.org/learn/introspection/)
|
|
|
|
### Odcisk palca
|
|
|
|
Narzędzie [**graphw00f**](https://github.com/dolevf/graphw00f) jest zdolne do wykrywania, który silnik GraphQL jest używany na serwerze, a następnie wyświetla przydatne informacje dla audytora bezpieczeństwa.
|
|
|
|
#### Uniwersalne zapytania <a href="#universal-queries" id="universal-queries"></a>
|
|
|
|
Aby sprawdzić, czy dany URL jest usługą GraphQL, można wysłać **uniwersalne zapytanie**, `query{__typename}`. Jeśli odpowiedź zawiera `{"data": {"__typename": "Query"}}`, potwierdza to, że URL hostuje punkt końcowy GraphQL. Ta metoda opiera się na polu `__typename` GraphQL, które ujawnia typ zapytanego obiektu.
|
|
```javascript
|
|
query{__typename}
|
|
```
|
|
### Podstawowa enumeracja
|
|
|
|
GraphQL zazwyczaj obsługuje **GET**, **POST** (x-www-form-urlencoded) i **POST**(json). Chociaż ze względów bezpieczeństwa zaleca się zezwalać tylko na json, aby zapobiec atakom CSRF.
|
|
|
|
#### Introspekcja
|
|
|
|
Aby użyć introspekcji do odkrywania informacji o schemacie, zapytaj pole `__schema`. To pole jest dostępne w głównym typie wszystkich zapytań.
|
|
```bash
|
|
query={__schema{types{name,fields{name}}}}
|
|
```
|
|
Z tym zapytaniem znajdziesz nazwę wszystkich używanych typów:
|
|
|
|
![](<../../.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 %}
|
|
|
|
Z tym zapytaniem możesz wydobyć wszystkie typy, ich pola i argumenty (oraz typ argumentów). Będzie to bardzo przydatne, aby wiedzieć, jak zapytać bazę danych.
|
|
|
|
![](<../../.gitbook/assets/image (207) (3).png>)
|
|
|
|
**Błędy**
|
|
|
|
Warto wiedzieć, czy **błędy** zostaną **wyświetlone**, ponieważ przyczynią się do uzyskania przydatnych **informacji**.
|
|
```
|
|
?query={__schema}
|
|
?query={}
|
|
?query={thisdefinitelydoesnotexist}
|
|
```
|
|
**Wylicz schemat bazy danych za pomocą introspekcji**
|
|
|
|
{% hint style="info" %}
|
|
Jeśli introspekcja jest włączona, ale powyższe zapytanie nie działa, spróbuj usunąć dyrektywy `onOperation`, `onFragment` i `onField` z struktury zapytania.
|
|
{% 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
|
|
}
|
|
}
|
|
}
|
|
}
|
|
```
|
|
Zapytanie o inspekcję w linii:
|
|
```
|
|
/?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}+}
|
|
```
|
|
Ostatnia linia kodu to zapytanie graphql, które wyciągnie wszystkie metadane z graphql (nazwy obiektów, parametry, typy...)
|
|
|
|
![](<../../.gitbook/assets/image (206).png>)
|
|
|
|
Jeśli introspekcja jest włączona, możesz użyć [**GraphQL Voyager**](https://github.com/APIs-guru/graphql-voyager), aby zobaczyć w GUI wszystkie opcje.
|
|
|
|
### Zapytywanie
|
|
|
|
Teraz, gdy wiemy, jakie informacje są zapisane w bazie danych, spróbujmy **wydobyć pewne wartości**.
|
|
|
|
W introspekcji możesz znaleźć **który obiekt możesz bezpośrednio zapytać** (ponieważ nie możesz zapytać obiektu tylko dlatego, że istnieje). Na poniższym obrazku możesz zobaczyć, że "_queryType_" nazywa się "_Query_" i że jednym z pól obiektu "_Query_" jest "_flags_", który również jest typem obiektu. Dlatego możesz zapytać obiekt flagi.
|
|
|
|
![](../../.gitbook/assets/screenshot-from-2021-03-13-18-17-48.png)
|
|
|
|
Zauważ, że typ zapytania "_flags_" to "_Flags_", a ten obiekt jest zdefiniowany poniżej:
|
|
|
|
![](../../.gitbook/assets/screenshot-from-2021-03-13-18-22-57.png)
|
|
|
|
Możesz zobaczyć, że obiekty "_Flags_" składają się z **nazwy** i **wartości**. Następnie możesz uzyskać wszystkie nazwy i wartości flag za pomocą zapytania:
|
|
```javascript
|
|
query={flags{name, value}}
|
|
```
|
|
Zauważ, że w przypadku, gdy **obiekt do zapytania** jest **typem podstawowym** jak **string** jak w poniższym przykładzie
|
|
|
|
![](<../../.gitbook/assets/image (441).png>)
|
|
|
|
Możesz po prostu zapytać o to:
|
|
```javascript
|
|
query={hiddenFlags}
|
|
```
|
|
W innym przykładzie, gdzie wewnątrz obiektu "_Query_" znajdowały się 2 obiekty: "_user_" i "_users_".\
|
|
Jeśli te obiekty nie wymagają żadnego argumentu do wyszukiwania, można **pobrać wszystkie informacje z nich**, pytając o dane, których potrzebujesz. W tym przykładzie z Internetu można wydobyć zapisane nazwy użytkowników i hasła:
|
|
|
|
![](<../../.gitbook/assets/image (208).png>)
|
|
|
|
Jednakże, w tym przykładzie, jeśli spróbujesz to zrobić, otrzymasz ten **błąd**:
|
|
|
|
![](<../../.gitbook/assets/image (210).png>)
|
|
|
|
Wygląda na to, że wyszukiwanie odbywa się za pomocą argumentu "_**uid**_" typu _**Int**_.\
|
|
Tak czy inaczej, już wiedzieliśmy, że w sekcji [Podstawowa Enumeracja](graphql.md#basic-enumeration) zaproponowano zapytanie, które pokazywało nam wszystkie potrzebne informacje: `query={__schema{types{name,fields{name, args{name,description,type{name, kind, ofType{name, kind}}}}}}}`
|
|
|
|
Jeśli przeczytasz dostarczone zdjęcie, gdy wykonasz to zapytanie, zobaczysz, że "_**user**_" miał **arg** "_**uid**_" typu _Int_.
|
|
|
|
Więc, wykonując lekkie bruteforce _**uid**_, odkryłem, że dla _**uid**=**1**_ została pobrana nazwa użytkownika i hasło:\
|
|
`query={user(uid:1){user,password}}`
|
|
|
|
![](<../../.gitbook/assets/image (211).png>)
|
|
|
|
Zauważ, że **odkryłem**, że mogę prosić o parametry "_**user**_" i "_**password**_", ponieważ jeśli spróbuję szukać czegoś, czego nie ma (`query={user(uid:1){noExists}}`), otrzymam ten błąd:
|
|
|
|
![](<../../.gitbook/assets/image (213).png>)
|
|
|
|
Podczas fazy **enumeracji** odkryłem, że obiekt "_**dbuser**_" miał pola "_**user**_" i "_**password**_.
|
|
|
|
**Sztuczka z wydobywaniem ciągu zapytań (dzięki @BinaryShadow\_)**
|
|
|
|
Jeśli możesz wyszukiwać według typu ciągów, np.: `query={theusers(description: ""){username,password}}` i **szukasz pustego ciągu**, wtedy **wydobyte zostaną wszystkie dane**. (_Zauważ, że ten przykład nie jest związany z przykładem z samouczków, dla tego przykładu załóż, że możesz szukać używając "**theusers**" według pola typu ciągowego o nazwie "**description**"_).
|
|
|
|
### Wyszukiwanie
|
|
|
|
W tej konfiguracji **baza danych** zawiera **osoby** i **filmy**. **Osoby** są identyfikowane przez swoje **adresy e-mail** i **imię**; **filmy** przez swoje **nazwy** i **ocenę**. **Osoby** mogą być przyjaciółmi między sobą oraz mieć filmy, co wskazuje na relacje w bazie danych.
|
|
|
|
Możesz **wyszukiwać** osoby **po** nazwie i otrzymywać ich adresy e-mail:
|
|
```javascript
|
|
{
|
|
searchPerson(name: "John Doe") {
|
|
email
|
|
}
|
|
}
|
|
```
|
|
Możesz **wyszukiwać** osoby **po** nazwie i otrzymywać informacje o ich **subskrybowanych** filmach:
|
|
```javascript
|
|
{
|
|
searchPerson(name: "John Doe") {
|
|
email
|
|
subscribedMovies {
|
|
edges {
|
|
node {
|
|
name
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
```
|
|
Zauważ, jak wskazano na pobranie `name` z `subscribedMovies` osoby.
|
|
|
|
Możesz również **wyszukiwać kilka obiektów jednocześnie**. W tym przypadku wyszukiwane są 2 filmy:
|
|
```javascript
|
|
{
|
|
searchPerson(subscribedMovies: [{name: "Inception"}, {name: "Rocky"}]) {
|
|
name
|
|
}
|
|
}r
|
|
```
|
|
Lub nawet **relacje kilku różnych obiektów za pomocą aliasów**:
|
|
```javascript
|
|
{
|
|
johnsMovieList: searchPerson(name: "John Doe") {
|
|
subscribedMovies {
|
|
edges {
|
|
node {
|
|
name
|
|
}
|
|
}
|
|
}
|
|
}
|
|
davidsMovieList: searchPerson(name: "David Smith") {
|
|
subscribedMovies {
|
|
edges {
|
|
node {
|
|
name
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
```
|
|
### Mutacje
|
|
|
|
**Mutacje są używane do wprowadzania zmian po stronie serwera.**
|
|
|
|
W **introspekcji** można znaleźć **zadeklarowane** **mutacje**. Na poniższym obrazie "_MutationType_" jest nazywany "_Mutation_", a obiekt "_Mutation_" zawiera nazwy mutacji (takie jak "_addPerson_" w tym przypadku):
|
|
|
|
![](../../.gitbook/assets/screenshot-from-2021-03-13-18-26-27.png)
|
|
|
|
W tej konfiguracji **baza danych** zawiera **osoby** i **filmy**. **Osoby** są identyfikowane przez swój **adres e-mail** i **imię**; **filmy** przez swoją **nazwę** i **ocenę**. **Osoby** mogą być przyjaciółmi między sobą oraz mieć filmy, co wskazuje na relacje w bazie danych.
|
|
|
|
Mutacja do **tworzenia nowych** filmów w bazie danych może wyglądać jak poniższa (w tym przykładzie mutacja nazywa się `addMovie`):
|
|
```javascript
|
|
mutation {
|
|
addMovie(name: "Jumanji: The Next Level", rating: "6.8/10", releaseYear: 2019) {
|
|
movies {
|
|
name
|
|
rating
|
|
}
|
|
}
|
|
}
|
|
```
|
|
**Zauważ, jak w zapytaniu wskazane są zarówno wartości, jak i typ danych.**
|
|
|
|
Dodatkowo, baza danych obsługuje operację **mutacji**, o nazwie `addPerson`, która umożliwia tworzenie **osób** wraz z ich powiązaniami z istniejącymi **przyjaciółmi** i **filmami**. Ważne jest, aby zauważyć, że przyjaciele i filmy muszą istnieć w bazie danych przed ich powiązaniem z nowo utworzoną osobą.
|
|
```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
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
```
|
|
### Przeładowanie dyrektywy
|
|
|
|
Jak wyjaśniono w [**jednej z podatności opisanych w tym raporcie**](https://www.landh.tech/blog/20240304-google-hack-50000/), przeładowanie dyrektywy oznacza wywołanie dyrektywy nawet miliony razy, aby zmusić serwer do marnowania operacji, aż będzie możliwe przeprowadzenie ataku typu DoS.
|
|
|
|
### Atak siłowy wsadowy w 1 żądaniu API
|
|
|
|
Ta informacja została zaczerpnięta z [https://lab.wallarm.com/graphql-batching-attack/](https://lab.wallarm.com/graphql-batching-attack/).\
|
|
Uwierzytelnianie poprzez interfejs API GraphQL z **równoczesnym wysyłaniem wielu zapytań z różnymi danymi uwierzytelniającymi** w celu sprawdzenia go. To klasyczny atak siłowy, ale teraz możliwe jest wysłanie więcej niż jednej pary login/hasło w jednym żądaniu HTTP ze względu na funkcję wsadowego przetwarzania GraphQL. Ten sposób działania zmyliłby zewnętrzne aplikacje monitorujące częstotliwość, sugerując, że wszystko jest w porządku i nie ma botów próbujących odgadnąć hasła metodą siłową.
|
|
|
|
Poniżej znajdziesz najprostsze przedstawienie żądania uwierzytelniającego aplikacji, z **3 różnymi parami adres e-mail/hasło jednocześnie**. Oczywiście możliwe jest wysłanie tysięcy w jednym żądaniu w ten sam sposób:
|
|
|
|
![](<../../.gitbook/assets/image (182) (1).png>)
|
|
|
|
Jak widać na zrzucie ekranu odpowiedzi, pierwsze i trzecie żądania zwróciły _null_ i odzwierciedliły odpowiednie informacje w sekcji _error_. **Druga mutacja miała poprawne dane uwierzytelniające** i odpowiedź zawierała poprawny token sesji uwierzytelniającej.
|
|
|
|
![](<../../.gitbook/assets/image (119) (1).png>)
|
|
|
|
## GraphQL Bez Introspekcji
|
|
|
|
Coraz więcej **punktów końcowych GraphQL wyłącza introspekcję**. Jednak błędy, które GraphQL zwraca, gdy otrzymuje nieoczekiwane żądanie, są wystarczające dla narzędzi takich jak [**clairvoyance**](https://github.com/nikitastupin/clairvoyance), aby odtworzyć większość schematu.
|
|
|
|
Ponadto rozszerzenie Burp Suite [**GraphQuail**](https://github.com/forcesunseen/graphquail) **obserwuje żądania interfejsu API GraphQL przechodzące przez Burp** i **tworzy** wewnętrzny GraphQL **schemat** z każdym nowym zapytaniem, które widzi. Może również ujawnić schemat dla GraphiQL i Voyager. Rozszerzenie zwraca fałszywą odpowiedź, gdy otrzymuje zapytanie introspekcyjne. W rezultacie GraphQuail pokazuje wszystkie zapytania, argumenty i pola dostępne do użycia w interfejsie API. Aby uzyskać więcej informacji, [**sprawdź to**](https://blog.forcesunseen.com/graphql-security-testing-without-a-schema).
|
|
|
|
Ładna **lista słów** do odkrywania [**encji GraphQL można znaleźć tutaj**](https://github.com/Escape-Technologies/graphql-wordlist?).
|
|
|
|
### Omijanie obrony przed introspekcją GraphQL <a href="#bypassing-graphql-introspection-defences" id="bypassing-graphql-introspection-defences"></a>
|
|
|
|
### **Omijanie Obrony Przed Introspekcją GraphQL**
|
|
|
|
Aby ominąć ograniczenia dotyczące zapytań introspekcyjnych w interfejsach API, skuteczne okazuje się wstawienie **specjalnego znaku po słowie kluczowym `__schema`**. Ta metoda wykorzystuje powszechne przeoczenia programistów w wzorcach wyrażeń regularnych, które mają na celu zablokowanie introspekcji poprzez skupienie się na słowie kluczowym `__schema`. Dodanie znaków takich jak **spacje, nowe linie i przecinki**, które GraphQL ignoruje, ale mogą nie być uwzględnione w wyrażeniach regularnych, pozwala ominąć ograniczenia. Na przykład zapytanie introspekcyjne z nową linią po `__schema` może ominąć takie zabezpieczenia:
|
|
```bash
|
|
# Example with newline to bypass
|
|
{
|
|
"query": "query{__schema
|
|
{queryType{name}}}"
|
|
}
|
|
```
|
|
Jeśli nie uda się, rozważ alternatywne metody żądania, takie jak **żądania GET** lub **POST z `x-www-form-urlencoded`**, ponieważ ograniczenia mogą dotyczyć tylko żądań POST.
|
|
|
|
### **Odkrywanie Ujawnionych Struktur GraphQL**
|
|
|
|
Kiedy introspekcja jest wyłączona, badanie kodu źródłowego witryny w poszukiwaniu wcześniej załadowanych zapytań w bibliotekach JavaScript jest przydatną strategią. Te zapytania można znaleźć, korzystając z karty `Sources` w narzędziach deweloperskich, co pozwala uzyskać wgląd w schemat API i ujawnić potencjalnie **ujawnione wrażliwe zapytania**. Polecenia do wyszukiwania w narzędziach deweloperskich to:
|
|
```javascript
|
|
Inspect/Sources/"Search all files"
|
|
file:* mutation
|
|
file:* query
|
|
```
|
|
## CSRF w GraphQL
|
|
|
|
Jeśli nie wiesz, czym jest CSRF, przeczytaj następującą stronę:
|
|
|
|
{% 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 %}
|
|
|
|
Możesz tam znaleźć kilka punktów końcowych GraphQL **skonfigurowanych bez tokenów CSRF.**
|
|
|
|
Zauważ, że zazwyczaj żądania GraphQL są wysyłane za pomocą żądań POST z użyciem Content-Type **`application/json`**.
|
|
```javascript
|
|
{"operationName":null,"variables":{},"query":"{\n user {\n firstName\n __typename\n }\n}\n"}
|
|
```
|
|
Jednak większość punktów końcowych GraphQL obsługuje również żądania POST w formacie **`form-urlencoded`:**
|
|
```javascript
|
|
query=%7B%0A++user+%7B%0A++++firstName%0A++++__typename%0A++%7D%0A%7D%0A
|
|
```
|
|
Dlatego, ponieważ żądania CSRF, takie jak poprzednie, są wysyłane **bez żądań wstępnych**, możliwe jest **wykonanie** **zmian** w GraphQL, nadużywając CSRF.
|
|
|
|
Należy jednak zauważyć, że nowa domyślna wartość ciasteczka flagi `samesite` w Chrome to `Lax`. Oznacza to, że ciasteczko będzie wysyłane tylko z witryny stron trzecich w żądaniach GET.
|
|
|
|
Należy pamiętać, że zazwyczaj możliwe jest wysłanie **żądania zapytania** również jako **żądanie GET, a token CSRF może nie być weryfikowany w żądaniu GET.**
|
|
|
|
Nadużywając również ataku [**XS-Search**](../../pentesting-web/xs-search.md), możliwe jest wycieknięcie zawartości z punktu końcowego GraphQL, nadużywając poświadczeń użytkownika.
|
|
|
|
Aby uzyskać więcej informacji, **sprawdź** [**oryginalny post tutaj**](https://blog.doyensec.com/2021/05/20/graphql-csrf.html).
|
|
|
|
## Autoryzacja w GraphQL
|
|
|
|
Wiele funkcji GraphQL zdefiniowanych na punkcie końcowym może sprawdzać tylko uwierzytelnienie żądającego, ale nie autoryzację.
|
|
|
|
Modyfikacja zmiennych wejściowych zapytania może prowadzić do wycieku wrażliwych danych konta [leaked](https://hackerone.com/reports/792927).
|
|
|
|
Mutacja może nawet prowadzić do przejęcia konta, próbując zmodyfikować dane innego konta.
|
|
```javascript
|
|
{
|
|
"operationName":"updateProfile",
|
|
"variables":{"username":INJECT,"data":INJECT},
|
|
"query":"mutation updateProfile($username: String!,...){updateProfile(username: $username,...){...}}"
|
|
}
|
|
```
|
|
### Ominięcie autoryzacji w GraphQL
|
|
|
|
[Łączenie zapytań](https://s1n1st3r.gitbook.io/theb10g/graphql-query-authentication-bypass-vuln) może ominąć słaby system uwierzytelniania.
|
|
|
|
W poniższym przykładzie można zobaczyć, że operacja to "forgotPassword" i powinna wykonać tylko związane z nią zapytanie forgotPassword. Można to ominąć dodając zapytanie na końcu, w tym przypadku dodajemy "register" i zmienną użytkownika, aby system zarejestrował nowego użytkownika.
|
|
|
|
<figure><img src="../../.gitbook/assets/GraphQLAuthBypassMethod.PNG" alt=""><figcaption></figcaption></figure>
|
|
|
|
## Ominięcie limitów szybkości za pomocą aliasów w GraphQL
|
|
|
|
W GraphQL aliasy są potężną funkcją, która pozwala **nazwać właściwości jawnie** podczas wysyłania żądania API. Ta funkcjonalność jest szczególnie przydatna do pobierania **wielu instancji tego samego typu** obiektu w jednym żądaniu. Aliasy mogą być wykorzystane do pokonania ograniczenia, które uniemożliwia obiektom GraphQL posiadanie wielu właściwości o tej samej nazwie.
|
|
|
|
Dla szczegółowego zrozumienia aliasów w GraphQL, zaleca się skorzystanie z następującego źródła: [Aliasy](https://portswigger.net/web-security/graphql/what-is-graphql#aliases).
|
|
|
|
Podstawowym celem aliasów jest zmniejszenie konieczności wykonywania licznych wywołań API, jednak zidentyfikowano niezamierzone zastosowanie, gdzie aliasy mogą być wykorzystane do przeprowadzania ataków brutalnej siły na punkt końcowy GraphQL. Jest to możliwe, ponieważ niektóre punkty końcowe są chronione przez limity szybkości zaprojektowane do powstrzymywania ataków brutalnej siły poprzez ograniczenie **liczby żądań HTTP**. Niemniej jednak te limity szybkości mogą nie uwzględniać liczby operacji w każdym żądaniu. Ponieważ aliasy pozwalają na dodanie wielu zapytań w jednym żądaniu HTTP, mogą one obejść takie środki ograniczające szybkość.
|
|
|
|
Rozważ poniższy przykład, który ilustruje, jak zapytania z aliasami mogą być użyte do weryfikacji poprawności kodów rabatowych sklepu. Ta metoda mogłaby ominąć limity szybkości, ponieważ kompiluje kilka zapytań w jedno żądanie HTTP, potencjalnie umożliwiając weryfikację licznych kodów rabatowych jednocześnie.
|
|
```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
|
|
}
|
|
}
|
|
```
|
|
## Narzędzia
|
|
|
|
### Skanery podatności
|
|
|
|
* [https://github.com/gsmith257-cyber/GraphCrawler](https://github.com/gsmith257-cyber/GraphCrawler): Zestaw narzędzi, który można użyć do pobierania schematów i wyszukiwania danych wrażliwych, testowania autoryzacji, atakowania schematów siłowego, oraz znajdowania ścieżek do określonego typu.
|
|
* [https://blog.doyensec.com/2020/03/26/graphql-scanner.html](https://blog.doyensec.com/2020/03/26/graphql-scanner.html): Może być używany samodzielnie lub jako [rozszerzenie dla Burp](https://github.com/doyensec/inql).
|
|
* [https://github.com/swisskyrepo/GraphQLmap](https://github.com/swisskyrepo/GraphQLmap): Może być używany jako klient CLI do automatyzacji ataków.
|
|
* [https://gitlab.com/dee-see/graphql-path-enum](https://gitlab.com/dee-see/graphql-path-enum): Narzędzie, które wymienia różne sposoby dotarcia do określonego typu w schemacie GraphQL.
|
|
* [https://github.com/doyensec/inql](https://github.com/doyensec/inql): Rozszerzenie dla Burp do zaawansowanego testowania GraphQL. Komponent _**Scanner**_ stanowi rdzeń InQL v5.0, gdzie można analizować punkt końcowy GraphQL lub lokalny plik z introspekcją schematu. Automatycznie generuje wszystkie możliwe zapytania i mutacje, organizując je w strukturalny widok do analizy. Komponent _**Attacker**_ pozwala uruchamiać wsadowe ataki GraphQL, co może być przydatne do omijania słabo zaimplementowanych limitów szybkości.
|
|
|
|
### Klienci
|
|
|
|
* [https://github.com/graphql/graphiql](https://github.com/graphql/graphiql): Klient GUI
|
|
* [https://altair.sirmuel.design/](https://altair.sirmuel.design/): Klient GUI
|
|
|
|
### Automatyczne testy
|
|
|
|
{% embed url="https://graphql-dashboard.herokuapp.com/" %}
|
|
|
|
* Film wyjaśniający AutoGraphQL: [https://www.youtube.com/watch?v=JJmufWfVvyU](https://www.youtube.com/watch?v=JJmufWfVvyU)
|
|
|
|
## Odnośniki
|
|
|
|
* [**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>Naucz się hakować AWS od zera do bohatera z</strong> <a href="https://training.hacktricks.xyz/courses/arte"><strong>htARTE (HackTricks AWS Red Team Expert)</strong></a><strong>!</strong></summary>
|
|
|
|
Inne sposoby wsparcia HackTricks:
|
|
|
|
* Jeśli chcesz zobaczyć swoją **firmę reklamowaną w HackTricks** lub **pobrać HackTricks w formacie PDF** sprawdź [**PLANY SUBSKRYPCYJNE**](https://github.com/sponsors/carlospolop)!
|
|
* Zdobądź [**oficjalne gadżety PEASS & HackTricks**](https://peass.creator-spring.com)
|
|
* Odkryj [**Rodzinę PEASS**](https://opensea.io/collection/the-peass-family), naszą kolekcję ekskluzywnych [**NFT**](https://opensea.io/collection/the-peass-family)
|
|
* **Dołącz do** 💬 [**grupy Discord**](https://discord.gg/hRep4RUj7f) lub [**grupy telegramowej**](https://t.me/peass) lub **śledź** nas na **Twitterze** 🐦 [**@carlospolopm**](https://twitter.com/hacktricks\_live)**.**
|
|
* **Podziel się swoimi sztuczkami hakerskimi, przesyłając PR-y do** [**HackTricks**](https://github.com/carlospolop/hacktricks) i [**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud) github repos.
|
|
|
|
</details>
|