
كان المساعد writeToFile في Consul Template يفتح وجهة يقدّمها المُشغِّل مباشرةً ويتبع مكوّنات المسار المرتبطة، مما سمح للمخرجات المُولَّدة بالخروج عن الدليل المقصود والكتابة فوق ملف موجود مسبقًا.
الأداة المساعدة writeToFile في Consul Template كانت تفتح الوجهة التي يوفّرها المشغِّل مباشرةً وتتتبّع مكوّنات المسار المرتبطة (روابط)، مما يسمح للمخرجات المُولَّدة بالخروج من الدليل المقصود واستبدال ملف موجود مسبقًا.
اكتشفت هذه المشكلة أثناء مراجعة HashiCorp Consul Template، مع سؤال أمني مباشر يتعلق بنظام الملفات في ذهني:
إذا استلمت writeToFile مسارًا يبدو أنه داخل الدليل المقصود، فهل تتحقق من المكان الذي سيكتب فيه نظام الملفات البيانات فعلًا؟
في هذه الحالة، كان الجواب: لا.
كانت أداة القالب المساعدة writeToFile تفتح المسار النهائي الذي يوفّره المستخدم مباشرةً عبر os.Create() أو os.OpenFile().
هذه العمليات كانت تتتبّع الروابط الرمزية (symbolic links) ونقاط التحام الدلائل (directory junctions) وأي إعادة توجيه مكافئة في نظام الملفات موجودة مسبقًا في مسار الوجهة.
هذا يعني أن سلسلة المسار قد تبقى ضمن الجذر المقصود للمشغِّل بينما تقع الكتابة الفعلية في مكان آخر.
في إثبات المفهوم المُتحكَّم به، أعاد دليل أصلي مرتبط (رابط) توجيه المخرجات المُولَّدة إلى خارج الشجرة المقصودة وتسبّب في استبدال ملف هدف موجود مسبقًا.
أصبحت هذه المشكلة CVE-2026-14361.
نشرة HashiCorp: HCSEC-2026-20
نشرة IBM: النشرة الأمنية CVE-2026-14361
CVE: CVE-2026-14361
تم الإصلاح في: 0.42.1
photo0
operator-supplied destination appears inside intended root -> attacker pre-positions linked parent or final path component -> writeToFile opens the path directly -> filesystem resolves the write outside the intended directory -> rendered secret is redirected -> preexisting target may be overwritten
تقوم Consul Template بتوليد (عرض) البيانات من مصادر مثل Consul وVault.
تسمح الأداة المساعدة writeToFile لقالب بكتابة محتوى مختار إلى ملف محلي منفصل مع تطبيق المالك والمجموعة ووضع الصلاحيات المطلوبة.
توثيق HashiCorp يعرض تحديدًا هذه الأداة المساعدة مع مواد البنية التحتية للمفاتيح العامة (PKI):
private key -> writeToFile /my/path/to/cert.key
certificate authority -> writeToFile /my/path/to/cert.pem
certificate -> append to /my/path/to/cert.pem
هذا يجعلها أكثر من مجرد أداة إخراج عادية.
المحتوى الذي يعبر هذه الحدود قد يتضمن:
السؤال المهم لم يكن ما إذا كانت writeToFile تستطيع إنشاء اسم ملف مطلوب.
السؤال الحقيقي كان:
هل تكتب العملية في موقع نظام الملفات الذي يقصده المشغِّل، أم فقط في أي كائن يتحلَّل إليه المسار وقت الفتح؟
في الإصدارات المصابة، كانت تثق في الخيار الثاني.
أدوات الكتابة المساعدة هي أسطح أمنية عالية القيمة لأنها تنتقل من بيانات التطبيق إلى التعديل على نظام الملفات.
الإخفاقات المثيرة للاهتمام غالبًا ليست اجتياز المسار التقليدي ../.
إنها إخفاقات في التحليل (resolution):
هذا مهم بشكل خاص عندما تعمل العملية بصلاحيات نظام ملفات أعلى من صلاحيات المهاجم.
قد لا يتمكن مهاجم محلي بصلاحيات منخفضة من استبدال ملف حساس مباشرة.
لكن إذا استطاعوا التأثير على مكوّن مسار ضمن دليل كتابة مقصود، فقد تقوم عملية Consul Template بصلاحيات أعلى بتنفيذ الكتابة نيابةً عنهم.
تلك كانت الحدود التي ركّزت عليها.
لم أتعامل مع هذا كمراجعة عامة لاجتياز المسار.
المسار المقدَّم لم يكن بحاجة إلى مقاطع ...
كان بإمكانه البقاء معجميًا (lexically) داخل الجذر المقصود طوال الوقت.
السؤال الأقوى كان:
هل تُرفض مكوّنات الوجهة المرتبطة قبل كتابة المحتوى الحساس؟
هذا السؤال مهم في الحالتين:
الحالة الثانية مفيدة بشكل خاص لأن السجلات والإعدادات ما زالت تعرض مسارًا يبدو بريئًا تحت الدليل المتوقع.
نظام الملفات يحلّه في مكان آخر.
السبب الجذري كان إنشاء ملف مباشر يعتمد على المسار دون تحقق يراعي الروابط (links).
في النسخة المختبرة، كانت writeToFile() تختار واحدة من مساري فتح.
وضع الإلحاق (append) كان يستخدم:
f, err = os.OpenFile(
path,
os.O_APPEND|os.O_WRONLY|os.O_CREATE,
perm,
)
وضع الكتابة العادي كان يستخدم:
dirPath := filepath.Dir(path)
if _, err := os.Stat(dirPath); err != nil {
err := os.MkdirAll(dirPath, os.ModePerm)
if err != nil {
return "", err
}
}
f, err = os.Create(path)
لا أحد من المسارين كان يرفض مكوّنات الوجهة المرتبطة قبل فتح الملف.
هذا مهم لأن:
os.Create(path) يتبع إعادة توجيه نظام الملفات الموجودة ويقتطع (truncate) الملف المُحلَّلos.OpenFile(path, ...) يتبع مكوّنات المسار المرتبطة أثناء وضع الإلحاقنفس الافتراض القائم على المسار استمر بعد الكتابة.
كانت الملكية والصلاحيات تُطبَّق باستخدام المسار مرة أخرى:
err = os.Chown(path, uid, gid)
err = os.Chmod(path, perm)
هذا يعني أن عمليات البيانات الوصفية (metadata) كانت أيضًا مرتبطة باسم مسار قابل للتغيير بدلًا من واصف الملف المفتوح بالفعل.
لأن المهاجم يمكنه تجهيز إعادة التوجيه قبل تشغيل writeToFile.
لا يُشترط سباق (race) احتمالي للهجوم الأساسي.
التسلسل واضح ومباشر:
writeToFile تفتحه مباشرةهذا هو جوهر الثغرة بالكامل.
صحيح أن أنظمة التشغيل تتبع الروابط الرمزية عادةً أثناء فتح الملفات بناءً على المسار.
لكن هذا لا يجعل هذا سلوك تطبيق آمنًا.
السؤال الأمني ليس:
"هل تصرّفت Go وفق ما هو موثّق؟"
السؤال الحقيقي هو:
هل تحققت أداة مساعدة تكتب بيانات حساسة مُولَّدة من أن الوجهة المُحلَّلة تطابق موقع المشغِّل المقصود؟
في الإصدارات المصابة، لم تكن تفعل.
هذا التمييز مهم لأن Consul Template قد تعمل:
اتباع إعادة توجيه نظام ملفات يضعها المهاجم في هذا السياق يخلق مشكلة حقيقية في الصلاحيات وحدود الثقة.
بنيت أداة إعادة إنتاج مستقلة حول سلوك writeToFile الدقيق في commit المختبر.
الإعداد المُتحكَّم به استخدم:
writeToFileتدفق إعادة الإنتاج كان:
السلوك الملاحَظ كان:
writeToFile اتبعت إعادة التوجيههذا أثبت جزأي الادعاء:
رابط رمزي في المكوّن النهائي كان سيثبت بالفعل اتباع الروابط غير الآمن.
لكن مكوّنًا أصليًا مرتبطًا يثبت نقطة تشغيلية أقوى:
الهدف الموجود مسبقًا كان مهمًا أيضًا.
بدونه، كان إثبات المفهوم سيعرض فقط إنشاء ملف غير متوقع.
بالبدء بملف موجود والتحقق من التجزئة (hash) النهائية، أثبتت إعادة الإنتاج سلوك الاستبدال مباشرة.
هذا جعل النتيجة أكثر واقعية من مجرد ادعاء قائم على المصدر.
هذه المشكلة تتطلب تأثيرًا محليًا على نظام الملفات.
يحتاج المهاجم إلى وصول كافٍ لإنشاء أو تعديل رابط رمزي أو نقطة التحام دليل أو إعادة توجيه مكافئة في موقع الكتابة المقصود أو تحته.
ثم يجب أن تكتب عملية Consul Template عبر ذلك المسار.
التأثير يعتمد بشكل كبير على صلاحيات العملية والمحتوى المُولَّد.
النتائج العملية تشمل:
لم تكن هذه كتابة عشوائية عن بُعد غير مصادق عليها من تثبيت افتراضي.
لكنها كانت فشلًا واضحًا في حدود الثقة المحلية مع تأثير ملموس في النشر بصلاحيات مرتفعة أو على أنظمة ملفات مشتركة.
صنّفت HashiCorp المشكلة على النحو التالي:
CVSS:3.1/AV:L/AC:H/PR:L/UI:N/S:U/C:H/I:N/A:N
درجة الأساس المنشورة هي:
4.7 / Medium
هذا المتجه (vector) يعكس:
النشرات الرسمية توثق أيضًا صراحةً أن الكتابة المُعاد توجيهها يمكن أن تستبدل ملفًا موجودًا مسبقًا.
الدرجة متوسطة لأن الاستغلال يعتمد على شروط محلية للتحكم في المسار، وليس لأن تأثير نظام الملفات نظري.
نشرة HashiCorp تدرج:
Affected: consul-template up to and including 0.42.0
Fixed: consul-template 0.42.1
الإصلاح صدر في إصدار 0.42.1 بتاريخ 8 يوليو 2026.
تصحيح 0.42.1 عزّز مسار الكتابة في عدة طبقات.
الأداة المساعدة المُصحَّحة تستخدم os.Lstat() لفحص:
وترفض هذه المكوّنات عندما تكون روابط.
هذا يغلق حالات إعادة توجيه الدليل الأصلي المباشر والرابط الرمزي للملف النهائي التي غطاها التقرير واختبارات الانحدار (regression tests).
على منصات Unix المدعومة، تُفتح الوجهة مع O_NOFOLLOW.
هذا يجعل الفتح نفسه يفشل إذا أصبح المكوّن النهائي رابطًا رمزيًا بين الفحص المسبق والفتح.
هذا مهم لأن الفحص المسبق وحده يمكن أن يخلق نافذة TOCTOU أخرى.
التنفيذ الخاص بالمنصة هو عملية فارغة (no-op) على Windows، حيث O_NOFOLLOW غير متاح عبر نفس الآلية.
التصحيح استبدل عمليات الملكية والوضع القائمة على المسار بعمليات قائمة على الواصف:
f.Chown(uid, gid)
f.Chmod(perm)
هذا يربط تغييرات البيانات الوصفية بالملف الذي فُتح فعلًا بدلًا من تحليل المسار مرة أخرى لاحقًا.
الأداة المساعدة الآن تنشئ الدليل الأصلي فقط عندما يكون فشل stat حقيقيًا os.IsNotExist.
الأخطاء الأخرى، مثل فشل الصلاحيات، تُعاد بدلًا من المتابعة إلى مسار الكتابة.
التصحيح أضاف اختبارات مركزة لـ:
نقاش التصحيح العلني يوثق قيدًا مهمًا بوضوح.
التحقق الجديد يفحص:
لا يجتاز ويرفض كل مكوّن أصلي أعلى.
اختار القائمون على الصيانة هذا الحد لأن writeToFile لا تملك جذر حاوية (sandbox root) مهيأً لترسيخ فحص احتواء كامل، ولأن أنظمة التشغيل الشائعة قد تتضمن روابط مدارة مشروعة في بادئات المسار، مثل /var -> /private/var على macOS.
O_NOFOLLOW تحمي أيضًا المكوّن النهائي، وليس كل دليل أصلي.
هذا لا يغيّر حالة الثغرة المبلَّغ عنها أو الإصدار الرسمي المُصلَح.
لكنه يوضح الخاصية الأمنية الدقيقة التي يقدمها التصحيح:
هذا التمييز يستحق الحفاظ عليه في كتابة فنية.
أبلغت عن هذه الثغرة بشكل خاص إلى HashiCorp Security في 21 مارس 2026 كاكتشاف ثانٍ مستقل في Consul Template.
التقرير تضمن:
البريد الإلكتروني الأصلي للمتابعة لم يكن موجودًا في قائمة انتظار تقارير فريق الأمان، على الأرجح لأنه اعترضه مرشح البريد العشوائي للقائمة البريدية.
بعد أن أعدت توجيه التقرير الكامل، تواصلت HashiCorp مع فريق الهندسة وحققت في الأمر بشكل منفصل عن مشكلة Consul Template الأولى.
أصلحت HashiCorp الثغرة في 0.42.1 ونشرت HCSEC-2026-20 في 8 يوليو 2026.
نشرت IBM نشرة أمنية مقابلة لنفس CVE.
كلا النشرتين الرسميتين أشارتا إلى التقرير باسم:
Mohamed Abdelaal (0xmrma)
الدرس الرئيسي بسيط:
سلسلة المسار ليست نفس كائن نظام الملفات الذي تسميه
هذا التمييز مهم كلما كتبت تعليمات برمجية بصلاحيات مرتفعة مسارات يتأثر بها المهاجم.
التحقق من أن سلسلة تبدأ بدليل مقصود ليس كافيًا.
حتى المسار النظيف دون تسلسلات اجتياز يمكن أن يُحلَّل إلى مكان آخر عبر:
العملية الحساسة يجب أن تكون مرتبطة بوجهة تم التحقق من هويتها عند الحد الصحيح.
هذه المشكلة تعزز أيضًا قاعدة أوسع:
عندما يكون لديك بالفعل واصف ملف مفتوح، طبّق العمليات الحساسة أمنيًا عبر ذلك الواصف بدلًا من تحليل المسار مرة أخرى
هذا هو بالضبط سبب أهمية تغييرات Chown وChmod القائمة على الواصف.
writeToFile موثقة للمواد الحساسة مثل الشهادات والمفاتيح الخاصةos.Create أو os.OpenFileChown وChmod القائمتان على المسار أدخلتا ثقة إضافية بمسار قابل للتغيير0.42.1 أضاف فحوصات الروابط وO_NOFOLLOW حيثما كان مدعومًا وعمليات البيانات الوصفية القائمة على الواصف واختبارات الانحدارهذه الثغرة لم تكن حول اجتياز ../.
سلسلة المسار كانت تبدو صحيحة.
وجهة نظام الملفات لم تكن كذلك.
قبلت Consul Template مسار المشغِّل المقصود، واتبعت إعادة توجيه يضعها المهاجم، وكتبت المحتوى المُولَّد إلى ملف مختلف.
لهذا أصبحت CVE-2026-14361.
تم الإصلاح في consul-template 0.42.1.