flawopen.com/Incidents/gitlab-commits-api-path-traversal-september-2026
CVE-2026-85706:GitLabでのサーバーファイル露出
GitLabの未認証アップロード解析でサーバーファイルを選択でき、不正なエスケープによりエラーへ断片が現れる場合がありました。[1], [3]
受付係が受取箱の書類ではなく、来訪者が紙に書いた書庫の場所を受け取ったとします。身分証を確認する前に書庫を開き、読めない一行があればエラーの説明で声に出してしまいます。開くことが最初の失敗で、内容を読み上げることが次の失敗です。[1]
主要な概念と専門用語
Upload metadata- 要求側の file.path や metadata.path で、信頼できるアップロード処理が登録したバッファを置き換えてはいけません。[1]
Percent decoding- フォーム解析では % と続く二桁の十六進数を復号します。不正な並びは問題の断片を含むエラーを起こし得ます。[1]
CWE-22 / CVSS- CNA は
CVE-2026-85706をパストラバーサル、CVSS 10.0 の重大な問題に分類しています。越えた境界は要求側によるサーバーファイルの選択です。[3]
フレームワーク対応
- このガイドで説明する弱点の CWE 参照: CWE-22
- CISA KEV — 追加日 2026-09-11 — CVE-2026-85706
インシデントのタイムライン
GitLab は修正版 19.1.8、19.2.6、19.3.2 を公開しました。[2]
公開情報に 18.11.12 と 19.0.9 への修正の移植が追記されました。[2]
ステップ・バイ・ステップの攻撃フロー
認証前の選択
アップロードのメタデータにより、認証前に想定された一時バッファをサーバーのパスで置き換えられました。[1]
内容をフォームとして解析
application/x-www-form-urlencoded では選択されたファイルがフォームデータとして扱われ、通常のコミット応答にはなりません。[1]
エラーへの反映
不正なパーセントエスケープは該当断片をエラーに含め得ます。& と = が範囲を左右し、読み取りだけでは漏えいを証明できません。[1]
調査
correlation_id で Rails API と Workhorse の記録を関連付け、api_error と written_bytes を比較して、応答でバイトが送信されたかを判断します。[1]
ソースコード比較:脆弱 vs 堅牢化
これは Python の教育用モデルで、GitLab のソースコードや修正ではありません。脆弱な例はメモリ内のバッファだけを使い、どんな % もエラーとみなします。実際の検証や断片の選択は再現しません。改善例は先に認証し、パスのメタデータを拒否し、信頼できる処理が登録したバイトだけを受け取り、サイズを制限して一般的なエラーを返します。製品更新も必要です。前段でファイルを読んだ後に経路内で認証しても手遅れです。[1]
from urllib.parse import unquote_to_bytes
def parse_upload(authenticated, metadata, buffers):
# 無害なメモリ内フィクスチャで、ファイルシステムにはアクセスしません。
body = buffers.get(metadata.get("path"), b"")
if b"%" in body:
return 400, body.decode("ascii", errors="replace")
if not authenticated:
return 401, "Unauthorized"
return 200, unquote_to_bytes(body)
from urllib.parse import unquote_to_bytes
MAX_BYTES = 4096
HEX = frozenset(b"0123456789abcdefABCDEF")
def parse_upload(authenticated, metadata, registered_body):
# registered_body は信頼できるアップロードミドルウェアから受け取り、
# リクエストパラメーターで選ばれたパスからは取得しません。
if not authenticated:
return 401, "Unauthorized"
if metadata or type(registered_body) is not bytes:
return 400, "Invalid upload"
if len(registered_body) > MAX_BYTES:
return 413, "Upload too large"
for i, byte in enumerate(registered_body):
if byte == 37 and (i + 2 >= len(registered_body) or
registered_body[i+1] not in HEX or
registered_body[i+2] not in HEX):
return 400, "Invalid upload"
return 200, unquote_to_bytes(registered_body)
エンジニアリング&システム堅牢化チェックリスト
- ✓サポート対象の修正版に更新してください。各系列の最低修正版は 18.11.12、19.0.9、19.1.8、19.2.6、19.3.2 で、影響は 18.7 以降から始まります。導入版を公式告知と照合します。[2], [3]
- ✓無害なメモリ上の目印で匿名要求を試験し、認証前のバッファ検索がなく、応答や通常ログに目印が出ないことを確認します。解析前にパスのメタデータを拒否します。[1]
- ✓Rails API と Workhorse のログを保全し、
correlation_idで各記録を結び付けてから、api_errorとwritten_bytesを比較します。正常なアップロードのパスが同じ行にあるだけで除外せず、復号済みの file.path と metadata.path を調べます。 ファイルが読み取られたことだけでは漏えいの証拠になりません。GitLabによると、api_errorに反映された断片をWorkhorseのwritten_bytesで裏付けると、クライアントへ送信されたバイトを確認できます。HTTPステータスだけでは判断できません。[1] - ✓ローテーションは、確認済みまたは合理的に疑われる情報漏えいに含まれた認証情報に限定してください。
db_key_baseまたはotp_key_baseが関係する場合は、ローテーション前に GitLab サポートへ相談してください。GitLab によると、現在db_key_baseにはサポート対象のローテーション手順がなく、非対応の鍵ローテーションはデータ損失を招く可能性があります。ログがないことは漏えいがなかった証拠ではありません。[1]