Skip to content
KitploitKITPLOIT
OutilsBlog
Soumettre
OutilsBlog
Soumettre

Outils de Hacking, PenTest et Cybersécurité pour votre Arsenal de Sécurité !

Kitploit est un répertoire d'outils de hacking, de cybersécurité et de pentesting. Découvrez les dernières mises à jour des projets pour trouver des vulnérabilités, analyser des systèmes, automatiser les tests et renforcer votre sécurité.

··Flux·Contact·Confidentialité·© 2026 Kitploit

Répertoire d'outils

Catégories

Voir toutes les catégories
Loading categories
gortex-PoC — PoC — suivi de lien symbolique vers une divulgation de contenu hors dépôt via search_text dans Gortex (GHSA-6vhf-4wcm-2r83, CVE-2026-87003, CVSS 5.5). | Kitploit
Outils/GitHubGitHub/squeeze440/gortex-poc
Analyse StatiqueAnalyse des VulnérabilitésAnalyse de CodeExploitationCollecte d'InformationsSécurité WebArticles et Recherche
GitHubsqueeze440/gortex-poc

gortex-PoC

PoC — suivi de lien symbolique vers une divulgation de contenu hors dépôt via search_text dans Gortex (GHSA-6vhf-4wcm-2r83, CVE-2026-87003, CVSS 5.5).

Voir le dépôt
il y a 7 joursPas encore vérifié

Populaires

Voir tout →

Découvrez les outils les plus utilisés par notre communauté.

Explorer tous les outils

Parcourez notre collection d'outils

Voir tous les outils →
Partager

Suivi de liens symboliques dans le parcours de fichiers de l'indexeur → Divulgation de contenu hors dépôt via search_text

Statut CVE : demandé, en attente d'attribution. Cette découverte est publiée sous GHSA-6vhf-4wcm-2r83. À l'attribution de la CVE, ce dépôt sera renommé CVE-YYYY-NNNNN-gortex-PoC et cette bannière sera remplacée par le lien CVE.

ChercheurDostxodjayev Abdullox (@squeeze440)
AvisGHSA-6vhf-4wcm-2r83
CVSS 3.15.5 (Moyen)
FaiblesseCWE-59, CWE-200

Suivi de liens symboliques dans le parcours de fichiers de l'indexeur → Divulgation de contenu hors dépôt via search_text

Résumé

Une faille de résolution de liens incorrecte (CWE-59) dans l'indexeur de dépôt de gortex permet à une entrée de fichier symbolique pointant en dehors de la racine du dépôt indexé d'être silencieusement parcourue, indexée en contenu, puis servie verbatim — y compris vers une cible entièrement hors racine — par le puits de relecture en direct de l'outil MCP search_text, qui n'applique jamais la vérification de confinement guardSymlinkWithinRepo propre au projet.

Produit

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

Version testée

Commit b5a63e25c0719c8ce935e59abf85618e2b4b1e64 (git describe : v0.62.0-35-gb5a63e25), le HEAD du dépôt au moment de l'audit. Confirmé toujours présent dans la dernière version étiquetée v0.62.0, c'est-à-dire après l'arrivée du correctif pour l'avis non lié GHSA-w42c-h7hr-f67p (v0.55.0).

CVSS v3.1 estimé

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

  • AV:L — correspond au précédent de notation propre à ce projet pour sa surface MCP (GHSA-w42c-h7hr-f67p utilisait également AV:L) : le déploiement par défaut est un démon MCP local atteint via stdio par le processus agent sur le même hôte ; AV:N ne s'appliquerait qu'au mode optionnel --http-addr.
  • UI:R — nécessite que le client MCP (un agent LLM, ou un humain via gortex call) émette un appel search_text / search_text regexp:true. Un littéral large (par exemple un seul caractère courant) ou une regex correspondante (.) contre un dépôt indexé contenant le lien symbolique planté suffit à exfiltrer le fichier cible — aucune connaissance secrète de son contenu n'est nécessaire — c'est donc un seuil bas, mais il s'agit d'une action distincte de la simple indexation.
  • C:H — divulgation de contenu de fichier arbitraire, sans limite par les frontières du dépôt, de tout ce que l'utilisateur OS du démon peut lire (clés SSH, fichiers d'identifiants cloud, dépôts d'autres locataires dans un démon multi-dépôts, etc.), renvoyé verbatim dans la réponse de l'outil.
  • I:N / A:N — il s'agit d'un bug pur de chemin de lecture ; rien n'est écrit ni rendu indisponible.

