T09 · Insecure Skill Coding Practices
Error
- Location
- SKILL.md:73
- Finding
- Unverified Signing of a Server-Controlled Solana Transaction<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md`, lines 73–105 **Vulnerability Type**: Blind signing of an opaque, remotely supplied blockchain transaction **Risk Level**: High ### Vulnerable Code ```javascript const tx = VersionedTransaction.deserialize( Buffer.from(transaction, "base64") ); tx.sign([yourKeypair]); ``` ```javascript const signedTxBase64 = Buffer.from(tx.serialize()).toString("base64"); ``` ```bash curl -X POST https://clawsnft.com/api/execute \ -H "Content-Type: application/json" \ -d '{ "transaction": "<base64_encoded_signed_transaction>" }' ``` ### Technical Analysis The Skill instructs the agent to deserialize a base64-encoded Solana versioned transaction supplied by the remote `/api/mint` endpoint and sign it using the user's keypair. It does not require semantic validation of the transaction before signing. No checks are specified for: - Solana program IDs invoked by the transaction - Instruction types and instruction data - Recipient and destination accounts - SOL or SPL token transfer amounts - Signer and writable-account permissions - Mint, Candy Machine, collection, or backend signer addresses - Address lookup tables used by the versioned transaction - Additional instructions unrelated to the intended NFT mint - Transaction fees and the user's maximum expected cost - Authority delegation, approval, revocation, or account-closing operations Local signing protects the private key from direct disclosure, but it does not protect assets from malicious transactions authorized by that key. A malicious or compromised backend can return a transaction containing unintended instructions alongside, or instead of, the expected NFT mint operation. Once signed, the transaction carries valid authorization from the wallet. Submitting the signed transaction back to the service compounds the issue because the backend controls both transaction construction and the suggested submission channel. The documentation prov ...[truncated 2063 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. **Decode and validate every transaction before signing** - Parse every compiled instruction, including instructions that use address lookup tables. - Reject transactions containing unknown, unsupported, or unparseable instructions. - Do not rely solely on a backend-provided description of transaction behavior. 2. **Use strict allowlists** - Allow only the expected Solana program IDs. - Pin the expected Candy Machine, collection, mint authority, backend signer, treasury, and recipient addresses. - Reject unexpected signer accounts, writable accounts, program invocations, and address lookup tables. 3. **Validate financial effects** - Calculate the maximum SOL debit, token movements, rent, mint price, and transaction fees. - Enforce a documented maximum cost. - Reject transfers or debits unrelated to the intended mint. 4. **Validate instruction semantics** - Require the exact expected mint instruction sequence. - Reject token approvals, delegate assignments, authority changes, account closures, arbitrary transfers, and unrelated instructions. - Verify that the returned `nftMint` matches the mint account encoded in the transaction. 5. **Present a human-readable summary** - Display programs, accounts, transfers, fees, mint identity, and all expected balance changes. - Require explicit user approval after presenting this information and before accessing the signing key. 6. **Limit wallet exposure** - Recommend a dedicated wallet funded only with the minimum amount needed for the verified mint and fees. - Avoid using a wallet that holds unrelated SOL, tokens, NFTs, or valuable authorities. 7. **Use an independent submission path** - After validation and signing, submit through a trusted Solana RPC provider rather than returning the signed transaction exclusively to the transaction-construction service. - Verify transaction confirmation and post-transaction balance changes i ...[truncated 227 chars]
