Skip to content
KitploitKITPLOIT
ツールブログ
提出
ツールブログ
提出

ハッキング、侵入テスト、サイバーセキュリティツールをあなたのセキュリティアーセナルに!

Kitploitはハッキング、サイバーセキュリティ、ペネトレーションテストのツールディレクトリです。最新のプロジェクトアップデートを見つけて、脆弱性の発見、システム分析、テストの自動化、セキュリティの強化を行いましょう。

··フィード·お問い合わせ·プライバシー·© 2026 Kitploit

ツールディレクトリ

カテゴリ

すべてのカテゴリを見る
Loading categories
gortex-PoC — PoC — Gortex の search_text を介したリポジトリ外コンテンツ漏洩につながるシンボリックリンク追跡(GHSA-6vhf-4wcm-2r83、CVE-2026-87003、CVSS 5.5)。 | Kitploit
ツール/GitHubGitHub/squeeze440/gortex-poc
静的分析脆弱性分析コード分析エクスプロイト情報収集ウェブセキュリティ論文と研究
GitHubsqueeze440/gortex-poc

gortex-PoC

PoC — Gortex の search_text を介したリポジトリ外コンテンツ漏洩につながるシンボリックリンク追跡(GHSA-6vhf-4wcm-2r83、CVE-2026-87003、CVSS 5.5)。

リポジトリを見る
7日前未レビュー

人気

すべて見る →

コミュニティで最も使われているツールを見つけましょう。

すべてのツールを探索

ツールコレクションを閲覧

すべてのツールを見る →
共有

インデクサーのファイル走査におけるシンボリックリンク追跡 → search_text によるリポジトリ外コンテンツの開示

CVE ステータス: 申請済み、割り当て待ち。この脆弱性は GHSA-6vhf-4wcm-2r83 として公開されています。CVE が割り当てられると、このリポジトリは CVE-YYYY-NNNNN-gortex-PoC に改名され、このバナーは CVE リンクに置き換えられます。

研究者Dostxodjayev Abdullox (@squeeze440)
アドバイザリGHSA-6vhf-4wcm-2r83
CVSS 3.15.5 (Medium)
脆弱性CWE-59, CWE-200

インデクサーのファイル走査におけるシンボリックリンク追跡 → search_text によるリポジトリ外コンテンツの開示

概要

gortex のリポジトリインデクサーにおける不適切なリンク解決の欠陥 (CWE-59) により、インデックス対象リポジトリのルート外を指すシンボリックリンクのファイルエントリが、黙って走査され、コンテンツがインデックス化され、後にそのまま提供されることが可能になります。これはルート外のターゲットに対しても同様であり、search_text MCP ツールのライブ再読み込みシンクによって行われます。このシンクは、プロジェクト自身の guardSymlinkWithinRepo 封じ込めチェックを一切適用しません。

製品

zzet/gortex — https://github.com/zzet/gortex

テスト済みバージョン

コミット b5a63e25c0719c8ce935e59abf85618e2b4b1e64 (git describe: v0.62.0-35-gb5a63e25)、監査時点のリポジトリ HEAD。最新のタグ付きリリース v0.62.0 でも依然として存在することを確認済み。すなわち、無関係な GHSA-w42c-h7hr-f67p (v0.55.0) の修正が適用された後も存在しています。

推定 CVSS v3.1

CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:N/A:N — 5.5 (Medium)

  • AV:L — このプロジェクト自身の MCP サーフェスに対するスコアリング前例 (GHSA-w42c-h7hr-f67p も AV:L を使用) と一致します。デフォルトのデプロイは、同一ホスト上のエージェントプロセスから stdio 経由で到達されるローカル MCP デーモンです。AV:N はオプトインの --http-addr モードにのみ適用されます。
  • UI:R — MCP クライアント (LLM エージェント、または gortex call 経由の人間) が search_text / search_text regexp:true 呼び出しを発行する必要があります。植え付けられたシンボリックリンクを含むインデックス済みリポジトリに対して、広範なリテラル (例えば単一の一般的な文字) またはマッチする正規表現 (.) で十分にターゲットファイルをダンプできます — その内容に関する秘密の知識は不要です — したがってハードルは低いですが、単なるインデックス化とは別個のアクションです。
  • C:H — リポジトリ境界に制限されない任意のファイルコンテンツの開示。デーモンの OS ユーザーが読み取れるものすべて (SSH キー、クラウド認証情報ファイル、マルチリポジトリデーモンにおける兄弟テナントのリポジトリなど) が、ツール応答でそのまま返されます。
  • I:N / A:N — これは純粋な読み取りパスのバグです。何も書き込まれたり、利用不能にされたりしません。