Détails

Cause racine 1 — le parcours de l'indexeur ne vérifie jamais les entrées de fichiers symboliques

internal/indexer/indexer.go:2549-2604, le parcours principal d'admission au corpus :

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 utilise la sémantique Lstat, donc un répertoire symbolique est naturellement protégé — d.IsDir() est faux pour lui et il n'est jamais parcouru récursivement. Une entrée de fichier symbolique ne bénéficie d'aucune protection équivalente : rien ici (ni dans shouldExclude/shouldPruneDir, internal/indexer/indexer.go:5724-5813, qui ne font correspondre que des motifs de chemin de style gitignore) n'appelle d.Type()&os.ModeSymlink, os.Lstat ou filepath.EvalSymlinks sur l'entrée de fichier avant de la mettre en file. Un fichier nommé par exemple pwn.go dont la cible est /etc/passwd, ~/.ssh/id_rsa, ou un dépôt d'un locataire voisin est admis uniquement parce que son nom correspond à une extension enregistrée (effectiveLanguage, internal/parser DetectLanguageContent, basé sur le chemin). Cette liste exacte de fichiers parcourus devient idx.fileMtimes (internal/indexer/indexer.go:3760 : idx.fileMtimes[idx.relKey(f.path)] = f.mtimeNano).

Cause racine 2 — le puits search_text relit les fichiers en direct sans garde de confinement

internal/indexer/grep.go:17-64 (GrepText / warmTrigramSearcher) construit le chercheur de trigrammes directement à partir des clés de 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) et :55-86/:102-176 (Grep/GrepRegexp) font ensuite :

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 suivent les liens symboliques par défaut (pas de O_NOFOLLOW). Ni ces fonctions, ni internal/mcp/tools_search_text.go:35-126 (handleSearchText, le gestionnaire MCP qui appelle s.indexer.GrepText/GrepRegexp), n'appellent jamais resolveFilePath ou guardSymlinkWithinRepo. Ces deux fonctions de confinement existent et sont correctement câblées dans tous les autres chemins servant du contenu — internal/mcp/tools_fileops.go (read_file, write_file/edit_file depuis le correctif v0.55.0), internal/mcp/tools_coding.go:919 (get_symbol_source), internal/mcp/tools_lsp.go:264, internal/mcp/tools_export.go:95 — mais le chemin de grep par trigrammes de search_text est un puits séparé qui n'a jamais reçu la même garde. grep -rn guardSymlinkWithinRepo dans tout l'arbre confirme zéro référence dans tools_search_text.go, grep.go ou trigram/searcher.go.

Vérification de risque de doublon / de distinction

Ceci est véritablement distinct de GHSA-w42c-h7hr-f67p :

  • Cet avis : chemin d'écriture, resolveFilePath exemptant les chemins absolus + write_file/edit_file/move_inline n'appelant jamais guardSymlinkWithinRepo (corrigé dans v0.55.0).
  • Cette découverte : chemin de lecture, le parcours de découverte propre à l'indexeur (et non un gestionnaire MCP) admet un fichier symbolique sans vérification Lstat, et un autre outil MCP (search_text, pas read_file/get_symbol_source) sert son contenu via un puits (internal/search/trigram) qui, indépendamment, n'appelle jamais guardSymlinkWithinRepo. Le correctif du chemin d'écriture n'a touché ni l'un ni l'autre de ces deux emplacements. Cela correspond au schéma d'outils similaires déjà confirmé dans ozgurcd/gograph (GHSA-6h6w-vhgr-2hp6) et vitali87/code-graph-rag (GHSA-85gg-2gfq-q95m) : un parcours de découverte de fichiers sans protection contre les liens symboliques, associé à un puits servant du contenu qui, indépendamment, manque d'une garde de confinement.

Preuve de concept

Trace statique (références fichier:ligne ci-dessus) est complète et reproductible par inspection. La vérification dynamique a été tentée et est véritablement bloquée par une lacune de chaîne d'outils dans ce bac à sable, documentée honnêtement plutôt que dissimulée :

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

Chaque extracteur de langage tree-sitter de cette base de code (y compris l'extracteur Go simple, github.com/tree-sitter/tree-sitter-go's bindings/go/binding.go : // #cgo CFLAGS: -std=c11 -fPIC / import "C") nécessite cgo pour compiler — il n'existe aucun chemin de code purement Go pour enregistrer ne serait-ce qu'un seul langage, donc parser.Registry ne peut pas être construit sans un compilateur C fonctionnel. Ce bac à sable n'a ni gcc/cc/clang sur $PATH ni root sans mot de passe (sudo -l exige un mot de passe interactif) pour en installer un via apt-get install gcc (présent dans le cache apt, gcc-16/kali-last-snapshot, mais inaccessible sans privilège). Il s'agit d'une limitation d'environnement, non d'une conclusion sur gortex.

Le test PoC lui-même — écrit selon le modèle de harnais de test existant du projet (internal/mcp/tools_search_text_test.go's TestSearchText, et la même forme newSingleRepoServer/callTool que le PoC de l'avis précédent utilisait) — est laissé en place à internal/mcp/poc_symlink_search_disclosure_test.go pour que le mainteneur puisse l'exécuter dans un environnement disposant d'une chaîne d'outils C :

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

Compte tenu de la trace au niveau source ci-dessus — chaque étape nommée par fonction exacte et aucun appel de confinement nulle part sur le chemin depuis filepath.WalkDir en passant par trigram.Build/Grep jusqu'à la réponse de handleSearchText — ceci est présenté comme tracé statiquement, non confirmé dynamiquement, conformément à la règle fondamentale de cet audit contre la fabrication de preuves PoC.

Impact

Tout dépôt indexé contenant un lien symbolique dont la cible se résout en dehors de la racine de ce dépôt (planté par un contributeur malveillant, présent dans un dépôt de dépendance/plugin cloné, ou introduit via une PR de chaîne d'approvisionnement que le démon indexe ensuite) permet à un client MCP — un appel search_text normal, ou un appel injecté par prompt/adversarial, même modèle de menace que GHSA-w42c-h7hr-f67p — de lire le contenu verbatim de ce fichier cible, indépendamment de la garde de confinement que le projet applique par ailleurs sur read_file et get_symbol_source. Dans un démon multi-dépôts (internal/indexer/multi.go:3525-3562, qui déploie l'appel identique non gardé par dépôt suivi), cela franchit également les frontières de locataires/dépôts au sein de la même instance de démon.

Faiblesses

  • CWE-59 — Résolution de lien incorrecte avant accès au fichier (« suivi de lien ») : le parcours de l'indexeur (internal/indexer/indexer.go:2549) admet une entrée de fichier symbolique sans vérification Lstat/ModeSymlink.
  • CWE-200 — Exposition d'informations sensibles à un acteur non autorisé : search_text (internal/mcp/tools_search_text.go) → trigram.Build/Grep/GrepRegexp (internal/search/trigram/searcher.go) servent le contenu résolu par lien symbolique sans vérification de confinement guardSymlinkWithinRepo, contrairement à tous les autres puits de lecture de la même base de code.

Remédiation

Appliquer la fonction guardSymlinkWithinRepo déjà existante du projet (internal/mcp/tools_fileops.go:353) — ou une vérification équivalente basée sur Lstat — aux deux points :

  1. Au moment du parcours : dans le callback filepath.WalkDir (internal/indexer/indexer.go:2549), rejeter ou ignorer une DirEntry de fichier dont d.Type()&os.ModeSymlink != 0 (reproduisant la protection implicite que Walk accorde déjà aux répertoires), ou résoudre filepath.EvalSymlinks(path) et confirmer le confinement dans absRoot avant de l'ajouter à files.
  2. Au moment de la lecture (défense en profondeur) : dans trigram.Build/Grep/GrepRegexp (internal/search/trigram/searcher.go), ou au site d'appel de handleSearchText, appliquer la même vérification de confinement par chemin réel de style guardSymlinkWithinRepo déjà utilisée par read_file/get_symbol_source/tools_lsp/tools_export avant de renvoyer le Text d'une correspondance. L'un ou l'autre correctif seul ferme cette faille ; les deux ensemble correspondent à la posture de confinement que le reste de la base de code s'impose déjà.

Crédit

Dostxodjayev Abdullox (GitHub : @squeeze440)

Canal de signalement

Le signalement privé de vulnérabilités (PVR) est activé et confirmé sur zzet/gortex ; le flux standard de brouillon d'avis de sécurité GitHub (GHSA) s'applique, conformément à la divulgation antérieure GHSA-w42c-h7hr-f67p du projet.

Télécharger l’outil