
Hyperswitch WorldpayxmlのWebhookにおける未認証の脆弱性(CWE-345/306)のPoC。PSPキャプチャなしで支払いを成功扱いにするSETTLED XMLを偽造する。
abraxaslabs.tech · github.com/abraxas · @abraxas_null · [email protected] · hyperswitch-unsigned-webhook
Hyperswitch 2026.09.21.0 - Juspay
Worldpayxml::verify_webhook_source は、検証は mTLS 経由で行われるというコメントとともに Ok(true) を返す。このルートに mTLS はない。この関数はボディとヘッダーを無視する。incoming.rs はその後 source_verified を信頼し、PSync なしで HandleResponse を取る。LastEvent::Settled は PaymentIntentSuccess にマップされる。デフォルトの IncomingWebhook アルゴリズムは NoAlgorithm である。BitPay と Shift4 はそれを継承している。Worldpayxml はそこまでたどり着きもしない。ショートサーキットする。
未認証のダミー SETTLED XML が支払いを succeeded にする。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 受信 Webhook |
| 認証 | 未認証。merchant_id と orderCode が必要 |
| ライセンス | GNU Affero GPL v3.0 |
| ラボ | 127.0.0.1 のみ |
merchant_id(すべての Hyperswitch ダッシュボード URL に含まれる)と、その試行の connector_transaction_id と等しい orderCode が必要である。署名なしの SETTLED を /webhooks/{merchant_id}/worldpayxml に POST する。もし source_verified が false であれば、incoming は Trigger してコネクタを PSync するだろう。Worldpayxml はそのパスを決してたどらない。Intent は Charged になる。ラボでは status=succeeded。ダミー XML。PSP なし。
支払い ID を推測することはバグではない。すでに見た ID に対して SETTLED を偽造することがバグである。シェルではない。実際の Worldpay 決済ではない。
同じ製品、別のバグ: payout confirm unbound client_secret。
私は Ok(true) を読み、次に HandleResponse 対 Trigger を読み、次に Settled -> PaymentIntentSuccess を読んだ。コメントには mTLS と書かれている。ラボの POST にはクライアント証明書がない。
これに最初に目を付けるクライアントは、requires_confirmation に対して Webhook を POST するだろう。Incoming は confirmation を抜けていない支払いを Charged にはしない。試行にスタンプを押すか、requires_capture / processing になるまで待つ。ラボはスタンプを押す。そのスタンプはラボのフィクスチャである。実際の試行にはすでにコネクタ ID がある。Webhook にはない。
すでに記録済みの誤った道: 実際に検証するコネクタに対する署名付き HMAC(このパスは検証しない)、Webhook の HTTP 200 をオラクルとして扱うこと(status=succeeded になるまで GET /payments/{id} をポーリングする)、connector_transaction_id ではない orderCode、リバースシェル。茶番である。
そして: マーチャント、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 の notify を検証する(署名 / ルートが実際に終端する mTLS)。no-op から Ok(true) を返したコネクタで HandleResponse してはならない。署名なしの SETTLED が intent を succeeded に動かしてはならない。
worldpayxml.rs · transformers.rs · incoming.rs · webhooks.rs · crypto.rsGNU Affero GPL v3.0。LICENSE を参照。ループバックラボのみ。無保証。