Skip to content
KitploitKITPLOIT
OutilsExploitsBlog
Log in
Soumettre
OutilsExploitsBlog
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é.

FluxContactConfidentialité© 2026 Kitploit

Répertoire d'outils

Catégories

Voir toutes les catégories
Loading categories
code-graph-rag-PoC — PoC — suivi de lien symbolique permettant la lecture/écriture arbitraire de fichiers en dehors de la racine du projet dans code-graph-rag (GHSA-85gg-2gfq-q95m, CVE-2026-87008, CVSS 7.1). | Kitploit
Outils/GitHubGitHub/squeeze440/code-graph-rag-poc
Analyse des VulnérabilitésExploitationExploitation d'Applications WebTests d'IntrusionArticles et RechercheApprentissage et Éducation
GitHubsqueeze440/code-graph-rag-poc

code-graph-rag-PoC

PoC — suivi de lien symbolique permettant la lecture/écriture arbitraire de fichiers en dehors de la racine du projet dans code-graph-rag (GHSA-85gg-2gfq-q95m, CVE-2026-87008, CVSS 7.1).

Voir le dépôt
10il y a 20 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

code-graph-rag : avis de sécurité

Statut CVE : demandé, en attente d'attribution. Cette découverte est publiée sous GHSA-85gg-2gfq-q95m. À l'attribution de la CVE, ce dépôt sera renommé CVE-YYYY-NNNNN-code-graph-rag-PoC et cette bannière sera remplacée par le lien CVE.

ChercheurDostxodjayev Abdullox (@squeeze440)
AvisGHSA-85gg-2gfq-q95m
CVSS 3.17.1 (Élevé)
FaiblesseCWE-59, CWE-22

Résumé

