Skip to content
KitploitKITPLOIT
ToolsBlog
Einreichen
ToolsBlog
Einreichen

Hacking-, PenTest- und Cybersicherheits-Tools für Ihr Sicherheitsarsenal!

Kitploit ist ein Verzeichnis von Hacking-, Cybersicherheits- und Pentesting-Tools. Entdecken Sie die neuesten Projekt-Updates, um Schwachstellen zu finden, Systeme zu analysieren, Tests zu automatisieren und Ihre Sicherheit zu stärken.

··Feeds·Kontakt·Datenschutz·© 2026 Kitploit

Tool-Verzeichnis

Kategorien

Alle Kategorien anzeigen
Loading categories
gortex-PoC — PoC — Symlink-Verfolgung zur Offenlegung von Inhalten außerhalb des Repos über search_text in Gortex (GHSA-6vhf-4wcm-2r83, CVE-2026-87003, CVSS 5.5). | Kitploit
Tools/GitHubGitHub/squeeze440/gortex-poc
Statische AnalyseSchwachstellenanalyseCode-AnalyseExploitationInformationsbeschaffungWebsicherheitPapers & Forschung
GitHubsqueeze440/gortex-poc

gortex-PoC

PoC — Symlink-Verfolgung zur Offenlegung von Inhalten außerhalb des Repos über search_text in Gortex (GHSA-6vhf-4wcm-2r83, CVE-2026-87003, CVSS 5.5).

Repository anzeigen
vor 7 TagenNoch nicht geprüft

Beliebteste

Alle anzeigen →

Entdecken Sie die meistgenutzten Tools unserer Community.

Alle Tools erkunden

Durchsuchen Sie unsere Tool-Sammlung

Alle Tools anzeigen →
Teilen

Symlink-Verfolgung im Indexer-Datei-Walk → Offenlegung von Inhalten außerhalb des Repos über search_text

CVE-Status: beantragt, Zuweisung ausstehend. Dieser Befund wird als GHSA-6vhf-4wcm-2r83 veröffentlicht. Bei CVE-Zuweisung wird dieses Repository in CVE-YYYY-NNNNN-gortex-PoC umbenannt und dieses Banner durch den CVE-Link ersetzt.

ForscherDostxodjayev Abdullox (@squeeze440)
AdvisoryGHSA-6vhf-4wcm-2r83
CVSS 3.15.5 (Mittel)
SchwächeCWE-59, CWE-200

Symlink-Verfolgung im Indexer-Datei-Walk → Offenlegung von Inhalten außerhalb des Repos über search_text

Zusammenfassung

Ein Fehler bei der unsachgemäßen Link-Auflösung (CWE-59) im Repository-Indexer von gortex ermöglicht es, dass ein symlinkter Datei-Eintrag, der auf ein Ziel außerhalb des indizierten Repository-Roots zeigt, stillschweigend durchlaufen, in den Index aufgenommen und später wörtlich ausgeliefert wird — einschließlich an ein vollständig außerhalb des Roots liegendes Ziel — durch die Live-Re-Read-Senke des MCP-Tools search_text, die niemals die eigene guardSymlinkWithinRepo-Eingrenzungsprüfung des Projekts anwendet.

Produkt

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

Getestete Version

Commit b5a63e25c0719c8ce935e59abf85618e2b4b1e64 (git describe: v0.62.0-35-gb5a63e25), der Repository-HEAD zum Zeitpunkt des Audits. Bestätigt weiterhin vorhanden im neuesten getaggten Release v0.62.0, d. h. nachdem der Fix für das unabhängige GHSA-w42c-h7hr-f67p (v0.55.0) eingeflossen ist.

Geschätzte CVSS v3.1

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

  • AV:L — entspricht dem eigenen Scoring-Präzedenzfall dieses Projekts für seine MCP-Oberfläche (GHSA-w42c-h7hr-f67p verwendete ebenfalls AV:L): Die Standardbereitstellung ist ein lokaler MCP-Daemon, der über stdio vom Agent-Prozess auf demselben Host erreicht wird; AV:N würde nur für den optionalen --http-addr-Modus gelten.
  • UI:R — erfordert, dass der MCP-Client (ein LLM-Agent oder ein Mensch über gortex call) einen search_text- / search_text regexp:true-Aufruf ausführt. Ein breites Literal (z. B. ein einzelnes häufiges Zeichen) oder eine passende Regex (.) gegen ein indiziertes Repo, das den platzierten Symlink enthält, reicht aus, um die Zieldatei auszugeben — kein geheimes Wissen über deren Inhalt erforderlich —, also eine niedrige Hürde, aber dennoch eine eigenständige Aktion gegenüber dem bloßen Indexieren.
  • C:H — Offenlegung beliebiger Dateiinhalte, unbegrenzt durch Repo-Grenzen, von allem, was der OS-Benutzer des Daemons lesen kann (SSH-Schlüssel, Cloud-Credential-Dateien, Repos benachbarter Tenants in einem Multi-Repo-Daemon usw.), wörtlich in der Tool-Antwort zurückgegeben.
  • I:N / A:N — dies ist ein reiner Lesepfad-Fehler; nichts wird geschrieben oder unverfügbar gemacht.

