
إثبات مفهوم ومختبر لثغرة تأكيد الدفع في Hyperswitch: إضافة client_secret وهمي مع المبلغ في POST /payouts/{id}/confirm يؤدي إلى رفع مبلغ الدفع المخزّن.
abraxaslabs.tech · github.com/abraxas · @abraxas_null · [email protected] · hyperswitch-payout-confirm-secret
Hyperswitch 2026.09.21.0 - Juspay
payouts_confirm يوثّق pk_ باستخدام check_value_present("client_secret"). موجود. غير مطابق. تأكيد المدفوعات لا يزال يربط: authenticate_client_secret يُرجع IR_09 عند عدم التطابق. جسم التأكيد هو PayoutCreateRequest كامل. update_payouts_and_payout_attempt يكتب amount قبل توجيه الموصل.
client_secret وهمي مع amount=99900 على تأكيد المدفوعات يخزّن 99900. تأكيد المدفوعات بنفس الوهمي لا يزال IR_09.
| ID | لا CVE بعد |
| CWE | CWE-863، CWE-345 |
| 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 تأكيد المدفوعات |
| المصادقة | pk_ قابل للنشر مع معرّف الدفعة؛ سر وهمي |
| الترخيص | GNU Affero GPL v3.0 |
| المختبر | 127.0.0.1 فقط |
يحتاج إلى المفتاح القابل للنشر (عام، الدفع) ومعرّف الدفعة (رابط الدفعة، لوحة تحكم التاجر، استجابة إنشاء العميل). POST /payouts/{id}/confirm بسر وهمي وamount جديد. الاسترجاع يُظهر المبلغ المرفوع حتى عندما يُرجع توجيه الموصل 400 IR_39 (لا يوجد موصل دفعات مؤهل). الكتابة حدثت بالفعل. التنفيذ على متجر لديه معالج دفعات هو الخطوة التالية.
الضيف بدون pk_ ليس هذه الثغرة. تأكيد sk_ السري من جانب التاجر. ليس shell. ليس دفعة موصل ناجحة على صورة المختبر هذه.
نفس المنتج مثل unsigned Worldpayxml webhook. ثغرة مختلفة.
قرأت check_value_present، ثم المدفوعات req_cs != pi_cs، ثم update_payouts_and_payout_attempt قبل payouts_core. مصادقة pk_ هي "checkout SDK." يُفترض أن يربط السر جلسة المتصفح بـهذه الدفعة. المدفوعات تفعل المساواة. المدفوعات تفعل الوجود. وهمي غير فارغ كافٍ. JSON لا يزال طلب إنشاء، لذا amount ليس no-op عند التأكيد.
أول عميل ينظر إلى هذا سيرسل تأكيدًا بدون client_secret ويحصل على MissingRequiredField. موجود. أرسل السر الحقيقي وستكون الدافع. أرسل وهميًا على تأكيد المدفوعات وستحصل على IR_09. هذا هو الضابط. أرسل الوهمي على تأكيد المدفوعات.
المنعطفات الخاطئة المسجلة بالفعل: التعامل مع HTTP 400 IR_39 على أنه "لم يحدث شيء" (استرجع الدفعة)؛ المبلغ يبقى 1000 لأن التأكيد حذف amount (unwrap_or يحتفظ بالقيمة القديمة)؛ الدفعة نهائية بالفعل (Success / Failed / Cancelled)؛ reverse shell. مسرح. الشاهد هو استرجاع amount=99900 بعد سر وهمي، مع تأكيد المدفوعات لا يزال IR_09.
ثم: أنشئ تاجرًا، مفتاحًا سريًا، pk_ قابلًا للنشر، موصل دفعات worldpayxml، وPOST /payouts/create مع confirm=false مبلغ 1000. RequiresConfirmation. ضابط السر الوهمي للمدفوعات. وهمي المدفوعات 99900. استرجع.
cd lab
./run.sh
استهدف فقط http://127.0.0.1:18083. لا تشارك مشروع compose مع مختبر webhook.
IOC amount_before=1000 status_before=requires_confirmation
IOC payments.confirm dummy-secret status=400 IR_09 client_secret mismatch
IOC payouts.confirm dummy-secret status=400 IR_39 no eligible connector
IOC retrieve amount=99900 status=requires_confirmation
SUCCESS Hyperswitch payout confirm unbound client_secret amount raise
قارن client_secret بـpayouts.client_secret بالطريقة التي تفعلها المدفوعات، ولا تطبّق PayoutCreateRequest.amount عند التأكيد حتى ينجح هذا الفحص. يجب أن يكون السر الوهمي IR_09 (أو 401)، ويجب أن يبقى مبلغ الاسترجاع 1000.
payouts.rs routes · authentication.rs · payouts/helpers.rs · core/payouts.rs · payments/helpers.rsGNU Affero GPL v3.0. انظر LICENSE. مختبر loopback فقط. لا ضمان.