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

CVE-2026-104935: Ivanti تثق بالترويسة لا بالرابط

تدمج CVE-2026-104935 بين ترويسة توجيه وسباق في رفع الملفات لتحقيق تنفيذ شيفرة في Ivanti Connect Secure. وصلاحيات الجذر الكاملة تحتاج ثغرة ثالثة لم تُصلَح بعد.

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

جهاز VPN مُحدَّث بالكامل، وصلاحيات جذر رغم ذلك

هذه هي الخطوة الأولى كاملة: طلب POST إلى صفحة تسجيل الدخول العامة في Ivanti Connect Secure، welcome.cgi، التي لا تحتاج جلسة إطلاقاً، يحمل ترويسة إضافية واحدة، X-DSCGI-PATH، مضبوطة على مسار ملف CGI آخر يتطلب المصادقة عادة. فحص المصادقة يقرأ الرابط. الترويسة تقرّر إلى أين يذهب الطلب فعلاً. [1]

تتعقّب Volexity الجهة المسؤولة باسم UTA0576، وبحلول نشر تحليلها التقني في 13 أكتوبر 2026، كانت المجموعة قد استخدمت خدعة الترويسة هذه، مدمجة مع ثغرة ثانية، للحصول على تنفيذ شيفرة بصلاحيات مستخدم cgi-server غير المميَّز، على جهاز يعمل بنسخة محدّثة بالكامل، 22.8R2.4. [1] الثغرتان اللتان تقودان إلى ذلك مُسجَّلتان معاً تحت CVE-2026-104935: درجة CVSS 8.6، وCWE-290 (تجاوز المصادقة بالتزييف)، وتؤثران في Ivanti Connect Secure من 25.1.0.0 حتى ما قبل 25.1.1.2، ومن 25.1.2.0 حتى ما قبل 25.1.3.0، وكل إصدارات 22.8R2 حتى 22.8R2.4، وأُصلحت في 25.1.1.2 و25.1.3.0 و25.1.3.1 و22.8R2.5 في 13 أكتوبر 2026. [2] أضافت CISA الثغرة إلى فهرس الثغرات المستغلة المعروفة في اليوم نفسه، مع موعد نهائي قدره ثلاثة أيام للوكالات الفيدرالية، وقد انقضى بالفعل. [2][3]

تنفيذ الشيفرة بصلاحيات cgi-server ليس نهاية القصة. أعادت Volexity أيضاً بناء مرحلة ثالثة، لا تزال دون معرّف CVE ودون إصلاح، تنتقل من هناك إلى صلاحيات الجذر، دون باب خلفي ودون شِل. لم أُجرِّب أي من هذا بنفسي؛ ما يلي مبني على تحليل Volexity التقني وسجلّي CVE وKEV، قرأتها في 18 أكتوبر 2026. [1][2][3] الجزء الذي يستحق اهتمامك ليس أن جهاز VPN تعرّض للاختراق. بل أن ثغرتين من الثلاث هما تماماً ما يرصده مراجع الشيفرة، لا ما يكشفه مستجيب الحوادث بعد وقوعه.

POST /dana-na/auth/url_default/welcome.cgi HTTP/1.1
Host: vpn.example.com
X-DSCGI-PATH: /home/webserver/htdocs/dana/fb/smb/wu.cgi
Content-Type: application/x-www-form-urlencoded

(welcome.cgi needs no session at all)

// Host and paths are illustrative, not turnkey.
// The URL says "public login page" -> the auth check reads the URL.
// The header says "run wu.cgi instead" -> routing reads the header.
// Nothing checks that the two agree.

صفحة تسجيل الدخول تفحص الرابط. ترويسة تقرّر أي سكربت يعمل

