المدونة / شرح ثغرات CVE

CVE-2026-21589: قراءة ملفات Atlassian المختبئة خلف «::»

الثغرة CVE-2026-21589 قراءة ملفات دون مصادقة في ثمانية منتجات Atlassian Data Center. أشرح خطأ ترتيب التحقق وكيف تكتشفه في شيفرتك أنت.

أكملت دراسة الدكتوراه في أمن المعلوماتاجتزت المناقشة الجامعية · المناقشة النهائية للأطروحة لم تُجرَ بعد

مكتبة واحدة، ثمانية منتجات، طلب واحد يفصلك عن ملفات الإعداد

في 5 أكتوبر 2026 أصدرت Atlassian إصلاحاً طارئاً للثغرة CVE-2026-21589، وهي قراءة ملفات عشوائية دون مصادقة بتقييم CVSS 4.0 9.3. [1] عيب واحد يطال ثمانية منتجات من Data Center وServer: Jira Software وJira Service Management وConfluence وBitbucket وBamboo وCrowd وCrucible وFisheye. وهي لا تتعطل معاً مصادفةً، بل تتشارك مكتبة واحدة اسمها atlassian-plugins-webresource، والخلل يسكن داخلها. ويذكر التنبيه الإصدار المصحح لكل منتج؛ أما المكتبة نفسها فأُصلحت في 6.0.8. [1][2]

تتوخى Atlassian الدقة في وصف الحدود. يستطيع المهاجم قراءة «ملفات محددة داخل الدليل الجذر لتطبيق الويب»، وفقط إذا كان يعرف المسار الدقيق مسبقاً، دون سرد محتويات الأدلة. [1] يبدو هذا محدوداً. وليس كذلك، وسبب ذلك هو لبّ هذه المقالة. لم أُعد إنتاج الثغرة على نظام حيّ؛ وما يلي مبني على تنبيه Atlassian وتحليل السبب الجذري من watchTowr، قرأتهما في 8 أكتوبر 2026. [1][2]

// atlassian-plugins-webresource, before 6.0.8
escapeSlashes(String string)   { return string.replaceAll("/", "::"); }
unescapeSlashes(String string) { return string.replaceAll("::", "/"); }

// How a web-resource name is handled on the way in:
resourceName                       // attacker-controlled
  -> check for ".." / "/" traversal   // sees "..::", no slash -> looks safe
  -> unescapeSlashes(resourceName)    // "..::"  becomes  "../"
  -> load file relative to web root   // traversal now active

الخادم ظنّ «::» آمنة، والمهاجم تحكّم بما ستصير إليه

هذا هو التصميم الذي وثق به المطورون. تحمل روابط موارد الويب مفتاح المورد واسمه. والشرطة المائلة داخل الاسم تربك الموجّه، فتهرّبها المكتبة: كل «/» تصير «::» عند الدخول، وكل «::» تعود «/» لاحقاً. [2] هذا الترميز معقول، وليس الخلل فيه، بل في الترتيب.

تتحقق الشيفرة أولاً من النص الوارد بحثاً عن اجتياز المسار، والشرطات ما زالت مكتوبة «::». فبالنسبة لهذا التحقق لا يحوي تسلسل مثل «..::» أي شرطة، فيبدو اسم ملف عادياً ويمر. وبعد ذلك فقط تفك المكتبة التهريب، فتصير «..::» هي «../». [2] المكوّن الذي حكم بأن الطلب آمن والمكوّن الذي سار في نظام الملفات كانا ينظران إلى نصّين مختلفين. والفجوة بينهما يملكها المهاجم.

لماذا قراءة ملف هي في الحقيقة استيلاء على طبقة الهوية لديك

لنمشِ في الخطوات. الاسم المهرَّب الذي يشبه الاجتياز يجتاز التحقق، ثم يُفكّ إلى مقاطع «../» حقيقية، ويُسلَّم إلى المحمّل الذي يقرأ من classpath ومن الجذر الخاص بتطبيق الويب. [2] وتذكر watchTowr أن القراءة تبقى داخل سياق Tomcat، فلا يمكن الخروج إلى /etc/passwd. وبالنسبة للمهاجم لا يكاد هذا الحد يهم، لأن الملفات التي تستحق القراءة موجودة داخله أصلاً.

الأهم هو WEB-INF/classes/crowd.properties. ففي المنتجات الموصولة بـAtlassian Crowd يحمل هذا الملف اسم التطبيق وكلمة مرور التطبيق المستخدمة للتخاطب مع خادم الهوية. [2] وهكذا تسلّم ثغرة «قراءة ملف» دون مصادقة بيانات اعتماد للنظام الذي يدير مستخدميك. وتمشي watchTowr في هذه الخطوة بالضبط: اقرأ الملف، خذ كلمة مرور تطبيق Crowd، فلم تعد تقرأ ملفات، بل تخاطب طبقة الهوية بوصفك تطبيقاً موثوقاً. [2]

