
GitLab - 高 - Gitea HTTPSインポートSSRF - UrlBlockerピンドロップ
abraxaslabs.tech · github.com/abraxas · @abraxas_null · [email protected] · gitlab-gitea-import-ssrf
解説記事: https://abraxaslabs.tech/research/gitlab-gitea-import-ssrf
GitLab CE 19.4.1 - GitLab Inc.
Gitea インポートは UrlBlocker.validate! を一度だけ呼び出し、その後 https の場合には IP を固定した URI を破棄して、元のホスト名をそのまま Faraday に渡します。Octokit の UrlValidation は再度検証を行いますが、env[:url] を書き換えません。これらのルックアップの間に名前を再バインドすると、GitLab は Gitea であるかのようにループバックへ GET リクエストを送信します。リポジトリ一覧の JSON は GET /import/gitea/status.json で返されます。HTTP インポートでは IP 固定が維持されます。https://127.0.0.1 を直接指定した場合はブロックされます。
個人名前空間で Gitea インポートを開く権限を持つサインイン済みユーザーは、自身がループバックへ再バインドするホストを GitLab に取得させることができ、そのレスポンスボディを取得できます。
| ID | CVE 未割当 |
| CWE | CWE-918, CWE-367 |
| CVSS | High: 7.7 CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:C/C:H/I:L/A:N |
| 製品 | GitLab CE |
| 影響を受けるバージョン | 19.4.1-ce.0 (v19.4.1-ee ツリー上の 26212baacadb) |
| 認証 | 認証済みユーザー、Gitea インポート、個人名前空間 |
| ライセンス | GNU Affero GPL v3.0 |
| ラボ | 127.0.0.1 のみ |
Gitea のホスト URL を https://<攻撃者が制御する名前>/ に設定します。UrlBlocker は最初のパブリック A レコードを許可します。HTTPS の client_options はそのホスト名を保持します。その後 127.0.0.1 へ再バインドします。Faraday は GitLab ワーカーのループバック上でキャッチャーに対して GET リクエストを送信します。status.json の provider_repos はキャッチャーが返した内容そのものです。
オールインワンの omnibus 構成では、これは Workhorse、Puma、エクスポーター、およびワーカー上でリッスンしているその他すべてのサービスを指します。ホストの RCE ではありません。未認証でもありません。HTTP のホスト URL は固定された IP のままです。
同じ製品における類似の残存問題: Gitaly fetch が resolved_address を省略、webhook 0.0.0.0/8。
まず 2026 年 9 月 23 日の 19.4.1 / 19.3.3 / 19.2.7 のパッチノートを読み、その後 v19.4.1-ee ツリーを確認しました。CI の正規表現 RCE、MR diff の XSS、Workhorse の file.path 読み取りはこのピンでは修正済みです。インポートの SSRF は未修正のままでした。
Import::GiteaController#client_options が残存箇所です:
host: verified_url.scheme == 'https' ? provider_url : verified_url.to_s,
HTTP は verified_url(IP)を使用する。HTTPS は provider_url(ユーザーが入力した名前)を使用する。LegacyGithubImport::Client はそのホストから Octokit の api_endpoint を構築する。Gitlab::Octokit::UrlValidation は validate! を呼び出した後、書き換えなしで @app.call(env) を呼び出す。
私は素の gitlab/gitlab-ce:19.4.1-ce.0 を立ち上げた。ローカルリクエストは無効。Faraday の ssl.verify が true であるため、ラボの CA を /etc/gitlab/trusted-certs に配置した。gitea.lab の DNS は、1〜2 回目のルックアップが 1.1.1.1 を返し、以降のルックアップが 127.0.0.1 を返す。キャッチャーは :443 で GitLab の netns を共有している。GET /import/gitea/status.json は GITLAB-GITEA-IMPORT-SSRF-WITNESS という名前の provider_repos を返した。キャッチャーのログ: GET /api/v1/user/repos peer=127.0.0.1 host=gitea.lab。
直接 https://127.0.0.1/ を指定した場合: 「指定された URL は使用できません」。HTTP の http://gitea.lab/ はピン留めされたままで、ウィットネスを返さなかった。
すでに記録済みの失敗: Accept: application/json を付けた /users/sign_in は 404(HTML は 200)。シードパスワード CveLabRoot9! は root を含むため拒否された。CA のバインドマウントを ro にすると、再設定がファイルをリンクした後に EROFS となる。ルックアップ 2 で最初の 2 回の後にループバックを返すと、Faraday が接続する前に UrlValidation がブロックする。最終的に成立したのはペアモード(奇数回はパブリック、偶数回はループバック)である。リバースシェル。芝居。オラクルは status.json 内のウィットネスと、キャッチャーのピアである。
cd lab
./run.sh
対象は http://127.0.0.1:18400 のみです。GitLab CE は数 GB の RAM を必要とします。初回起動は遅くなります。許可されたラボ環境でのみ使用してください。
status.json status=200 ... provider_repos ... GITLAB-GITEA-IMPORT-SSRF-WITNESS
catcher GET /api/v1/user/repos peer=127.0.0.1 host=gitea.lab status=200 witness=True
negative-a https://127.0.0.1 blocked
negative-b http://gitea.lab no-witness
SUCCESS GITLAB-GITEA-IMPORT-SSRF status.json mode=pairs http=200 GITLAB-GITEA-IMPORT-SSRF-WITNESS
def client_options
verified_url, provider_hostname = verify_blocked_uri
{
host: verified_url.scheme == 'https' ? provider_url : verified_url.to_s,
api_version: 'v1',
hostname: provider_hostname
}
end
HTTP がすでに行っているのと同じ方法で、https には verified_url.to_s を使用します。Octokit の UrlValidation で env[:url] をピン留めされた IP に書き換えます。
gitea_controller.rb · legacy_github_import/client.rb · octokit/url_validation.rbGNU Affero GPL v3.0。LICENSE を参照してください。ループバックラボ専用です。保証はありません。