
Hyperswitch Worldpayxml webhook में unauthenticated flaw (CWE-345/306) के लिए proof-of-concept, जो SETTLED XML को forge करके PSP capture के बिना payments को succeeded mark करता है।
abraxaslabs.tech · github.com/abraxas · @abraxas_null · [email protected] · hyperswitch-unsigned-webhook
राइट-अप: https://abraxaslabs.tech/research/hyperswitch-unsigned-webhook
Hyperswitch 2026.09.21.0 - Juspay
Worldpayxml::verify_webhook_source Ok(true) लौटाता है, साथ में एक टिप्पणी कि सत्यापन mTLS के माध्यम से किया जाता है। इस रूट पर कोई mTLS नहीं है। यह फ़ंक्शन बॉडी और हेडर को अनदेखा करता है। इसके बाद incoming.rs source_verified पर भरोसा करता है और बिना PSync के HandleResponse ले लेता है। LastEvent::Settled PaymentIntentSuccess पर मैप होता है। डिफ़ॉल्ट IncomingWebhook एल्गोरिदम NoAlgorithm है। BitPay और Shift4 इसे विरासत में लेते हैं। Worldpayxml तो इतनी दूर तक भी नहीं पहुँचता। यह शॉर्ट-सर्किट कर देता है।
अप्रमाणित डमी SETTLED XML भुगतान को सफल के रूप में चिह्नित कर देता है। कोई Worldpay कैप्चर नहीं। कोई क्लाइंट सर्ट नहीं।
| 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 तक Worldpayxml इनबाउंड वेबहुक्स |
| प्रमाणीकरण | अप्रमाणित; merchant_id और orderCode आवश्यक |
| लाइसेंस | GNU Affero GPL v3.0 |
| लैब | केवल 127.0.0.1 |
merchant_id (हर Hyperswitch डैशबोर्ड URL में मौजूद) और orderCode जो attempt के connector_transaction_id के बराबर हो, आवश्यक है। /webhooks/{merchant_id}/worldpayxml पर अहस्ताक्षरित SETTLED POST करें। यदि source_verified false होता, तो incoming Trigger करता और कनेक्टर को PSync करता। Worldpayxml कभी उस पथ पर नहीं जाता। Intent Charged हो जाता है। लैब: status=succeeded। डमी XML। कोई PSP नहीं।
पेमेंट आईडी का अनुमान लगाना बग नहीं है। जिस आईडी को आप पहले ही देख चुके हैं, उसके लिए SETTLED को फ़ोर्ज करना बग है। यह शेल नहीं है। यह लाइव Worldpay सेटलमेंट नहीं है।
वही उत्पाद, अलग बग: payout confirm unbound client_secret।
मैंने Ok(true) पढ़ा, फिर HandleResponse बनाम Trigger, फिर Settled -> PaymentIntentSuccess। टिप्पणी mTLS कहती है। लैब POST में कोई क्लाइंट सर्ट नहीं है।
जो भी पहला क्लाइंट इसे देखेगा, वह requires_confirmation के विरुद्ध वेबहुक POST करेगा। Incoming उस भुगतान को Charged नहीं करेगा जो अभी confirmation से बाहर नहीं निकला है। attempt पर स्टैम्प लगाएँ, या requires_capture / processing तक प्रतीक्षा करें। लैब स्टैम्प करता है। वह स्टैम्प एक लैब फ़िक्स्चर है। एक लाइव attempt में पहले से ही कनेक्टर आईडी होती है। वेबहुक में नहीं होती।
पहले से दर्ज गलत रास्ते: ऐसे कनेक्टर पर साइन्ड HMAC जो वास्तव में सत्यापित करता है (यह पथ नहीं करता); वेबहुक HTTP 200 को ओरेकल मानना (GET /payments/{id} को तब तक पोल करें जब तक status=succeeded न हो जाए); orderCode जो connector_transaction_id नहीं है; एक रिवर्स शेल। नाटक।
फिर: merchant, API key, worldpayxml MCA, confirm=false के साथ भुगतान, connector_transaction_id स्टैम्प करें, अहस्ताक्षरित SETTLED, retrieve।
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। केवल लूपबैक लैब। कोई वारंटी नहीं।