هذه هي الحركة التي أغفلتها العناوين. «يقرأ ملفات محددة» و«يسلّم المهاجم أسرار هويتك» هما الخلل نفسه، لا يفصل بينهما سوى معرفة أي ملف تطلب. والشرط المسبق في التنبيه، أن تعرف المسار الدقيق، حقيقي، لكن crowd.properties ليس موضعاً سرياً، بل الموضع الذي يوجد فيه الملف دائماً. [1][2]

# WEB-INF/classes/crowd.properties  (illustrative keys, not real values)
application.name       = my-confluence
application.password   = <shared secret to the Crowd identity server>
crowd.server.url       = https://id.example.internal/crowd/services/
crowd.base.url         = https://id.example.internal/crowd/

الرقعة تعيد ترتيب خطوتين. تأكد أن ما نشرته هو إعادة الترتيب هذه

يغلق الإصلاح في atlassian-plugins-webresource 6.0.8 الفجوة بجعل التحقق ونظام الملفات يريان النص نفسه: يُفكّ الاسم إلى صورته الحقيقية قبل أن يجري التحقق من الاجتياز، لا بعده. [2] ويستحق المبدأ أن يُحفظ باسمه: التوحيد القياسي أولاً، ثم التحقق. فالتحقق الأمني لا يعني شيئاً إلا إذا فحص القيمة نفسها التي تصل إلى المصبّ الخطير. والمخطط أدناه هو هذا الترتيب المصحح كأداة للمراجعة، لا نسخة من فرق شيفرة Atlassian.

بالنسبة لصاحب المنتج، الفخّ هنا هو تصديق رقم الإصدار بدل القطعة الفعلية. فعدة من هذه المنتجات تصدر أكثر من فرع مصحح، وقد تظل صورة تراجع أو نظام اختبار أو خادم Crucible منسيّ يحمّل المكتبة 6.0.7 بينما تقول ملاحظاتك «مُرقّع». [1] افحص ملف jar المحمّل فعلاً، على النسخة التي يمكن الوصول إليها فعلاً. فنسخة مصححة على حاسوب مطوّر لا تثبت شيئاً عن الخادم المكشوف على الإنترنت.

// The fix, as a principle (not Atlassian's exact diff):
// decode to the real value FIRST, then validate THAT value.
resourceName
  -> unescapeSlashes(resourceName)    // "..::"  becomes  "../"  up front
  -> check the decoded value for ".." traversal   // now the check sees it
  -> reject, or load file relative to web root

كيف أبحث عن هذا الشكل في شيفرة لا علاقة لها بـAtlassian

هذا الخلل ليس عن Atlassian في الحقيقة، بل هو صنف اسمه «تحقّق ثم حوّل». فمتى فكّت الشيفرة ترميزاً أو طبّعت أو وحّدت قياسياً قيمةً بعد أن فحصت تلك القيمة بحثاً عن الخطر، ظهرت الثغرة نفسها، سواء كان المصبّ قراءة ملف أو SSRF أو نص SQL. وحين أراجع شيفرة، أول ما أبحث عنه بـgrep هو خطوة فكّ ترميز تقع بعد خطوة تحقق على المتغير نفسه.

النمط أدناه هو حيث أبدأ. يُظهر استدعاءات فكّ الترميز والمصبّ؛ أما العمل الحقيقي فيدوي. ولكل نتيجة أسأل سؤالاً واحداً: هل النص الذي فحصه تحقق «..» هو بايتاً ببايت النص الذي يصل إلى نظام الملفات؟ فإن جلس تحويل بينهما، صار التحقق يفحص قيمة لم تعد موجودة حين يهمّ الأمر. هذا السؤال يجد الخلل أسرع من أي توقيع لماسح، لأن الماسح يطابق الأسماء وأنا أطابق الترتيب.

# Smell to hunt: a decode/normalise step that runs AFTER the path check,
# on the same variable. Start broad, then read each hit by hand.
rg -n "replaceAll|URLDecoder|decode|unescape|normalize|canonical" \
   --glob '!**/test/**'

# The one question that confirms the bug:
#   is the string that reaches the file sink byte-for-byte the string
#   the ".." check inspected? If a transform sits between them, it is vulnerable.

هل هذا حريقك؟ بالنسبة لبعضكم، نعم، اليوم

لنكن صريحين بشأن الحجم. يطال هذا نسخ Data Center وServer المستضافة ذاتياً فقط. وتقول Atlassian إن نسخ Cloud رُقّعت ولا دليل على استغلال فيها. [1] فإن كنت كلياً على Atlassian Cloud، فهذا ليس حادثك. وإن كنت تستضيف ذاتياً أياً من المنتجات الثمانية وتكشفه على الإنترنت، فهو حادثك، وقد بدأ العدّاد.