詳細

根本原因 1 — インデクサーの走査がシンボリックリンクのファイルエントリを一切チェックしない

internal/indexer/indexer.go:2549-2604、主要なコーパス受け入れ走査:

root@kitploit:~
err = filepath.WalkDir(absRoot, func(path string, d os.DirEntry, err error) error {
    if err != nil { return nil }
    if d.IsDir() {
        if idx.shouldPruneDir(path, absRoot) { return filepath.SkipDir }
        return nil
    }
    lang, ok := idx.effectiveLanguage(path, nil)   // detects by extension; symlink name is enough
    if !ok { return nil }
    if idx.shouldExclude(path, absRoot, false) { return nil }
    info, statErr := d.Info()                      // Lstat'd DirEntry.Info() — no ModeSymlink check
    ...
    files = append(files, walkedFile{path: path, lang: lang, size: info.Size(), mtimeNano: info.ModTime().UnixNano()})
    return nil
})

filepath.WalkDir は Lstat セマンティクスを使用するため、シンボリックリンクのディレクトリは自然に保護されます — それに対して d.IsDir() は false であり、再帰的に進入されることはありません。シンボリックリンクのファイルエントリには同等の保護がありません。ここでは (また shouldExclude/shouldPruneDir、internal/indexer/indexer.go:5724-5813 でも、これらは gitignore スタイルのパスパターンのみをマッチします) 、ファイルエントリをキューに入れる前に d.Type()&os.ModeSymlink、os.Lstat、または filepath.EvalSymlinks を呼び出すものはありません。例えば pwn.go という名前のファイルで、そのターゲットが /etc/passwd、~/.ssh/id_rsa、または兄弟テナントリポジトリである場合、その名前が登録された拡張子にマッチするという理由だけで受け入れられます (effectiveLanguage、internal/parser の DetectLanguageContent、パスベース)。この正確な走査済みファイルリストが idx.fileMtimes になります (internal/indexer/indexer.go:3760: idx.fileMtimes[idx.relKey(f.path)] = f.mtimeNano)。

根本原因 2 — search_text シンクが封じ込めガードなしでライブファイルを再読み込みする

internal/indexer/grep.go:17-64 (GrepText / warmTrigramSearcher) は idx.fileMtimes のキーから直接トライグラムサーチャーを構築します:

root@kitploit:~
rels := ... // from idx.fileMtimes, i.e. includes the symlinked file's rel path
idx.trigramSearcher = trigram.Build(root, rels)

internal/search/trigram/searcher.go:32-48 (Build) および :55-86/:102-176 (Grep/GrepRegexp) は次を行います:

root@kitploit:~
content, err := os.ReadFile(filepath.Join(root, filepath.FromSlash(rel)))  // Build — follows symlink
...
f, err := os.Open(filepath.Join(s.root, filepath.FromSlash(rel)))          // Grep/GrepRegexp — follows symlink

os.ReadFile/os.Open はデフォルトでシンボリックリンクを追跡します (O_NOFOLLOW なし)。どちらの関数も、また internal/mcp/tools_search_text.go:35-126 (handleSearchText、s.indexer.GrepText/GrepRegexp を呼び出す MCP ハンドラ) も、resolveFilePath や guardSymlinkWithinRepo を一切呼び出しません。これら 2 つの封じ込め関数は存在し、他のすべてのコンテンツ提供パスに正しく配線されています — internal/mcp/tools_fileops.go (read_file、v0.55.0 修正時点の write_file/edit_file)、internal/mcp/tools_coding.go:919 (get_symbol_source)、internal/mcp/tools_lsp.go:264、internal/mcp/tools_export.go:95 — しかし search_text のトライグラム grep パスは、同じガードが与えられなかった別個のシンクです。ツリー全体での grep -rn guardSymlinkWithinRepo は、tools_search_text.go、grep.go、または trigram/searcher.go に参照がゼロであることを確認しています。

