# Project funds can be drained by reusing signatures, in some cases

Signature reuse in changeOrder() and setComplete() plus missing onlyActive modifier on Tasks.unApprove() lets a subcontractor replay completion to drain project funds.

- Payout: Contest reward
- Severity: high
- Program: Rigor Protocol
- Credited: 0xA5DF
- Disclosed: 2022-09-12
- Write-up quality: 8/10
- Original: https://github.com/code-423n4/2022-08-rigor-findings/issues/95
- Library page: https://bugbountylibrary.com/r/c4%3A2022-08-rigor%3AH-04
- Tags: domain:web3 impact:funds-loss kind:contest platform:code4rena sev:high tech:evm tech:solidity technique:chained technique:source-review vuln:access-control vuln:business-logic vuln:signature-replay

**Target:** Solidity Project.sol / Tasks.sol escrow contracts
**Root cause:** changeOrder() and setComplete() verify signatures without a nonce, so previously signed payloads remain valid after state changes; Tasks.unApprove() also lacks the onlyActive() modifier, allowing tasks to be unapproved out of band.
**Steps:**
1. Legitimate users sign and run setComplete() once for a task
2. Replay the old changeOrder() signature to change the subcontractor or set price above available funds (unapproving the task)
3. Replay changeOrder() again to restore original price/subcontractor
4. Call allocateFunds() to mark the task funded again
5. Current SC calls acceptInvite() to reactivate the task
6. Replay setComplete() with the old signatures (swap SC signature if SC changed) to release funds
7. Repeat until project funds are exhausted
**How found:** Code review of signature-verification paths in Project.sol and the Tasks library, noticing no nonce/consumed-signature tracking and a missing onlyActive() modifier on unApprove(), then reasoning about state transitions (SC change, price change) that leave old signatures valid.
**Exploitation:** A current or previous subcontractor replays captured signatures across changeOrder()/setComplete() cycles, re-funding and re-completing the same task repeatedly to extract the project's balance; external users can also grief by replaying changeOrder() to alter cost or unapprove tasks.
**Escalation:** Chaining signature replay across two functions plus the missing modifier turns a single legitimate completion into unlimited fund withdrawals; colluding successive subcontractors widen the set of replayable signatures.
**Insight:** When a signed payload authorizes a state transition, check whether the same signature stays valid after the state is mutated back to a prior value; look for missing nonces and for library functions lacking the modifiers their callers assume.