Details

Ursache 1 — der Indexer-Walk prüft niemals auf symlinkte Datei-Einträge

internal/indexer/indexer.go:2549-2604, der primäre Korpus-Aufnahme-Walk:

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 verwendet Lstat-Semantik, sodass ein symlinktes Verzeichnis natürlicherweise geschützt ist — d.IsDir() ist dafür false und es wird nie rekursiv betreten. Ein symlinkter Datei-Eintrag erhält keinen äquivalenten Schutz: Nichts hier (noch in shouldExclude/shouldPruneDir, internal/indexer/indexer.go:5724-5813, die nur gitignore-artige Pfadmuster abgleichen) ruft d.Type()&os.ModeSymlink, os.Lstat oder filepath.EvalSymlinks für den Datei-Eintrag auf, bevor er in die Warteschlange gestellt wird. Eine Datei namens z. B. pwn.go, deren Ziel /etc/passwd, ~/.ssh/id_rsa oder ein Repo eines benachbarten Tenants ist, wird allein deshalb aufgenommen, weil ihr Name auf eine registrierte Erweiterung passt (effectiveLanguage, internal/parser DetectLanguageContent, pfadbasiert). Genau diese walked-file-Liste wird zu idx.fileMtimes (internal/indexer/indexer.go:3760: idx.fileMtimes[idx.relKey(f.path)] = f.mtimeNano).

Ursache 2 — die search_text-Senke liest Live-Dateien ohne Eingrenzungsschutz erneut

internal/indexer/grep.go:17-64 (GrepText / warmTrigramSearcher) baut den Trigram-Searcher direkt aus den Schlüsseln von 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) und :55-86/:102-176 (Grep/GrepRegexp) tun dann:

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 folgen standardmäßig Symlinks (kein O_NOFOLLOW). Weder diese Funktionen noch internal/mcp/tools_search_text.go:35-126 (handleSearchText, der MCP-Handler, der s.indexer.GrepText/GrepRegexp aufruft) rufen jemals resolveFilePath oder guardSymlinkWithinRepo auf. Diese beiden Eingrenzungsfunktionen existieren und sind korrekt in jeden anderen inhaltsausliefernden Pfad eingebunden — internal/mcp/tools_fileops.go (read_file, write_file/edit_file seit dem v0.55.0-Fix), internal/mcp/tools_coding.go:919 (get_symbol_source), internal/mcp/tools_lsp.go:264, internal/mcp/tools_export.go:95 —, aber der Trigram-Grep-Pfad von search_text ist eine separate Senke, die nie denselben Schutz erhalten hat. grep -rn guardSymlinkWithinRepo über den gesamten Baum bestätigt null Referenzen in tools_search_text.go, grep.go oder trigram/searcher.go.

Prüfung auf Duplikatrisiko / Eigenständigkeit

Dies ist tatsächlich eigenständig gegenüber GHSA-w42c-h7hr-f67p:

  • Jene Advisory: Schreibpfad, resolveFilePath nimmt absolute Pfade aus + write_file/edit_file/move_inline rufen nie guardSymlinkWithinRepo auf (behoben in v0.55.0).
  • Dieser Befund: Lesepfad, der eigene Discovery-Walk des Indexers (nicht irgendein MCP-Handler) nimmt eine symlinkte Datei ohne Lstat-Prüfung auf, und ein anderes MCP-Tool (search_text, nicht read_file/get_symbol_source) liefert deren Inhalt über eine Senke (internal/search/trigram) aus, die unabhängig davon nie guardSymlinkWithinRepo aufruft. Der Schreibpfad-Fix hat keinen dieser beiden Orte berührt. Dies entspricht dem bereits in ozgurcd/gograph (GHSA-6h6w-vhgr-2hp6) und vitali87/code-graph-rag (GHSA-85gg-2gfq-q95m) bestätigten Schwester-Tool-Muster: ein Datei-Discovery-Walk ohne Symlink-Schutz, gepaart mit einer inhaltsausliefernden Senke, der unabhängig davon ein Eingrenzungsschutz fehlt.

Proof of Concept

Statische Spur (Datei:Zeile-Referenzen oben) ist vollständig und durch Inspektion reproduzierbar. Dynamische Verifikation wurde versucht und wird tatsächlich durch eine Toolchain-Lücke in dieser Sandbox blockiert, ehrlich dokumentiert statt übertüncht:

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