重複リスク / 相違性チェック

これは GHSA-w42c-h7hr-f67p とは真に異なります:

  • そのアドバイザリ: 書き込みパス、resolveFilePath が絶対パスを免除 + write_file/edit_file/move_inline が guardSymlinkWithinRepo を一切呼び出さない (v0.55.0 で修正)。
  • この脆弱性: 読み取りパス、インデクサー自身の発見走査 (MCP ハンドラではない) が Lstat チェックなしでシンボリックリンクファイルを受け入れ、別の MCP ツール (read_file/get_symbol_source ではなく search_text) が、独立して guardSymlinkWithinRepo を一切呼び出さないシンク (internal/search/trigram) を通じてそのコンテンツを提供します。書き込みパスの修正は、これらの 2 つの場所のいずれにも触れていません。 これは、ozgurcd/gograph (GHSA-6h6w-vhgr-2hp6) および vitali87/code-graph-rag (GHSA-85gg-2gfq-q95m) で既に確認されている兄弟ツールパターンと一致します: シンボリックリンク保護のないファイル発見走査と、独立して封じ込めガードを欠くコンテンツ提供シンクの組み合わせです。

概念実証

静的トレース (上記の file:line 参照) は完全であり、検査により再現可能です。動的検証は試みられましたが、このサンドボックスのツールチェーンギャップによって真にブロックされています。ごまかすことなく正直に文書化します:

root@kitploit:~
$ CGO_ENABLED=1 go test ./internal/mcp/... -run TestPoC_SymlinkFollow_SearchTextDisclosesOutOfRepoContent -v
mcp [build failed]
  cgo: C compiler "gcc" not found: exec: "gcc": executable file not found in $PATH

