
إثبات مفهوم لثغرة Hyperswitch Worldpayxml webhook غير المصادق عليها (CWE-345/306) التي تزوّر XML بحالة SETTLED لتحديد المدفوعات كناجحة دون التقاط PSP.
abraxaslabs.tech · github.com/abraxas · @abraxas_null · [email protected] · hyperswitch-unsigned-webhook
Hyperswitch 2026.09.21.0 - Juspay
Worldpayxml::verify_webhook_source يُرجع Ok(true) مع تعليق يفيد بأن التحقق يتم عبر mTLS. لا يوجد mTLS على هذا المسار. تتجاهل الدالة الجسم والترويسات. ثم يثق incoming.rs بـ source_verified ويتخذ HandleResponse بدون PSync. يُترجم LastEvent::Settled إلى PaymentIntentSuccess. خوارزمية IncomingWebhook الافتراضية هي NoAlgorithm. يرث BitPay وShift4 ذلك. أما Worldpayxml فلا يصل إلى ذلك الحد حتى. إنه يقطع الطريق.
XML SETTLED وهمي غير موثّق يجعل الدفع ناجحًا. لا Worldpay capture. لا شهادة عميل.
| ID | لا يوجد CVE بعد |
| CWE | CWE-345، CWE-306 |
| CVSS | High: 7.5 CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:N |
| المنتج | Hyperswitch |
| المتأثر | حتى 2026.09.21.0 webhooks الواردة من Worldpayxml |
| المصادقة | غير موثّق؛ يلزم merchant_id وorderCode |
| الترخيص | GNU Affero GPL v3.0 |
| المختبر | 127.0.0.1 فقط |
يحتاج إلى merchant_id (موجود في كل رابط لوحة تحكم Hyperswitch) وorderCode مساوٍ لـ connector_transaction_id الخاص بالمحاولة. أرسل POST إلى /webhooks/{merchant_id}/worldpayxml بـ SETTLED غير موقّع. لو كان source_verified خاطئًا، لكان incoming قد نفّذ Trigger وPSync للموصل. لكن Worldpayxml لا يسلك هذا المسار أبدًا. تتحول النية إلى Charged. في المختبر: status=succeeded. XML وهمي. لا PSP.
تخمين معرّفات الدفع ليس هو الثغرة. بل تزوير SETTLED لمعرّف رأيته بالفعل. ليس shell. وليس تسوية Worldpay حقيقية.
نفس المنتج، ثغرة مختلفة: payout confirm unbound client_secret.
قرأت Ok(true)، ثم HandleResponse مقابل Trigger، ثم Settled -> PaymentIntentSuccess. يقول التعليق mTLS. لكن طلب POST في المختبر لا يحمل شهادة عميل.
أول عميل ينظر إلى هذا سيرسل POST للـ webhook مقابل requires_confirmation. لن يجعل incoming الدفع Charged إذا لم يخرج من مرحلة التأكيد. اختم المحاولة (stamp)، أو انتظر حتى requires_capture / processing. المختبر يقوم بالختم. ذلك الختم هو تجهيز مختبري. المحاولة الحية تملك بالفعل معرّف الموصل. أما الـ webhook فلا.
مسارات خاطئة مسجّلة مسبقًا: HMAC موقّع على موصل يتحقق فعلاً (هذا المسار لا يفعل)؛ اعتبار HTTP 200 للـ webhook هو الدليل (استعلم عبر GET /payments/{id} حتى status=succeeded)؛ orderCode ليس connector_transaction_id؛ reverse shell. مسرحية.
ثم: merchant، مفتاح API، worldpayxml MCA، دفع بـ confirm=false، اختم connector_transaction_id، SETTLED غير موقّع، استرجع.
cd lab
./run.sh
الهدف فقط http://127.0.0.1:18082. يقوم run.sh بنسخ الوسم 2026.09.21.0 إلى lab/hyperswitch-src للإعداد والترحيلات.
IOC status_before=requires_confirmation
IOC webhook status=200 unsigned lastEvent=SETTLED
IOC poll status=succeeded
SUCCESS Hyperswitch unsigned Worldpayxml webhook Charged
تحقق من إشعار Worldpay (التوقيع / mTLS الذي ينهيه المسار فعلاً). لا تنفّذ HandleResponse على موصل أعاد Ok(true) من عملية لا تفعل شيئًا. يجب ألا ينقل SETTLED غير الموقّع النية إلى succeeded.
worldpayxml.rs · transformers.rs · incoming.rs · webhooks.rs · crypto.rsGNU Affero GPL v3.0. راجع LICENSE. مختبر loopback فقط. دون أي ضمان.