T09 · Insecure Skill Coding Practices
Error
- Location
- scripts/order_sign.py:31
- Finding
- Blind Signing of Server-Controlled Hashes and Transactions<![CDATA[ ## Vulnerability Details **File Location**: `scripts/order_sign.py:31-49`, `scripts/order_sign.py:71-101`; related trust instructions in `SKILL.md:362-400` and `SKILL.md:499-508` **Vulnerability Type**: Signing unverified API-controlled payloads **Risk Level**: Critical ### Vulnerable Code ```python def sign_order_signatures(order_data: dict, private_key: str) -> list[str]: acct = Account.from_key(private_key) signed_list = [] sigs = order_data.get("signatures", []) if not sigs: raise ValueError("No signatures in order data. Is this a 'txs' mode order?") for item in sigs: api_hash = item.get("hash") if not api_hash: raise ValueError(f"Missing 'hash' field in signature item: {item}") hash_bytes = bytes.fromhex(api_hash[2:]) signed = acct.unsafe_sign_hash(hash_bytes) sig_hex = "0x" + signed.signature.hex() signed_list.append(sig_hex) return signed_list ``` The normal transaction mode similarly trusts every transaction field supplied by the API: ```python for tx_item in txs: tx_data = tx_item["data"] cid = chain_id or int(tx_item.get("chainId", 1)) tx_dict = { "to": tx_data["to"], "data": tx_data["calldata"], "gas": int(tx_data["gasLimit"]), "nonce": int(tx_data["nonce"]), "chainId": cid, } if tx_data.get("supportEIP1559") or tx_data.get("maxFeePerGas"): tx_dict["maxFeePerGas"] = int(tx_data["maxFeePerGas"]) tx_dict["maxPriorityFeePerGas"] = int(tx_data["maxPriorityFeePerGas"]) tx_dict["type"] = 2 else: tx_dict["gasPrice"] = int(tx_data["gasPrice"]) value = tx_data.get("value", "0") if isinstance(value, str) and "." in value: tx_dict["value"] = int(float(value) * 1e18) else: tx_dict["value"] = int(value) signed_tx = acct.sign_transaction(tx_dict) signed_list.append("0x" + signed_tx.raw_transaction.hex()) ``` ### Technical ...[truncated 2464 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Independently reconstruct every EIP-712 and EIP-7702 digest and compare it with the API-provided hash before signing. 2. Reject signatures when the structured message cannot be deterministically verified. 3. Decode calldata and validate: - Function selectors; - Token contracts; - Approval spenders and allowance amounts; - Swap routers; - Input and minimum output amounts; - Recipients; - Deadlines; - Native-token values. 4. Verify the sender, chain ID, nonce, verifying contract, and EIP-7702 delegation target against explicit allowlists. 5. Bind the signed payload to the exact order details previously shown to and approved by the user. 6. Require confirmation through a wallet-native interface that displays decoded transaction effects. 7. Treat unknown fields, unknown contracts, hash mismatches, and malformed hashes as fatal errors. 8. Add tests using malicious API responses to ensure altered recipients, values, calldata, chain IDs, delegation targets, and approval amounts are rejected. ]]>