Jeder tree-sitter-Sprach-Extraktor in dieser Codebasis (einschließlich des reinen Go-Extraktors, github.com/tree-sitter/tree-sitter-go's bindings/go/binding.go: // #cgo CFLAGS: -std=c11 -fPIC / import "C") erfordert cgo zum Kompilieren — es gibt keinen reinen Go-Codepfad, um auch nur eine einzige Sprache zu registrieren, sodass parser.Registry ohne einen funktionierenden C-Compiler nicht gebaut werden kann. Diese Sandbox hat kein gcc/cc/clang auf $PATH und kein passwortloses Root (sudo -l verlangt ein interaktives Passwort), um eines über apt-get install gcc zu installieren (im apt-Cache vorhanden, gcc-16/kali-last-snapshot, aber ohne Privilegien unerreichbar). Dies ist eine Umgebungsbeschränkung, kein Befund über gortex.

Der PoC-Test selbst — geschrieben gegen das eigene bestehende Test-Harness-Muster des Projekts (internal/mcp/tools_search_text_test.go's TestSearchText und dieselbe newSingleRepoServer/callTool-Form, die der PoC der vorherigen Advisory verwendete) — bleibt unter internal/mcp/poc_symlink_search_disclosure_test.go liegen, damit der Maintainer ihn in einer Umgebung mit C-Toolchain ausführen kann:

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.
}

Angesichts der obigen Quellcode-Spur — jeder Schritt durch exakte Funktion benannt und kein Eingrenzungsaufruf irgendwo auf dem Pfad von filepath.WalkDir über trigram.Build/Grep bis zur Antwort von handleSearchText — wird dies als statisch nachverfolgt, nicht dynamisch bestätigt präsentiert, gemäß der Grundregel dieses Audits gegen die Fabrikation von PoC-Beweisen.

Auswirkung

Jedes indizierte Repository, das einen Symlink enthält, dessen Ziel außerhalb des Repository-Roots aufgelöst wird (platziert von einem bösartigen Contributor, vorhanden in einem geklonten Dependency-/Plugin-Repo oder eingeführt über einen Supply-Chain-PR, den der Daemon später indiziert), ermöglicht es einem MCP-Client — ein normaler search_text-Aufruf oder ein prompt-injizierter/adversarialer, dasselbe Bedrohungsmodell wie GHSA-w42c-h7hr-f67p —, den wörtlichen Inhalt dieser Zieldatei zu lesen, ungeachtet des Eingrenzungsschutzes, den das Projekt ansonsten für read_file und get_symbol_source durchsetzt. In einem Multi-Repo-Daemon (internal/indexer/multi.go:3525-3562, der den identischen ungeschützten Aufruf pro verfolgtem Repo ausfächert) überschreitet dies zudem Tenant-/Repo-Grenzen innerhalb derselben Daemon-Instanz.

Schwächen

  • CWE-59 — Unsachgemäße Link-Auflösung vor Dateizugriff ('Link Following'): Der Indexer-Walk (internal/indexer/indexer.go:2549) nimmt einen symlinkten Datei-Eintrag ohne Lstat/ModeSymlink-Prüfung auf.
  • CWE-200 — Offenlegung sensibler Informationen gegenüber einem nicht autorisierten Akteur: search_text (internal/mcp/tools_search_text.go) → trigram.Build/Grep/GrepRegexp (internal/search/trigram/searcher.go) liefern den symlink-aufgelösten Inhalt ohne guardSymlinkWithinRepo-Eingrenzungsprüfung aus, anders als jede andere Lese-Senke in derselben Codebasis.

Behebung

Wenden Sie das eigene bestehende guardSymlinkWithinRepo des Projekts (internal/mcp/tools_fileops.go:353) — oder eine äquivalente Lstat-basierte Prüfung — an beiden Stellen an:

  1. Zur Walk-Zeit: im filepath.WalkDir-Callback (internal/indexer/indexer.go:2549) einen Datei-DirEntry ablehnen oder überspringen, dessen d.Type()&os.ModeSymlink != 0 (spiegelt den impliziten Schutz, den Walk Verzeichnissen bereits gewährt), oder filepath.EvalSymlinks(path) auflösen und die Eingrenzung innerhalb absRoot bestätigen, bevor an files angehängt wird.
  2. Zur Lesezeit (Defense in Depth): in trigram.Build/Grep/GrepRegexp (internal/search/trigram/searcher.go) oder an der Aufrufstelle handleSearchText dieselbe guardSymlinkWithinRepo-artige Real-Path-Eingrenzungsprüfung anwenden, die bereits von read_file/get_symbol_source/tools_lsp/tools_export verwendet wird, bevor der Text eines Treffers zurückgegeben wird. Jeder Fix allein schließt dies; beide zusammen entsprechen der Eingrenzungshaltung, die der Rest der Codebasis bereits einhält.

Danksagung

Dostxodjayev Abdullox (GitHub: @squeeze440)

Meldeweg

Private Vulnerability Reporting (PVR) ist auf zzet/gortex aktiviert und bestätigt; der standardmäßige GitHub Security Advisory (GHSA) Draft-Advisory-Ablauf gilt, konsistent mit der vorherigen GHSA-w42c-h7hr-f67p-Offenlegung des Projekts.

Tool herunterladen