كل موجِّه كهذا يجيب عن سؤالين منفصلين لكل طلب: هل يُسمح لهذا الطالب بالوجود هنا، وأي ملف يتولى المعالجة فعلاً. يجيب ICS عن السؤال الأول بفحص مسار الرابط، أي هل هذه نقطة من النقاط العامة غير الموثَّقة، وعن الثاني بفحص ترويسة توجيه، X-DSCGI-PATH، تسمّي سكربت CGI المطلوب استدعاؤه. [1] فصل ذلك إلى سؤالين هندسة معقولة. لا شيء في هذا التصميم بذاته متهاون.

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

معالج الرفع يثق بالتوقيت، لا بالمحتوى

الوصول إلى wu.cgi عبر التوجيه هو الخطوة الأولى. الخطوة الثانية تحوّل ذلك التوجيه إلى تنفيذ شيفرة، وهي خلل توقيت، لا خلل محتوى. يكتب wu.cgi الملف المرفوع إلى /home/runtime/tmp/<MD5 لاسم الملف الذي اخترته>، ولا يفحص المعالج بعد ذلك إلا هل المحتوى أرشيف ZIP صالح فعلاً. فإن لم يكن، يحذف الملف. [1] سمِّ سكربت Perl بامتداد .zip، وستجد، لنافذة قصيرة، سكربتاً سيُنفَّذ بسرور في مسار تعرفه مسبقاً، لأنك اخترت الاسم وحوّله تجزيء (hash) متوقَّع إلى ذلك المسار.

تصف Volexity كيف يُسابق المهاجم طلبات متوازية بالضبط خلال تلك النافذة: طلبات كافية، موجَّهة عبر خدعة الترويسة من المرحلة الأولى، نحو ذلك المسار المؤقت، لتنفيذ سكربت Perl قبل أن يفوز الحذف بالسباق. طلب واحد يصل في الوقت المناسب يكفي. [1]

ولم تتوقف الحمولة التي استعادتها Volexity عند Perl. فقد انتقلت إلى سكربت Python، سُلِّم مضغوطاً بـBase64 وzlib ونُفِّذ في الذاكرة عبر exec() بدلاً من كتابته كملف مستقل. [1] الهدف في كل المراحل واحد: ترك أقل ما يمكن اكتشافه على القرص لاحقاً.

// wu.cgi's order of operations, reconstructed from Volexity's writeup.
// This is the sequence, not a working exploit.

1. Attacker uploads "payload.zip" containing a Perl script, not a real ZIP.
2. wu.cgi writes it to /home/runtime/tmp/<MD5("payload.zip")> immediately.
3. wu.cgi then checks: is this actually a valid ZIP archive?
4. Check fails -> wu.cgi deletes the file.

// The gap between step 2 and step 4 is the whole bug.
// The attacker fires repeated requests (via the stage-one header trick)
// at the path from step 2, racing to execute it before step 4 wins.
// One request landing inside that window is enough.

مرحلتان أُصلحتا. الثالثة لم تُصلح، وهي الأكثر إثارة

إصلاح Ivanti لـCVE-2026-104935، الصادر في 13 أكتوبر 2026، يغلق المرحلتين الأولى والثانية: 25.1.1.2 و25.1.3.0 و25.1.3.1 و22.8R2.5 هي الإصدارات التي تتوقف فيها هذه السلسلة بالتحديد عن العمل. [2] إن كنت تشغّل نسخة أقدم على أي من هذه الخطوط الثلاثة، فهذا ليس بنداً تؤجله لسبرينت قادم. بيانات سجل CVE نفسه عن الاستغلال تصفه بأنه "نشط"، بتأثير تقني "كامل"، وموعد CISA النهائي الفيدرالي البالغ ثلاثة أيام قد انقضى. [2][3]

عند المرحلة الثالثة تتوقف ملاحظات الإصلاح. فبعد أن ينال المهاجم تنفيذ شيفرة بصلاحيات مستخدم cgi-server غير المميَّز، وجدت Volexity أن UTA0576 أعاد توجيه dsinvoked، مُشغِّل الصيانة الخاص بـICS بصلاحيات الجذر، لتنفيذ شيفرة يختارها المهاجم كجذر، عبر إعادة توجيه ما تشير إليه مهمة قائمة بالفعل، كله داخل ذاكرة عملية تعمل حالياً. [1] لن أشرح تفاصيل المكتبة أو الإعدادات التي ذكرتها Volexity. الآلية معلنة للعموم، لكن لا يوجد إصلاح لها، وتكرار تفاصيل بمستوى الاستغلال لثغرة تصعيد مفتوحة ليس شيئاً أفعله هنا بصرف النظر عمّن نشره.