このコードベースのすべての tree-sitter 言語エクストラクタ (プレーンな Go エクストラクタ、github.com/tree-sitter/tree-sitter-go の bindings/go/binding.go: // #cgo CFLAGS: -std=c11 -fPIC / import "C" を含む) はコンパイルに cgo を必要とします — 単一の言語を登録するための純粋な Go コードパスさえないため、parser.Registry は動作する C コンパイラなしでは構築できません。このサンドボックスには $PATH 上に gcc/cc/clang がなく、apt-get install gcc (apt キャッシュに存在、gcc-16/kali-last-snapshot、しかし特権なしでは到達不能) でインストールするためのパスワードなし root (sudo -l は対話的パスワードを要求) もありません。これは環境の制限であり、gortex に関する所見ではありません。

PoC テスト自体 — プロジェクト自身の既存テストハーネスパターン (internal/mcp/tools_search_text_test.go の TestSearchText、および以前のアドバイザリ自身の PoC が使用したのと同じ newSingleRepoServer/callTool の形) に対して書かれています — は、メンテナーが C ツールチェーンを持つ環境で実行できるよう、internal/mcp/poc_symlink_search_disclosure_test.go にそのまま残されています:

root@kitploit:~
func TestPoC_SymlinkFollow_SearchTextDisclosesOutOfRepoContent(t *testing.T) {
	outsideDir := t.TempDir()
	secretPath := filepath.Join(outsideDir, "secret.txt")
	const secretMarker = "GORTEX-POC-SECRET-OUTSIDE-REPO-9f3a1c"
	os.WriteFile(secretPath, []byte("BEGIN OUTSIDE-REPO FILE\n"+secretMarker+"\nEND OUTSIDE-REPO FILE\n"), 0o600)

	repoDir := t.TempDir()
	os.WriteFile(filepath.Join(repoDir, "main.go"), []byte("package app\n\nfunc Hello() {}\n"), 0o644)
	os.Symlink(secretPath, filepath.Join(repoDir, "pwn.go"))   // symlink INSIDE repo -> OUTSIDE target

	idx := indexer.New(g, reg, cfg.Index, zap.NewNop())
	idx.Index(repoDir)                                          // walk admits pwn.go, no symlink check
	srv := NewServer(eng, g, idx, nil, zap.NewNop(), nil)

	res := callTool(t, srv, "search_text", map[string]any{"query": secretMarker})
	// asserts: res.IsError == false, exactly 1 match, Path == "pwn.go",
	// Text contains secretMarker — i.e. the OUTSIDE file's real content.
}

上記のソースレベルのトレース — すべてのステップが正確な関数名で示され、filepath.WalkDir から trigram.Build/Grep を経て handleSearchText の応答に至るパス上に封じ込め呼び出しが一切ない — を踏まえ、これは PoC 証拠を捏造しないというこの監査の基本ルールに従い、静的にトレース済み、動的に確認済みではないものとして提示されます。

影響

ターゲットがそのリポジトリのルート外に解決されるシンボリックリンクを含むインデックス済みリポジトリ (悪意のあるコントリビューターによって植え付けられた、クローンされた依存関係/プラグインリポジトリに存在する、またはデーモンが後にインデックスするサプライチェーン PR を介して導入された) は、MCP クライアント — 通常の search_text 呼び出し、またはプロンプトインジェクション/敵対的な呼び出し、GHSA-w42c-h7hr-f67p と同じ脅威モデル — に、そのターゲットファイルの内容をそのまま読み取らせます。プロジェクトが read_file と get_symbol_source に適用している封じ込めガードに関係なく。マルチリポジトリデーモン (internal/indexer/multi.go:3525-3562、追跡対象リポジトリごとに同一のガードなし呼び出しをファンアウトする) では、これは同じデーモンインスタンス内のテナント/リポジトリ境界も越えます。

脆弱性

  • CWE-59 — ファイルアクセス前の不適切なリンク解決 ('リンク追跡'): インデクサーの走査 (internal/indexer/indexer.go:2549) が、Lstat/ModeSymlink チェックなしでシンボリックリンクのファイルエントリを受け入れます。
  • CWE-200 — 不正なアクターへの機密情報の露出: search_text (internal/mcp/tools_search_text.go) → trigram.Build/Grep/GrepRegexp (internal/search/trigram/searcher.go) が、同じコードベース内の他のすべての読み取りシンクとは異なり、guardSymlinkWithinRepo 封じ込めチェックなしでシンボリックリンク解決済みコンテンツを提供します。

修復

プロジェクト自身の既存の guardSymlinkWithinRepo (internal/mcp/tools_fileops.go:353) — または同等の Lstat ベースのチェック — を両方のポイントに適用してください:

  1. 走査時: filepath.WalkDir コールバック (internal/indexer/indexer.go:2549) で、d.Type()&os.ModeSymlink != 0 であるファイル DirEntry を拒否またはスキップする (Walk がディレクトリに既に与えている暗黙の保護を反映)、または files に追加する前に filepath.EvalSymlinks(path) を解決し absRoot 内への封じ込めを確認する。
  2. 読み取り時 (多層防御): trigram.Build/Grep/GrepRegexp (internal/search/trigram/searcher.go) で、または handleSearchText 呼び出しサイトで、マッチの Text を返す前に read_file/get_symbol_source/tools_lsp/tools_export が既に使用しているのと同じ guardSymlinkWithinRepo スタイルの実パス封じ込めチェックを適用する。 どちらか一方の修正だけでもこれを塞ぎます。両方を合わせると、コードベースの残りの部分が既に自らに課している封じ込め態勢と一致します。

クレジット

Dostxodjayev Abdullox (GitHub: @squeeze440)

報告チャネル

Private Vulnerability Reporting (PVR) は zzet/gortex で有効化され確認されています。標準の GitHub Security Advisory (GHSA) ドラフトアドバイザリフローが適用され、プロジェクトの以前の GHSA-w42c-h7hr-f67p 開示と一致します。

ツールをダウンロード