
إثبات مفهوم يعيد إنتاج ثغرة المفتاح الثابت في CVE-2021-22681 ويتحقق من صحة إصلاح لكل جهاز يعتمد على TLS المتبادل وCRL عبر محاكاة EtherNet/IP، مع ربطه بمعيار IEC 62443-4-2.
ستة نصوص برمجية قابلة للتشغيل — حركة مرور حقيقية لبروتوكول EtherNet/IP (الاختبار 1)، وآليات حقيقية لـ TLS/PKI (الاختبارات 3–6) — بدون أي برمجيات أو تراخيص من Rockwell في أي مكان في السلسلة. بُني لاختبار ادعاء قبل توثيقه، وليس للجدال فيه على أساس الإيمان.
المصدر: بُني في 2026-07-31، بالتوازي مع تحقيق موازٍ في محطة معالجة مياه الصرف الصحي في براهام، مينيسوتا — واحدة من أربع مرافق تم الكشف عنها علنًا في الحادث المنسق لقطاع المياه في مينيسوتا في 26–27 يوليو 2026. سياق ذلك الحادث موجود في نشرة CISA الاستشارية AA26-097A (مشتركة بين FBI/CISA/NSA/EPA/DOE/USCYBERCOM/وزارة الخزانة؛ صدرت في 2026-04-07، وتم توسيعها في 2026-07-22)، والتي تغطي حملة CyberAv3ngers المستمرة المرتبطة بـ IRGC. تحفظ بشأن الإسناد، مع الدقة الكاملة: لم تنسب أي جهة رسميًا حادث مينيسوتا تحديدًا إلى تلك المجموعة — فقط الحملة الأوسع المستمرة هي المنسوبة. هذا المجلد هو جانب الإصلاح التقني، ويُحفظ منفصلاً عن تحقيق الحادث عن قصد.
تم التواصل مع Rockwell PSIRT ([email protected]) و RA Secure Mail ([email protected]) في 2026-07-31، قبل نشر هذا المستودع والكتابة (~30 دقيقة قبل ذلك، حسب الطوابع الزمنية للملفات). راجع فريق هندسة الأمان في Rockwell المستودع ورد في 2026-08-03. مقتبس مباشرة، وليس مُعاد صياغته في ادعاء أقوى مما ذكروه:
لا تؤيد Rockwell Automation تفسيرك، أو تعيينك لمعيار IEC 62443-4-2، أو أي استنتاجات مستخلصة من إثبات المفهوم. يرجى عدم تقديم العمل على أنه مُراجع، أو معتمد، أو مُصرح به من قبل Rockwell Automation... النصوص البرمجية توضح مبادئ التشفير والمصادقة العامة بدلاً من أي شيء خاص بـ CIP Security أو بـ CVE-2021-22681.
لم تتم مراجعة هذا العمل أو الموافقة عليه أو التصريح به من قبل Rockwell Automation، نقطة انتهى الكلام.
توصيفهم الفني — مبادئ عامة، وليست خاصة بـ CIP Security — هو نفس التمييز
الذي يرسمه جدول "الحد الصارم" أدناه بالفعل حول ادعاءات هذا المستودع نفسه؛ مراجعتهم تؤكده
بشكل مستقل بدلاً من الطعن فيه. بالنسبة للمرافق التي تعمل على أجهزة لا تصل إلى CIP Security،
أشارت Rockwell إلى دليلهم الخاص Converged Plantwide Ethernet (CPwE) Design and Implementation Guide
(المُشار إليه في PHASED_ROLLOUT.md المرحلة 1) — المورد الحالي من البائع الذي يحاول هذا المشروع توجيه
الناس إليه بدلاً من تكراره.
نتيجة الاختبار 1 — عدم وجود مصادقة في الحالة الافتراضية للبروتوكول — ليست فريدة لـ EtherNet/IP. Modbus TCP، الذي لا يزال أحد أكثر البروتوكولات انتشارًا في أنظمة التحكم في المياه/مياه الصرف الصحي، ليس لديه مفهوم مصادقة في مواصفات البروتوكول على الإطلاق؛ يعود تاريخه إلى اتصالات تسلسلية عام 1979 ولم يُصمم أبدًا مع وضع الأمان في الاعتبار. سمّت CISA مرارًا هذا الفشل بالضبط عبر النشرات الاستشارية ICS (مثل سلسلة Mitsubishi Electric MELSEC iQ-F: "MODBUS/TCP يفتقر إلى المصادقة المناسبة"، مما يسمح بالقراءة/الكتابة/الإيقاف غير المصرح به). إجابة منظمة Modbus نفسها، Modbus/TCP Security، هي تغليف TLS مع شهادات X.509 — هيكليًا نفس فئة الإصلاح التي يوضحها الاختبار 3 هنا، موحّدة على مستوى منظمة البروتوكول بدلاً من بائع واحد. DNP3 لديه امتداد مصادقة آمن اختياري (SAv5، تم توحيده في 2012)؛ تصف التحليلات المستقلة وحسابات المنفذين كلاهما بأنه نادرًا ما يتم تكوينه عمليًا، مع الاستشهاد بفجوات التشغيل البيني بين مصنعي OT والتعقيد الحقيقي للبروتوكول — مما يترك نفس التعرض الذي يوضحه الاختبار 1 لـ EtherNet/IP.
النقطة المعمارية، مُذكورة بدقة حتى لا يتم المبالغة في بيعها: إصلاح الاختبار 3 — TLS متبادل، هوية لكل جهاز مرتبطة في الشهادة (وليس مجرد صلاحية CA)، والإبطال عبر CRL — يعمل على طبقة النقل، وليس بروتوكول تطبيق ICS. المبدأ قابل للتطبيق بالتساوي تحت Modbus أو DNP3 أو بروتوكول خاص؛ ما يتغير هو الغلاف، وليس شكل الإصلاح. لم يقم هذا المستودع ببناء أو تشغيل PoC خاص بـ Modbus أو DNP3 — هذا تعميم معماري من الوثائق العامة، مُخضع لنفس التصنيف "مُثبت مقابل مُوثق" مثل كل شيء آخر هنا، وليس ادعاءً جديدًا مُختبرًا.
المصادر: نشرات CISA الاستشارية ICS حول فجوات مصادقة Modbus/TCP — Industrial Cyber · نظرة عامة على أمان Modbus/TCP — Veridify · تحديات تبني DNP3 SAv5/SAv6 — Step Function I/O
حزمة الإفصاح تنجو أو تموت على عدم خلط هذه معًا، لأن لكل منها إصلاحًا مختلفًا:
الإصلاح المُثبت هنا — ربط الهوية لكل جهاز (الاختبار 3) — يعالج فشل مفتاح الأسطول.
| الادعاء | التصنيف | لماذا | |
|---|---|---|---|
| المبدأ المعماري | "سر مشترك واحد عبر أسطول يتم اختراقه على مستوى الأسطول بتسريب واحد؛ المصادقة المرتبطة بالهوية لكل جهاز تسد ذلك" | مُثبت | مُثبت بكود حقيقي يعمل بما في ذلك التحكم السلبي الذي يثبت أن الفحص ضروري، وليس فقط أنه يعمل: على نقطة نهاية صارمة، شهادة الجهاز B الصالحة فعلاً من CA يتم رفضها على أساس الهوية (الاختبار 3 · الحالة 3)، ولكن على نقطة نهاية فقط لصلاحية CA، نفس الشهادة يتم قبولها (الحالة 4 — التحكم) → "التوقيع الصالح من CA وحده == وصول على مستوى الأسطول == الاختبار 2 في ثياب TLS." الربط يصمد أيضًا في الاتجاه العكسي: خادم مارق يقدم شهادة أسطول صالحة يتم رفضه من قبل العميل (الحالة 5). يُظهر الاختبار 1 بشكل منفصل خط الأساس الأوسع بدون مصادقة. |
| تنفيذ Rockwell المحدد لـ CIP Security يتصرف بشكل مماثل | "تمكين CIP Security على أجهزة Rockwell الحقيقية يعالج CVE-2021-22681 بهذه الطريقة بالضبط" | افتراض رائد، مُوثق وليس مُتحققًا | هذه لغة نشرة Rockwell الاستشارية نفسها (PN1550) — "عند نشره بشكل صحيح، يعالج CIP Security هذه الثغرة... ولا يستخدم أي مفاتيح مضمّنة" — وليس شيئًا أكدناه بشكل مستقل ضد أجهزة Logix حقيقية. اختبرنا المبدأ الذي تصفه نشرتهم الاستشارية، وليس تنفيذهم الدقيق على مستوى الأسلاك. |
لا تخلط بين الصفين. المبدأ مُثبت. التنفيذ المحدد للبائع له موثوق (إنها نية التصميم المعلنة من قبلهم) لكنه غير مُختبر من قبلنا ضد معدات حقيقية.
test1_baseline_vulnerable.py — خط الأساس بدون مصادقة، مباشريشغّل محاكي PLC حقيقي لـ EtherNet/IP (cpppo، يحاكي Allen-Bradley ControlLogix) و
يقرأ ويكتب علامة تحكم مع صفر بيانات اعتماد. (النطاق: هذا هو خط الأساس الواسع
بدون مصادقة الذي اعتمدت عليه الحملة — وليس آلية المفتاح المضمّن المحددة
لـ CVE-2021-22681. يُحفظ متميزًا عن قصد؛ انظر "ثلاثة إخفاقات مختلفة" أعلاه.)
python test1_baseline_vulnerable.py
test2_shared_secret_fails.py — شكل خلل مفتاح الأسطول (جسر سردي، وليس اختبارًا)نقطتا نهاية تحملان مفتاحًا ثابتًا واحدًا؛ بيانات اعتماد من الجهاز A تفتح الجهاز B دون تغيير — أقرب تماثل هيكلي لخلل المفتاح الواحد للجميع في CVE-2021-22681. لكنه حشو منطقي: كلا المعالجين مُصممان لقبول ذلك المفتاح، لذلك لا يوجد مسار تنفيذ يمكن أن يفشل فيه. إنه لا يثبت شيئًا لا يعرّفه الكود إلى الوجود. يُحفظ كـ جسر سردي من الاختبار 1 إلى الاختبار 3؛ لا يحمل أي وزن إثباتي وهو عمدًا ليس ساق إثبات.
python test2_shared_secret_fails.py
test3_mutual_tls_fix.py — الإصلاح، مع تحكمه السلبي وكلا الاتجاهينCA حقيقي، شهادتا جهاز فريدتان بشكل فردي — الهوية مرتبطة في SubjectAlternativeName، وليس في CommonName المهجور. خمس حالات، جميعها منفذة:
device-a (check_hostname ضد SAN حقيقي). متبادل — كلا الطرفين يربط الهوية.python test3_mutual_tls_fix.py
test4_revocation.py — ساق دورة الحياة: الإبطالCRL حقيقي موقّع من CA. بيانات اعتماد عميل (engineer-1) ممنوحة؛ ثم يُضاف تسلسلها إلى
CRL، ونفس بيانات الاعتماد الصالحة وغير المنتهية والموقعة من CA يتم رفضها — المحتوى التجريبي
لبند الإبطال CR 1.8 / 1.9. التفرد (الاختبار 3) ≠ قابلية الإبطال؛ هذا يُظهر أن بيانات الاعتماد يمكن استرجاعها.
python test4_revocation.py
test5_rotation.py — ساق دورة الحياة التي لم يغلقها الاختبار 4: التدويريصدر بيانات اعتماد بديلة (v2) لنفس الهوية (engineer-1) التي تحمل بالفعل
بيانات اعتماد صالحة (v1). التحكم (الحالة 3): يتم تقديم v1 مرة أخرى بعد وجود v2 ولكن قبل
إيقاف v1 صراحةً → لا تزال ممنوحة — مما يثبت أن إعادة الإصدار وحدها لا توقف بيانات الاعتماد القديمة.
فقط بعد إضافة v1 صراحةً إلى CRL (الحالة 4) يتم رفضها؛ v2 غير متأثرة
طوال الوقت (الحالة 5) — الهوية لا تفقد الوصول أبدًا أثناء الانتقال. "إصدار بديل
وإيقاف السابق" في CR 1.8 هما إجراءان، وهذا يُظهر كليهما، بشكل منفصل.
python test5_rotation.py
test6_tamper_injection.py — الساق التي سمّاها CR 3.1 "بالبناء" فقط: اختبار سلامة مخصصمرحّل على مستوى السجلات يجلس بين عميل وخادم TLS متبادل حقيقي، ويعيد توجيه سجلات TLS عبر
تحليل رأس 5 بايت فقط — لا يرى أبدًا النص الصريح للحمولة المشفرة. التحكم: كل
بايت يُعاد توجيهه دون تعديل → الرسالة تُسلَّم سليمة. العبث: قلب بت واحد داخل
نص مشفر لسجل بيانات تطبيق حي → فشل فحص AEAD لمكدس TLS المستقبِل
(SSLV3_ALERT_BAD_RECORD_MAC) ويتم تمزيق الاتصال — البيانات التالفة لا تُسلَّم أبدًا كما لو
كانت صالحة.
أي بايت، ولماذا هو محدد: جسم سجل AEAD لـ TLS 1.2 هو
explicit_nonce(8) ‖ ciphertext ‖ auth_tag(16)، لذا البايت 0 هو nonce، وليس الحمولة. قلب
الـ nonce أيضًا يوقع فحص AEAD — ولكن بتشويش فك التشفير بدلاً من أن يلتقط الوسم حمولة معدلة.
CR 3.1 يتعلق بـ تعديل غير مصرح به للمعلومات المرسلة، لذا
يستهدف القلب بايتًا في منتصف النص المشفر، ثم يوضح الاختبار بالضبط
الجملة التي يدعيها.
python test6_tamper_injection.py
maximum_version = TLSv1_2 لسببين يتعلقان بـ الملاحظة، وليس
الأمان: تحت TLS 1.2، شهادة العميل المفقودة أو المرفوضة تفشل أثناء المصافحة، لذا
يحصل الاختبار على خطأ محدد وقابل للإسناد بدلاً من فشل TLS 1.3 بعد المصافحة؛ و
يبقى نوع محتوى السجل مرئيًا في النص الصريح، وهو ما يحتاجه مرحّل الاختبار 6 لتحديد
سجل بيانات التطبيق على الإطلاق. انشر أعلى إصدار TLS تدعمه أجهزتك — TLS 1.3
حيثما كان متاحًا. لا شيء في هذا المستودع يجب أن يُقرأ كنصيحة لتقييد نظام إنتاجي عند 1.2.presented == KEY، identity in SAN) ليست بزمن ثابت. غير قابلة للاستغلال هنا — القيم
المقارنة هي سلاسل هوية شبه عامة وقد قام TLS بالفعل بالمصادقة التشفيرية الحقيقية
قبل تشغيل المقارنة — ولكن تم وضع علامة عليها لأن النمط يُنسخ في أماكن
يهم فيها.python -m venv venv
venv\Scripts\activate # أو: source venv/bin/activate
pip install -r requirements.txt
62443-4-2_SL2_MAPPING.md (CR 1.2 / 1.8 / 1.9 / 1.14 / 3.1،
مُصنف بأمانة، كل فجوة مسماة؛ CR 1.2 و CR 1.9 وساق الإصدار/التحقق/الإبطال في CR 1.8
أصبحت الآن مُثبتة). قبل PSIRT ([email protected] / [email protected]):
تحقق من النص المعياري لكل CR مقابل نسخة مشتراة من IEC 62443-4-2:2019.PHASED_ROLLOUT.md (المرحلة 0 إيقاف النزيف · 1 تقسيم · 2
ضوابط تعويضية · 3 CIP Security/PKI، حسب ما تسمح به الأجهزة · 4 تشغيل). بحجم مناسب لمرافق
صغيرة؛ صادق في أن CIP Security مقيد بالأجهزة، لذا تحمل المراحل 0–2 تقليل المخاطر بغض النظر.PHASE0_INVENTORY_WORKSHEET.md (جرد أجهزة قابل للتعبئة، وليس مجرد تعليمات لإنشاء
واحد)، RESOURCES.md (مساعدة مجانية من CISA / EPA / WaterISAC / AWWA، تم التحقق منها مباشرة، وليست من الذاكرة)،
وINCIDENT_RESPONSE_QUICK_REFERENCE.md (بطاقة أول 60 دقيقة، صراحةً ليست خطة
استجابة كاملة للحوادث — سلامة العمليات دائمًا أولاً فيها).test5_rotation.py ينقل CR 1.8
من "مُفعّل" إلى مُثبت بالكامل (التدوير، مع تحكمه الخاص)؛ test6_tamper_injection.py
ينقل CR 3.1 من "بالبناء" إلى مُثبت (قلب بت حقيقي، مرفوض بفحص AEAD الخاص بـ TLS).
كلاهما أُعيد تشغيله مرارًا دون أي تذبذب قبل إضافته هنا. أُضيف أيضًا:
PHASE3_CA_QUICKSTART.md (CA "ببضعة أسطر من الكود"، كأوامر openssl حقيقية مُختبرة) و
مثال ملموس لقائمة السماح في PHASED_ROLLOUT.md المرحلة 1.GLOSSARY.md، INTEGRATOR_CHECKLIST.md، PHASE3_CA_QUICKSTART.md) أصبح الآن
مرتبطًا فعلاً من الخطة بدلاً من أن يظل بلا إشارة. لا شيء في هذه القائمة لا يزال
مسودة — كل ما سبق وما يلي منشور ومباشر على المستودع العام.كل معرف خارجي هنا تم سحبه من مصدر مباشر في 2026-07-31، وليس من الذاكرة
التدريبية: AA26-097A (متعدد المصادر، بما في ذلك WaterISAC / Tenable / SecurityWeek)، براهام كواحدة من
الضحايا الأربعة المكشوفين، CyberAv3ngers/IRGC، PN1550 أكد نشرة Rockwell الاستشارية الحقيقية مع
اقتباس الصف 2 المُتحقق منه حرفيًا ("عند نشره بشكل صحيح، يعالج CIP Security هذه الثغرة"
ICSA-21-056-03، و62443-4-2 CR 1.8 (PKI) + CR 3.1
(سلامة الاتصالات) مؤكدة بدقة.
انضباط الحزمة: أعد سحب كل معرف من المصادر الأولية وقت التقديم.
النشرات الاستشارية يُعاد ترقيمها وتوسيعها واستبدالها — AA26-097A يُظهر بالفعل توسيعًا واحدًا — لذا
"تم التحقق في 2026-07-31" ليس "تم التحقق عند التقديم." التعيين الرسمي الكامل CR-by-CR لـ 62443-4-2 هو
الآن مكتوب (62443-4-2_SL2_MAPPING.md) — ما تبقى هو إعادة سحب نصه المعياري المُستشهد به مقابل
نسخة معيارية مشتراة قبل التقديم، وليس كتابة التعيين نفسه.l0gic — Patrick Crosby · 2026-07-31.
سجل التقوية، 2026-08-03: تمت إضافة test5_rotation.py وtest6_tamper_injection.py، مما أغلق
العنصرين التقنيين المسميين مفتوحين منذ 2026-07-31 — كل منهما أُعيد تشغيله ثلاث مرات دون تذبذب قبل
توثيقه هنا. PHASE3_CA_QUICKSTART.md (أوامر openssl حقيقية مُختبرة)، مثال ملموس
لقائمة سماح جدار الحماية للمرحلة 1، PHASE0_INVENTORY_WORKSHEET.md، RESOURCES.md،
INCIDENT_RESPONSE_QUICK_REFERENCE.md، والتعميم Modbus/DNP3 أُضيفت أيضًا في نفس اليوم.
سجل التقوية، 2026-08-03 (متابعة) — جولتا مراجعة مستقلتان، كلتاهما مطبقتان: فريق أمان أحمر
وجد وأصلح ثغرة PKI حقيقية في بدء CA السريع (-copy_extensions copyall
سمح لطلب شهادة خبيث بتعريف نفسه CA:TRUE؛ تم الإصلاح عبر -extfile صريح، تم التحقق
ضد طلب خبيث عمدًا في كلا الاتجاهين) وصحح test6 لقلب النص المشفر
تحديدًا بدلاً من البايت 0 (الـ nonce)، بالإضافة إلى إصلاح حقيقي لسلامة الخيوط وتثبيت التبعيات.
تدقيق هيكلي منفصل — قراءة المراحل كنظام عبر الزمن، وليس قائمة تحقق — وجد
وأصلح: عنوان المرحلة 3 "بضعة أسطر" أخفى أن الإبطال/التدوير ليسا بسيطين؛ CRL
الفتح عند الفشل/الإغلاق عند الفشل لم يُقرر أبدًا (الآن مقرر، مع افتراضي ومنطق)؛ انتهاء صلاحية الشهادة
كان وضع انقطاع جديدًا غير مسمى (المرحلة 4 الآن تحمل درس التداخل الآمن الخاص بالاختبار 5)؛ المرحلة 3
تعمي بصمت منبه كتابة المرحلة 2 (الآن مُسمى، مع بدائل مقترحة)؛ NTP كان
متطلبًا مسبقًا غير مذكور؛ CR 1.14 كان مؤطرًا كـ "غير مستوفى" حيث "غير قابل للتطبيق على
التصميم المعالج" هو البيان الدقيق؛ وINTEGRATOR_CHECKLIST.md/GLOSSARY.md كانا مستندين
يتيمين لا شيء يرتبط بهما (الآن مرتبطان من المرحلة 3 ومن أعلى هذه الخطة). كل إصلاح تم التحقق منه
بإعادة تشغيل الاختبارات المتأثرة، وليس فقط بإعادة قراءة الفرق. منشور ومباشر — جميع المراحل الخمس
لخطة النشر مكتملة، ومُراجعة مرتين، ولا شيء من اليوم لا يزال محليًا.
سجل التقوية: تم تقوية الاختبار 3 ليشمل التحكم السلبي (الحالة 4، إثبات الضرورة وليس فقط الإطلاق)، وحالة الاتجاه العكسي (الحالة 5، الربط المتبادل)، والهوية القائمة على SAN (وليس CN)، ثم أُعيد التحقق بإعادة تشغيل جميع الحالات الخمس؛ تمت إضافة الاختبار 4 (إبطال CRL). تسميات الاختبار 1/2 تم ضبط حجمها لإبقاء فئات الإخفاق الثلاث متميزة؛ تم تخفيض الاختبار 2 من "اختبار" إلى جسر سردي؛ تمت إضافة حدود نطاق الإبطال والزمن الثابت. سلسلة الاستشهادات تم استرجاعها من المصادر الأولية وختمها لإعادة السحب عند التقديم.