T09 · Insecure Skill Coding Practices
- Location
SKILL.md:31- Finding
Unverified Remote Vault Address Used for Signed Mainnet Financial Transactions
- Content
View full analysis
Vulnerability Details
File Location:
SKILL.md, lines 31 and 115–160
Vulnerability Type: Untrusted remote address resolution and unsafe private-key handling
Risk Level: HighVulnerable Code
sh VAULT_ADDRESS=$(curl -s "https://api.terminal.markets/api/v1/vault?ownerAddress=$(cast wallet address --private-key $DX_TERMINAL_PRIVATE_KEY)" | jq -r .vaultAddress)The remotely returned address is subsequently used as the destination for signed Base mainnet transactions:
sh cast send "$VAULT_ADDRESS" "updateSettings((uint256,uint256,uint8,uint8,uint8,uint8,uint8))" "(5000,200,3,3,3,3,3)" --private-key "$DX_TERMINAL_PRIVATE_KEY" --rpc-url "https://mainnet.base.org"sh cast send "$VAULT_ADDRESS" "addStrategy(string,uint64,uint8)" "Rotate into strongest relative volume while keeping 20% idle ETH for opportunities." "$(( $(date +%s) + 86400 ))" "2" --private-key "$DX_TERMINAL_PRIVATE_KEY" --rpc-url "https://mainnet.base.org"sh cast send "$VAULT_ADDRESS" "disableStrategy(uint256)" "1" --private-key "$DX_TERMINAL_PRIVATE_KEY" --rpc-url "https://mainnet.base.org"sh cast send "$VAULT_ADDRESS" "depositETH()" --value 0.05ether --private-key "$DX_TERMINAL_PRIVATE_KEY" --rpc-url "https://mainnet.base.org"sh cast send "$VAULT_ADDRESS" "withdrawETH(uint256)" "50000000000000000" --private-key "$DX_TERMINAL_PRIVATE_KEY" --rpc-url "https://mainnet.base.org"Technical Analysis
The destination stored in
VAULT_ADDRESSis obtained directly fromapi.terminal.marketsand then trusted for every state-changing transaction. The documented workflow does not:- Validate that the response is a syntactically valid Ethereum address.
- Independently derive the expected vault address.
- Verify the address through an authenticated on-chain registry.
- Confirm that the returned contract is owned by or associated with the expected wallet.
- Check the deployed contract bytecode or implementation.
- Confirm the Base chain ID ...[truncated 2543 chars]
- Remediation
View remediation
Remediation Suggestions
-
Verify the vault through an authoritative on-chain source
- Resolve the vault using a documented registry or factory contract on Base.
- Confirm that the registry associates the vault with the address derived from the user’s wallet.
- If deterministic deployment is used, independently calculate the expected vault address.
-
Validate the remote API response
- Reject empty, null, malformed, zero, or non-checksummed addresses.
- Treat the API result as an untrusted hint rather than the source of authority.
- Fail closed when the API and on-chain registry disagree.
-
Verify the target contract
- Confirm the Base chain ID before signing.
- Require deployed bytecode at the destination.
- Compare the runtime bytecode or implementation against an approved contract version.
- Verify proxy implementations and upgrade administrators where proxy contracts are used.
-
Add transaction safety controls
- Simulate each transaction before broadcasting it.
- Display the chain, destination, decoded function, parameters, ETH value, gas estimate, and simulation result.
- Require explicit user confirmation for settings changes, strategy changes, deposits, and withdrawals.
- Apply configurable transaction-value and slippage limits.
-
Use a secure signer
- Do not pass raw private keys through command-line arguments.
- Use a hardware wallet, encrypted keystore, operating-system keychain, or isolated signing service.
- Ensure the signer presents transaction details for approval.
- Disable shell tracing and avoid recording commands containing secrets.
-
Improve failure handling
- Use strict shell behavior and make
curlfail on HTTP errors. - Validate JSON parsing before assigning
VAULT_ADDRESS. - Abort transaction execution whenever address verification, contract verification, or simulation fails.
- Use strict shell behavior and make
-
