T05 · Unauthorized Access and Privilege Escalation
Error
- Location
- src/index.ts:136
- Finding
- Public Wallet Address Is Used as the Sole Client-Side Payment Identity<![CDATA[ ## Vulnerability Details **File Location**: `src/index.ts:16-17, 136-143` **Vulnerability Type**: Missing proof of wallet ownership for chargeable requests **Risk Level**: High ### Vulnerable Code ```ts const SOLANAPROX_URL = process.env.SOLANAPROX_URL || "https://solanaprox.com"; const WALLET_ADDRESS = process.env.SOLANA_WALLET || ""; ``` ```ts const res = await fetch(`${SOLANAPROX_URL}/v1/messages`, { method: "POST", headers: { "Content-Type": "application/json", "X-Wallet-Address": WALLET_ADDRESS, }, body: JSON.stringify(body), }); ``` The same pattern also appears in `agent-exammple.js:13-14, 23-34`: ```js const SOLANAPROX_URL = process.env.SOLANAPROX_URL || "https://solanaprox.com"; const WALLET = process.env.SOLANA_WALLET; async function askAI(prompt, model = "claude-sonnet-4-20250514", maxTokens = 512) { const res = await fetch(`${SOLANAPROX_URL}/v1/messages`, { method: "POST", headers: { "Content-Type": "application/json", "X-Wallet-Address": WALLET, }, body: JSON.stringify({ model, max_tokens: maxTokens, messages: [{ role: "user", content: prompt }], }), }); ``` ### Technical Analysis The client identifies the payer solely through the `X-Wallet-Address` header. A Solana wallet address is public information and is not an authentication secret. The request contains no wallet signature, challenge nonce, request-body hash, timestamp, session credential, or other cryptographic proof that the caller controls the wallet. The Skill states that paid inference costs are automatically deducted from the balance associated with this address. Consequently, the visible client protocol does not establish authorization to spend that balance. The audited repository does not include the remote service implementation. Therefore, backend exploitability depends on whether `solanaprox.com` applies an undocumented authorization mechanism. Nevertheless, the client itself neither supplies ...[truncated 1473 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Require cryptographic proof of wallet ownership before accepting chargeable requests. 2. Have the server issue a short-lived, single-use challenge containing: - A cryptographically random nonce - The intended service domain - The wallet address - An expiration timestamp - The network identifier 3. Require the wallet to sign a domain-separated authorization message. 4. Bind authorization to the exact request by including a canonical hash of the model, prompt, token limit, and maximum authorized charge. 5. Verify the signature server-side against the claimed wallet address. 6. Store and invalidate used nonces to prevent replay attacks. 7. Issue short-lived authenticated sessions only after successful signature verification. 8. Apply server-side per-request, hourly, and daily spending limits. 9. Allow users to revoke sessions and configure maximum charges. 10. Require explicit user confirmation when a request exceeds a configurable cost threshold. 11. Document the authentication and billing protocol instead of describing a public wallet address as a credential. 12. Add integration tests proving that requests with a copied wallet address but no valid signature are rejected. ]]>
