# The signatures are replayable

Krystal V3Automation validates user signatures but never binds them to operator-supplied ExecuteParams and lacks a nonce, so signed orders are replayable and decoupled from executed actions.

- Payout: Audit contest
- Severity: medium
- Program: Krystal DeFi
- Found by: Dup1337
- Disclosed: 2024-07-12
- Write-up quality: 7/10
- Original: https://code4rena.com/reports/2024-06-krystal-defi#m-02
- Library page: https://bugbountylibrary.com/r/c4%3A2024-06-krystal-defi%3AM-02
- Tags: domain:web3 impact:funds-loss kind:contest platform:code4rena sev:medium tech:evm tech:solidity technique:source-review vuln:access-control vuln:business-logic vuln:signature-replay

**Target:** V3Automation.execute / EIP712 order validation (Solidity contract)
**Root cause:** StructHash.Order has no nonce and _validateOrder only checks the signer equals the position owner; the operator-supplied ExecuteParams (action, liquidity, targetToken, amounts, deadline) are never compared against the signed order.
**Steps:**
1. Observe `_validateOrder` calls `recover(order, orderSignature)` and only requires `userAddress == actor` plus `!_cancelledOrder[keccak256(orderSignature)]`.
2. Note `StructHash.Order` contains no nonce, so the same signature digest is valid indefinitely.
3. Note `execute()` uses `params.action`, `params.liquidity`, `params.targetToken`, `params.amountIn*`, `params.deadline` etc., never `params.userOrder` fields.
4. Sign an empty `StructHash.Order` (all zeros) as in the integration test `_signOrder(emptyUserConfig, privateKey)`.
5. Call `v3automation.execute(params)` with arbitrary operator params and the empty-order signature; it passes validation and executes the action.
6. Replay the same signature repeatedly for further executions.
**How found:** Code review of the signature validation path: noticed the signed Order struct lacks a nonce and that execute() consumes operator params rather than the signed order, then confirmed via the project's own integration test that an all-zero signed order executes a real liquidity action.
**Exploitation:** Requires OPERATOR_ROLE. A compromised operator key, breached backend, or buggy order-sending service can submit arbitrary ExecuteParams (e.g. rebalance/swap with attacker-chosen targetToken, amounts, recipient) while presenting any previously signed user order, draining or misdirecting the user's approved position funds.
**Insight:** When a contract separates a signed 'intent' struct from an operator-supplied execution struct, diff the two field-by-field: any execution parameter not covered by the signed hash (and any missing nonce) is attacker/operator-controlled. Project test fixtures signing empty structs are a strong tell.