نشرت watchTowr السبب الجذري مع إثبات مفهوم في 6 أكتوبر، وتقدّر عدد النسخ المكشوفة «في نطاق ستة إلى سبعة أرقام»، وConfluence وحده قرابة 700000 نسخة بحسابها. [2] انسب هذا الرقم إليها لا إليّ. أما ما لا خلاف فيه فهو السرعة. فتذكر The Hacker News وBleepingComputer أن المصائد رصدت محاولات استغلال خلال نحو ساعتين من نشر التفاصيل، أغلبها حتى الآن بصم للبصمة ومسح لملفات الإعداد. [3][4] وحتى 8 أكتوبر ليست الثغرة في فهرس CISA للثغرات المستغلّة المعروفة (KEV)، وهذا يعني أن الوقت مبكر، لا أن الأمر هادئ. [3]

إذاً الشروط الثلاثة لأن يصير هذا طوارئك بسيطة. أنت تستضيف ذاتياً أحد المنتجات الثمانية. وهو قابل للوصول من الإنترنت، أو من شريحة شبكة لا تثق بها تماماً. وقد شغّل بناء مكتبة غير مُرقّع خلال النافذة منذ 5 أكتوبر. فإن صحّت الثلاثة، فالترقيع خطوة أولى لا العمل كله، لأن ملفاً كان يمكن قراءته ربما قُرئ بالفعل.

ما الذي تفعله قبل أن تغلق هذه الصفحة

القائمة أدناه بالترتيب الذي كنت سأعمل به. رقّع أولاً، لأن نافذة التخفيف هنا تقاس بالساعات لا بالأسابيع. [3] لكن إن كنت تستضيف ذاتياً وكنت مكشوفاً، فافترض أن كلمة مرور تطبيق Crowd وكل ما تحت جذر تطبيق الويب قد قُرئت، وبدّلها. الرقعة توقف القراءة التالية، لكنها لا تلغي تسرّب سرّ غادر فعلاً.

سجلات الوصول هي حيث تعرف هل حاول أحد. الطلبات السابقة للترقيع إلى مسارات موارد الويب التي تحمل «::» أو «%3a%3a» أو «..» في اسم المورد هي التوقيع الذي تبحث عنه. والمطابقة ليست دليلاً على قراءة ناجحة، لكنها تدلّك على النسخ التي تعاملها كمشبوهة والأسرار التي تبدّلها أولاً.

Patch and verify (in this order):
  [ ] List every self-hosted install: Jira, JSM, Confluence, Bitbucket,
      Bamboo, Crowd, Crucible, Fisheye.
  [ ] Confirm the build that is RUNNING, not the one in your notes.
  [ ] Patch each product to its fixed version from the advisory [1].
  [ ] Cannot patch yet? Apply Atlassian's WAF / RewriteValve mitigation [1].

If you were exposed and unpatched, assume read, then rotate:
  [ ] The Crowd application password in crowd.properties.
  [ ] Any other secret stored under the web application root.

Hunt the access logs for pre-patch probing:
  grep -E "/(s|resources|sources)/[^ ]*(::|%3a%3a|\.\.)" access.log

الجزء الذي لن يخبرك به رفع رقم الإصدار

تمثّل CVE-2026-21589 مثالاً نظيفاً على خلل لا تراه من رقم الإصدار. فالإصلاح يعيد ترتيب خطوتين داخليتين، والسبيل الوحيد لتعرف أن نشرك آمن هو النظر إلى القطعة التي تعمل ومسار الشيفرة الذي يصل إلى نظام الملفات. تلك هي الفجوة بين «طبّقنا الرقعة» و«تحققنا أن ما يهمّنا مُغلق».

تتبّع طلب من الحافة، عبر إطار العمل، إلى السطر الدقيق الذي يقرر ما يُقرأ هو جوهر مراجعة أمان الشيفرة، ورائحة «تحقّق ثم حوّل» في هذه الثغرة أبحث عنها في شيفرة لا علاقة لها بـAtlassian. فإن كنت تصدر شيئاً يحوّل مدخلاً خارجياً إلى وصول لملفات أو استعلامات قاعدة بيانات أو طلبات داخلية، فهذا هو الحد الذي سأفحصه لك. أخبرني بما يعمل لديك ونحدّد النطاق.

المصادر

هل تحتاج إلى هذا الفحص في منتجك؟

مراجعة أمان الشيفرة
جميع المقالات

08تواصل

هل تطلق منتجًا يتعامل مع بيانات المستخدمين؟

أرسل لي وصفًا مختصرًا. أرد خلال ثلاثة أيام بأسئلة أو بمخطط للعرض، ونتفق على السعر كتابيًا قبل بدء العمل.