Skip to content
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 46 additions & 0 deletions security/VULNERABILITY_REPORT.md
Original file line number Diff line number Diff line change
@@ -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