
Consul Template ने टेम्पलेट मूल्यांकन के दौरान सत्यापित किया कि symlink किस ओर इंगित कर रहा था, लेकिन उसके बाद की dependency fetch ने मूल पथ को पढ़ा। उन कार्यों के बीच लिंक को पुनः लक्षित करने से in-sandbox फ़ाइल संदर्भ out-of-sandbox फ़ाइल प्रकटीकरण में बदल गया।
Consul Template ने जाँच की कि टेम्पलेट मूल्यांकन के दौरान एक सिमलिंक किस ओर इंगित कर रहा था, लेकिन उसकी बाद की निर्भरता फ़ेच ने मूल पथ को पढ़ा। उन ऑपरेशनों के बीच लिंक को पुनः लक्षित करने से सैंडबॉक्स के अंदर की फ़ाइल का संदर्भ, सैंडबॉक्स के बाहर की फ़ाइल के प्रकटीकरण में बदल गया।
मुझे यह समस्या HashiCorp Consul Template की समीक्षा करते समय मिली, जब मेरे मन में एक बहुत ही विशिष्ट सुरक्षा प्रश्न था:
यदि sandbox_path टेम्पलेट मूल्यांकन के दौरान किसी सिमलिंक को वैलिडेट करता है, तो क्या बाद में फ़ाइल पढ़ना उसी वैलिडेट किए गए लक्ष्य से बंधा रहता है?
इस मामले में, उत्तर नहीं था।
file टेम्पलेट हेल्पर ने टेम्पलेट मूल्यांकन के दौरान पथ को रिज़ॉल्व किया और कॉन्फ़िगर किए गए सैंडबॉक्स को लागू किया। लेकिन उस जाँच के बाद, उसने मूल कच्चे पथ का उपयोग करके एक फ़ाइल निर्भरता बनाई।
वह निर्भरता बाद में फ़ेच की गई।
यदि कोई हमलावर वैलिडेशन और निर्भरता फ़ेच के बीच के अंतराल में सिमलिंक को पुनः लक्षित कर देता, तो Consul Template सैंडबॉक्स से बाहर की फ़ाइल पढ़ सकता था। यदि अगले रेंडर से पहले लिंक को पुनर्स्थापित कर दिया जाता, तो सैंडबॉक्स वैलिडेशन फिर से पास हो जाता और कैश की गई बाहरी सामग्री फिर भी रेंडर हो जाती।
वही समस्या CVE-2026-5061 बन गई।
HashiCorp बुलेटिन: HCSEC-2026-12
IBM बुलेटिन: CVE-2026-5061 सुरक्षा बुलेटिन
CVE: CVE-2026-5061
इसमें ठीक किया गया: 0.42.0
photo0
attacker-controlled in-sandbox symlink -> sandbox validation resolves to safe target -> dependency stores original raw path -> attacker retargets link outside sandbox -> dependency fetch reads external file -> attacker restores safe link -> next validation passes -> cached external content is rendered
Consul Template Consul और Vault डेटा के लिए एक टेम्पलेट रेंडरिंग टूल है।
यह लगातार चल सकता है, परिवर्तनों के लिए निर्भरताओं पर नज़र रख सकता है, और एप्लिकेशन द्वारा उपभोग के लिए डेटा को फ़ाइलों या पर्यावरण चर में रेंडर कर सकता है।
file टेम्पलेट हेल्पर एक स्थानीय फ़ाइल पढ़ता है और उसकी सामग्री को रेंडर किए गए आउटपुट में डालता है।
चूँकि स्थानीय फ़ाइल पठन प्रक्रिया-पठनीय गोपनीय डेटा उजागर कर सकता है, Consul Template इस हेल्पर के लिए sandbox_path को एक सीमा के रूप में प्रदान करता है।
प्रलेखित सुरक्षा गुण सीधा है:
file को दिए गए पथ कॉन्फ़िगर किए गए सैंडबॉक्स के अंदर होने चाहिएयह sandbox_path को एक वास्तविक सुरक्षा सीमा बनाता है।
महत्वपूर्ण प्रश्न यह नहीं था कि क्या पथ सैंडबॉक्स के अंतर्गत दिख रहा था।
असली प्रश्न था:
क्या जो फ़ाइल पढ़ी जाती है, वह वही फ़ाइल रहती है जिसने सैंडबॉक्स वैलिडेशन पास किया था?
कमजोर संस्करणों में, ऐसा नहीं था।
फाइलसिस्टम सैंडबॉक्स अक्सर पथ वैलिडेशन और फ़ाइल एक्सेस के बीच के अंतराल पर विफल होते हैं।
सामान्य पैटर्न यह है:
वह धारणा असुरक्षित है जब कोई हमलावर दोनों ऑपरेशनों के बीच सिमलिंक या समकक्ष फाइलसिस्टम पुनर्निर्देशन को संशोधित कर सकता है।
Consul Template ने इस सतह को विशेष रूप से दिलचस्प बना दिया क्योंकि टेम्पलेट मूल्यांकन और निर्भरता फ़ेच अलग-अलग चरण थे।
उस अलगाव ने सही सुरक्षा प्रश्न निर्मित किया:
क्या निर्भरता फ़ेच वैलिडेट किए गए लक्ष्य से बंधा है, या क्या यह हमलावर-नियंत्रित पथ को फिर से रिज़ॉल्व करता है?
मैंने इसी सीमा पर ध्यान केंद्रित किया।
मूल कारण जाँच-के-समय से उपयोग-के-समय (time-of-check to time-of-use) के बीच का बेमेल था:
template/funcs.go में सैंडबॉक्स वैलिडेशनdependency/file.go में बाद में निर्भरता पठनपरीक्षण किए गए संशोधन पर, fileFunc() ने यह किया:
normalized := strings.TrimSpace(s)
err := pathInSandbox(sandboxPath, normalized)
if err != nil {
return "", err
}
d, err := dep.NewFileQuery(s)
वैलिडेशन हेल्पर ने समावेशन की जाँच करने से पहले सिमलिंक को सही ढंग से रिज़ॉल्व किया:
sandboxResolved, err := filepath.EvalSymlinks(filepath.Clean(sandbox))
targetResolved, err := filepath.EvalSymlinks(filepath.Clean(path))
rel, err := filepath.Rel(sandboxResolved, targetResolved)
if rel == ".." || strings.HasPrefix(rel, ".."+string(filepath.Separator)) {
return fmt.Errorf("'%s' is outside of sandbox", path)
}
अतः सैंडबॉक्स जाँच ने स्वयं रिज़ॉल्व किए गए लक्ष्य को समझा।
लेकिन वह रिज़ॉल्व किया गया लक्ष्य छोड़ दिया गया।
NewFileQuery() ने इसके बजाय मूल स्ट्रिंग संग्रहीत की:
return &FileQuery{
stopCh: make(chan struct{}, 1),
path: s,
}, nil
फिर बाद की निर्भरता फ़ेच ने उस पथ को फिर से पढ़ा:
data, err := os.ReadFile(d.path)
यही पूरी कमजोरी है।
कोड ने एक फाइलसिस्टम रिज़ॉल्यूशन की जाँच की, फिर बाद में दूसरे का उपयोग किया।
क्योंकि एक सिमलिंक परिवर्तनशील (mutable) है।
हमलावर को सैंडबॉक्स से बाहर के लक्ष्य को सीधे pathInSandbox() से पास कराने की आवश्यकता नहीं है।
उन्हें केवल यह बदलना होता है कि वैलिडेशन के बाद लेकिन Fetch() द्वारा पठन करने से पहले पहले से स्वीकृत कच्चा पथ किस ओर रिज़ॉल्व होता है।
अनुक्रम यह है:
pathInSandbox() के दौरान लिंक एक सुरक्षित फ़ाइल की ओर रिज़ॉल्व होता हैFileQuery कच्चे लिंक पथ को दर्ज करता हैos.ReadFile(d.path) लिंक को फिर से रिज़ॉल्व करता हैवह अंतिम चरण महत्वपूर्ण है।
सुरक्षित लिंक को पुनर्स्थापित करने से पहले से फ़ेच किया गया गोपनीय डेटा निर्भरता कैश से नहीं हटा।
महत्वपूर्ण अंतर एक स्पष्ट सुरक्षा नियंत्रण का बायपास है।
यह केवल यह नहीं था:
"फ़ाइल तब बदल गई जब Consul Template उसे देख रहा था"
परिवर्तनों के लिए फ़ाइलों पर नज़र रखना अपेक्षित व्यवहार है।
असली मुद्दा था:
एक पथ जिसने प्रलेखित सैंडबॉक्स प्रतिबंध को पार किया था, बाद में उस सैंडबॉक्स के बाहर की फ़ाइल को पढ़ने के लिए उपयोग किया जा सकता था
यह प्रत्यक्ष विश्वास-सीमा (trust-boundary) विफलता है।
एप्लिकेशन पहले ही एक सुरक्षा निर्णय ले चुका था:
लेकिन बाद की फ़ेच उस लक्ष्य से बंधी नहीं थी जिसने उस निर्णय को उचित ठहराया था।
इसी ने सामान्य फाइलसिस्टम परिवर्तनशीलता को एक कमजोरी में बदल दिया।
मैंने सटीक मूल्यांकन और निर्भरता-फ़ेच अनुक्रम के आसपास एक स्टैंडअलोन रिप्रोड्यूसर बनाया।
नियंत्रित सेटअप में उपयोग किया गया:
पुनरुत्पादन प्रवाह यह था:
file हेल्पर का मूल्यांकन करें ताकि सैंडबॉक्स वैलिडेशन सफल हो और कच्चा पथ एक निर्भरता के रूप में पंजीकृत हो।os.ReadFile(d.path) को पुनर्निर्देशित लिंक के माध्यम से पढ़ने दें।अवलोकित व्यवहार यह था: