Duplicate signatures are counted towards the threshold
contracts/MessageReceiver.sol:L84–L121
- Likelihood
- High
- Impact
- High
- Weakness
- CWE-347
- Description
_verifySignaturesloops over the submitted signatures, recovers each signer, checksvalidatorSet.isValidator(signer)and increments a counter. It never checks that signers are distinct.A relayer can submit the same valid signature
thresholdtimes. A single validator key — or a single signature leaked from a legitimate message to the same digest — therefore satisfies a threshold intended to require several independent parties.- Preconditions
- One validator key is compromised, or one validator signs a malicious digest
- Exploit scenario
- 1.An attacker obtains one validator key.
- 2.They sign a mint message for an arbitrary amount to their address.
- 3.The relayer submits the signature repeated
thresholdtimes. - 4.MessageReceiver counts
thresholdvalid signers and mints the wrapped tokens.
- Impact
Unbacked minting of wrapped tokens on every destination chain, or release of the entire vault on the return path, limited only by the rate limiter.
- Recommendation
Require signatures sorted by strictly increasing signer address and revert when
signer <= lastSigner. This rejects duplicates in O(n) without extra storage.- Fix
- contracts/MessageReceiver.soldiff
function _verifySignatures(bytes32 digest, bytes[] calldata sigs) internal view {+ address last; uint256 valid; for (uint256 i; i < sigs.length; ++i) { address signer = ECDSA.recover(digest, sigs[i]);+ if (signer <= last) revert UnsortedOrDuplicateSigner();+ last = signer; if (validatorSet.isValidator(signer)) ++valid; } if (valid < validatorSet.threshold()) revert InsufficientSignatures(); - Resolution
Strict ordering enforced; fuzz tests with duplicated and permuted signatures added. INV-02 holds.