ما يهم بصرف النظر عن حالة الإصلاح: التصعيد يتراجع عن نفسه. فهو يعيد مُدخَل الإعداد وبايتات المكتبة في الذاكرة إلى أصلها بعد تأكيد صلاحيات الجذر، عبر فحص إمكانية قراءة /etc/shadow، فيبدو الجهاز بعدها وكأن لم يُلمَس. تقول Volexity بوضوح إن أداة Ivanti الخاصة، Integrity Checker Tool، لا ترصد هذا، لأن ICT تفحص ما على القرص، وهذا لا يلمس القرص إطلاقاً. [1] فتشغيل ICT نظيف اليوم لا يقول شيئاً عن تأثّر الجهاز بالمرحلة الثالثة، سواء قبل 13 أكتوبر أو الآن، لأنه لا يوجد إصلاح لها حتى الآن.

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

المرحلتان الأولى والثانية ليستا خطأين خاصّين بـICS. إنهما نمطان أتحقق منهما في أي بروكسي عكسي، أو بوابة API، أو موجِّه شبيه بـCGI، على أي بيئة. الأول: هل يقرأ المكوّن الذي يقرّر السماح بالدخول الحقل نفسه الذي يقرأه المكوّن الذي يقرّر أي معالِج يعمل؟ فإن اعتمد التفويض على الرابط واعتمد التوجيه على ترويسة أو كوكي أو معامل طلب لا ينظر إليه طبقة التفويض أبداً، فهذا الخلل موجود هنا أيضاً، بأسماء حقول مختلفة فقط.

الثاني: هل يوجد ما يكتبه خدمتك إلى مسار قابل للإشارة، حتى لفترة قصيرة، قبل أن يُفحَص؟ معالِجات الرفع هي المنزل التقليدي لهذا، لكنه يظهر في أي مكان يحفظ فيه الخدمة أولاً ويفحص ثانياً: كتابة في ذاكرة تخزين مؤقت، ملف مؤقت، مُدخَل في طابور يستطيع عامل انتزاعه قبل أن يعمل المدقِّق. السؤالان أدناه هما ما سأطرحه فعلاً عند قراءة معالِج كـwu.cgi لأول مرة، ولا يحتاجان شيفرة Ivanti المصدرية للتطبيق.

// Two questions I ask reading any dispatcher or upload handler,
// independent of what language or framework it's written in.

// 1. Auth-vs-routing mismatch
//    - What field decides "is this caller allowed in"? (often the URL)
//    - What field decides "which handler actually runs"? (sometimes a
//      header, a cookie, a query parameter)
//    - If those are two different fields, does anything enforce that
//      they agree?

// 2. Persist-before-validate
//    - Where does a handler write attacker-influenced content to a
//      referenceable path (disk, cache, queue) before fully validating it?
//    - Is there a window, however small, where something worse can
//      reach what was just written before cleanup or validation runs?

هل هذه حالتك الطارئة؟ إن كنت تشغّل Connect Secure، فنعم

لنكن صريحين بشأن من يخصّه هذا. إن لم تكن تشغّل Ivanti Connect Secure، فهذا لا يخصّك. وإن كنت تشغّله، فلا يوجد هبوط لطيف هنا كما يحدث أحياناً مع نتيجة منخفضة الخطورة يُضخَّم أمرها. CVE-2026-104935 قبل المصادقة، وبيانات الاستغلال نفسها تصفه بأنه "نشط"، وتبيّن من إعادة بناء Volexity الجنائية أنه ضرب جهازاً كانت كل إصلاحاته المتاحة مُطبَّقة فعلاً. [1][2] عبارة "نحن مُحدَّثون" كانت صحيحة لذلك العميل، ولم تنقذه.

