abraxaslabs.tech · github.com/abraxas · @abraxas_null · [email protected] · gitlab-webhook-0net-ssrf
GitLab CE 19.4.1 - GitLab Inc.
Gitlab::HTTP_V2::UrlBlocker 会拦截精确的 0.0.0.0、:: 和 127.0.0.0/8。它不会拦截 0.0.0.0/8 中的其余地址。Linux 将该范围视为“本网络”,并会将 0.0.0.1 投递到本地协议栈。项目 Maintainer 可以在 allow_local_requests_from_web_hooks_and_services 关闭的情况下,在 http://0.0.0.1:<port>/ 创建一个 webhook。GitLab 会在 hook 日志中存储 8 KB 的响应内容。
自己项目的 Maintainer 可以让 GitLab 通过 0.0.0.1 向 loopback 发起 POST,并从 hook 日志中读回响应体。
| ID | 暂无 CVE |
| CWE | CWE-918 |
| CVSS | High: 8.5 CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:L/A:N |
| 产品 | GitLab CE |
| 受影响版本 | 19.4.1-ce.0(v19.4.1-ee 树上的 26212baacadb) |
| 认证 | 已认证的 Maintainer(admin_web_hook) |
| 许可证 | GNU Affero GPL v3.0 |
| 实验环境 | 仅 127.0.0.1 |
使用 url=http://0.0.0.1:<port>/ 调用 POST /api/v4/projects/:id/hooks。触发或测试。从 hook events API 读取 response_body。在 all-in-one omnibus 中,Sidekiq 与 Puma、Workhorse 和 exporters 共享容器 netns。自己项目的 Maintainer 不应读取这些内容。
http://127.0.0.1/ 和 http://169.254.169.254/ 仍被拦截。这不是通过 link-local 访问 IMDS。这是 this-network 漏洞。
同一产品,同类遗留问题:Gitea HTTPS import pin drop、Gitaly fetch omits resolved_address。
在 19.4.1 的 SSRF 修复之后,我阅读了 url_blocker.rb。validate_localhost 精确匹配 "::" 和 "0.0.0.0",再加上 Socket.ip_address_list。validate_loopback 是 ipv4_loopback?(127.0.0.0/8)和 ipv6 loopback。测试用例覆盖了 127.0.0.1 的八进制/十六进制别名。它们从未提及 0.0.0.1。
Webhook 是响应体接收端:WebHookService 发起 POST 并存储 8 KB。Maintainer 拥有 admin_web_hook。
我启动了原版 gitlab/gitlab-ce:19.4.1-ce.0。本地请求关闭。在 GitLab netns 内的 0.0.0.1:18080 上运行 catcher。创建 hook http://127.0.0.1:18080/ 和 http://169.254.169.254/:422 Invalid url given。创建 hook http://0.0.0.1:18080/:201。测试 push 事件:201。Hook events 的 response_body 包含 GITLAB-WEBHOOK-0NET-SSRF-WITNESS。
已记录的弯路:带 Accept: application/json 的 /users/sign_in 返回 404;包含 root 的种子密码会触发 WeakPasswords;将 0.0.0.1 添加到 lo 会使其出现在 Socket.ip_address_list 中,因此 validate_localhost 会对“阳性”用例返回 422;IP_FREEBIND 加上 ip route add local 0.0.0.1/32 dev lo 可以在不加入地址列表的情况下绑定;compose 中密码里的 $ 需要写成 $$。反弹 shell。花架子。判定标准是 0.0.0.1 返回 201,127.0.0.1 返回 422,日志中出现 witness。
cd lab
./run.sh
目标仅限 http://127.0.0.1:18420。GitLab CE 需要数 GB 内存。仅限授权实验环境。
negative-a 127.0.0.1 blocked
negative-b link-local blocked
positive-hook id=1
SUCCESS GITLAB-WEBHOOK-0NET-SSRF webhook response_body from 0.0.0.1:18080 hook_id=1 GITLAB-WEBHOOK-0NET-SSRF-WITNESS
def validate_localhost(addrs_info)
local_ips = ["::", "0.0.0.0"]
local_ips.concat(Socket.ip_address_list.map(&:ip_address))
return if (local_ips & addrs_info.map(&:ip_address)).empty?
raise BlockedUrlError, "Requests to localhost are not allowed"
end
def validate_loopback(addrs_info)
return unless addrs_info.any? { |addr| addr.ipv4_loopback? || addr.ipv6_loopback? }
raise BlockedUrlError, "Requests to loopback addresses are not allowed"
end
像拒绝 127.0.0.0/8 一样拒绝 0.0.0.0/8(以及 ::ffff:0.0.0.0/104)。
url_blocker.rb · web_hook_service.rb · project_hooks.rbGNU Affero GPL v3.0。参见 LICENSE。仅限 loopback 实验环境。无任何担保。