T09 · Insecure Skill Coding Practices
Warning
- Location
- SKILL.md:119
- Finding
- Replayable and Insufficiently Scoped Ethereum Defense Signature<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md`, lines 119-128 **Vulnerability Type**: Replayable cryptographic signature caused by missing domain separation and freshness controls **Risk Level**: Medium ### Vulnerable Code ```text signature (String): A standard Ethereum signature (hex string). Prerequisite: Before calling this API, you must join the case on the Blockchain to match the plaintiff's stake. Function: joinCase(string caseId) ABI Snippet: ["function joinCase(string calldata caseId) external"] Signature Requirements To generate the signature, the agent must sign a message with the exact format below: "Submit defense for case {case_id}: {defense_evidence}" ``` ### Technical Analysis The prescribed signed message binds only the case identifier and defense evidence to the signature. It does not bind the authorization to: - The intended service or API origin - The blockchain chain ID - The verifying court contract - The defendant's wallet address - A unique, server-issued nonce - An issuance timestamp or expiration time - A specific action or request identifier represented through typed structured data Consequently, the same valid Ethereum signature can remain usable indefinitely wherever the documented message format is accepted. An intermediary, compromised service, log reader, or other party that obtains the submitted signature could attempt to replay it against the original endpoint or another verifier implementing the same signing convention. The use of an unstructured personal-sign-style message also makes contextual verification less reliable than EIP-712 typed structured data. The signature does not reveal the private key and does not itself authorize a general blockchain transaction, but it may be reused as proof that the wallet approved the defense statement. ### Attack Path 1. A defendant follows the Skill and signs `Submit defense for case {case_id}: {defense_evidence}` with the defendant wallet. 2. The agent send ...[truncated 1236 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions Replace the unstructured signature with EIP-712 typed structured data containing, at minimum: - A fixed application name and version - The blockchain chain ID - The verifying court contract address - The exact action, such as `SubmitDefense` - The case ID - The defendant wallet address - A cryptographic hash of the defense evidence - A unique server-issued nonce - An issuance timestamp and short expiration timestamp The server should: 1. Generate a cryptographically random, single-use nonce for the wallet and case. 2. Validate the EIP-712 domain, chain ID, contract, action, case ID, address, evidence hash, nonce, and expiration. 3. Recover the signer and require an exact match with the registered defendant address. 4. Atomically mark the nonce as consumed before accepting the defense. 5. Reject consumed, unknown, expired, or context-mismatched signatures. 6. Enforce case-state rules to prevent duplicate or unauthorized defense submissions. 7. Avoid recording complete signatures or sensitive defense content in application and infrastructure logs. 8. Require explicit user confirmation before signing and display the service, chain, contract, case, and expiration in the signing prompt. ]]>
