T09 · Insecure Skill Coding Practices
- Location
SKILL.md:436- Finding
Unvalidated Remote Calldata Can Cause Unauthorized On-Chain Fund Movements
- Content
View full analysis
Vulnerability Details
File Location:
SKILL.md:436-449andSKILL.md:474-481
Vulnerability Type: Trusting externally generated transaction destinations and calldata without local semantic validation
Risk Level: HighVulnerable Code
python resp = requests.post("https://zhenghe-system.vercel.app/api/a2a", json={ "jsonrpc": "2.0", "id": 1, "method": "callSkill", "params": { "skill": "zhenghe.vault", "input": {"action": "deposit", "address": WALLET, "amount": suggested_deposit} } }) steps = resp.json()["result"]["steps"] # Step 4: Show operator exactly what will be signed, then sign show_transaction_preview(steps) # Display to/from/data/value # Step 1: approve USDC (operator already approved above) send_transaction(to=steps[0]["to"], data=steps[0]["data"]) wait_for_confirmation() # Step 2: deposit to vault send_transaction(to=steps[1]["to"], data=steps[1]["data"])The withdrawal flow has the same trust issue:
python resp = requests.post("https://zhenghe-system.vercel.app/api/a2a", json={ "jsonrpc": "2.0", "id": 1, "method": "callSkill", "params": { "skill": "zhenghe.vault", "input": {"action": "withdraw", "address": WALLET} } }) steps = resp.json()["result"]["steps"] show_transaction_preview(steps) # Display to/from/data/value # Single step: redeem LOVE → USDC send_transaction(to=steps[0]["to"], data=steps[0]["data"])Technical Analysis
The Skill delegates construction of financially sensitive Base-mainnet transactions to the external endpoint
https://zhenghe-system.vercel.app/api/a2a. The response directly controls each transaction'stoaddress and calldata.The example does not locally verify:
- That the transaction uses Base mainnet chain ID
8453. - That the destination is the documented USDC or LoveVault contract.
- That the function selector represents the expected `a ...[truncated 3190 chars]
- That the transaction uses Base mainnet chain ID
- Remediation
View remediation
Remediation Suggestions
- Generate transaction calldata locally using a pinned, reviewed ABI instead of relying on remotely supplied calldata.
- If the API remains in use, decode and validate every transaction before presenting it for signature:
- Require chain ID
8453. - Allowlist the documented USDC and LoveVault addresses.
- Require the expected function selector for the current operation.
- Verify the exact token amount against the operator-approved amount.
- Verify the approval spender is the documented LoveVault.
- Verify deposit, owner, and redemption receiver addresses match the operator's wallet.
- Require native value to be zero.
- Reject unexpected steps, unknown fields, malformed responses, and incorrect step ordering.
- Require chain ID
- Display human-readable decoded transaction semantics rather than raw calldata alone.
- Obtain a new explicit operator confirmation after the final decoded transactions are displayed and immediately before each signature. Earlier approval of an intended amount must not authorize subsequently returned arbitrary calldata.
- Independently simulate transactions against a trusted Base RPC endpoint and reject reverts, unexpected token movements, or unrecognized state changes.
- Pin expected contract addresses and ABI hashes in the Skill rather than accepting them from the server.
- Apply strict response schemas, request timeouts, response-size limits, and fail-closed error handling.
- Minimize external disclosure by performing read-only balance queries directly through a trusted Base RPC provider and constructing calldata locally.
- Clearly notify operators that the remote service receives their wallet address, requested amount, operation type, request timing, and network metadata.
