参考文章:
- 发现GraphQL端点和SQL注入漏洞
- 玩转graphQL
- GraphQL(一)基础介绍及应用示例
- Attacking GraphQL——从DVGA靶场学习GraphQL安全
- 攻击GraphQL
- GraphQL安全指北
- 我的GraphQL安全学习之旅
源起
题目来源于 NewStarCTF 的 WEEK2-ezAPI
通过 POST 不同的id
值来得到相应的查询信息,最多可以查到 6 ,再往后就是返回 DEBUG 信息
一开始想的是不是存在 SQL 注入,但是尝试过后发现返回的是Hacker! Only Number!
,想着应该是做了is_numric($id)
处理;尝试着换个思路,发现存在备份文件www.zip
打开index.php
发现使用了GraphQL
<?php
...
$result = file_get_contents("http://graphql:8080/v1/graphql", false, $context);
if (isset($id)) {
if (waf($id)) {
isset($_POST['data']) ? $data = $_POST['data'] : $data = '{"query":"query{\nusers_user_by_pk(id:' . $id . ') {\nname\n}\n}\n", "variables":null}';
$res = json_decode(send($data));
if ($res->data->users_user_by_pk->name !== NULL) {
echo "ID: " . $id . "<br>Name: " . $res->data->users_user_by_pk->name;}
else {
echo "<b>Can't found it!</b><br><br>DEBUG: ";
var_dump($res->data);
}
} else {
...
}
...
?>
先查下中文文档,里面有关于GraphQL
的表述
GraphQL 是一个用于 API 的查询语言,是一个使用基于类型系统来执行查询的服务端运行时(类型系统由你的数据定义)。GraphQL 并没有和任何特定数据库或者存储引擎绑定,而是依靠你现有的代码和数据支撑。
多数文章里都是将GraphQL
与REST API
进行比较,前者可以不用维护过多的API
,只需要一个URL
就可以处理不同的请求,改变POST
传输的值即可,下图也很好的解释了前者相较于后者的优点
GraphQL
提供了一种Introspection
机制
We designed the type system, so we know what types are available, but if we didn’t, we can ask GraphQL, by querying the __schema field, always available on the root type of a Query. Let’s do so now, and ask what types are available.
在内省的机制下,可以利用schema
和type
查询出可用的对象和对象的所有字段;但是如果配置不当,很容易造成信息泄漏
在 InQL Scanner 可以找到用于查询的 payload
,返回包返回的就是该API端点的所有信息
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 ofType{kind name ofType{kind name ofType{kind name}}}}}}}}"
在题目里直接传入$data
,用Postman
发包可能方便点
object(stdClass)#181 (8) {
["kind"]=>
string(6) "OBJECT"
["name"]=>
string(28) "ffffllllaaagggg_1n_h3r3_flag"
["description"]=>
string(59) "columns and relationships of "ffffllllaaagggg_1n_h3r3.flag""
["fields"]=>
array(1) {
[0]=>
object(stdClass)#182 (6) {
["name"]=>
string(4) "flag"
["description"]=>
NULL
["args"]=>
array(0) {
}
["type"]=>
object(stdClass)#183 (3) {
["kind"]=>
string(8) "NON_NULL"
["name"]=>
NULL
["ofType"]=>
object(stdClass)#184 (3) {
["kind"]=>
string(6) "SCALAR"
["name"]=>
string(6) "String"
["ofType"]=>
NULL
}
}
["isDeprecated"]=>
bool(false)
["deprecationReason"]=>
NULL
}
}
["inputFields"]=>
NULL
["interfaces"]=>
array(0) {
}
["enumValues"]=>
NULL
["possibleTypes"]=>
NULL
}
所以直接到ffffllllaaagggg_1n_h3r3_flag
去查flag
就可以了
Every GraphQL service has aquery
type and may or may not have amutation
type. These types are the same as a regular object type, but they are special because they define the entry point of every GraphQL query
$data={"query":"query{ffffllllaaagggg_1n_h3r3_flag{flag}}"}
学习 && 靶场练习
往届各类 CTF 比赛中也出现过不少GraphQL
的考点,p神在先知白帽大会上也发表了相关安全议题
1.注入
如果没有对用户输入的参数值进行合理处理而且选择了拼接的方式进行查询,有可能就会导致攻击者注入恶意语句从而改变GraphQL
的语句结构
危害性比较大的一个实例就是 CVE-2020-9483 Apache SkyWalking graphql SQL注入漏洞
在 Fix 中可以比对修复前后的代码
可以看到,在原版本中idValues
是直接拼接过去的,修复后的代码引入了?
进行占位预编译
此外,在 LogQuery
也 remove了String metricName
虽然有注入类的问题,但是在正常的业务中应该不会遇到很多,参数化查询的引入可以很好的解决可能出现的安全问题
用 Vulhub 搭建漏洞环境复现下:
{
"query":"query queryLogs($condition: LogQueryCondition) {
queryLogs(condition: $condition) {
total
logs {
serviceId
serviceName
isError
content
}
}
}
",
"variables":{
"condition":{
"metricName":"1ndweb",
"endpointId":"1",
"traceId":"1",
"state":"ALL",
"stateCode":"1",
"paging":{
"pageSize":10
}
}
}
}
此时我们传入的metricName
参数的值被拼接到from
后面,如果拼接恶意查询语句即可造成注入
"metricName":"INFORMATION_SCHEMA.USERS union all select h2version())a where 1=? or 1=? or 1=? --"
当然,也可以利用file_write
写入恶意类,再利用LINK_SCHEMA
加载恶意类从而造成 RCE
具体操作可参考 Apache Skywalking <=8.3 SQL注入分析复现
2.信息泄漏
在实际业务场景中,出现相对较多的应该还是权限配置不当导致的信息泄漏和拒绝服务攻击,当然出现信息泄露本身和 API 调用没有关系,更多的是调用时的鉴权没有做好
在文档的 Authorization 部分GraphQL
也给出了建议
Delegate authorization logic to the business logic layer
如果开发人员没有做好各个Query
和Mutation
的授权鉴权,攻击者很容易利用GraphQL
的内省机制,直接获取后端定义的所有接口信息
CVE-2020-26413 GitLab Graphql邮箱信息泄露漏洞 就是在已知用户名的前提下,利用Graphql
进行查询造成邮箱泄露
但是如果没有已知的用户名,也可以利用GraphqQL
查询一次性得到所有的用户名和邮箱,具体的 payload 可参考 PeiQi文库
除此之外,在p神的文档中也提到了
多多关注废弃的字段(deprecated fields)多多关注废弃的字段(deprecated fields)
如果开发人员没有对废弃字段进行合理的处理,通过指定includeDeprecated
参数为true
,__type
仍然可以将废弃字段暴露出来,并有可能继续利用这些字段进行查询
解决方案可以参考GraphQL安全指北
3.拒绝服务攻击
拒绝服务攻击主要体现在GraphQL
对象间包含组合的嵌套关系,如果不对嵌套深度进行限制,就会被攻击者利用进行拒绝服务攻击
4.DVGA 靶场
(1)graphw00f
graphw00f is GraphQL Server Engine Fingerprinting utility for software security professionals looking to learn more about what technology is behind a given GraphQL endpoint.
python main.py -d -t http://ip
这个主要是去遍历 detect,如果确实使用了GraphQL
会给出路径
python main.py -f -t http://ip/{graphql}
这个就是把上一个得到的路径进行指纹识别
Point graphw00f at DVGA to figure out what technology it’s running.
链接里会给出当前Engine
的 Security Considerations 作为参考
(2)Batch Query Attack
GraphQL supports Request Batching. Batched requests are processed one after the other by GraphQL, which makes it a good candidate for Denial of Service attacks, as well as other attacks such as Brute Force and Enumeration.
If a resource intensive GraphQL query is identified, an attacker may leverage batch processing to call the query and potentially overwhelm the service for a prolonged period of time.
The query systemUpdate seems to be taking a long time to complete, and can be used to overwhelm the server by batching a system update request query.
这个其实很容易理解,GraphQL
支持批处理查询,可以在一个POST
的请求下面发送多个查询,这样就可能会导致出现拒绝服务攻击和暴力破解,比如给出的systemUpdate
可以想象到如果一次性发送多个systemUpdate
进行批处理时返回响应包需要多少时间
同样的,如果对于用户登录等需要身份验证鉴权的操作没有进行次数限制,很容易造成暴力破解,因为GraphQL
的批处理会把这样的请求当成正常的操作,也不会造成请求时间的倍增
(3)Deep Recursion Query Attack
In GraphQL, when types reference eachother, it is often possible to build a circular query that grows exponentially to a point it could bring the server down to its knees. Countermeasures such as max_depth can help mitigate these types of attacks.
当查询出现互相引用嵌套时,可能会出现深度递归查询攻击,当嵌套层数足够多时,就会引起服务器的崩溃
避免此问题我们需要在GraphQL
服务器上限制查询深度,同时在设计GraphQL
接口时应尽量避免出现此类问题
(4)Resource Intensive Query Attack
Sometimes, certain queries may be computationally more expensive than others. A query may include certain fields that would trigger more complex backend logic in order to fulfill the query resolution. As attackers, we can abuse it by calling these actions frequently in order to cause resource exhaustion.
这个就像刚才测试的systemUpdate
,这种查询会触发更复杂的后端逻辑以实现查询解析,如果多次请求就会造成资源耗尽;缓解措施就是利用Query Cost Analysis
,通过设置一个upper threshold
来拒绝高额资源消耗的查询或是避免在同一时间内有重复的类似请求
(5)Information Disclosure
内省查询在刚刚的 CTF 比赛题目里已经见到了,可以将返回结果粘贴到 GraphiQL 里进行可视化的查看
靶场里还提到了一种信息泄漏是GraphQL Field Suggestions
,这个主要是用在当内省查询不被允许时,可以输入一个错误的字段,然后GraphQL
会返回相似的字段;这个可发挥的空间应该不是很大🤔
(6)Code Execution && Injection
代码执行和注入这两部分在真实业务环境中应该不会遇到,应该是靶场提供的特有环境,可以适当的玩一玩
具体的可以参考以下两篇文章: