
GitLab CEのWebhook SSRF(0.0.0.0/8のthis-network範囲を経由)の概念実証およびラボ。Maintainerがループバックに到達し、フックログからレスポンスを読み取ることができる。
abraxaslabs.tech · github.com/abraxas · @abraxas_null · [email protected] · gitlab-webhook-0net-ssrf
解説記事: https://abraxaslabs.tech/research/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 はこの範囲を「this network」として扱い、0.0.0.1 をローカルスタックに配送する。プロジェクトの Maintainer は、allow_local_requests_from_web_hooks_and_services がオフの状態でも http://0.0.0.1:<port>/ に webhook を作成できる。GitLab はレスポンスの 8 KB をフックログに保存する。
自身のプロジェクトの Maintainer は、0.0.0.1 経由で GitLab にループバックへ POST させ、フックログからレスポンスボディを読み出せる。
| 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 のみ |
POST /api/v4/projects/:id/hooks を url=http://0.0.0.1:<port>/ で実行する。トリガーまたはテストを行う。フックイベント API から response_body を読み取る。オールインワンの omnibus では、Sidekiq は Puma、Workhorse、およびエクスポーターとコンテナの netns を共有している。自身のプロジェクトの Maintainer がこれらを読むべきではない。
http://127.0.0.1/ と http://169.254.169.254/ は引き続きブロックされる。これはリンクローカル経由の IMDS ではない。this-network の穴である。
同じ製品の関連する残存問題: Gitea HTTPS インポートのピン留め欠落、Gitaly fetch が 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 ループバックである。スペックは 127.0.0.1 の8進/16進エイリアスをカバーしている。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 にキャッチャーを配置。フック作成 http://127.0.0.1:18080/ と http://169.254.169.254/ : 422 Invalid url given。フック作成 http://0.0.0.1:18080/ : 201。プッシュイベントのテスト : 201。フックイベントの 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 のパスワード内の $ には $$ が必要。リバースシェル。茶番。オラクルは 0.0.0.1 で 201、127.0.0.1 で 422、ログに証拠。
cd lab
./run.sh
対象は http://127.0.0.1:18420 のみです。GitLab CE は数 GB の RAM を必要とします。許可されたラボ環境でのみ使用してください。
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 を参照してください。ループバックラボ専用です。保証はありません。