ثلاثة أمور تحدّد مستوى القلق المطلوب. أولاً، النسخة التي تعمل فعلاً على الجهاز الآن، لا تلك التي يقول سجل التغييرات إنك قصدت الوصول إليها: 25.1.1.1 وما قبله، و25.1.2.2 وما قبله، و22.8R2.4 وما قبله، كلها لا تزال مفتوحة للمرحلتين الأولى والثانية. [2] ثانياً، هل إصلاح CVE-2026-104935 نهاية تقييمك للخطر أم بدايته. فهو يُغلق الباب الذي عبر منه UTA0576، لا الباب الذي وجدته Volexity مُستخدَماً بعد الدخول. ثالثاً، هل تُستخدَم نتيجة نظيفة من Integrity Checker Tool كدليل على عدم وجود اختراق، في حين يقول تحليل Volexity نفسه إن هذا غير صحيح لهذه السلسلة. [1]

درجة 8.6 صادقة بخصوص المرحلتين الأولى والثانية. لكنها لا تقول شيئاً عن المرحلة الثالثة، لأن المرحلة الثالثة لا درجة لها بعد، والمهاجم الذي يسلسل الثلاث لا يتوقف عن الاهتمام بها لمجرد أن CVSS توقف.

ما الذي تتحقق منه قبل إغلاق الحادثة

رقّع أولاً، بالطبع. لكن الترقيع بعد الحادثة يجيب عن "هل نحن بأمان من الآن فصاعداً"، لا عن "هل أصابنا ذلك سابقاً"، ونافذة استغلال هذه السلسلة لم تتطلب أن تكون غير مُحدَّث بأي طريقة كنت ستلاحظها وقتها. [1]

تتعقّب Volexity البنية التحتية للمهاجم إلى 30 سبتمبر 2026، وتَرصُد الاستغلال منذ 3 أكتوبر. [1] إن كان الجهاز مواجِهاً للإنترنت بنسخة معرَّضة في أي وقت منذ نهاية سبتمبر 2026، فاعتبر الاختراق محتملاً وابحث عن أثره قبل إغلاق الحادثة. القائمة أدناه هي الترتيب الذي سأعمل به.

Before you call this closed:
  [ ] Confirm the build actually RUNNING on the appliance, not the one
      your change log says. 25.1.1.2 / 25.1.3.0 / 25.1.3.1 / 22.8R2.5 or
      later closes CVE-2026-104935 (stages one and two only).
  [ ] Pull logs for requests to welcome.cgi where X-DSCGI-PATH does not
      match the URL path. That mismatch is this chain's fingerprint.
  [ ] Do not close the incident on a clean Integrity Checker Tool run
      alone. ICT checks on-disk state; stage three never touches disk.
  [ ] If the appliance was internet-facing on a vulnerable build any time
      from late September 2026 on, treat it as possibly compromised, not
      just unpatched.
  [ ] Track Ivanti's advisory for the still-missing privilege-escalation
      fix and apply it the day it ships. There is no CVE for it yet.

الثغرة نفسها هي الجزء السهل

ستخرج CVE-2026-104935 نفسها من دورة الأخبار قريباً. أما صنفا الخلل خلفها فلن يختفيا، لأنهما لم يكونا خاصّين بـIvanti وحدها يوماً. فحص تفويض وقرار توجيه يثقان بصمت بحقلين مختلفين من نفس الطلب، ومسار كتابة يحفظ قبل أن يفحص: أجد كلاهما في شيفرة ويب وAPI لم تقترب يوماً من جهاز VPN، عادة في زوايا لم ينظر إليها أحد مرتين لأن الفحص البديهي، هل هذه النقطة موثَّقة، هل نوع الرفع مفحوص، قد نجح فعلاً.

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

المصادر

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

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

08تواصل

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

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