From 8b31bfe32cec92dbcfce77029af81ea28b8a97b1 Mon Sep 17 00:00:00 2001 From: Ishant5436 Date: Fri, 22 May 2026 21:08:03 +0530 Subject: [PATCH] security: report critical double-spend and subscription authority flaws --- security/VULNERABILITY_REPORT.md | 46 ++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 security/VULNERABILITY_REPORT.md diff --git a/security/VULNERABILITY_REPORT.md b/security/VULNERABILITY_REPORT.md new file mode 100644 index 0000000..86ca079 --- /dev/null +++ b/security/VULNERABILITY_REPORT.md @@ -0,0 +1,46 @@ +# [CRITICAL] Systematic Double-Spend and Unauthorized Subscription Hijacking in Zoneless + +## 1. Summary +The `zoneless` payment protocol contains multiple critical architectural and logic flaws that permit systematic fund theft and unauthorized billing. A race condition in the payout confirmation logic allows attackers to double-spend protocol liquidity by triggering ledger refunds for successfully broadcasted transactions. Additionally, an authorization bypass in the subscription program permits merchants to unilaterally resume paused user subscriptions, leading to unauthorized fund extraction. + +## 2. Technical Details + +### A. Critical: Double-Spend via Confirmation Race Condition +**Affected Assets:** `apps/api/src/modules/Payout.ts`, `apps/api/src/modules/chains/Solana.ts` +The backend handles transaction broadcasting and confirmation in a single block. If the `confirmTransaction` call times out or fails due to network congestion (even if the transaction was successfully broadcast), the API marks the payout as "failed": + +```typescript +// Solana.ts: BroadcastSignedTransaction() +try { + const signature = await this.connection.sendRawTransaction(...); + const confirmation = await this.connection.confirmTransaction(...); // <-- MIGHT TIMEOUT +} catch (error) { + return { status: 'failed', ... }; // <-- TRIGGERS REFUND +} +``` + +When a failure is returned, the `Payout` module immediately refunds the payout amount to the merchant's internal ledger balance (`MarkPayoutFailed`). If the transaction subsequently lands on-chain (which is common after a confirmation timeout), the recipient receives the funds on-chain AND has their ledger balance restored, permitting an immediate second payout of the same funds. + +### B. High: Unilateral Subscription Hijacking +**Affected Asset:** `apps/programs/src/lib.rs`, `ChangeSubscriptionStatus` +The subscription program allows either the `subscriber` or the `merchant` to modify the subscription status using a shared account constraint: +```rust +#[account( + constraint = (signer.key() == subscriber.key() || signer.key() == merchant.key()) + @ SubsError::Unauthorized, +)] +pub signer: Signer<'info>, +``` +This logic permits a merchant to unilaterally call `resume_subscription` on a position that was paused by the user. Once resumed, the merchant can immediately execute `charge_subscription` to drain the user's USDC without their consent, violating the fundamental "user-controlled" property of the protocol. + +## 3. Impact +Critical. Total loss of protocol solvency and systematic theft of user funds. Attackers can leverage the confirmation race to drain the entire payout pool, while malicious merchants can hijack paused subscriptions to extract unauthorized recurring payments. + +## 4. Recommended Mitigation +1. **Asynchronous Confirmation:** Separate broadcasting from confirmation. Do not trigger ledger refunds until a transaction is proven to have expired on-chain (exceeding `lastValidBlockHeight`). +2. **Strict Resume Authority:** Modify the `resume_subscription` instruction to strictly require the `subscriber` as the signer, or track the `paused_by` state to ensure only the pausing entity can resume. + +--- +**Independent Security Research** +- Researcher: Ishant5436 +- Institutional ID: ishant.p@somaiya.edu