Skip to content
KitploitKITPLOIT
उपकरणब्लॉग
जमा करें
उपकरणब्लॉग
जमा करें

हैकिंग, पेनटेस्ट और साइबर सुरक्षा उपकरण आपके सुरक्षा शस्त्रागार के लिए!

Kitploit हैकिंग, साइबर सुरक्षा और पेंटेस्टिंग टूल्स की एक निर्देशिका है। कमजोरियों को खोजने, सिस्टम का विश्लेषण करने, परीक्षण को स्वचालित करने और अपनी सुरक्षा को मजबूत करने के लिए नवीनतम प्रोजेक्ट अपडेट खोजें।

··फ़ीड·संपर्क·गोपनीयता·© 2026 Kitploit

टूल निर्देशिका

श्रेणियाँ

सभी श्रेणियाँ देखें
Loading categories
gortex-PoC — PoC — Gortex में search_text के माध्यम से रेपो से बाहर की सामग्री के प्रकटीकरण हेतु symlink फ़ॉलोइंग (GHSA-6vhf-4wcm-2r83, CVE-2026-87003, CVSS 5.5)। | Kitploit
उपकरण/GitHubGitHub/squeeze440/gortex-poc
स्थैतिक विश्लेषणभेद्यता विश्लेषणकोड विश्लेषणशोषणजानकारी एकत्र करनावेब सुरक्षापेपर और शोध
GitHubsqueeze440/gortex-poc

gortex-PoC

PoC — Gortex में search_text के माध्यम से रेपो से बाहर की सामग्री के प्रकटीकरण हेतु symlink फ़ॉलोइंग (GHSA-6vhf-4wcm-2r83, CVE-2026-87003, CVSS 5.5)।

रिपॉजिटरी देखें
7 दिन पहलेअभी तक समीक्षित नहीं

सबसे लोकप्रिय

सभी देखें →

हमारे समुदाय द्वारा सबसे अधिक उपयोग किए जाने वाले उपकरण खोजें।

सभी उपकरण खोजें

हमारे उपकरणों का संग्रह ब्राउज़ करें

सभी उपकरण देखें →
साझा करें

Indexer File-Walk में Symlink Following → search_text के माध्यम से Out-of-Repo Content Disclosure

CVE स्थिति: अनुरोध किया गया, असाइनमेंट लंबित। यह निष्कर्ष GHSA-6vhf-4wcm-2r83 के रूप में प्रकाशित किया गया है। CVE असाइनमेंट पर इस रिपॉज़िटरी का नाम बदलकर CVE-YYYY-NNNNN-gortex-PoC कर दिया जाएगा और इस बैनर को CVE लिंक से बदल दिया जाएगा।

शोधकर्ताDostxodjayev Abdullox (@squeeze440)
सलाहGHSA-6vhf-4wcm-2r83
CVSS 3.15.5 (मध्यम)
कमज़ोरीCWE-59, CWE-200

Indexer File-Walk में Symlink Following → search_text के माध्यम से Out-of-Repo Content Disclosure

सारांश

gortex के रिपॉज़िटरी indexer में एक अनुचित लिंक रिज़ॉल्यूशन दोष (CWE-59) एक symlinked file प्रविष्टि को, जो indexed रिपॉज़िटरी रूट के बाहर इंगित करती है, चुपचाप walk होने, content-indexed होने, और बाद में verbatim रूप से serve होने की अनुमति देता है — जिसमें पूरी तरह से out-of-root लक्ष्य भी शामिल है — search_text MCP टूल के live re-read sink द्वारा, जो कभी प्रोजेक्ट के स्वयं के guardSymlinkWithinRepo confinement check को लागू नहीं करता।

उत्पाद

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

परीक्षण किया गया संस्करण

Commit b5a63e25c0719c8ce935e59abf85618e2b4b1e64 (git describe: v0.62.0-35-gb5a63e25), ऑडिट के समय रिपॉज़िटरी HEAD। नवीनतम tagged release v0.62.0 पर भी अभी भी मौजूद होने की पुष्टि की गई, अर्थात् असंबंधित GHSA-w42c-h7hr-f67p (v0.55.0) के fix के बाद भी।

