GraphQL
Last updated
Last updated
Learn & practice AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE) Learn & practice GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
GraphQL è evidenziato come un alternativa efficiente alle API REST, offrendo un approccio semplificato per interrogare i dati dal backend. A differenza di REST, che spesso richiede numerose richieste attraverso vari endpoint per raccogliere dati, GraphQL consente di recuperare tutte le informazioni necessarie tramite una singola richiesta. Questa semplificazione beneficia notevolmente gli sviluppatori riducendo la complessità dei loro processi di recupero dati.
Con l'avvento di nuove tecnologie, inclusa GraphQL, emergono anche nuove vulnerabilità di sicurezza. Un punto chiave da notare è che GraphQL non include meccanismi di autenticazione per impostazione predefinita. È responsabilità degli sviluppatori implementare tali misure di sicurezza. Senza una corretta autenticazione, gli endpoint GraphQL possono esporre informazioni sensibili a utenti non autenticati, ponendo un rischio significativo per la sicurezza.
Per identificare le istanze GraphQL esposte, si raccomanda di includere percorsi specifici negli attacchi di brute force alle directory. Questi percorsi sono:
/graphql
/graphiql
/graphql.php
/graphql/console
/api
/api/graphql
/graphql/api
/graphql/graphql
Identificare le istanze GraphQL aperte consente di esaminare le query supportate. Questo è cruciale per comprendere i dati accessibili tramite l'endpoint. Il sistema di introspezione di GraphQL facilita questo fornendo dettagli sulle query supportate da uno schema. Per ulteriori informazioni su questo, fare riferimento alla documentazione di GraphQL sull'introspezione: GraphQL: A query language for APIs.
Lo strumento graphw00f è in grado di rilevare quale motore GraphQL viene utilizzato in un server e poi stampa alcune informazioni utili per l'auditor di sicurezza.
Per controllare se un URL è un servizio GraphQL, può essere inviata una query universale, query{__typename}
. Se la risposta include {"data": {"__typename": "Query"}}
, conferma che l'URL ospita un endpoint GraphQL. Questo metodo si basa sul campo __typename
di GraphQL, che rivela il tipo dell'oggetto interrogato.
Graphql di solito supporta GET, POST (x-www-form-urlencoded) e POST(json). Anche se per motivi di sicurezza è consigliato consentire solo json per prevenire attacchi CSRF.
Per utilizzare l'introspezione per scoprire informazioni sullo schema, interroga il campo __schema
. Questo campo è disponibile sul tipo radice di tutte le query.
Con questa query troverai il nome di tutti i tipi utilizzati:
Con questa query puoi estrarre tutti i tipi, i loro campi e i loro argomenti (e il tipo degli argomenti). Questo sarà molto utile per sapere come interrogare il database.
Errori
È interessante sapere se gli errori verranno mostrati poiché contribuiranno con informazioni utili.
Enumerare lo Schema del Database tramite Introspezione
Se l'introspezione è abilitata ma la query sopra non viene eseguita, prova a rimuovere le direttive onOperation
, onFragment
e onField
dalla struttura della query.
Query di introspezione inline:
L'ultima riga di codice è una query graphql che estrarrà tutte le meta-informazioni dal graphql (nomi degli oggetti, parametri, tipi...)
Se l'introspezione è abilitata, puoi utilizzare GraphQL Voyager per visualizzare in un'interfaccia grafica tutte le opzioni.
Ora che sappiamo che tipo di informazioni sono salvate nel database, proviamo a estrarre alcuni valori.
Nell'introspezione puoi trovare quale oggetto puoi interrogare direttamente (perché non puoi interrogare un oggetto solo perché esiste). Nell'immagine seguente puoi vedere che il "queryType" si chiama "Query" e che uno dei campi dell'oggetto "Query" è "flags", che è anche un tipo di oggetto. Pertanto, puoi interrogare l'oggetto flag.
Nota che il tipo della query "flags" è "Flags", e questo oggetto è definito come segue:
Puoi vedere che gli oggetti "Flags" sono composti da name e value. Quindi puoi ottenere tutti i nomi e i valori dei flag con la query:
Nota che nel caso in cui l'oggetto da interrogare sia un tipo primitivo come stringa come nel seguente esempio
Puoi semplicemente interrogarlo con:
In un altro esempio in cui c'erano 2 oggetti all'interno dell'oggetto di tipo "Query": "user" e "users". Se questi oggetti non necessitano di alcun argomento per la ricerca, potresti recuperare tutte le informazioni da essi semplicemente chiedendo i dati che desideri. In questo esempio da Internet potresti estrarre i nomi utente e le password salvate:
Tuttavia, in questo esempio se provi a farlo ottieni questo errore:
Sembra che in qualche modo cercherà utilizzando l'argomento "uid" di tipo Int.
Comunque, già sapevamo che, nella sezione Basic Enumeration era stata proposta una query che ci mostrava tutte le informazioni necessarie: query={__schema{types{name,fields{name, args{name,description,type{name, kind, ofType{name, kind}}}}}}}
Se leggi l'immagine fornita quando eseguo quella query vedrai che "user" aveva l'arg "uid" di tipo Int.
Quindi, eseguendo un leggero uid bruteforce ho scoperto che in uid=1 è stato recuperato un nome utente e una password:
query={user(uid:1){user,password}}
Nota che ho scoperto che potevo chiedere i parametri "user" e "password" perché se provo a cercare qualcosa che non esiste (query={user(uid:1){noExists}}
) ottengo questo errore:
E durante la fase di enumerazione ho scoperto che l'oggetto "dbuser" aveva come campi "user" e "password.
Trucco di dump della stringa di query (grazie a @BinaryShadow_)
Se puoi cercare per un tipo di stringa, come: query={theusers(description: ""){username,password}}
e cerchi una stringa vuota esso dump tutte le informazioni. (Nota che questo esempio non è correlato all'esempio dei tutorial, per questo esempio supponi di poter cercare utilizzando "theusers" tramite un campo String chiamato "description").
In questa configurazione, un database contiene persone e film. Le persone sono identificate dalla loro email e nome; i film dal loro nome e valutazione. Le persone possono essere amiche tra loro e avere anche film, indicando relazioni all'interno del database.
Puoi cercare persone per il nome e ottenere le loro email:
Puoi cercare persone per il nome e ottenere i loro film sottoscritti:
Nota come è indicato per recuperare il name
dei subscribedMovies
della persona.
Puoi anche cercare più oggetti contemporaneamente. In questo caso, viene effettuata una ricerca di 2 film:
O anche relazioni di diversi oggetti utilizzando alias:
Le mutazioni sono utilizzate per apportare modifiche lato server.
Nell'introspezione puoi trovare le mutazioni dichiarate. Nell'immagine seguente, il "MutationType" è chiamato "Mutation" e l'oggetto "Mutation" contiene i nomi delle mutazioni (come "addPerson" in questo caso):
In questa configurazione, un database contiene persone e film. Le persone sono identificate dalla loro email e nome; i film dal loro nome e valutazione. Le persone possono essere amiche tra loro e avere anche film, indicando relazioni all'interno del database.
Una mutazione per creare nuovi film all'interno del database può essere simile alla seguente (in questo esempio la mutazione è chiamata addMovie
):
Nota come sia i valori che il tipo di dati sono indicati nella query.
Inoltre, il database supporta un'operazione di mutazione, chiamata addPerson
, che consente la creazione di persone insieme alle loro associazioni con amici e film esistenti. È fondamentale notare che gli amici e i film devono esistere nel database prima di collegarli alla persona appena creata.
Come spiegato in una delle vulnerabilità descritte in questo rapporto, un overload di direttiva implica la chiamata di una direttiva anche milioni di volte per far sprecare operazioni al server fino a quando non è possibile effettuare un DoS.
Queste informazioni sono state tratte da https://lab.wallarm.com/graphql-batching-attack/. Autenticazione tramite API GraphQL con invio simultaneo di molte query con credenziali diverse per verificarlo. È un attacco di brute force classico, ma ora è possibile inviare più di una coppia login/password per richiesta HTTP grazie alla funzionalità di batching di GraphQL. Questo approccio ingannerebbe le applicazioni esterne di monitoraggio del tasso, facendole pensare che tutto va bene e che non ci sia un bot di brute-forcing che cerca di indovinare le password.
Di seguito puoi trovare la dimostrazione più semplice di una richiesta di autenticazione dell'applicazione, con 3 coppie di email/password diverse alla volta. Ovviamente è possibile inviare migliaia in una singola richiesta nello stesso modo:
Come possiamo vedere dallo screenshot della risposta, la prima e la terza richiesta hanno restituito null e riflettevano le informazioni corrispondenti nella sezione error. La seconda mutazione aveva i dati di autenticazione corretti e la risposta ha il token di sessione di autenticazione corretto.
Sempre più endpoint graphql stanno disabilitando l'introspezione. Tuttavia, gli errori che graphql genera quando viene ricevuta una richiesta inaspettata sono sufficienti per strumenti come clairvoyance per ricreare la maggior parte dello schema.
Inoltre, l'estensione di Burp Suite GraphQuail osserva le richieste API GraphQL che passano attraverso Burp e costruisce uno schema GraphQL interno con ogni nuova query che vede. Può anche esporre lo schema per GraphiQL e Voyager. L'estensione restituisce una risposta falsa quando riceve una query di introspezione. Di conseguenza, GraphQuail mostra tutte le query, gli argomenti e i campi disponibili per l'uso all'interno dell'API. Per ulteriori informazioni controlla questo.
Una bella wordlist per scoprire entità GraphQL può essere trovata qui.
Per bypassare le restrizioni sulle query di introspezione nelle API, inserire un carattere speciale dopo la parola chiave __schema
si è dimostrato efficace. Questo metodo sfrutta le comuni distrazioni degli sviluppatori nei modelli regex che mirano a bloccare l'introspezione concentrandosi sulla parola chiave __schema
. Aggiungendo caratteri come spazi, nuove righe e virgole, che GraphQL ignora ma che potrebbero non essere considerati nel regex, le restrizioni possono essere eluse. Ad esempio, una query di introspezione con una nuova riga dopo __schema
può bypassare tali difese:
Se non hai successo, considera metodi di richiesta alternativi, come GET requests o POST con x-www-form-urlencoded
, poiché le restrizioni potrebbero applicarsi solo alle richieste POST.
Come menzionato in questo intervento, verifica se potrebbe essere possibile connettersi a graphQL tramite WebSockets, poiché ciò potrebbe consentirti di bypassare un potenziale WAF e far sì che la comunicazione websocket riveli lo schema del graphQL:
Quando l'introspezione è disabilitata, esaminare il codice sorgente del sito web per query precaricate nelle librerie JavaScript è una strategia utile. Queste query possono essere trovate utilizzando la scheda Sources
negli strumenti per sviluppatori, fornendo informazioni sullo schema dell'API e rivelando potenzialmente query sensibili esposte. I comandi per cercare all'interno degli strumenti per sviluppatori sono:
Se non sai cos'è il CSRF, leggi la pagina seguente:
Là fuori puoi trovare diversi endpoint GraphQL configurati senza token CSRF.
Nota che le richieste GraphQL vengono solitamente inviate tramite richieste POST utilizzando il Content-Type application/json
.
Tuttavia, la maggior parte degli endpoint GraphQL supporta anche form-urlencoded
richieste POST:
Pertanto, poiché le richieste CSRF come quelle precedenti vengono inviate senza richieste preflight, è possibile eseguire modifiche nel GraphQL abusando di un CSRF.
Tuttavia, nota che il nuovo valore predefinito del cookie per il flag samesite
di Chrome è Lax
. Ciò significa che il cookie verrà inviato solo da un sito web di terze parti in richieste GET.
Nota che è solitamente possibile inviare la richiesta query anche come richiesta GET e il token CSRF potrebbe non essere convalidato in una richiesta GET.
Inoltre, abusando di un XS-Search attacco potrebbe essere possibile esfiltrare contenuti dall'endpoint GraphQL abusando delle credenziali dell'utente.
Per ulteriori informazioni controlla il post originale qui.
Simile alle vulnerabilità CRSF che abusano di GraphQL, è anche possibile eseguire un hijacking WebSocket cross-site per abusare di un'autenticazione con GraphQL con cookie non protetti e far eseguire all'utente azioni inaspettate in GraphQL.
Per ulteriori informazioni controlla:
Molte funzioni GraphQL definite sull'endpoint potrebbero controllare solo l'autenticazione del richiedente ma non l'autorizzazione.
Modificare le variabili di input della query potrebbe portare a dettagli sensibili dell'account leaked.
Le mutazioni potrebbero persino portare a un takeover dell'account tentando di modificare i dati di un altro account.
Chaining queries insieme può bypassare un sistema di autenticazione debole.
Nell'esempio sottostante puoi vedere che l'operazione è "forgotPassword" e che dovrebbe eseguire solo la query forgotPassword associata. Questo può essere bypassato aggiungendo una query alla fine, in questo caso aggiungiamo "register" e una variabile utente affinché il sistema registri un nuovo utente.
In GraphQL, gli alias sono una funzionalità potente che consente di nominare esplicitamente le proprietà quando si effettua una richiesta API. Questa capacità è particolarmente utile per recuperare più istanze dello stesso tipo di oggetto all'interno di una singola richiesta. Gli alias possono essere utilizzati per superare la limitazione che impedisce agli oggetti GraphQL di avere più proprietà con lo stesso nome.
Per una comprensione dettagliata degli alias GraphQL, si consiglia la seguente risorsa: Aliases.
Sebbene lo scopo principale degli alias sia ridurre la necessità di numerose chiamate API, è stato identificato un caso d'uso non intenzionale in cui gli alias possono essere sfruttati per eseguire attacchi di forza bruta su un endpoint GraphQL. Questo è possibile perché alcuni endpoint sono protetti da limitatori di velocità progettati per ostacolare gli attacchi di forza bruta limitando il numero di richieste HTTP. Tuttavia, questi limitatori di velocità potrebbero non tenere conto del numero di operazioni all'interno di ciascuna richiesta. Dato che gli alias consentono l'inclusione di più query in una singola richiesta HTTP, possono eludere tali misure di limitazione della velocità.
Considera l'esempio fornito di seguito, che illustra come le query con alias possono essere utilizzate per verificare la validità dei codici sconto del negozio. Questo metodo potrebbe eludere la limitazione della velocità poiché compila diverse query in una sola richiesta HTTP, consentendo potenzialmente la verifica di numerosi codici sconto simultaneamente.
https://github.com/dolevf/graphql-cop: Testa le configurazioni errate comuni degli endpoint graphql
https://github.com/assetnote/batchql: Script di auditing della sicurezza GraphQL con un focus sull'esecuzione di query e mutazioni batch GraphQL.
https://github.com/dolevf/graphw00f: Fingerprint del graphql in uso
https://github.com/gsmith257-cyber/GraphCrawler: Toolkit che può essere utilizzato per acquisire schemi e cercare dati sensibili, testare l'autorizzazione, forzare schemi e trovare percorsi per un dato tipo.
https://blog.doyensec.com/2020/03/26/graphql-scanner.html: Può essere utilizzato come standalone o estensione Burp.
https://github.com/swisskyrepo/GraphQLmap: Può essere utilizzato anche come client CLI per automatizzare attacchi
https://gitlab.com/dee-see/graphql-path-enum: Strumento che elenca i diversi modi per raggiungere un dato tipo in uno schema GraphQL.
https://github.com/doyensec/GQLSpection: Il successore delle modalità Standalone e CLI di InQL
https://github.com/doyensec/inql: Estensione Burp per test avanzati di GraphQL. Lo Scanner è il nucleo di InQL v5.0, dove puoi analizzare un endpoint GraphQL o un file di schema di introspezione locale. Genera automaticamente tutte le possibili query e mutazioni, organizzandole in una vista strutturata per la tua analisi. Il componente Attacker ti consente di eseguire attacchi batch GraphQL, che possono essere utili per eludere limiti di velocità mal implementati.
https://github.com/nikitastupin/clairvoyance: Prova a ottenere lo schema anche con l'introspezione disabilitata utilizzando l'aiuto di alcuni database Graphql che suggeriranno i nomi delle mutazioni e dei parametri.
https://github.com/graphql/graphiql: Client GUI
https://altair.sirmuel.design/: Client GUI
Video che spiega AutoGraphQL: https://www.youtube.com/watch?v=JJmufWfVvyU
Learn & practice AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE) Learn & practice GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)