T05 · Unauthorized Access and Privilege Escalation
Error
- Location
- src/server.ts:634
- Finding
- Unauthenticated Facilitator Endpoint Allows Unauthorized Gas Sponsorship<![CDATA[ ## Vulnerability Details **File Location**: `src/server.ts:634-645`, `src/facilitator.ts:218-259` **Vulnerability Type**: Missing authentication and server-side authorization **Risk Level**: High ### Vulnerable Code ```ts // src/server.ts:634-645 app.post('/facilitator/settle', async (req, res) => { try { const { paymentPayload, paymentRequirements } = req.body; const result = await facilitatorSettle(paymentPayload, paymentRequirements); res.json(result); } catch (err: any) { res.json({ success: false, error: err.message, }); } }); ``` ```ts // src/facilitator.ts:218-259 export async function settle( paymentPayload: PaymentPayload, paymentRequirements: PaymentRequirements ): Promise<SettleResponse> { try { const { walletClient, publicClient } = getSignerWallet(); const { authorization, signature } = paymentPayload.payload; const { v, r, s } = parseSignature(signature); console.log('[facilitator] Settling payment...'); console.log(` From: ${authorization.from}`); console.log(` To: ${authorization.to}`); console.log(` Value: ${authorization.value} (${Number(authorization.value) / 1e6} USDC)`); const hash = await walletClient.writeContract({ address: USDC_ADDRESS, abi: TRANSFER_WITH_AUTH_ABI, functionName: 'transferWithAuthorization', args: [ authorization.from as `0x${string}`, authorization.to as `0x${string}`, BigInt(authorization.value), BigInt(authorization.validAfter), BigInt(authorization.validBefore), authorization.nonce as `0x${string}`, v, r, s, ], }); console.log(`[facilitator] Tx submitted: ${hash}`); const receipt = await publicClient.waitForTransactionReceipt({ hash }); if (receipt.status === 'success') { console.log(`[facilitator] ✓ Payment settled: ${hash}`); return { success: true, txHash: hash }; } else { console. ...[truncated 2310 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Make the facilitator endpoints private and accessible only to the payment middleware or a dedicated trusted service. 2. Require authenticated, integrity-protected requests, such as mutually authenticated TLS or an HMAC signature with timestamp and nonce. 3. Invoke strict verification from inside `settle()` rather than relying on callers to invoke `/verify` first. 4. Ignore caller-provided merchant requirements where possible. Construct the expected network, asset, recipient, and amount from trusted server configuration. 5. Require the authorization recipient to equal the configured `PAYMENT_RECIPIENT`. 6. Require the token contract to equal the supported Base USDC contract and require the exact configured mint amount. 7. Bind each settlement to a short-lived, server-generated payment request identifier. 8. Store and reject previously settled nonces before broadcasting transactions. 9. Add per-client and global rate limits, gas-spending limits, monitoring, and emergency circuit breakers. 10. Use a dedicated facilitator wallet with only the minimum gas balance required for expected operations. ]]>