अनुमानित CVSS v3.1

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

  • AV:L — इस प्रोजेक्ट के अपने MCP सरफेस के लिए scoring precedent से मेल खाता है (GHSA-w42c-h7hr-f67p ने भी AV:L का उपयोग किया): डिफ़ॉल्ट deployment एक local MCP daemon है जिसे उसी होस्ट पर agent प्रक्रिया द्वारा stdio के माध्यम से पहुँचा जाता है; AV:N केवल opt-in --http-addr मोड पर लागू होगा।
  • UI:R — इसके लिए MCP client (एक LLM agent, या gortex call के माध्यम से एक मानव) को search_text / search_text regexp:true कॉल जारी करने की आवश्यकता होती है। एक व्यापक literal (जैसे एक सामान्य वर्ण) या एक मेल खाता regex (.) जो planted symlink वाली indexed repo के विरुद्ध है, target फ़ाइल को dump करने के लिए पर्याप्त है — इसकी सामग्री का कोई गुप्त ज्ञान आवश्यक नहीं — इसलिए यह एक निम्न बाधा है, लेकिन यह केवल indexing से एक अलग क्रिया है।
  • C:H — मनमानी-फ़ाइल-सामग्री का disclosure, repo सीमाओं से अपरिबद्ध, जो भी daemon का OS उपयोगकर्ता पढ़ सकता है (SSH keys, cloud credential files, multi-repo daemon में sibling tenants की repos, आदि), टूल प्रतिक्रिया में verbatim लौटाया गया।
  • I:N / A:N — यह एक शुद्ध read-path बग है; कुछ भी लिखा या अनुपलब्ध नहीं किया जाता।

विवरण

मूल कारण 1 — indexer walk कभी symlinked file प्रविष्टियों की जाँच नहीं करता

internal/indexer/indexer.go:2549-2604, प्राथमिक corpus-admission 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 Lstat semantics का उपयोग करता है, इसलिए एक symlinked directory स्वाभाविक रूप से सुरक्षित है — इसके लिए d.IsDir() false है और इसमें कभी recurse नहीं किया जाता। एक symlinked file प्रविष्टि को कोई समकक्ष सुरक्षा नहीं मिलती: यहाँ कुछ भी (और न ही shouldExclude/shouldPruneDir में, internal/indexer/indexer.go:5724-5813, जो केवल gitignore-style path patterns से मेल खाते हैं) फ़ाइल प्रविष्टि को queue करने से पहले d.Type()&os.ModeSymlink, os.Lstat, या filepath.EvalSymlinks को कॉल नहीं करता। pwn.go नाम की एक फ़ाइल जिसका लक्ष्य /etc/passwd, ~/.ssh/id_rsa, या एक sibling tenant repo है, केवल इसलिए स्वीकार की जाती है क्योंकि इसका नाम एक पंजीकृत extension से मेल खाता है (effectiveLanguage, internal/parser DetectLanguageContent, path-based)। यह ठीक walked-file सूची idx.fileMtimes बन जाती है (internal/indexer/indexer.go:3760: idx.fileMtimes[idx.relKey(f.path)] = f.mtimeNano)।

मूल कारण 2 — search_text sink बिना किसी confinement guard के live फ़ाइलों को फिर से पढ़ता है