Un attaquant distant/local capable de faire inclure un lien symbolique dans un dépôt de code source que code-graph-rag analyse peut amener les outils structural_search et structural_replace (reposant sur AstGrepService, exposés à la fois comme outils MCP et comme outils d'IA agentique) à lire et — via structural_replace avec dry_run=False — à écraser des fichiers arbitraires en dehors de la racine de projet configurée, car la vérification de confinement de chemin de l'outil (should_skip_path/_classify_file) est effectuée de manière lexicale sur le chemin non résolu et ne vérifie jamais Path.is_symlink() ni n'appelle .resolve(), contrairement au décorateur validate_project_path du projet lui-même (correct) utilisé ailleurs.

Produit

vitali87/code-graph-rag (PyPI : code-graph-rag, CLI : cgr)

Version testée

Commit 90a3ed3cbdc7d3bb8036985b851cc7c9a3ba9c57 (version pyproject 0.0.550) — main actuel au moment du test. Confirmé que ce commit contient déjà le correctif de l'avis précédent (GHSA-vvr2-h2jp-838m : traversée de chemin dans read_file paginé) et la nouvelle barrière d'authentification bearer HTTP-MCP (_validate_http_exposure dans codebase_rag/mcp/server.py), il s'agit donc d'un problème distinct, toujours ouvert, par-dessus ces correctifs.

CVSS v3.1 estimé

CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:N — 7.0 (Élevé)

Métriques non évidentes :

  • AV:L — la faille est exploitable purement via l'usage local/CLI/stdio-MCP de base de l'outil (analyse d'un dépôt contenant un lien symbolique implanté) ; elle ne dépend pas du transport HTTP MCP optionnel, désormais protégé par jeton bearer, donc le vecteur de base est évalué sur la surface locale toujours présente plutôt qu'en supposant un déploiement distant.
  • UI:R — l'attaquant fournit le dépôt malveillant (le lien symbolique), mais une partie distincte (un utilisateur, ou un agent d'IA agissant en son nom) doit réellement exécuter structural_search/structural_replace contre ce projet pour que la lecture/écriture se déclenche.
  • A:N — l'écrasement arbitraire du contenu d'un fichier a été démontré ; aucune chaîne de crash/DoS système n'a été démontrée, donc la Disponibilité est évaluée de manière conservatrice comme None plutôt que supposée.

Détails

AstGrepService (codebase_rag/tools/ast_grep_service.py) alimente à la fois les outils MCP/agentiques structural_search et structural_replace. Il énumère les fichiers candidats avec os.walk() et ne filtre chaque chemin que via should_skip_path() :

  • codebase_rag/tools/ast_grep_service.py:84-109 (_iter_source_files) — parcourt self.project_root avec os.walk() ; chaque entrée de répertoire non-répertoire retournée par os.walk (y compris un lien symbolique pointant n'importe où sur le disque) est traitée comme un fichier dans le périmètre.
  • codebase_rag/tools/ast_grep_service.py:61-82 (_classify_file) — la seule vérification de confinement est should_skip_path(...) suivie de abs_path.relative_to(self.project_root) (ligne 82) ; abs_path n'est jamais résolu, donc cette vérification est purement lexicale/basée sur des chaînes.
  • codebase_rag/utils/path_utils.py:80-109 (should_skip_path) et codebase_rag/utils/path_utils.py:35-37 (cached_relative_path) — calcule rel_path = file_path.relative_to(repo_path) sans jamais appeler .resolve() ni Path.is_symlink(). Rien dans cette fonction ne traite un lien symbolique différemment d'un fichier réel.
  • Puits de lecture : codebase_rag/tools/ast_grep_service.py:143-144 (search) — source = self._read(abs_path) appelle path.read_text(), que Python suit à travers le lien symbolique jusqu'à sa cible réelle, peu importe où cette cible se trouve.
  • Puits d'écriture : codebase_rag/tools/ast_grep_service.py:187-208 (replace), spécifiquement la ligne 208 — abs_path.write_text(new_source, ...) lorsque dry_run=False, suivant à nouveau le lien symbolique et écrasant le contenu du fichier cible réel.

Ceci est incohérent avec la façon dont le reste du codebase gère exactement la même classe de risque. Les outils de lecture/écriture/édition de fichiers (file_reader.py, file_writer.py, file_editor.py) sont tous protégés par validate_project_path (codebase_rag/decorators.py:73-75) :

full_path = (self.project_root / file_path_str).resolve()
project_root = self.project_root.resolve()
full_path.relative_to(project_root)

.resolve() suit les liens symboliques avant que la vérification de confinement ne s'exécute, donc ces outils rejettent correctement une cible hors racine. Le propre absolute_path_within_project_root() de path_utils.py (codebase_rag/utils/path_utils.py:156-176) documente explicitement le même principe dans sa docstring : « Les appels à resolve() sont porteurs : le confinement est vérifié de manière lexicale, donc un segment .. non résolu ou un lien symbolique s'échapperait de la racine. » — mais should_skip_path()/AstGrepService, utilisés par structural_search/structural_replace, n'appliquent jamais ce modèle.

Accessibilité / exposition des deux outils :

  • structural_search (codebase_rag/tools/structural_search.py:24-46) ne porte aucun indicateur requires_approval — le modèle d'un client MCP peut l'appeler de manière autonome sans confirmation humaine.
  • structural_replace (codebase_rag/tools/structural_editor.py:52-57) est marqué requires_approval=True, mais cet indicateur n'est appliqué que par la propre boucle d'agent de pydantic-ai. Le chemin du serveur MCP le contourne entièrement : MCPToolsRegistry.structural_replace (codebase_rag/mcp/tools.py:586-596) appelle directement self._structural_editor_tool.function(...), et l'outil MCP est enregistré sans aucun concept d'approbation à codebase_rag/mcp/tools.py:369-388 (ToolMetadata pour MCPToolName.STRUCTURAL_REPLACE) — tout client MCP capable d'appeler des outils peut invoquer structural_replace avec dry_run=False en une seule fois.

Preuve de concept

Confirmé dynamiquement contre le paquet installé (mgclient/pymgclient simulés exactement comme dans le PoC de l'avis précédent, car le client natif Memgraph n'est pas nécessaire pour ce chemin de code).

mkdir -p /tmp/poc_symlink/safe-project-root
cat > /tmp/poc_symlink/outside-secret.py << 'EOF'
API_TOKEN = "sk-live-EXAMPLE-NOT-A-REAL-SECRET-1234567890"
def get_token():
    return API_TOKEN
EOF
ln -s /tmp/poc_symlink/outside-secret.py /tmp/poc_symlink/safe-project-root/linked_module.py
# poc.py
import sys
from pathlib import Path
from unittest.mock import MagicMock
sys.modules["mgclient"] = MagicMock()
sys.modules["pymgclient"] = MagicMock()
from codebase_rag.tools.ast_grep_service import AstGrepService

SAFE_ROOT = "/tmp/poc_symlink/safe-project-root"
svc = AstGrepService(project_root=SAFE_ROOT)

matches = svc.search(pattern="API_TOKEN", language="python")
# -> match in reported file='linked_module.py' text='API_TOKEN'  (read escape)

changes = svc.replace(pattern="API_TOKEN", rewrite="PWNED_BY_STRUCTURAL_REPLACE",
                       language="python", dry_run=False)
print(Path("/tmp/poc_symlink/outside-secret.py").read_text())
Télécharger l’outil