Featured image of post GraphQL渗透

GraphQL渗透

GraphQL渗透

参考:浅谈GraphQL渗透测试 - Yuy0ung - 博客园

渗透测试之graphQL_graphql 漏洞-CSDN博客

基本概念

GraphQL是一个用于 API的查询语言,下面我用通俗的语言来谈谈它的特点:

简单说,GraphQL 是一种“灵活取数据”的工具。比如你点外卖时,可以自由组合菜品,而不用按固定套餐点。GraphQL 的作用类似:前端可以按需“点”数据,后端精准返回

那么这里就可以看出GraphQL与传统Rest API的区别了,我们同样以”点外卖“来描述:

  • Rest API:固定套餐,比如

    • 套餐A:用户信息(姓名、头像)
    • 套餐B:用户的朋友列表
    • 套餐C:用户的订单记录

    那么可以看出来它的局限:如果你想同时要“用户姓名”和“朋友列表”,得点两次套餐(发两次请求),或者让后端临时做个新套餐(改接口)

  • GraphQL:自助餐,想要什么直接通过接口告诉后端,比如下面这个请求:

    1
    2
    3
    4
    5
    6
    7
    8
    
    query {
      user {
        name          # 只要姓名
        friends {     # 和朋友列表
          name
        }
      }
    }
    

    这个请求能够,一次精准拿到 namefriends 数据,不会多拿(比如不需要头像),也不会少拿

通过上面的描述,我们能够知道GraphQL API与Rest API最大的区别:GraphQL 通过将数据查询和数据修改分离开来,使得客户端能够更灵活地控制所需数据的粒度和类型,并且在多个资源之间建立关系

GraphQL查询方式

根据官方文档,主要的操作类型有三种:query(查询)、mutation(变更)、subscription(订阅),最常用的就是query,所有的查询都需要操作类型,除了简写查询语法。

类型语言TypeLanguage,type来定义对象的类型和字段,理解成一个数据结构,可以无关实现graphQL的语言类型。类型语言包括Scalar(标量)和Object(对象)两种。并且支持接口抽象类型。

Schema用于描述数据逻辑,Schema就是对象的合计,其中定义的大部分为普通对象类型。一定包括query,可能包含mutation,作为一个GraphQL的查询入口。

Resolver用于实现解析逻辑,当一个字段被执行时,相应的 resolver 被调用以产生下一个值。

query

用于获取数据,只读取不修改:

1
2
3
4
5
query 操作名称(可选参数) {
  字段名(参数) {
    子字段
  }
}

mutation

用于修改数据,属于写操作,会改变服务器状态:

1
2
3
4
5
mutation 操作名称(输入参数) {
  操作名(输入) {
    返回的字段
  }
}

Subscription

实时监听数据变化(类似 WebSocket),属于长连接,服务器主动推送数据:

1
2
3
4
5
subscription 操作名称 {
  监听的事件名 {
    返回的字段
  }
}

举个query查询的例子

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# 查询用户信息及其文章标题
query GetUserWithPosts($userId: ID!) {
  user(id: $userId) {
    name
    email
    posts(limit: 5) {
      title
      createdAt
    }
  }
}

那么响应格式为:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
{
  "data": {
    "user": {
      "name": "小明",
      "email": "xiaoming@example.com",
      "posts": [
        { "title": "GraphQL 教程", "createdAt": "2023-01-01" }
      ]
    }
  }
}

GraphQL API探测(实战)

常见路径

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
/graphql
/graphiql
/gql
/query
/graph
/v1/graphql
/v2/graphql
/v3/graphql
/v1/graphiql
/v2/graphiql
/v3/graphiql
/api/graphql
/api/graphiql
/graphql/api
/v1/api/graphql
/v2/api/graphql
/console
/playground
/graphql/console
/graphql-devtools
/graphql-explorer
/graphql-playground
/graphql-playground-html
/laravel-graphql-playground
/graphql.php                  # PHP 实现
/index.php?graphql            # PHP 参数化
/HyperGraphQL                 # 特定框架
/portal-graphql
/graphql/schema.json          # Schema 文件
/graphql/schema.xml
/graphql/schema.yaml
......

或者用通用payload测试,比如query{__typename}

我们可以使用fofa随便找一个使用了GraphQL的网站,首先用fofa语法搜集graphql报错的网页:

1
body="Must provide query string" || body="GraphQL validation failed"

我这里找到的都是米国的,因为其实国外用的比较多

image-20250813153725208

然后可以看到没有传入参数报错了

image-20250813154010137

然后打payload测试

image-20250813154233551

接下来我们可以进一步查询,这里用到bp的一款插件InQL,它可以帮我们自动化扫描生成payload

image-20250813171703224

我们先手动用__schema字段进行内省查询

查询接口中的可用类型:

1
2
3
4
5
6
7
query {
  __schema {
    types {
      name
    }
  }
}

image-20250813154921470

查询类型所有字段:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
{
  __type (name: "Query") {
    name
    fields {
      name
      type {
        name
        kind
        ofType {
          name
          kind
        }
      }
    }
  }
}

更详细的架构信息:

1
query {__schema{types{name,fields{name,args{name,description,type{name,kind,ofType{name, kind}}}}}}}

image-20250813155058076

信息泄露/越权

不当的配置可能导致网站的graphql架构以及存储的敏感数据暴露

有的API使用内省查询可以列出列出GraphQL中所有Query、Mutation、ObjectType、Field、Arguments:

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
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
                    ofType {
                      kind
                      name
                      ofType {
                        kind
                        name
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    }

image-20250813155353968

可以丢到GraphQL Voyager生成可视化文档

在列出的信息中,寻找敏感信息比如email、password、secretKey、token、licenseKey、session,还可以多多关注废弃的字段(deprecated fields),除此以外还可以搜索类型中是否有edit、delete、remove、add等功能,来达到数据编辑、删除、添加的功能。

我们上面查询的是有挺多password的,试试有没有鉴权,不会构造语句直接问gpt

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
query PoC_ObservationImages {
  serviceRequestReportObservationImages(observationId: "1") {
    id
    imageUrl
    detail
    createdBy {
      id
      email
      password
    }
    updatedBy {
      id
      email
    }
    createdAt
  }
}

image-20250813165910288

测试了是没有的,如果有可能会有信息泄露,测试了一堆payload最后喜提封ip

GraphQL注入

相信在介绍GraphQL的时候也能注意到,它的功能其实有点类似于SQL的增删改查,这个,而这个GraphQL注入其实也和SQL注入类似

p神之前在先知说过

image-20250813170630630

漏洞的出现场景大致为graphql语句不可控,但语句中的某一部分参数用户可控,网页逻辑大致为:

用户访问URL -> 前端获取参数 -> 拼接成GraphQL语句 -> 发送 -> 后端执行

在这种情况下,就可以尝试GraphQL注入改变原本的GraphQL语义进行漏洞利用

除了注入还有可能出现CSRF,不过基本上不可能实现

bp靶场练习

GraphQL API vulnerabilities | Web Security Academy

比较简单,主要是介绍InQL这个自动化插件的用法,没什么意思

使用 Hugo 构建
主题 StackJimmy 设计