internal/indexer/grep.go:17-64 (GrepText / warmTrigramSearcher) सीधे idx.fileMtimes की keys से trigram searcher बनाता है:

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) और :55-86/:102-176 (Grep/GrepRegexp) फिर यह करते हैं:

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 डिफ़ॉल्ट रूप से symlinks का अनुसरण करते हैं (कोई O_NOFOLLOW नहीं)। न तो कोई फ़ंक्शन, और न ही internal/mcp/tools_search_text.go:35-126 (handleSearchText, वह MCP handler जो s.indexer.GrepText/GrepRegexp को कॉल करता है), कभी resolveFilePath या guardSymlinkWithinRepo को कॉल करता है। वे दो confinement फ़ंक्शन मौजूद हैं और हर अन्य content-serving path में सही ढंग से wired हैं — internal/mcp/tools_fileops.go (read_file, write_file/edit_file 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 — लेकिन search_text का trigram grep path एक अलग sink है जिसे कभी वही guard नहीं दिया गया। पूरे tree में grep -rn guardSymlinkWithinRepo tools_search_text.go, grep.go, या trigram/searcher.go में शून्य संदर्भों की पुष्टि करता है।

Duplicate-risk / distinctness जाँच

यह वास्तव में GHSA-w42c-h7hr-f67p से अलग है:

  • वह advisory: write path, resolveFilePath absolute paths को छूट देता है + write_file/edit_file/move_inline कभी guardSymlinkWithinRepo को कॉल नहीं करते (v0.55.0 में fixed)।
  • यह निष्कर्ष: read path, indexer का स्वयं का discovery walk (कोई भी MCP handler नहीं) बिना किसी Lstat check के एक symlinked फ़ाइल को स्वीकार करता है, और एक अलग MCP टूल (search_text, न कि read_file/get_symbol_source) अपनी सामग्री को एक sink (internal/search/trigram) के माध्यम से serve करता है जो स्वतंत्र रूप से कभी guardSymlinkWithinRepo को कॉल नहीं करता। write-path fix ने इन दोनों स्थानों में से किसी को भी नहीं छुआ। यह sibling-tool पैटर्न से मेल खाता है जिसकी पुष्टि पहले ही ozgurcd/gograph (GHSA-6h6w-vhgr-2hp6) और vitali87/code-graph-rag (GHSA-85gg-2gfq-q95m) में की जा चुकी है: symlink सुरक्षा के बिना एक file-discovery walk, जिसके साथ एक content-serving sink जोड़ा गया है जिसमें स्वतंत्र रूप से confinement guard का अभाव है।

Proof of Concept

Static trace (ऊपर file:line संदर्भ) पूर्ण है और निरीक्षण द्वारा पुनरुत्पादनीय है। Dynamic verification का प्रयास किया गया और यह इस sandbox में एक toolchain gap द्वारा वास्तव में अवरुद्ध है, जिसे छिपाने के बजाय ईमानदारी से प्रलेखित किया गया है:

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

इस codebase में प्रत्येक tree-sitter language extractor (सादे Go extractor सहित, github.com/tree-sitter/tree-sitter-go का bindings/go/binding.go: // #cgo CFLAGS: -std=c11 -fPIC / import "C") को compile करने के लिए cgo की आवश्यकता होती है — एक भी भाषा को register करने के लिए कोई pure-Go code path नहीं है, इसलिए parser.Registry को काम करने वाले C compiler के बिना नहीं बनाया जा सकता। इस sandbox में $PATH पर कोई gcc/cc/clang नहीं है और apt-get install gcc के माध्यम से एक स्थापित करने के लिए कोई passwordless root नहीं है (sudo -l एक interactive password की मांग करता है) (apt cache में मौजूद, gcc-16/kali-last-snapshot, लेकिन privilege के बिना अगम्य)। यह एक environment सीमा है, gortex के बारे में कोई निष्कर्ष नहीं।

PoC test स्वयं — प्रोजेक्ट के अपने मौजूदा test harness पैटर्न के विरुद्ध लिखा गया (internal/mcp/tools_search_text_test.go का TestSearchText, और वही newSingleRepoServer/callTool आकार जो पूर्व advisory के अपने PoC ने उपयोग किया) — maintainer के लिए C toolchain वाले वातावरण में चलाने के लिए internal/mcp/poc_symlink_search_disclosure_test.go पर छोड़ दिया गया है:

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

ऊपर दिए गए source-level trace को देखते हुए — प्रत्येक चरण का नाम सटीक फ़ंक्शन द्वारा और filepath.WalkDir से trigram.Build/Grep के माध्यम से handleSearchText की प्रतिक्रिया तक के path पर कहीं भी कोई confinement कॉल नहीं — इसे statically traced, dynamically confirmed नहीं के रूप में प्रस्तुत किया गया है, इस ऑडिट के PoC साक्ष्य को गढ़ने के विरुद्ध ground rule के अनुसार।

प्रभाव

कोई भी indexed रिपॉज़िटरी जिसमें एक symlink है जिसका लक्ष्य उस repo के रूट के बाहर resolve होता है (एक दुर्भावनापूर्ण contributor द्वारा planted, एक cloned dependency/plugin repo में मौजूद, या एक supply-chain PR के माध्यम से पेश किया गया जिसे daemon बाद में index करता है) एक MCP client को — एक सामान्य search_text कॉल, या एक prompt-injected/adversarial कॉल, वही threat model जो GHSA-w42c-h7hr-f67p का है — उस target फ़ाइल की verbatim सामग्री पढ़ने देता है, चाहे प्रोजेक्ट अन्यथा read_file और get_symbol_source पर जो confinement guard लागू करता है। एक multi-repo daemon में (internal/indexer/multi.go:3525-3562, जो प्रत्येक tracked repo के लिए समान unguarded कॉल को fan out करता है) यह उसी daemon instance के भीतर tenant/repo सीमाओं को भी पार करता है।

कमज़ोरियाँ

  • CWE-59 — Improper Link Resolution Before File Access ('Link Following'): indexer walk (internal/indexer/indexer.go:2549) बिना किसी Lstat/ModeSymlink check के एक symlinked file प्रविष्टि को स्वीकार करता है।
  • CWE-200 — Exposure of Sensitive Information to an Unauthorized Actor: search_text (internal/mcp/tools_search_text.go) → trigram.Build/Grep/GrepRegexp (internal/search/trigram/searcher.go) symlink-resolved सामग्री को बिना किसी guardSymlinkWithinRepo confinement check के serve करते हैं, जो उसी codebase में हर अन्य read sink के विपरीत है।

उपचार

प्रोजेक्ट के अपने मौजूदा guardSymlinkWithinRepo (internal/mcp/tools_fileops.go:353) — या एक समकक्ष Lstat-आधारित check — को दोनों बिंदुओं पर लागू करें:

  1. Walk-time: filepath.WalkDir callback (internal/indexer/indexer.go:2549) में, एक फ़ाइल DirEntry को अस्वीकार या छोड़ें जिसका d.Type()&os.ModeSymlink != 0 है (उस अंतर्निहित सुरक्षा को प्रतिबिंबित करते हुए जो Walk पहले से directories को देता है), या filepath.EvalSymlinks(path) को resolve करें और files में जोड़ने से पहले absRoot के भीतर containment की पुष्टि करें।
  2. Read-time (defense in depth): trigram.Build/Grep/GrepRegexp (internal/search/trigram/searcher.go) में, या handleSearchText call site पर, एक match का Text लौटाने से पहले वही guardSymlinkWithinRepo-शैली real-path containment check लागू करें जो पहले से read_file/get_symbol_source/tools_lsp/tools_export द्वारा उपयोग किया जाता है। अकेले कोई भी fix इसे बंद कर देता है; दोनों मिलकर उस confinement posture से मेल खाते हैं जिसे शेष codebase पहले से बनाए रखता है।

श्रेय

Dostxodjayev Abdullox (GitHub: @squeeze440)

रिपोर्टिंग चैनल

Private Vulnerability Reporting (PVR) zzet/gortex पर सक्षम और पुष्ट है; मानक GitHub Security Advisory (GHSA) draft-advisory प्रवाह लागू होता है, जो प्रोजेक्ट के पूर्व GHSA-w42c-h7hr-f67p disclosure के अनुरूप है।

टूल डाउनलोड करें