code-423n4 / 2024-07-karak-findings

0 stars 0 forks source link

Race Condition between Withdrawals and Slashing #63

Closed howlbot-integration[bot] closed 2 months ago

howlbot-integration[bot] commented 2 months ago

Lines of code

https://github.com/code-423n4/2024-07-karak/blob/main/src/Vault.sol#L125 https://github.com/code-423n4/2024-07-karak/blob/main/src/Vault.sol#L157 https://github.com/code-423n4/2024-07-karak/blob/main/src/entities/SlasherLib.sol#L129

Vulnerability details

Impact

An operator can create a Race Condition between his withdrawal request and the DSS's slash request, allowing him to avoid slashing after a slashable offense. This is possible due to the slash delay, SLASHING_VETO_WINDOW, imposed on the DSS. The comment in the vault contract states, "To prevent someone from sitting on queuedWithdrawals to front-run a slashing, DSS shouldn't consider stakes queued in withdrawals." Unfortunately, an operator can, after joining a DSS, create a withdrawal request via startRedeem(). By doing this, the DSS has more than likely counted all of the operator's stake since the queuedWithdrawals is created after joining the DSS.

The operator's withdrawal request takes 9 days to process while the DSS slash request takes 2 days.

 uint256 public constant SLASHING_WINDOW = 7days;

...snip...

uint256 public constant SLASHING_VETO_WINDOW = 2 days;

...snip...

uint256 public constant MIN_WITHDRAWAL_DELAY = SLASHING_WINDOW + SLASHING_VETO_WINDOW;

On its face, this should be fine but if an operator commits the slashable offense with less than 2 days left for his withdrawal request to finish processing the DSS will be unable to slash him in time since the DSS has to wait at least two days before slashing.

function finalizeSlashing(CoreLib.Storage storage self, QueuedSlashing memory queuedSlashing) external {

        bytes32 slashRoot = calculateRoot(queuedSlashing);

        if (!self.slashingRequests[slashRoot]) revert InvalidSlashingParams();

        if (queuedSlashing.timestamp + Constants.SLASHING_VETO_WINDOW > block.timestamp) {

            revert MinSlashingDelayNotPassed();

        }

...snip...

As a result, an operator can create a Race Condition between his withdrawal request and the DSS's slash request, allowing him to avoid slashing after a slashable offense.

Proof of Concept

Operator after registering with DSS creates a withdrawal request via startRedeem().

Operator waits for 9 days.

Operator performs a slashable offense.

DSS detects slashable offense committed by the operator and calls. requestSlashing().

Operator calls finishRedeem() to withdraw his assets

DSS was unable to successfully slash the Operator before his withdrawal request was executed because the DSS had to wait SLASHING_VETO_WINDOW or 2 days.

Recommended Mitigation Steps

When a DSS submits a Slash Request the operator's Withdrawal Request should be cancelled or rescheduled.

Assessed type

Timing

c4-judge commented 2 months ago

MiloTruck marked the issue as unsatisfactory: Invalid